| 1 |
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 |
|
|---|
| 5 |
""" |
|---|
| 6 |
Standardized versions of various cool and/or strange things that you can do |
|---|
| 7 |
with Python's reflection capabilities. |
|---|
| 8 |
""" |
|---|
| 9 |
|
|---|
| 10 |
import sys |
|---|
| 11 |
import os |
|---|
| 12 |
import types |
|---|
| 13 |
import pickle |
|---|
| 14 |
import traceback |
|---|
| 15 |
import weakref |
|---|
| 16 |
import re |
|---|
| 17 |
import warnings |
|---|
| 18 |
import inspect |
|---|
| 19 |
import new |
|---|
| 20 |
try: |
|---|
| 21 |
from collections import deque |
|---|
| 22 |
except ImportError: |
|---|
| 23 |
deque = list |
|---|
| 24 |
|
|---|
| 25 |
RegexType = type(re.compile("")) |
|---|
| 26 |
|
|---|
| 27 |
|
|---|
| 28 |
try: |
|---|
| 29 |
from cStringIO import StringIO |
|---|
| 30 |
except ImportError: |
|---|
| 31 |
from StringIO import StringIO |
|---|
| 32 |
|
|---|
| 33 |
from twisted.python.util import unsignedID |
|---|
| 34 |
|
|---|
| 35 |
|
|---|
| 36 |
|
|---|
| 37 |
class Settable: |
|---|
| 38 |
""" |
|---|
| 39 |
A mixin class for syntactic sugar. Lets you assign attributes by |
|---|
| 40 |
calling with keyword arguments; for example, C{x(a=b,c=d,y=z)} is the |
|---|
| 41 |
same as C{x.a=b;x.c=d;x.y=z}. The most useful place for this is |
|---|
| 42 |
where you don't want to name a variable, but you do want to set |
|---|
| 43 |
some attributes; for example, C{X()(y=z,a=b)}. |
|---|
| 44 |
""" |
|---|
| 45 |
def __init__(self, **kw): |
|---|
| 46 |
self(**kw) |
|---|
| 47 |
|
|---|
| 48 |
def __call__(self,**kw): |
|---|
| 49 |
for key,val in kw.items(): |
|---|
| 50 |
setattr(self,key,val) |
|---|
| 51 |
return self |
|---|
| 52 |
|
|---|
| 53 |
|
|---|
| 54 |
class AccessorType(type): |
|---|
| 55 |
"""Metaclass that generates properties automatically. |
|---|
| 56 |
|
|---|
| 57 |
This is for Python 2.2 and up. |
|---|
| 58 |
|
|---|
| 59 |
Using this metaclass for your class will give you explicit accessor |
|---|
| 60 |
methods; a method called set_foo, will automatically create a property |
|---|
| 61 |
'foo' that uses set_foo as a setter method. Same for get_foo and del_foo. |
|---|
| 62 |
|
|---|
| 63 |
Note that this will only work on methods that are present on class |
|---|
| 64 |
creation. If you add methods after the class is defined they will not |
|---|
| 65 |
automatically become properties. Likewise, class attributes will only |
|---|
| 66 |
be used if they are present upon class creation, and no getter function |
|---|
| 67 |
was set - if a getter is present, the class attribute will be ignored. |
|---|
| 68 |
|
|---|
| 69 |
This is a 2.2-only alternative to the Accessor mixin - just set in your |
|---|
| 70 |
class definition:: |
|---|
| 71 |
|
|---|
| 72 |
__metaclass__ = AccessorType |
|---|
| 73 |
|
|---|
| 74 |
""" |
|---|
| 75 |
|
|---|
| 76 |
def __init__(self, name, bases, d): |
|---|
| 77 |
type.__init__(self, name, bases, d) |
|---|
| 78 |
accessors = {} |
|---|
| 79 |
prefixs = ["get_", "set_", "del_"] |
|---|
| 80 |
for k in d.keys(): |
|---|
| 81 |
v = getattr(self, k) |
|---|
| 82 |
for i in range(3): |
|---|
| 83 |
if k.startswith(prefixs[i]): |
|---|
| 84 |
accessors.setdefault(k[4:], [None, None, None])[i] = v |
|---|
| 85 |
for name, (getter, setter, deler) in accessors.items(): |
|---|
| 86 |
|
|---|
| 87 |
|
|---|
| 88 |
if getter is None: |
|---|
| 89 |
if hasattr(self, name): |
|---|
| 90 |
value = getattr(self, name) |
|---|
| 91 |
def getter(this, value=value, name=name): |
|---|
| 92 |
if name in this.__dict__: |
|---|
| 93 |
return this.__dict__[name] |
|---|
| 94 |
else: |
|---|
| 95 |
return value |
|---|
| 96 |
else: |
|---|
| 97 |
def getter(this, name=name): |
|---|
| 98 |
if name in this.__dict__: |
|---|
| 99 |
return this.__dict__[name] |
|---|
| 100 |
else: |
|---|
| 101 |
raise AttributeError("no such attribute %r" % name) |
|---|
| 102 |
if setter is None: |
|---|
| 103 |
def setter(this, value, name=name): |
|---|
| 104 |
this.__dict__[name] = value |
|---|
| 105 |
if deler is None: |
|---|
| 106 |
def deler(this, name=name): |
|---|
| 107 |
del this.__dict__[name] |
|---|
| 108 |
setattr(self, name, property(getter, setter, deler, "")) |
|---|
| 109 |
|
|---|
| 110 |
|
|---|
| 111 |
class PropertyAccessor(object): |
|---|
| 112 |
"""A mixin class for Python 2.2 that uses AccessorType. |
|---|
| 113 |
|
|---|
| 114 |
This provides compatability with the pre-2.2 Accessor mixin, up |
|---|
| 115 |
to a point. |
|---|
| 116 |
|
|---|
| 117 |
Extending this class will give you explicit accessor methods; a |
|---|
| 118 |
method called set_foo, for example, is the same as an if statement |
|---|
| 119 |
in __setattr__ looking for 'foo'. Same for get_foo and del_foo. |
|---|
| 120 |
|
|---|
| 121 |
There are also reallyDel and reallySet methods, so you can |
|---|
| 122 |
override specifics in subclasses without clobbering __setattr__ |
|---|
| 123 |
and __getattr__, or using non-2.1 compatible code. |
|---|
| 124 |
|
|---|
| 125 |
There is are incompatibilities with Accessor - accessor |
|---|
| 126 |
methods added after class creation will *not* be detected. OTOH, |
|---|
| 127 |
this method is probably way faster. |
|---|
| 128 |
|
|---|
| 129 |
In addition, class attributes will only be used if no getter |
|---|
| 130 |
was defined, and instance attributes will not override getter methods |
|---|
| 131 |
whereas in original Accessor the class attribute or instance attribute |
|---|
| 132 |
would override the getter method. |
|---|
| 133 |
""" |
|---|
| 134 |
|
|---|
| 135 |
|
|---|
| 136 |
|
|---|
| 137 |
|
|---|
| 138 |
|
|---|
| 139 |
__metaclass__ = AccessorType |
|---|
| 140 |
|
|---|
| 141 |
def reallySet(self, k, v): |
|---|
| 142 |
self.__dict__[k] = v |
|---|
| 143 |
|
|---|
| 144 |
def reallyDel(self, k): |
|---|
| 145 |
del self.__dict__[k] |
|---|
| 146 |
|
|---|
| 147 |
|
|---|
| 148 |
class Accessor: |
|---|
| 149 |
""" |
|---|
| 150 |
Extending this class will give you explicit accessor methods; a |
|---|
| 151 |
method called C{set_foo}, for example, is the same as an if statement |
|---|
| 152 |
in L{__setattr__} looking for C{'foo'}. Same for C{get_foo} and |
|---|
| 153 |
C{del_foo}. There are also L{reallyDel} and L{reallySet} methods, |
|---|
| 154 |
so you can override specifics in subclasses without clobbering |
|---|
| 155 |
L{__setattr__} and L{__getattr__}. |
|---|
| 156 |
|
|---|
| 157 |
This implementation is for Python 2.1. |
|---|
| 158 |
""" |
|---|
| 159 |
|
|---|
| 160 |
def __setattr__(self, k,v): |
|---|
| 161 |
kstring='set_%s'%k |
|---|
| 162 |
if hasattr(self.__class__,kstring): |
|---|
| 163 |
return getattr(self,kstring)(v) |
|---|
| 164 |
else: |
|---|
| 165 |
self.reallySet(k,v) |
|---|
| 166 |
|
|---|
| 167 |
def __getattr__(self, k): |
|---|
| 168 |
kstring='get_%s'%k |
|---|
| 169 |
if hasattr(self.__class__,kstring): |
|---|
| 170 |
return getattr(self,kstring)() |
|---|
| 171 |
raise AttributeError("%s instance has no accessor for: %s" % (qual(self.__class__),k)) |
|---|
| 172 |
|
|---|
| 173 |
def __delattr__(self, k): |
|---|
| 174 |
kstring='del_%s'%k |
|---|
| 175 |
if hasattr(self.__class__,kstring): |
|---|
| 176 |
getattr(self,kstring)() |
|---|
| 177 |
return |
|---|
| 178 |
self.reallyDel(k) |
|---|
| 179 |
|
|---|
| 180 |
def reallySet(self, k,v): |
|---|
| 181 |
""" |
|---|
| 182 |
*actually* set self.k to v without incurring side-effects. |
|---|
| 183 |
This is a hook to be overridden by subclasses. |
|---|
| 184 |
""" |
|---|
| 185 |
if k == "__dict__": |
|---|
| 186 |
self.__dict__.clear() |
|---|
| 187 |
self.__dict__.update(v) |
|---|
| 188 |
else: |
|---|
| 189 |
self.__dict__[k]=v |
|---|
| 190 |
|
|---|
| 191 |
def reallyDel(self, k): |
|---|
| 192 |
""" |
|---|
| 193 |
*actually* del self.k without incurring side-effects. This is a |
|---|
| 194 |
hook to be overridden by subclasses. |
|---|
| 195 |
""" |
|---|
| 196 |
del self.__dict__[k] |
|---|
| 197 |
|
|---|
| 198 |
|
|---|
| 199 |
OriginalAccessor = Accessor |
|---|
| 200 |
|
|---|
| 201 |
|
|---|
| 202 |
class Summer(Accessor): |
|---|
| 203 |
""" |
|---|
| 204 |
Extend from this class to get the capability to maintain 'related |
|---|
| 205 |
sums'. Have a tuple in your class like the following:: |
|---|
| 206 |
|
|---|
| 207 |
sums=(('amount','credit','credit_total'), |
|---|
| 208 |
('amount','debit','debit_total')) |
|---|
| 209 |
|
|---|
| 210 |
and the 'credit_total' member of the 'credit' member of self will |
|---|
| 211 |
always be incremented when the 'amount' member of self is |
|---|
| 212 |
incremented, similiarly for the debit versions. |
|---|
| 213 |
""" |
|---|
| 214 |
|
|---|
| 215 |
def reallySet(self, k,v): |
|---|
| 216 |
"This method does the work." |
|---|
| 217 |
for sum in self.sums: |
|---|
| 218 |
attr=sum[0] |
|---|
| 219 |
obj=sum[1] |
|---|
| 220 |
objattr=sum[2] |
|---|
| 221 |
if k == attr: |
|---|
| 222 |
try: |
|---|
| 223 |
oldval=getattr(self, attr) |
|---|
| 224 |
except: |
|---|
| 225 |
oldval=0 |
|---|
| 226 |
diff=v-oldval |
|---|
| 227 |
if hasattr(self, obj): |
|---|
| 228 |
ob=getattr(self,obj) |
|---|
| 229 |
if ob is not None: |
|---|
| 230 |
try:oldobjval=getattr(ob, objattr) |
|---|
| 231 |
except:oldobjval=0.0 |
|---|
| 232 |
setattr(ob,objattr,oldobjval+diff) |
|---|
| 233 |
|
|---|
| 234 |
elif k == obj: |
|---|
| 235 |
if hasattr(self, attr): |
|---|
| 236 |
x=getattr(self,attr) |
|---|
| 237 |
setattr(self,attr,0) |
|---|
| 238 |
y=getattr(self,k) |
|---|
| 239 |
Accessor.reallySet(self,k,v) |
|---|
| 240 |
setattr(self,attr,x) |
|---|
| 241 |
Accessor.reallySet(self,y,v) |
|---|
| 242 |
Accessor.reallySet(self,k,v) |
|---|
| 243 |
|
|---|
| 244 |
|
|---|
| 245 |
class QueueMethod: |
|---|
| 246 |
""" I represent a method that doesn't exist yet.""" |
|---|
| 247 |
def __init__(self, name, calls): |
|---|
| 248 |
self.name = name |
|---|
| 249 |
self.calls = calls |
|---|
| 250 |
def __call__(self, *args): |
|---|
| 251 |
self.calls.append((self.name, args)) |
|---|
| 252 |
|
|---|
| 253 |
|
|---|
| 254 |
def funcinfo(function): |
|---|
| 255 |
""" |
|---|
| 256 |
this is more documentation for myself than useful code. |
|---|
| 257 |
""" |
|---|
| 258 |
warnings.warn( |
|---|
| 259 |
"[v2.5] Use inspect.getargspec instead of twisted.python.reflect.funcinfo", |
|---|
| 260 |
DeprecationWarning, |
|---|
| 261 |
stacklevel=2) |
|---|
| 262 |
code=function.func_code |
|---|
| 263 |
name=function.func_name |
|---|
| 264 |
argc=code.co_argcount |
|---|
| 265 |
argv=code.co_varnames[:argc] |
|---|
| 266 |
defaults=function.func_defaults |
|---|
| 267 |
|
|---|
| 268 |
out = [] |
|---|
| 269 |
|
|---|
| 270 |
out.append('The function %s accepts %s arguments' % (name ,argc)) |
|---|
| 271 |
if defaults: |
|---|
| 272 |
required=argc-len(defaults) |
|---|
| 273 |
out.append('It requires %s arguments' % required) |
|---|
| 274 |
out.append('The arguments required are: %s' % argv[:required]) |
|---|
| 275 |
out.append('additional arguments are:') |
|---|
| 276 |
for i in range(argc-required): |
|---|
| 277 |
j=i+required |
|---|
| 278 |
out.append('%s which has a default of' % (argv[j], defaults[i])) |
|---|
| 279 |
return out |
|---|
| 280 |
|
|---|
| 281 |
|
|---|
| 282 |
ISNT=0 |
|---|
| 283 |
WAS=1 |
|---|
| 284 |
IS=2 |
|---|
| 285 |
|
|---|
| 286 |
|
|---|
| 287 |
def fullFuncName(func): |
|---|
| 288 |
qualName = (str(pickle.whichmodule(func, func.__name__)) + '.' + func.__name__) |
|---|
| 289 |
if namedObject(qualName) is not func: |
|---|
| 290 |
raise Exception("Couldn't find %s as %s." % (func, qualName)) |
|---|
| 291 |
return qualName |
|---|
| 292 |
|
|---|
| 293 |
|
|---|
| 294 |
def qual(clazz): |
|---|
| 295 |
"""Return full import path of a class.""" |
|---|
| 296 |
return clazz.__module__ + '.' + clazz.__name__ |
|---|
| 297 |
|
|---|
| 298 |
|
|---|
| 299 |
def getcurrent(clazz): |
|---|
| 300 |
assert type(clazz) == types.ClassType, 'must be a class...' |
|---|
| 301 |
module = namedModule(clazz.__module__) |
|---|
| 302 |
currclass = getattr(module, clazz.__name__, None) |
|---|
| 303 |
if currclass is None: |
|---|
| 304 |
return clazz |
|---|
| 305 |
return currclass |
|---|
| 306 |
|
|---|
| 307 |
|
|---|
| 308 |
def getClass(obj): |
|---|
| 309 |
"""Return the class or type of object 'obj'. |
|---|
| 310 |
Returns sensible result for oldstyle and newstyle instances and types.""" |
|---|
| 311 |
if hasattr(obj, '__class__'): |
|---|
| 312 |
return obj.__class__ |
|---|
| 313 |
else: |
|---|
| 314 |
return type(obj) |
|---|
| 315 |
|
|---|
| 316 |
|
|---|
| 317 |
|
|---|
| 318 |
|
|---|
| 319 |
def isinst(inst,clazz): |
|---|
| 320 |
if type(inst) != types.InstanceType or type(clazz)!= types.ClassType: |
|---|
| 321 |
return isinstance(inst,clazz) |
|---|
| 322 |
cl = inst.__class__ |
|---|
| 323 |
cl2 = getcurrent(cl) |
|---|
| 324 |
clazz = getcurrent(clazz) |
|---|
| 325 |
if issubclass(cl2,clazz): |
|---|
| 326 |
if cl == cl2: |
|---|
| 327 |
return WAS |
|---|
| 328 |
else: |
|---|
| 329 |
inst.__class__ = cl2 |
|---|
| 330 |
return IS |
|---|
| 331 |
else: |
|---|
| 332 |
return ISNT |
|---|
| 333 |
|
|---|
| 334 |
|
|---|
| 335 |
def namedModule(name): |
|---|
| 336 |
"""Return a module given its name.""" |
|---|
| 337 |
topLevel = __import__(name) |
|---|
| 338 |
packages = name.split(".")[1:] |
|---|
| 339 |
m = topLevel |
|---|
| 340 |
for p in packages: |
|---|
| 341 |
m = getattr(m, p) |
|---|
| 342 |
return m |
|---|
| 343 |
|
|---|
| 344 |
|
|---|
| 345 |
def namedObject(name): |
|---|
| 346 |
"""Get a fully named module-global object. |
|---|
| 347 |
""" |
|---|
| 348 |
classSplit = name.split('.') |
|---|
| 349 |
module = namedModule('.'.join(classSplit[:-1])) |
|---|
| 350 |
return getattr(module, classSplit[-1]) |
|---|
| 351 |
|
|---|
| 352 |
namedClass = namedObject |
|---|
| 353 |
|
|---|
| 354 |
|
|---|
| 355 |
|
|---|
| 356 |
class _NoModuleFound(Exception): |
|---|
| 357 |
""" |
|---|
| 358 |
No module was found because none exists. |
|---|
| 359 |
""" |
|---|
| 360 |
|
|---|
| 361 |
|
|---|
| 362 |
class InvalidName(ValueError): |
|---|
| 363 |
""" |
|---|
| 364 |
The given name is not a dot-separated list of Python objects. |
|---|
| 365 |
""" |
|---|
| 366 |
|
|---|
| 367 |
|
|---|
| 368 |
class ModuleNotFound(InvalidName): |
|---|
| 369 |
""" |
|---|
| 370 |
The module associated with the given name doesn't exist and it can't be |
|---|
| 371 |
imported. |
|---|
| 372 |
""" |
|---|
| 373 |
|
|---|
| 374 |
|
|---|
| 375 |
class ObjectNotFound(InvalidName): |
|---|
| 376 |
""" |
|---|
| 377 |
The object associated with the given name doesn't exist and it can't be |
|---|
| 378 |
imported. |
|---|
| 379 |
""" |
|---|
| 380 |
|
|---|
| 381 |
|
|---|
| 382 |
def _importAndCheckStack(importName): |
|---|
| 383 |
""" |
|---|
| 384 |
Import the given name as a module, then walk the stack to determine whether |
|---|
| 385 |
the failure was the module not existing, or some code in the module (for |
|---|
| 386 |
example a dependent import) failing. This can be helpful to determine |
|---|
| 387 |
whether any actual application code was run. For example, to distiguish |
|---|
| 388 |
administrative error (entering the wrong module name), from programmer |
|---|
| 389 |
error (writing buggy code in a module that fails to import). |
|---|
| 390 |
|
|---|
| 391 |
@raise Exception: if something bad happens. This can be any type of |
|---|
| 392 |
exception, since nobody knows what loading some arbitrary code might do. |
|---|
| 393 |
|
|---|
| 394 |
@raise _NoModuleFound: if no module was found. |
|---|
| 395 |
""" |
|---|
| 396 |
try: |
|---|
| 397 |
try: |
|---|
| 398 |
return __import__(importName) |
|---|
| 399 |
except ImportError: |
|---|
| 400 |
excType, excValue, excTraceback = sys.exc_info() |
|---|
| 401 |
while excTraceback: |
|---|
| 402 |
execName = excTraceback.tb_frame.f_globals["__name__"] |
|---|
| 403 |
if (execName is None or |
|---|
| 404 |
execName == importName): |
|---|
| 405 |
raise excType, excValue, excTraceback |
|---|
| 406 |
excTraceback = excTraceback.tb_next |
|---|
| 407 |
raise _NoModuleFound() |
|---|
| 408 |
except: |
|---|
| 409 |
|
|---|
| 410 |
sys.modules.pop(importName, None) |
|---|
| 411 |
raise |
|---|
| 412 |
|
|---|
| 413 |
|
|---|
| 414 |
|
|---|
| 415 |
def namedAny(name): |
|---|
| 416 |
""" |
|---|
| 417 |
Retrieve a Python object by its fully qualified name from the global Python |
|---|
| 418 |
module namespace. The first part of the name, that describes a module, |
|---|
| 419 |
will be discovered and imported. Each subsequent part of the name is |
|---|
| 420 |
treated as the name of an attribute of the object specified by all of the |
|---|
| 421 |
name which came before it. For example, the fully-qualified name of this |
|---|
| 422 |
object is 'twisted.python.reflect.namedAny'. |
|---|
| 423 |
|
|---|
| 424 |
@type name: L{str} |
|---|
| 425 |
@param name: The name of the object to return. |
|---|
| 426 |
|
|---|
| 427 |
@raise InvalidName: If the name is an empty string, starts or ends with |
|---|
| 428 |
a '.', or is otherwise syntactically incorrect. |
|---|
| 429 |
|
|---|
| 430 |
@raise ModuleNotFound: If the name is syntactically correct but the |
|---|
| 431 |
module it specifies cannot be imported because it does not appear to |
|---|
| 432 |
exist. |
|---|
| 433 |
|
|---|
| 434 |
@raise ObjectNotFound: If the name is syntactically correct, includes at |
|---|
| 435 |
least one '.', but the module it specifies cannot be imported because |
|---|
| 436 |
it does not appear to exist. |
|---|
| 437 |
|
|---|
| 438 |
@raise AttributeError: If an attribute of an object along the way cannot be |
|---|
| 439 |
accessed, or a module along the way is not found. |
|---|
| 440 |
|
|---|
| 441 |
@return: the Python object identified by 'name'. |
|---|
| 442 |
""" |
|---|
| 443 |
if not name: |
|---|
| 444 |
raise InvalidName('Empty module name') |
|---|
| 445 |
|
|---|
| 446 |
names = name.split('.') |
|---|
| 447 |
|
|---|
| 448 |
|
|---|
| 449 |
|
|---|
| 450 |
|
|---|
| 451 |
if '' in names: |
|---|
| 452 |
raise InvalidName( |
|---|
| 453 |
"name must be a string giving a '.'-separated list of Python " |
|---|
| 454 |
"identifiers, not %r" % (name,)) |
|---|
| 455 |
|
|---|
| 456 |
topLevelPackage = None |
|---|
| 457 |
moduleNames = names[:] |
|---|
| 458 |
while not topLevelPackage: |
|---|
| 459 |
if moduleNames: |
|---|
| 460 |
trialname = '.'.join(moduleNames) |
|---|
| 461 |
try: |
|---|
| 462 |
topLevelPackage = _importAndCheckStack(trialname) |
|---|
| 463 |
except _NoModuleFound: |
|---|
| 464 |
moduleNames.pop() |
|---|
| 465 |
else: |
|---|
| 466 |
if len(names) == 1: |
|---|
| 467 |
raise ModuleNotFound("No module named %r" % (name,)) |
|---|
| 468 |
else: |
|---|
| 469 |
raise ObjectNotFound('%r does not name an object' % (name,)) |
|---|
| 470 |
|
|---|
| 471 |
obj = topLevelPackage |
|---|
| 472 |
for n in names[1:]: |
|---|
| 473 |
obj = getattr(obj, n) |
|---|
| 474 |
|
|---|
| 475 |
return obj |
|---|
| 476 |
|
|---|
| 477 |
|
|---|
| 478 |
|
|---|
| 479 |
def macro(name, filename, source, **identifiers): |
|---|
| 480 |
"""macro(name, source, **identifiers) |
|---|
| 481 |
|
|---|
| 482 |
This allows you to create macro-like behaviors in python. |
|---|
| 483 |
""" |
|---|
| 484 |
if not identifiers.has_key('name'): |
|---|
| 485 |
identifiers['name'] = name |
|---|
| 486 |
source = source % identifiers |
|---|
| 487 |
codeplace = "<%s (macro)>" % filename |
|---|
| 488 |
code = compile(source, codeplace, 'exec') |
|---|
| 489 |
|
|---|
| 490 |
|
|---|
| 491 |
sm = sys.modules |
|---|
| 492 |
tprm = "twisted.python.reflect.macros" |
|---|
| 493 |
if not sm.has_key(tprm): |
|---|
| 494 |
macros = new.module(tprm) |
|---|
| 495 |
sm[tprm] = macros |
|---|
| 496 |
macros.count = 0 |
|---|
| 497 |
macros = sm[tprm] |
|---|
| 498 |
macros.count += 1 |
|---|
| 499 |
macroname = 'macro_' + str(macros.count) |
|---|
| 500 |
tprmm = tprm + '.' + macroname |
|---|
| 501 |
mymod = new.module(tprmm) |
|---|
| 502 |
sys.modules[tprmm] = mymod |
|---|
| 503 |
setattr(macros, macroname, mymod) |
|---|
| 504 |
dict = mymod.__dict__ |
|---|
| 505 |
|
|---|
| 506 |
|
|---|
| 507 |
|
|---|
| 508 |
|
|---|
| 509 |
|
|---|
| 510 |
|
|---|
| 511 |
|
|---|
| 512 |
|
|---|
| 513 |
exec code in dict, dict |
|---|
| 514 |
return dict[name] |
|---|
| 515 |
|
|---|
| 516 |
|
|---|
| 517 |
|
|---|
| 518 |
|
|---|
| 519 |
def _determineClass(x): |
|---|
| 520 |
try: |
|---|
| 521 |
return x.__class__ |
|---|
| 522 |
except: |
|---|
| 523 |
return type(x) |
|---|
| 524 |
|
|---|
| 525 |
|
|---|
| 526 |
|
|---|
| 527 |
def _determineClassName(x): |
|---|
| 528 |
c = _determineClass(x) |
|---|
| 529 |
try: |
|---|
| 530 |
return c.__name__ |
|---|
| 531 |
except: |
|---|
| 532 |
try: |
|---|
| 533 |
return str(c) |
|---|
| 534 |
except: |
|---|
| 535 |
return '<BROKEN CLASS AT 0x%x>' % unsignedID(c) |
|---|
| 536 |
|
|---|
| 537 |
|
|---|
| 538 |
|
|---|
| 539 |
def _safeFormat(formatter, o): |
|---|
| 540 |
""" |
|---|
| 541 |
Helper function for L{safe_repr} and L{safe_str}. |
|---|
| 542 |
""" |
|---|
| 543 |
try: |
|---|
| 544 |
return formatter(o) |
|---|
| 545 |
except: |
|---|
| 546 |
io = StringIO() |
|---|
| 547 |
traceback.print_exc(file=io) |
|---|
| 548 |
className = _determineClassName(o) |
|---|
| 549 |
tbValue = io.getvalue() |
|---|
| 550 |
return "<%s instance at 0x%x with %s error:\n %s>" % ( |
|---|
| 551 |
className, unsignedID(o), formatter.__name__, tbValue) |
|---|
| 552 |
|
|---|
| 553 |
|
|---|
| 554 |
|
|---|
| 555 |
def safe_repr(o): |
|---|
| 556 |
""" |
|---|
| 557 |
safe_repr(anything) -> string |
|---|
| 558 |
|
|---|
| 559 |
Returns a string representation of an object, or a string containing a |
|---|
| 560 |
traceback, if that object's __repr__ raised an exception. |
|---|
| 561 |
""" |
|---|
| 562 |
return _safeFormat(repr, o) |
|---|
| 563 |
|
|---|
| 564 |
|
|---|
| 565 |
|
|---|
| 566 |
def safe_str(o): |
|---|
| 567 |
""" |
|---|
| 568 |
safe_str(anything) -> string |
|---|
| 569 |
|
|---|
| 570 |
Returns a string representation of an object, or a string containing a |
|---|
| 571 |
traceback, if that object's __str__ raised an exception. |
|---|
| 572 |
""" |
|---|
| 573 |
return _safeFormat(str, o) |
|---|
| 574 |
|
|---|
| 575 |
|
|---|
| 576 |
|
|---|
| 577 |
|
|---|
| 578 |
|
|---|
| 579 |
def allYourBase(classObj, baseClass=None): |
|---|
| 580 |
"""allYourBase(classObj, baseClass=None) -> list of all base |
|---|
| 581 |
classes that are subclasses of baseClass, unless it is None, |
|---|
| 582 |
in which case all bases will be added. |
|---|
| 583 |
""" |
|---|
| 584 |
l = [] |
|---|
| 585 |
accumulateBases(classObj, l, baseClass) |
|---|
| 586 |
return l |
|---|
| 587 |
|
|---|
| 588 |
|
|---|
| 589 |
def accumulateBases(classObj, l, baseClass=None): |
|---|
| 590 |
for base in classObj.__bases__: |
|---|
| 591 |
if baseClass is None or issubclass(base, baseClass): |
|---|
| 592 |
l.append(base) |
|---|
| 593 |
accumulateBases(base, l, baseClass) |
|---|
| 594 |
|
|---|
| 595 |
|
|---|
| 596 |
def prefixedMethodNames(classObj, prefix): |
|---|
| 597 |
"""A list of method names with a given prefix in a given class. |
|---|
| 598 |
""" |
|---|
| 599 |
dct = {} |
|---|
| 600 |
addMethodNamesToDict(classObj, dct, prefix) |
|---|
| 601 |
return dct.keys() |
|---|
| 602 |
|
|---|
| 603 |
|
|---|
| 604 |
def addMethodNamesToDict(classObj, dict, prefix, baseClass=None): |
|---|
| 605 |
""" |
|---|
| 606 |
addMethodNamesToDict(classObj, dict, prefix, baseClass=None) -> dict |
|---|
| 607 |
this goes through 'classObj' (and its bases) and puts method names |
|---|
| 608 |
starting with 'prefix' in 'dict' with a value of 1. if baseClass isn't |
|---|
| 609 |
None, methods will only be added if classObj is-a baseClass |
|---|
| 610 |
|
|---|
| 611 |
If the class in question has the methods 'prefix_methodname' and |
|---|
| 612 |
'prefix_methodname2', the resulting dict should look something like: |
|---|
| 613 |
{"methodname": 1, "methodname2": 1}. |
|---|
| 614 |
""" |
|---|
| 615 |
for base in classObj.__bases__: |
|---|
| 616 |
addMethodNamesToDict(base, dict, prefix, baseClass) |
|---|
| 617 |
|
|---|
| 618 |
if baseClass is None or baseClass in classObj.__bases__: |
|---|
| 619 |
for name, method in classObj.__dict__.items(): |
|---|
| 620 |
optName = name[len(prefix):] |
|---|
| 621 |
if ((type(method) is types.FunctionType) |
|---|
| 622 |
and (name[:len(prefix)] == prefix) |
|---|
| 623 |
and (len(optName))): |
|---|
| 624 |
dict[optName] = 1 |
|---|
| 625 |
|
|---|
| 626 |
|
|---|
| 627 |
def prefixedMethods(obj, prefix=''): |
|---|
| 628 |
"""A list of methods with a given prefix on a given instance. |
|---|
| 629 |
""" |
|---|
| 630 |
dct = {} |
|---|
| 631 |
accumulateMethods(obj, dct, prefix) |
|---|
| 632 |
return dct.values() |
|---|
| 633 |
|
|---|
| 634 |
|
|---|
| 635 |
def accumulateMethods(obj, dict, prefix='', curClass=None): |
|---|
| 636 |
"""accumulateMethods(instance, dict, prefix) |
|---|
| 637 |
I recurse through the bases of instance.__class__, and add methods |
|---|
| 638 |
beginning with 'prefix' to 'dict', in the form of |
|---|
| 639 |
{'methodname':*instance*method_object}. |
|---|
| 640 |
""" |
|---|
| 641 |
if not curClass: |
|---|
| 642 |
curClass = obj.__class__ |
|---|
| 643 |
for base in curClass.__bases__: |
|---|
| 644 |
accumulateMethods(obj, dict, prefix, base) |
|---|
| 645 |
|
|---|
| 646 |
for name, method in curClass.__dict__.items(): |
|---|
| 647 |
optName = name[len(prefix):] |
|---|
| 648 |
if ((type(method) is types.FunctionType) |
|---|
| 649 |
and (name[:len(prefix)] == prefix) |
|---|
| 650 |
and (len(optName))): |
|---|
| 651 |
dict[optName] = getattr(obj, name) |
|---|
| 652 |
|
|---|
| 653 |
|
|---|
| 654 |
def accumulateClassDict(classObj, attr, adict, baseClass=None): |
|---|
| 655 |
"""Accumulate all attributes of a given name in a class heirarchy into a single dictionary. |
|---|
| 656 |
|
|---|
| 657 |
Assuming all class attributes of this name are dictionaries. |
|---|
| 658 |
If any of the dictionaries being accumulated have the same key, the |
|---|
| 659 |
one highest in the class heirarchy wins. |
|---|
| 660 |
(XXX: If \"higest\" means \"closest to the starting class\".) |
|---|
| 661 |
|
|---|
| 662 |
Ex:: |
|---|
| 663 |
|
|---|
| 664 |
| class Soy: |
|---|
| 665 |
| properties = {\"taste\": \"bland\"} |
|---|
| 666 |
| |
|---|
| 667 |
| class Plant: |
|---|
| 668 |
| properties = {\"colour\": \"green\"} |
|---|
| 669 |
| |
|---|
| 670 |
| class Seaweed(Plant): |
|---|
| 671 |
| pass |
|---|
| 672 |
| |
|---|
| 673 |
| class Lunch(Soy, Seaweed): |
|---|
| 674 |
| properties = {\"vegan\": 1 } |
|---|
| 675 |
| |
|---|
| 676 |
| dct = {} |
|---|
| 677 |
| |
|---|
| 678 |
| accumulateClassDict(Lunch, \"properties\", dct) |
|---|
| 679 |
| |
|---|
| 680 |
| print dct |
|---|
| 681 |
|
|---|
| 682 |
{\"taste\": \"bland\", \"colour\": \"green\", \"vegan\": 1} |
|---|
| 683 |
""" |
|---|
| 684 |
for base in classObj.__bases__: |
|---|
| 685 |
accumulateClassDict(base, attr, adict) |
|---|
| 686 |
if baseClass is None or baseClass in classObj.__bases__: |
|---|
| 687 |
adict.update(classObj.__dict__.get(attr, {})) |
|---|
| 688 |
|
|---|
| 689 |
|
|---|
| 690 |
def accumulateClassList(classObj, attr, listObj, baseClass=None): |
|---|
| 691 |
"""Accumulate all attributes of a given name in a class heirarchy into a single list. |
|---|
| 692 |
|
|---|
| 693 |
Assuming all class attributes of this name are lists. |
|---|
| 694 |
""" |
|---|
| 695 |
for base in classObj.__bases__: |
|---|
| 696 |
accumulateClassList(base, attr, listObj) |
|---|
| 697 |
if baseClass is None or baseClass in classObj.__bases__: |
|---|
| 698 |
listObj.extend(classObj.__dict__.get(attr, [])) |
|---|
| 699 |
|
|---|
| 700 |
|
|---|
| 701 |
def isSame(a, b): |
|---|
| 702 |
return (a is b) |
|---|
| 703 |
|
|---|
| 704 |
|
|---|
| 705 |
def isLike(a, b): |
|---|
| 706 |
return (a == b) |
|---|
| 707 |
|
|---|
| 708 |
|
|---|
| 709 |
def modgrep(goal): |
|---|
| 710 |
return objgrep(sys.modules, goal, isLike, 'sys.modules') |
|---|
| 711 |
|
|---|
| 712 |
|
|---|
| 713 |
def isOfType(start, goal): |
|---|
| 714 |
return ((type(start) is goal) or |
|---|
| 715 |
(isinstance(start, types.InstanceType) and |
|---|
| 716 |
start.__class__ is goal)) |
|---|
| 717 |
|
|---|
| 718 |
|
|---|
| 719 |
def findInstances(start, t): |
|---|
| 720 |
return objgrep(start, t, isOfType) |
|---|
| 721 |
|
|---|
| 722 |
|
|---|
| 723 |
def objgrep(start, goal, eq=isLike, path='', paths=None, seen=None, showUnknowns=0, maxDepth=None): |
|---|
| 724 |
'''An insanely CPU-intensive process for finding stuff. |
|---|
| 725 |
''' |
|---|
| 726 |
if paths is None: |
|---|
| 727 |
paths = [] |
|---|
| 728 |
if seen is None: |
|---|
| 729 |
seen = {} |
|---|
| 730 |
if eq(start, goal): |
|---|
| 731 |
paths.append(path) |
|---|
| 732 |
if id(start) in seen: |
|---|
| 733 |
if seen[id(start)] is start: |
|---|
| 734 |
return |
|---|
| 735 |
if maxDepth is not None: |
|---|
| 736 |
if maxDepth == 0: |
|---|
| 737 |
return |
|---|
| 738 |
maxDepth -= 1 |
|---|
| 739 |
seen[id(start)] = start |
|---|
| 740 |
if isinstance(start, types.DictionaryType): |
|---|
| 741 |
r = [] |
|---|
| 742 |
for k, v in start.items(): |
|---|
| 743 |
objgrep(k, goal, eq, path+'{'+repr(v)+'}', paths, seen, showUnknowns, maxDepth) |
|---|
| 744 |
objgrep(v, goal, eq, path+'['+repr(k)+']', paths, seen, showUnknowns, maxDepth) |
|---|
| 745 |
elif isinstance(start, (list, tuple, deque)): |
|---|
| 746 |
for idx in xrange(len(start)): |
|---|
| 747 |
objgrep(start[idx], goal, eq, path+'['+str(idx)+']', paths, seen, showUnknowns, maxDepth) |
|---|
| 748 |
elif isinstance(start, types.MethodType): |
|---|
| 749 |
objgrep(start.im_self, goal, eq, path+'.im_self', paths, seen, showUnknowns, maxDepth) |
|---|
| 750 |
objgrep(start.im_func, goal, eq, path+'.im_func', paths, seen, showUnknowns, maxDepth) |
|---|
| 751 |
objgrep(start.im_class, goal, eq, path+'.im_class', paths, seen, showUnknowns, maxDepth) |
|---|
| 752 |
elif hasattr(start, '__dict__'): |
|---|
| 753 |
for k, v in start.__dict__.items(): |
|---|
| 754 |
objgrep(v, goal, eq, path+'.'+k, paths, seen, showUnknowns, maxDepth) |
|---|
| 755 |
if isinstance(start, types.InstanceType): |
|---|
| 756 |
objgrep(start.__class__, goal, eq, path+'.__class__', paths, seen, showUnknowns, maxDepth) |
|---|
| 757 |
elif isinstance(start, weakref.ReferenceType): |
|---|
| 758 |
objgrep(start(), goal, eq, path+'()', paths, seen, showUnknowns, maxDepth) |
|---|
| 759 |
elif (isinstance(start, types.StringTypes+ |
|---|
| 760 |
(types.IntType, types.FunctionType, |
|---|
| 761 |
types.BuiltinMethodType, RegexType, types.FloatType, |
|---|
| 762 |
types.NoneType, types.FileType)) or |
|---|
| 763 |
type(start).__name__ in ('wrapper_descriptor', 'method_descriptor', |
|---|
| 764 |
'member_descriptor', 'getset_descriptor')): |
|---|
| 765 |
pass |
|---|
| 766 |
elif showUnknowns: |
|---|
| 767 |
print 'unknown type', type(start), start |
|---|
| 768 |
return paths |
|---|
| 769 |
|
|---|
| 770 |
|
|---|
| 771 |
def filenameToModuleName(fn): |
|---|
| 772 |
""" |
|---|
| 773 |
Convert a name in the filesystem to the name of the Python module it is. |
|---|
| 774 |
|
|---|
| 775 |
This is agressive about getting a module name back from a file; it will |
|---|
| 776 |
always return a string. Agressive means 'sometimes wrong'; it won't look |
|---|
| 777 |
at the Python path or try to do any error checking: don't use this method |
|---|
| 778 |
unless you already know that the filename you're talking about is a Python |
|---|
| 779 |
module. |
|---|
| 780 |
""" |
|---|
| 781 |
fullName = os.path.abspath(fn) |
|---|
| 782 |
base = os.path.basename(fn) |
|---|
| 783 |
if not base: |
|---|
| 784 |
|
|---|
| 785 |
base = os.path.basename(fn[:-1]) |
|---|
| 786 |
modName = os.path.splitext(base)[0] |
|---|
| 787 |
while 1: |
|---|
| 788 |
fullName = os.path.dirname(fullName) |
|---|
| 789 |
if os.path.exists(os.path.join(fullName, "__init__.py")): |
|---|
| 790 |
modName = "%s.%s" % (os.path.basename(fullName), modName) |
|---|
| 791 |
else: |
|---|
| 792 |
break |
|---|
| 793 |
return modName |
|---|
| 794 |
|
|---|
| 795 |
|
|---|
| 796 |
|
|---|
| 797 |
def fullyQualifiedName(obj): |
|---|
| 798 |
""" |
|---|
| 799 |
Return the fully qualified name of a module, class, method or function. |
|---|
| 800 |
Classes and functions need to be module level ones to be correctly |
|---|
| 801 |
qualified. |
|---|
| 802 |
|
|---|
| 803 |
@rtype: C{str}. |
|---|
| 804 |
""" |
|---|
| 805 |
name = obj.__name__ |
|---|
| 806 |
if inspect.isclass(obj) or inspect.isfunction(obj): |
|---|
| 807 |
moduleName = obj.__module__ |
|---|
| 808 |
return "%s.%s" % (moduleName, name) |
|---|
| 809 |
elif inspect.ismethod(obj): |
|---|
| 810 |
className = fullyQualifiedName(obj.im_class) |
|---|
| 811 |
return "%s.%s" % (className, name) |
|---|
| 812 |
return name |
|---|
| 813 |
|
|---|
| 814 |
|
|---|
| 815 |
|
|---|
| 816 |
|
|---|
| 817 |
from twisted.python.deprecate import deprecated |
|---|
| 818 |
from twisted.python.versions import Version |
|---|
| 819 |
macro = deprecated(Version("Twisted", 8, 2, 0))(macro) |
|---|
| 820 |
|
|---|
| 821 |
|
|---|
| 822 |
|
|---|
| 823 |
__all__ = [ |
|---|
| 824 |
'InvalidName', 'ModuleNotFound', 'ObjectNotFound', |
|---|
| 825 |
|
|---|
| 826 |
'ISNT', 'WAS', 'IS', |
|---|
| 827 |
|
|---|
| 828 |
'Settable', 'AccessorType', 'PropertyAccessor', 'Accessor', 'Summer', |
|---|
| 829 |
'QueueMethod', 'OriginalAccessor', |
|---|
| 830 |
|
|---|
| 831 |
'funcinfo', 'fullFuncName', 'qual', 'getcurrent', 'getClass', 'isinst', |
|---|
| 832 |
'namedModule', 'namedObject', 'namedClass', 'namedAny', 'macro', |
|---|
| 833 |
'safe_repr', 'safe_str', 'allYourBase', 'accumulatedBases', |
|---|
| 834 |
'prefixedMethodNames', 'addMethodNamesToDict', 'prefixedMethods', |
|---|
| 835 |
'accumulateClassDict', 'accumulateClassList', 'isSame', 'isLike', |
|---|
| 836 |
'modgrep', 'isOfType', 'findInstances', 'objgrep', 'filenameToModuleName', |
|---|
| 837 |
'fullyQualifiedName'] |
|---|