123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 |
- # -*- test-case-name: twisted.test.test_reflect -*-
- # Copyright (c) Twisted Matrix Laboratories.
- # See LICENSE for details.
- """
- Standardized versions of various cool and/or strange things that you can do
- with Python's reflection capabilities.
- """
- import sys
- from jsonschema.compat import PY3
- class _NoModuleFound(Exception):
- """
- No module was found because none exists.
- """
- class InvalidName(ValueError):
- """
- The given name is not a dot-separated list of Python objects.
- """
- class ModuleNotFound(InvalidName):
- """
- The module associated with the given name doesn't exist and it can't be
- imported.
- """
- class ObjectNotFound(InvalidName):
- """
- The object associated with the given name doesn't exist and it can't be
- imported.
- """
- if PY3:
- def reraise(exception, traceback):
- raise exception.with_traceback(traceback)
- else:
- exec("""def reraise(exception, traceback):
- raise exception.__class__, exception, traceback""")
- reraise.__doc__ = """
- Re-raise an exception, with an optional traceback, in a way that is compatible
- with both Python 2 and Python 3.
- Note that on Python 3, re-raised exceptions will be mutated, with their
- C{__traceback__} attribute being set.
- @param exception: The exception instance.
- @param traceback: The traceback to use, or C{None} indicating a new traceback.
- """
- def _importAndCheckStack(importName):
- """
- Import the given name as a module, then walk the stack to determine whether
- the failure was the module not existing, or some code in the module (for
- example a dependent import) failing. This can be helpful to determine
- whether any actual application code was run. For example, to distiguish
- administrative error (entering the wrong module name), from programmer
- error (writing buggy code in a module that fails to import).
- @param importName: The name of the module to import.
- @type importName: C{str}
- @raise Exception: if something bad happens. This can be any type of
- exception, since nobody knows what loading some arbitrary code might
- do.
- @raise _NoModuleFound: if no module was found.
- """
- try:
- return __import__(importName)
- except ImportError:
- excType, excValue, excTraceback = sys.exc_info()
- while excTraceback:
- execName = excTraceback.tb_frame.f_globals["__name__"]
- # in Python 2 execName is None when an ImportError is encountered,
- # where in Python 3 execName is equal to the importName.
- if execName is None or execName == importName:
- reraise(excValue, excTraceback)
- excTraceback = excTraceback.tb_next
- raise _NoModuleFound()
- def namedAny(name):
- """
- Retrieve a Python object by its fully qualified name from the global Python
- module namespace. The first part of the name, that describes a module,
- will be discovered and imported. Each subsequent part of the name is
- treated as the name of an attribute of the object specified by all of the
- name which came before it. For example, the fully-qualified name of this
- object is 'twisted.python.reflect.namedAny'.
- @type name: L{str}
- @param name: The name of the object to return.
- @raise InvalidName: If the name is an empty string, starts or ends with
- a '.', or is otherwise syntactically incorrect.
- @raise ModuleNotFound: If the name is syntactically correct but the
- module it specifies cannot be imported because it does not appear to
- exist.
- @raise ObjectNotFound: If the name is syntactically correct, includes at
- least one '.', but the module it specifies cannot be imported because
- it does not appear to exist.
- @raise AttributeError: If an attribute of an object along the way cannot be
- accessed, or a module along the way is not found.
- @return: the Python object identified by 'name'.
- """
- if not name:
- raise InvalidName('Empty module name')
- names = name.split('.')
- # if the name starts or ends with a '.' or contains '..', the __import__
- # will raise an 'Empty module name' error. This will provide a better error
- # message.
- if '' in names:
- raise InvalidName(
- "name must be a string giving a '.'-separated list of Python "
- "identifiers, not %r" % (name,))
- topLevelPackage = None
- moduleNames = names[:]
- while not topLevelPackage:
- if moduleNames:
- trialname = '.'.join(moduleNames)
- try:
- topLevelPackage = _importAndCheckStack(trialname)
- except _NoModuleFound:
- moduleNames.pop()
- else:
- if len(names) == 1:
- raise ModuleNotFound("No module named %r" % (name,))
- else:
- raise ObjectNotFound('%r does not name an object' % (name,))
- obj = topLevelPackage
- for n in names[1:]:
- obj = getattr(obj, n)
- return obj
|