1 # -*- test-case-name: twisted.test.test_reflect -*-
2 # Copyright (c) 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, deprecatedModuleAttribute
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)}.
48 deprecatedModuleAttribute(
49 Version("Twisted", 12, 1, 0),
50 "Settable is old and untested. Please write your own version of this "
51 "functionality if you need it.", "twisted.python.reflect", "Settable")
53 def __init__(self, **kw):
56 def __call__(self,**kw):
57 for key,val in kw.items():
62 class AccessorType(type):
64 Metaclass that generates properties automatically.
66 This is for Python 2.2 and up.
68 Using this metaclass for your class will give you explicit accessor
69 methods; a method called set_foo, will automatically create a property
70 'foo' that uses set_foo as a setter method. Same for get_foo and del_foo.
72 Note that this will only work on methods that are present on class
73 creation. If you add methods after the class is defined they will not
74 automatically become properties. Likewise, class attributes will only
75 be used if they are present upon class creation, and no getter function
76 was set - if a getter is present, the class attribute will be ignored.
78 This is a 2.2-only alternative to the Accessor mixin - just set in your
81 __metaclass__ = AccessorType
85 deprecatedModuleAttribute(
86 Version("Twisted", 12, 1, 0),
87 "AccessorType is old and untested. Please write your own version of "
88 "this functionality if you need it.", "twisted.python.reflect",
91 def __init__(self, name, bases, d):
92 type.__init__(self, name, bases, d)
94 prefixs = ["get_", "set_", "del_"]
98 if k.startswith(prefixs[i]):
99 accessors.setdefault(k[4:], [None, None, None])[i] = v
100 for name, (getter, setter, deler) in accessors.items():
101 # create default behaviours for the property - if we leave
102 # the getter as None we won't be able to getattr, etc..
104 if hasattr(self, name):
105 value = getattr(self, name)
106 def getter(this, value=value, name=name):
107 if name in this.__dict__:
108 return this.__dict__[name]
112 def getter(this, name=name):
113 if name in this.__dict__:
114 return this.__dict__[name]
116 raise AttributeError("no such attribute %r" % name)
118 def setter(this, value, name=name):
119 this.__dict__[name] = value
121 def deler(this, name=name):
122 del this.__dict__[name]
123 setattr(self, name, property(getter, setter, deler, ""))
126 class PropertyAccessor(object):
128 A mixin class for Python 2.2 that uses AccessorType.
130 This provides compatability with the pre-2.2 Accessor mixin, up
133 Extending this class will give you explicit accessor methods; a
134 method called set_foo, for example, is the same as an if statement
135 in __setattr__ looking for 'foo'. Same for get_foo and del_foo.
137 There are also reallyDel and reallySet methods, so you can
138 override specifics in subclasses without clobbering __setattr__
139 and __getattr__, or using non-2.1 compatible code.
141 There is are incompatibilities with Accessor - accessor
142 methods added after class creation will *not* be detected. OTOH,
143 this method is probably way faster.
145 In addition, class attributes will only be used if no getter
146 was defined, and instance attributes will not override getter methods
147 whereas in original Accessor the class attribute or instance attribute
148 would override the getter method.
151 # The behaviour of Accessor is wrong IMHO, and I've found bugs
155 deprecatedModuleAttribute(
156 Version("Twisted", 12, 1, 0),
157 "PropertyAccessor is old and untested. Please write your own version "
158 "of this functionality if you need it.", "twisted.python.reflect",
160 __metaclass__ = AccessorType
162 def reallySet(self, k, v):
165 def reallyDel(self, k):
171 Extending this class will give you explicit accessor methods; a
172 method called C{set_foo}, for example, is the same as an if statement
173 in L{__setattr__} looking for C{'foo'}. Same for C{get_foo} and
174 C{del_foo}. There are also L{reallyDel} and L{reallySet} methods,
175 so you can override specifics in subclasses without clobbering
176 L{__setattr__} and L{__getattr__}.
178 This implementation is for Python 2.1.
181 deprecatedModuleAttribute(
182 Version("Twisted", 12, 1, 0),
183 "Accessor is an implementation for Python 2.1 which is no longer "
184 "supported by Twisted.", "twisted.python.reflect", "Accessor")
186 def __setattr__(self, k,v):
188 if hasattr(self.__class__,kstring):
189 return getattr(self,kstring)(v)
193 def __getattr__(self, k):
195 if hasattr(self.__class__,kstring):
196 return getattr(self,kstring)()
197 raise AttributeError("%s instance has no accessor for: %s" % (qual(self.__class__),k))
199 def __delattr__(self, k):
201 if hasattr(self.__class__,kstring):
202 getattr(self,kstring)()
206 def reallySet(self, k,v):
208 *actually* set self.k to v without incurring side-effects.
209 This is a hook to be overridden by subclasses.
212 self.__dict__.clear()
213 self.__dict__.update(v)
217 def reallyDel(self, k):
219 *actually* del self.k without incurring side-effects. This is a
220 hook to be overridden by subclasses.
225 OriginalAccessor = Accessor
226 deprecatedModuleAttribute(
227 Version("Twisted", 12, 1, 0),
228 "OriginalAccessor is a reference to class twisted.python.reflect.Accessor "
229 "which is deprecated.", "twisted.python.reflect", "OriginalAccessor")
232 class Summer(Accessor):
234 Extend from this class to get the capability to maintain 'related
235 sums'. Have a tuple in your class like the following::
237 sums=(('amount','credit','credit_total'),
238 ('amount','debit','debit_total'))
240 and the 'credit_total' member of the 'credit' member of self will
241 always be incremented when the 'amount' member of self is
242 incremented, similiarly for the debit versions.
245 deprecatedModuleAttribute(
246 Version("Twisted", 12, 1, 0),
247 "Summer is a child class of twisted.python.reflect.Accessor which is "
248 "deprecated.", "twisted.python.reflect", "Summer")
250 def reallySet(self, k,v):
251 "This method does the work."
252 for sum in self.sums:
258 oldval=getattr(self, attr)
262 if hasattr(self, obj):
265 try:oldobjval=getattr(ob, objattr)
267 setattr(ob,objattr,oldobjval+diff)
270 if hasattr(self, attr):
274 Accessor.reallySet(self,k,v)
276 Accessor.reallySet(self,y,v)
277 Accessor.reallySet(self,k,v)
282 I represent a method that doesn't exist yet.
284 def __init__(self, name, calls):
287 def __call__(self, *args):
288 self.calls.append((self.name, args))
291 def funcinfo(function):
293 this is more documentation for myself than useful code.
296 "[v2.5] Use inspect.getargspec instead of twisted.python.reflect.funcinfo",
299 code=function.func_code
300 name=function.func_name
301 argc=code.co_argcount
302 argv=code.co_varnames[:argc]
303 defaults=function.func_defaults
307 out.append('The function %s accepts %s arguments' % (name ,argc))
309 required=argc-len(defaults)
310 out.append('It requires %s arguments' % required)
311 out.append('The arguments required are: %s' % argv[:required])
312 out.append('additional arguments are:')
313 for i in range(argc-required):
315 out.append('%s which has a default of' % (argv[j], defaults[i]))
324 def fullFuncName(func):
325 qualName = (str(pickle.whichmodule(func, func.__name__)) + '.' + func.__name__)
326 if namedObject(qualName) is not func:
327 raise Exception("Couldn't find %s as %s." % (func, qualName))
333 Return full import path of a class.
335 return clazz.__module__ + '.' + clazz.__name__
338 def getcurrent(clazz):
339 assert type(clazz) == types.ClassType, 'must be a class...'
340 module = namedModule(clazz.__module__)
341 currclass = getattr(module, clazz.__name__, None)
342 if currclass is None:
349 Return the class or type of object 'obj'.
350 Returns sensible result for oldstyle and newstyle instances and types.
352 if hasattr(obj, '__class__'):
357 # class graph nonsense
359 # I should really have a better name for this...
360 def isinst(inst,clazz):
361 if type(inst) != types.InstanceType or type(clazz)!= types.ClassType:
362 return isinstance(inst,clazz)
365 clazz = getcurrent(clazz)
366 if issubclass(cl2,clazz):
376 def namedModule(name):
378 Return a module given its name.
380 topLevel = __import__(name)
381 packages = name.split(".")[1:]
388 def namedObject(name):
390 Get a fully named module-global object.
392 classSplit = name.split('.')
393 module = namedModule('.'.join(classSplit[:-1]))
394 return getattr(module, classSplit[-1])
396 namedClass = namedObject # backwards compat
400 class _NoModuleFound(Exception):
402 No module was found because none exists.
406 class InvalidName(ValueError):
408 The given name is not a dot-separated list of Python objects.
412 class ModuleNotFound(InvalidName):
414 The module associated with the given name doesn't exist and it can't be
419 class ObjectNotFound(InvalidName):
421 The object associated with the given name doesn't exist and it can't be
426 def _importAndCheckStack(importName):
428 Import the given name as a module, then walk the stack to determine whether
429 the failure was the module not existing, or some code in the module (for
430 example a dependent import) failing. This can be helpful to determine
431 whether any actual application code was run. For example, to distiguish
432 administrative error (entering the wrong module name), from programmer
433 error (writing buggy code in a module that fails to import).
435 @raise Exception: if something bad happens. This can be any type of
436 exception, since nobody knows what loading some arbitrary code might do.
438 @raise _NoModuleFound: if no module was found.
442 return __import__(importName)
444 excType, excValue, excTraceback = sys.exc_info()
446 execName = excTraceback.tb_frame.f_globals["__name__"]
447 if (execName is None or # python 2.4+, post-cleanup
448 execName == importName): # python 2.3, no cleanup
449 raise excType, excValue, excTraceback
450 excTraceback = excTraceback.tb_next
451 raise _NoModuleFound()
453 # Necessary for cleaning up modules in 2.3.
454 sys.modules.pop(importName, None)
461 Retrieve a Python object by its fully qualified name from the global Python
462 module namespace. The first part of the name, that describes a module,
463 will be discovered and imported. Each subsequent part of the name is
464 treated as the name of an attribute of the object specified by all of the
465 name which came before it. For example, the fully-qualified name of this
466 object is 'twisted.python.reflect.namedAny'.
469 @param name: The name of the object to return.
471 @raise InvalidName: If the name is an empty string, starts or ends with
472 a '.', or is otherwise syntactically incorrect.
474 @raise ModuleNotFound: If the name is syntactically correct but the
475 module it specifies cannot be imported because it does not appear to
478 @raise ObjectNotFound: If the name is syntactically correct, includes at
479 least one '.', but the module it specifies cannot be imported because
480 it does not appear to exist.
482 @raise AttributeError: If an attribute of an object along the way cannot be
483 accessed, or a module along the way is not found.
485 @return: the Python object identified by 'name'.
488 raise InvalidName('Empty module name')
490 names = name.split('.')
492 # if the name starts or ends with a '.' or contains '..', the __import__
493 # will raise an 'Empty module name' error. This will provide a better error
497 "name must be a string giving a '.'-separated list of Python "
498 "identifiers, not %r" % (name,))
500 topLevelPackage = None
501 moduleNames = names[:]
502 while not topLevelPackage:
504 trialname = '.'.join(moduleNames)
506 topLevelPackage = _importAndCheckStack(trialname)
507 except _NoModuleFound:
511 raise ModuleNotFound("No module named %r" % (name,))
513 raise ObjectNotFound('%r does not name an object' % (name,))
515 obj = topLevelPackage
517 obj = getattr(obj, n)
523 def _determineClass(x):
531 def _determineClassName(x):
532 c = _determineClass(x)
539 return '<BROKEN CLASS AT 0x%x>' % unsignedID(c)
543 def _safeFormat(formatter, o):
545 Helper function for L{safe_repr} and L{safe_str}.
551 traceback.print_exc(file=io)
552 className = _determineClassName(o)
553 tbValue = io.getvalue()
554 return "<%s instance at 0x%x with %s error:\n %s>" % (
555 className, unsignedID(o), formatter.__name__, tbValue)
561 safe_repr(anything) -> string
563 Returns a string representation of an object, or a string containing a
564 traceback, if that object's __repr__ raised an exception.
566 return _safeFormat(repr, o)
572 safe_str(anything) -> string
574 Returns a string representation of an object, or a string containing a
575 traceback, if that object's __str__ raised an exception.
577 return _safeFormat(str, o)
581 ## the following were factored out of usage
583 @deprecated(Version("Twisted", 11, 0, 0), "inspect.getmro")
584 def allYourBase(classObj, baseClass=None):
586 allYourBase(classObj, baseClass=None) -> list of all base
587 classes that are subclasses of baseClass, unless it is None,
588 in which case all bases will be added.
591 _accumulateBases(classObj, l, baseClass)
595 @deprecated(Version("Twisted", 11, 0, 0), "inspect.getmro")
596 def accumulateBases(classObj, l, baseClass=None):
597 _accumulateBases(classObj, l, baseClass)
600 def _accumulateBases(classObj, l, baseClass=None):
601 for base in classObj.__bases__:
602 if baseClass is None or issubclass(base, baseClass):
604 _accumulateBases(base, l, baseClass)
607 def prefixedMethodNames(classObj, prefix):
609 A list of method names with a given prefix in a given class.
612 addMethodNamesToDict(classObj, dct, prefix)
616 def addMethodNamesToDict(classObj, dict, prefix, baseClass=None):
618 addMethodNamesToDict(classObj, dict, prefix, baseClass=None) -> dict
619 this goes through 'classObj' (and its bases) and puts method names
620 starting with 'prefix' in 'dict' with a value of 1. if baseClass isn't
621 None, methods will only be added if classObj is-a baseClass
623 If the class in question has the methods 'prefix_methodname' and
624 'prefix_methodname2', the resulting dict should look something like:
625 {"methodname": 1, "methodname2": 1}.
627 for base in classObj.__bases__:
628 addMethodNamesToDict(base, dict, prefix, baseClass)
630 if baseClass is None or baseClass in classObj.__bases__:
631 for name, method in classObj.__dict__.items():
632 optName = name[len(prefix):]
633 if ((type(method) is types.FunctionType)
634 and (name[:len(prefix)] == prefix)
639 def prefixedMethods(obj, prefix=''):
641 A list of methods with a given prefix on a given instance.
644 accumulateMethods(obj, dct, prefix)
648 def accumulateMethods(obj, dict, prefix='', curClass=None):
650 accumulateMethods(instance, dict, prefix)
651 I recurse through the bases of instance.__class__, and add methods
652 beginning with 'prefix' to 'dict', in the form of
653 {'methodname':*instance*method_object}.
656 curClass = obj.__class__
657 for base in curClass.__bases__:
658 accumulateMethods(obj, dict, prefix, base)
660 for name, method in curClass.__dict__.items():
661 optName = name[len(prefix):]
662 if ((type(method) is types.FunctionType)
663 and (name[:len(prefix)] == prefix)
665 dict[optName] = getattr(obj, name)
668 def accumulateClassDict(classObj, attr, adict, baseClass=None):
670 Accumulate all attributes of a given name in a class hierarchy into a single dictionary.
672 Assuming all class attributes of this name are dictionaries.
673 If any of the dictionaries being accumulated have the same key, the
674 one highest in the class heirarchy wins.
675 (XXX: If \"higest\" means \"closest to the starting class\".)
680 properties = {\"taste\": \"bland\"}
683 properties = {\"colour\": \"green\"}
685 class Seaweed(Plant):
688 class Lunch(Soy, Seaweed):
689 properties = {\"vegan\": 1 }
693 accumulateClassDict(Lunch, \"properties\", dct)
697 {\"taste\": \"bland\", \"colour\": \"green\", \"vegan\": 1}
699 for base in classObj.__bases__:
700 accumulateClassDict(base, attr, adict)
701 if baseClass is None or baseClass in classObj.__bases__:
702 adict.update(classObj.__dict__.get(attr, {}))
705 def accumulateClassList(classObj, attr, listObj, baseClass=None):
707 Accumulate all attributes of a given name in a class heirarchy into a single list.
709 Assuming all class attributes of this name are lists.
711 for base in classObj.__bases__:
712 accumulateClassList(base, attr, listObj)
713 if baseClass is None or baseClass in classObj.__bases__:
714 listObj.extend(classObj.__dict__.get(attr, []))
726 return objgrep(sys.modules, goal, isLike, 'sys.modules')
729 def isOfType(start, goal):
730 return ((type(start) is goal) or
731 (isinstance(start, types.InstanceType) and
732 start.__class__ is goal))
735 def findInstances(start, t):
736 return objgrep(start, t, isOfType)
739 def objgrep(start, goal, eq=isLike, path='', paths=None, seen=None, showUnknowns=0, maxDepth=None):
741 An insanely CPU-intensive process for finding stuff.
749 if id(start) in seen:
750 if seen[id(start)] is start:
752 if maxDepth is not None:
756 seen[id(start)] = start
757 if isinstance(start, types.DictionaryType):
758 for k, v in start.items():
759 objgrep(k, goal, eq, path+'{'+repr(v)+'}', paths, seen, showUnknowns, maxDepth)
760 objgrep(v, goal, eq, path+'['+repr(k)+']', paths, seen, showUnknowns, maxDepth)
761 elif isinstance(start, (list, tuple, deque)):
762 for idx in xrange(len(start)):
763 objgrep(start[idx], goal, eq, path+'['+str(idx)+']', paths, seen, showUnknowns, maxDepth)
764 elif isinstance(start, types.MethodType):
765 objgrep(start.im_self, goal, eq, path+'.im_self', paths, seen, showUnknowns, maxDepth)
766 objgrep(start.im_func, goal, eq, path+'.im_func', paths, seen, showUnknowns, maxDepth)
767 objgrep(start.im_class, goal, eq, path+'.im_class', paths, seen, showUnknowns, maxDepth)
768 elif hasattr(start, '__dict__'):
769 for k, v in start.__dict__.items():
770 objgrep(v, goal, eq, path+'.'+k, paths, seen, showUnknowns, maxDepth)
771 if isinstance(start, types.InstanceType):
772 objgrep(start.__class__, goal, eq, path+'.__class__', paths, seen, showUnknowns, maxDepth)
773 elif isinstance(start, weakref.ReferenceType):
774 objgrep(start(), goal, eq, path+'()', paths, seen, showUnknowns, maxDepth)
775 elif (isinstance(start, types.StringTypes+
776 (types.IntType, types.FunctionType,
777 types.BuiltinMethodType, RegexType, types.FloatType,
778 types.NoneType, types.FileType)) or
779 type(start).__name__ in ('wrapper_descriptor', 'method_descriptor',
780 'member_descriptor', 'getset_descriptor')):
783 print 'unknown type', type(start), start
787 def filenameToModuleName(fn):
789 Convert a name in the filesystem to the name of the Python module it is.
791 This is agressive about getting a module name back from a file; it will
792 always return a string. Agressive means 'sometimes wrong'; it won't look
793 at the Python path or try to do any error checking: don't use this method
794 unless you already know that the filename you're talking about is a Python
797 fullName = os.path.abspath(fn)
798 base = os.path.basename(fn)
800 # this happens when fn ends with a path separator, just skit it
801 base = os.path.basename(fn[:-1])
802 modName = os.path.splitext(base)[0]
804 fullName = os.path.dirname(fullName)
805 if os.path.exists(os.path.join(fullName, "__init__.py")):
806 modName = "%s.%s" % (os.path.basename(fullName), modName)
814 'InvalidName', 'ModuleNotFound', 'ObjectNotFound',
818 'Settable', 'AccessorType', 'PropertyAccessor', 'Accessor', 'Summer',
819 'QueueMethod', 'OriginalAccessor',
821 'funcinfo', 'fullFuncName', 'qual', 'getcurrent', 'getClass', 'isinst',
822 'namedModule', 'namedObject', 'namedClass', 'namedAny',
823 'safe_repr', 'safe_str', 'allYourBase', 'accumulateBases',
824 'prefixedMethodNames', 'addMethodNamesToDict', 'prefixedMethods',
825 'accumulateClassDict', 'accumulateClassList', 'isSame', 'isLike',
826 'modgrep', 'isOfType', 'findInstances', 'objgrep', 'filenameToModuleName',
827 'fullyQualifiedName']