root / trunk / twisted / python / reflect.py

Revision 25498, 26.0 kB (checked in by therve, 7 months ago)

Merge safe-repr-3315-2

Author: therve
Reviewer: exarkun
Fixes #3315

Fix traceback format of safe_repr and safe_str, make a bunch of cleanups in
twisted.python.reflect, deprecate macro.

Line 
1 # -*- test-case-name: twisted.test.test_reflect -*-
2 # Copyright (c) 2001-2008 Twisted Matrix Laboratories.
3 # See LICENSE for details.
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             # create default behaviours for the property - if we leave
87             # the getter as None we won't be able to getattr, etc..
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     # addendum to above:
135     # The behaviour of Accessor is wrong IMHO, and I've found bugs
136     # caused by it.
137     #  -- itamar
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 # just in case
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 # class graph nonsense
317
318 # I should really have a better name for this...
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 # backwards compat
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 # python 2.4+, post-cleanup
404                     execName == importName): # python 2.3, no cleanup
405                     raise excType, excValue, excTraceback
406                 excTraceback = excTraceback.tb_next
407             raise _NoModuleFound()
408     except:
409         # Necessary for cleaning up modules in 2.3.
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     # if the name starts or ends with a '.' or contains '..', the __import__
449     # will raise an 'Empty module name' error. This will provide a better error
450     # message.
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     # shield your eyes!
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     # Before we go on, I guess I should explain why I just did that.  Basically
507     # it's a gross hack to get epydoc to work right, but the general idea is
508     # that it will be a useful aid in debugging in _any_ app which expects
509     # sys.modules to have the same globals as some function.  For example, it
510     # would be useful if you were foolishly trying to pickle a wrapped function
511     # directly from a class that had been hooked.
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 ##the following were factored out of usage
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         # this happens when fn ends with a path separator, just skit it
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 # At the end for dependency reason
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']
Note: See TracBrowser for help on using the browser.