1
# -*- test-case-name: twisted.test.test_reflect -*-
2
# Copyright (c) 2001-2010 Twisted Matrix Laboratories.
3
# See LICENSE for details.
6
Standardized versions of various cool and/or strange things that you can do
7
with Python's reflection capabilities.
20
from collections import deque
24
RegexType = type(re.compile(""))
28
from cStringIO import StringIO
30
from StringIO import StringIO
32
from twisted.python.util import unsignedID
33
from twisted.python.deprecate import deprecated
34
from twisted.python.deprecate import _fullyQualifiedName as fullyQualifiedName
35
from twisted.python.versions import Version
41
A mixin class for syntactic sugar. Lets you assign attributes by
42
calling with keyword arguments; for example, C{x(a=b,c=d,y=z)} is the
43
same as C{x.a=b;x.c=d;x.y=z}. The most useful place for this is
44
where you don't want to name a variable, but you do want to set
45
some attributes; for example, C{X()(y=z,a=b)}.
47
def __init__(self, **kw):
50
def __call__(self,**kw):
51
for key,val in kw.items():
56
class AccessorType(type):
57
"""Metaclass that generates properties automatically.
59
This is for Python 2.2 and up.
61
Using this metaclass for your class will give you explicit accessor
62
methods; a method called set_foo, will automatically create a property
63
'foo' that uses set_foo as a setter method. Same for get_foo and del_foo.
65
Note that this will only work on methods that are present on class
66
creation. If you add methods after the class is defined they will not
67
automatically become properties. Likewise, class attributes will only
68
be used if they are present upon class creation, and no getter function
69
was set - if a getter is present, the class attribute will be ignored.
71
This is a 2.2-only alternative to the Accessor mixin - just set in your
74
__metaclass__ = AccessorType
78
def __init__(self, name, bases, d):
79
type.__init__(self, name, bases, d)
81
prefixs = ["get_", "set_", "del_"]
85
if k.startswith(prefixs[i]):
86
accessors.setdefault(k[4:], [None, None, None])[i] = v
87
for name, (getter, setter, deler) in accessors.items():
88
# create default behaviours for the property - if we leave
89
# the getter as None we won't be able to getattr, etc..
91
if hasattr(self, name):
92
value = getattr(self, name)
93
def getter(this, value=value, name=name):
94
if name in this.__dict__:
95
return this.__dict__[name]
99
def getter(this, name=name):
100
if name in this.__dict__:
101
return this.__dict__[name]
103
raise AttributeError("no such attribute %r" % name)
105
def setter(this, value, name=name):
106
this.__dict__[name] = value
108
def deler(this, name=name):
109
del this.__dict__[name]
110
setattr(self, name, property(getter, setter, deler, ""))
113
class PropertyAccessor(object):
114
"""A mixin class for Python 2.2 that uses AccessorType.
116
This provides compatability with the pre-2.2 Accessor mixin, up
119
Extending this class will give you explicit accessor methods; a
120
method called set_foo, for example, is the same as an if statement
121
in __setattr__ looking for 'foo'. Same for get_foo and del_foo.
123
There are also reallyDel and reallySet methods, so you can
124
override specifics in subclasses without clobbering __setattr__
125
and __getattr__, or using non-2.1 compatible code.
127
There is are incompatibilities with Accessor - accessor
128
methods added after class creation will *not* be detected. OTOH,
129
this method is probably way faster.
131
In addition, class attributes will only be used if no getter
132
was defined, and instance attributes will not override getter methods
133
whereas in original Accessor the class attribute or instance attribute
134
would override the getter method.
137
# The behaviour of Accessor is wrong IMHO, and I've found bugs
141
__metaclass__ = AccessorType
143
def reallySet(self, k, v):
146
def reallyDel(self, k):
152
Extending this class will give you explicit accessor methods; a
153
method called C{set_foo}, for example, is the same as an if statement
154
in L{__setattr__} looking for C{'foo'}. Same for C{get_foo} and
155
C{del_foo}. There are also L{reallyDel} and L{reallySet} methods,
156
so you can override specifics in subclasses without clobbering
157
L{__setattr__} and L{__getattr__}.
159
This implementation is for Python 2.1.
162
def __setattr__(self, k,v):
164
if hasattr(self.__class__,kstring):
165
return getattr(self,kstring)(v)
169
def __getattr__(self, k):
171
if hasattr(self.__class__,kstring):
172
return getattr(self,kstring)()
173
raise AttributeError("%s instance has no accessor for: %s" % (qual(self.__class__),k))
175
def __delattr__(self, k):
177
if hasattr(self.__class__,kstring):
178
getattr(self,kstring)()
182
def reallySet(self, k,v):
184
*actually* set self.k to v without incurring side-effects.
185
This is a hook to be overridden by subclasses.
188
self.__dict__.clear()
189
self.__dict__.update(v)
193
def reallyDel(self, k):
195
*actually* del self.k without incurring side-effects. This is a
196
hook to be overridden by subclasses.
201
OriginalAccessor = Accessor
204
class Summer(Accessor):
206
Extend from this class to get the capability to maintain 'related
207
sums'. Have a tuple in your class like the following::
209
sums=(('amount','credit','credit_total'),
210
('amount','debit','debit_total'))
212
and the 'credit_total' member of the 'credit' member of self will
213
always be incremented when the 'amount' member of self is
214
incremented, similiarly for the debit versions.
217
def reallySet(self, k,v):
218
"This method does the work."
219
for sum in self.sums:
225
oldval=getattr(self, attr)
229
if hasattr(self, obj):
232
try:oldobjval=getattr(ob, objattr)
234
setattr(ob,objattr,oldobjval+diff)
237
if hasattr(self, attr):
241
Accessor.reallySet(self,k,v)
243
Accessor.reallySet(self,y,v)
244
Accessor.reallySet(self,k,v)
248
""" I represent a method that doesn't exist yet."""
249
def __init__(self, name, calls):
252
def __call__(self, *args):
253
self.calls.append((self.name, args))
256
def funcinfo(function):
258
this is more documentation for myself than useful code.
261
"[v2.5] Use inspect.getargspec instead of twisted.python.reflect.funcinfo",
264
code=function.func_code
265
name=function.func_name
266
argc=code.co_argcount
267
argv=code.co_varnames[:argc]
268
defaults=function.func_defaults
272
out.append('The function %s accepts %s arguments' % (name ,argc))
274
required=argc-len(defaults)
275
out.append('It requires %s arguments' % required)
276
out.append('The arguments required are: %s' % argv[:required])
277
out.append('additional arguments are:')
278
for i in range(argc-required):
280
out.append('%s which has a default of' % (argv[j], defaults[i]))
289
def fullFuncName(func):
290
qualName = (str(pickle.whichmodule(func, func.__name__)) + '.' + func.__name__)
291
if namedObject(qualName) is not func:
292
raise Exception("Couldn't find %s as %s." % (func, qualName))
297
"""Return full import path of a class."""
298
return clazz.__module__ + '.' + clazz.__name__
301
def getcurrent(clazz):
302
assert type(clazz) == types.ClassType, 'must be a class...'
303
module = namedModule(clazz.__module__)
304
currclass = getattr(module, clazz.__name__, None)
305
if currclass is None:
311
"""Return the class or type of object 'obj'.
312
Returns sensible result for oldstyle and newstyle instances and types."""
313
if hasattr(obj, '__class__'):
318
# class graph nonsense
320
# I should really have a better name for this...
321
def isinst(inst,clazz):
322
if type(inst) != types.InstanceType or type(clazz)!= types.ClassType:
323
return isinstance(inst,clazz)
326
clazz = getcurrent(clazz)
327
if issubclass(cl2,clazz):
337
def namedModule(name):
338
"""Return a module given its name."""
339
topLevel = __import__(name)
340
packages = name.split(".")[1:]
347
def namedObject(name):
348
"""Get a fully named module-global object.
350
classSplit = name.split('.')
351
module = namedModule('.'.join(classSplit[:-1]))
352
return getattr(module, classSplit[-1])
354
namedClass = namedObject # backwards compat
358
class _NoModuleFound(Exception):
360
No module was found because none exists.
364
class InvalidName(ValueError):
366
The given name is not a dot-separated list of Python objects.
370
class ModuleNotFound(InvalidName):
372
The module associated with the given name doesn't exist and it can't be
377
class ObjectNotFound(InvalidName):
379
The object associated with the given name doesn't exist and it can't be
384
def _importAndCheckStack(importName):
386
Import the given name as a module, then walk the stack to determine whether
387
the failure was the module not existing, or some code in the module (for
388
example a dependent import) failing. This can be helpful to determine
389
whether any actual application code was run. For example, to distiguish
390
administrative error (entering the wrong module name), from programmer
391
error (writing buggy code in a module that fails to import).
393
@raise Exception: if something bad happens. This can be any type of
394
exception, since nobody knows what loading some arbitrary code might do.
396
@raise _NoModuleFound: if no module was found.
400
return __import__(importName)
402
excType, excValue, excTraceback = sys.exc_info()
404
execName = excTraceback.tb_frame.f_globals["__name__"]
405
if (execName is None or # python 2.4+, post-cleanup
406
execName == importName): # python 2.3, no cleanup
407
raise excType, excValue, excTraceback
408
excTraceback = excTraceback.tb_next
409
raise _NoModuleFound()
411
# Necessary for cleaning up modules in 2.3.
412
sys.modules.pop(importName, None)
419
Retrieve a Python object by its fully qualified name from the global Python
420
module namespace. The first part of the name, that describes a module,
421
will be discovered and imported. Each subsequent part of the name is
422
treated as the name of an attribute of the object specified by all of the
423
name which came before it. For example, the fully-qualified name of this
424
object is 'twisted.python.reflect.namedAny'.
427
@param name: The name of the object to return.
429
@raise InvalidName: If the name is an empty string, starts or ends with
430
a '.', or is otherwise syntactically incorrect.
432
@raise ModuleNotFound: If the name is syntactically correct but the
433
module it specifies cannot be imported because it does not appear to
436
@raise ObjectNotFound: If the name is syntactically correct, includes at
437
least one '.', but the module it specifies cannot be imported because
438
it does not appear to exist.
440
@raise AttributeError: If an attribute of an object along the way cannot be
441
accessed, or a module along the way is not found.
443
@return: the Python object identified by 'name'.
446
raise InvalidName('Empty module name')
448
names = name.split('.')
450
# if the name starts or ends with a '.' or contains '..', the __import__
451
# will raise an 'Empty module name' error. This will provide a better error
455
"name must be a string giving a '.'-separated list of Python "
456
"identifiers, not %r" % (name,))
458
topLevelPackage = None
459
moduleNames = names[:]
460
while not topLevelPackage:
462
trialname = '.'.join(moduleNames)
464
topLevelPackage = _importAndCheckStack(trialname)
465
except _NoModuleFound:
469
raise ModuleNotFound("No module named %r" % (name,))
471
raise ObjectNotFound('%r does not name an object' % (name,))
473
obj = topLevelPackage
475
obj = getattr(obj, n)
481
def macro(name, filename, source, **identifiers):
482
"""macro(name, source, **identifiers)
484
This allows you to create macro-like behaviors in python.
486
if not identifiers.has_key('name'):
487
identifiers['name'] = name
488
source = source % identifiers
489
codeplace = "<%s (macro)>" % filename
490
code = compile(source, codeplace, 'exec')
494
tprm = "twisted.python.reflect.macros"
495
if not sm.has_key(tprm):
496
macros = new.module(tprm)
501
macroname = 'macro_' + str(macros.count)
502
tprmm = tprm + '.' + macroname
503
mymod = new.module(tprmm)
504
sys.modules[tprmm] = mymod
505
setattr(macros, macroname, mymod)
506
dict = mymod.__dict__
508
# Before we go on, I guess I should explain why I just did that. Basically
509
# it's a gross hack to get epydoc to work right, but the general idea is
510
# that it will be a useful aid in debugging in _any_ app which expects
511
# sys.modules to have the same globals as some function. For example, it
512
# would be useful if you were foolishly trying to pickle a wrapped function
513
# directly from a class that had been hooked.
515
exec code in dict, dict
517
macro = deprecated(Version("Twisted", 8, 2, 0))(macro)
521
def _determineClass(x):
529
def _determineClassName(x):
530
c = _determineClass(x)
537
return '<BROKEN CLASS AT 0x%x>' % unsignedID(c)
541
def _safeFormat(formatter, o):
543
Helper function for L{safe_repr} and L{safe_str}.
549
traceback.print_exc(file=io)
550
className = _determineClassName(o)
551
tbValue = io.getvalue()
552
return "<%s instance at 0x%x with %s error:\n %s>" % (
553
className, unsignedID(o), formatter.__name__, tbValue)
559
safe_repr(anything) -> string
561
Returns a string representation of an object, or a string containing a
562
traceback, if that object's __repr__ raised an exception.
564
return _safeFormat(repr, o)
570
safe_str(anything) -> string
572
Returns a string representation of an object, or a string containing a
573
traceback, if that object's __str__ raised an exception.
575
return _safeFormat(str, o)
579
##the following were factored out of usage
581
def allYourBase(classObj, baseClass=None):
582
"""allYourBase(classObj, baseClass=None) -> list of all base
583
classes that are subclasses of baseClass, unless it is None,
584
in which case all bases will be added.
587
accumulateBases(classObj, l, baseClass)
591
def accumulateBases(classObj, l, baseClass=None):
592
for base in classObj.__bases__:
593
if baseClass is None or issubclass(base, baseClass):
595
accumulateBases(base, l, baseClass)
598
def prefixedMethodNames(classObj, prefix):
599
"""A list of method names with a given prefix in a given class.
602
addMethodNamesToDict(classObj, dct, prefix)
606
def addMethodNamesToDict(classObj, dict, prefix, baseClass=None):
608
addMethodNamesToDict(classObj, dict, prefix, baseClass=None) -> dict
609
this goes through 'classObj' (and its bases) and puts method names
610
starting with 'prefix' in 'dict' with a value of 1. if baseClass isn't
611
None, methods will only be added if classObj is-a baseClass
613
If the class in question has the methods 'prefix_methodname' and
614
'prefix_methodname2', the resulting dict should look something like:
615
{"methodname": 1, "methodname2": 1}.
617
for base in classObj.__bases__:
618
addMethodNamesToDict(base, dict, prefix, baseClass)
620
if baseClass is None or baseClass in classObj.__bases__:
621
for name, method in classObj.__dict__.items():
622
optName = name[len(prefix):]
623
if ((type(method) is types.FunctionType)
624
and (name[:len(prefix)] == prefix)
629
def prefixedMethods(obj, prefix=''):
630
"""A list of methods with a given prefix on a given instance.
633
accumulateMethods(obj, dct, prefix)
637
def accumulateMethods(obj, dict, prefix='', curClass=None):
638
"""accumulateMethods(instance, dict, prefix)
639
I recurse through the bases of instance.__class__, and add methods
640
beginning with 'prefix' to 'dict', in the form of
641
{'methodname':*instance*method_object}.
644
curClass = obj.__class__
645
for base in curClass.__bases__:
646
accumulateMethods(obj, dict, prefix, base)
648
for name, method in curClass.__dict__.items():
649
optName = name[len(prefix):]
650
if ((type(method) is types.FunctionType)
651
and (name[:len(prefix)] == prefix)
653
dict[optName] = getattr(obj, name)
656
def accumulateClassDict(classObj, attr, adict, baseClass=None):
657
"""Accumulate all attributes of a given name in a class heirarchy into a single dictionary.
659
Assuming all class attributes of this name are dictionaries.
660
If any of the dictionaries being accumulated have the same key, the
661
one highest in the class heirarchy wins.
662
(XXX: If \"higest\" means \"closest to the starting class\".)
667
| properties = {\"taste\": \"bland\"}
670
| properties = {\"colour\": \"green\"}
672
| class Seaweed(Plant):
675
| class Lunch(Soy, Seaweed):
676
| properties = {\"vegan\": 1 }
680
| accumulateClassDict(Lunch, \"properties\", dct)
684
{\"taste\": \"bland\", \"colour\": \"green\", \"vegan\": 1}
686
for base in classObj.__bases__:
687
accumulateClassDict(base, attr, adict)
688
if baseClass is None or baseClass in classObj.__bases__:
689
adict.update(classObj.__dict__.get(attr, {}))
692
def accumulateClassList(classObj, attr, listObj, baseClass=None):
693
"""Accumulate all attributes of a given name in a class heirarchy into a single list.
695
Assuming all class attributes of this name are lists.
697
for base in classObj.__bases__:
698
accumulateClassList(base, attr, listObj)
699
if baseClass is None or baseClass in classObj.__bases__:
700
listObj.extend(classObj.__dict__.get(attr, []))
712
return objgrep(sys.modules, goal, isLike, 'sys.modules')
715
def isOfType(start, goal):
716
return ((type(start) is goal) or
717
(isinstance(start, types.InstanceType) and
718
start.__class__ is goal))
721
def findInstances(start, t):
722
return objgrep(start, t, isOfType)
725
def objgrep(start, goal, eq=isLike, path='', paths=None, seen=None, showUnknowns=0, maxDepth=None):
726
'''An insanely CPU-intensive process for finding stuff.
734
if id(start) in seen:
735
if seen[id(start)] is start:
737
if maxDepth is not None:
741
seen[id(start)] = start
742
if isinstance(start, types.DictionaryType):
743
for k, v in start.items():
744
objgrep(k, goal, eq, path+'{'+repr(v)+'}', paths, seen, showUnknowns, maxDepth)
745
objgrep(v, goal, eq, path+'['+repr(k)+']', paths, seen, showUnknowns, maxDepth)
746
elif isinstance(start, (list, tuple, deque)):
747
for idx in xrange(len(start)):
748
objgrep(start[idx], goal, eq, path+'['+str(idx)+']', paths, seen, showUnknowns, maxDepth)
749
elif isinstance(start, types.MethodType):
750
objgrep(start.im_self, goal, eq, path+'.im_self', paths, seen, showUnknowns, maxDepth)
751
objgrep(start.im_func, goal, eq, path+'.im_func', paths, seen, showUnknowns, maxDepth)
752
objgrep(start.im_class, goal, eq, path+'.im_class', paths, seen, showUnknowns, maxDepth)
753
elif hasattr(start, '__dict__'):
754
for k, v in start.__dict__.items():
755
objgrep(v, goal, eq, path+'.'+k, paths, seen, showUnknowns, maxDepth)
756
if isinstance(start, types.InstanceType):
757
objgrep(start.__class__, goal, eq, path+'.__class__', paths, seen, showUnknowns, maxDepth)
758
elif isinstance(start, weakref.ReferenceType):
759
objgrep(start(), goal, eq, path+'()', paths, seen, showUnknowns, maxDepth)
760
elif (isinstance(start, types.StringTypes+
761
(types.IntType, types.FunctionType,
762
types.BuiltinMethodType, RegexType, types.FloatType,
763
types.NoneType, types.FileType)) or
764
type(start).__name__ in ('wrapper_descriptor', 'method_descriptor',
765
'member_descriptor', 'getset_descriptor')):
768
print 'unknown type', type(start), start
772
def filenameToModuleName(fn):
774
Convert a name in the filesystem to the name of the Python module it is.
776
This is agressive about getting a module name back from a file; it will
777
always return a string. Agressive means 'sometimes wrong'; it won't look
778
at the Python path or try to do any error checking: don't use this method
779
unless you already know that the filename you're talking about is a Python
782
fullName = os.path.abspath(fn)
783
base = os.path.basename(fn)
785
# this happens when fn ends with a path separator, just skit it
786
base = os.path.basename(fn[:-1])
787
modName = os.path.splitext(base)[0]
789
fullName = os.path.dirname(fullName)
790
if os.path.exists(os.path.join(fullName, "__init__.py")):
791
modName = "%s.%s" % (os.path.basename(fullName), modName)
799
'InvalidName', 'ModuleNotFound', 'ObjectNotFound',
803
'Settable', 'AccessorType', 'PropertyAccessor', 'Accessor', 'Summer',
804
'QueueMethod', 'OriginalAccessor',
806
'funcinfo', 'fullFuncName', 'qual', 'getcurrent', 'getClass', 'isinst',
807
'namedModule', 'namedObject', 'namedClass', 'namedAny', 'macro',
808
'safe_repr', 'safe_str', 'allYourBase', 'accumulateBases',
809
'prefixedMethodNames', 'addMethodNamesToDict', 'prefixedMethods',
810
'accumulateClassDict', 'accumulateClassList', 'isSame', 'isLike',
811
'modgrep', 'isOfType', 'findInstances', 'objgrep', 'filenameToModuleName',
812
'fullyQualifiedName']