def scan(self, package, categories=None): """ Scan a Python package and any of its subpackages. All top-level objects will be considered; those marked with venusian callback attributes related to ``category`` will be processed. The ``package`` argument should be a reference to a Python package or module object. The ``categories`` argument should be sequence of Venusian callback categories (each category usually a string) or the special value ``None`` which means all Venusian callback categories. The default is ``None``. """ seen = set() def invoke(name, ob): # in one scan, we only process each object once if id(ob) in seen: return seen.add(id(ob)) category_keys = categories try: attached_categories = getattr(ob, ATTACH_ATTR) except: # some metaclasses do insane things when asked for an attribute # (like not raising an AttributeError) return if not attached_categories.attached_to(ob): return if category_keys is None: category_keys = attached_categories.keys() category_keys.sort() for category in category_keys: callbacks = attached_categories.get(category, []) for callback in callbacks: callback(self, name, ob) for name, ob in inspect.getmembers(package): invoke(name, ob) if hasattr(package, "__path__"): # package, not module results = walk_packages(package.__path__, package.__name__ + ".") for importer, modname, ispkg in results: loader = importer.find_module(modname) if loader is not None: # happens on pypy with orphaned pyc module_type = loader.etc[2] # only scrape members from non-orphaned source files # and package directories if module_type in (imp.PY_SOURCE, imp.PKG_DIRECTORY): # NB: use __import__(modname) rather than # loader.load_module(modname) to prevent # inappropriate double-execution of module code __import__(modname) module = sys.modules[modname] for name, ob in inspect.getmembers(module, None): invoke(name, ob)
def scan(self, package, categories=None): """ Scan a Python package and any of its subpackages. All top-level objects will be considered; those marked with venusian callback attributes related to ``category`` will be processed. The ``package`` argument should be a reference to a Python package or module object. The ``categories`` argument should be sequence of Venusian callback categories (each category usually a string) or the special value ``None`` which means all Venusian callback categories. The default is ``None``. """ def invoke(name, ob): category_keys = categories attached_categories = getattr(ob, ATTACH_ATTR, {}) if category_keys is None: category_keys = attached_categories.keys() category_keys.sort() for category in category_keys: callbacks = attached_categories.get(category, []) for callback in callbacks: callback(self, name, ob) for name, ob in inspect.getmembers(package): invoke(name, ob) if hasattr(package, '__path__'): # package, not module results = walk_packages(package.__path__, package.__name__ + '.') for importer, modname, ispkg in results: loader = importer.find_module(modname) module_type = loader.etc[2] # only scrape members from non-orphaned source files # and package directories if module_type in (imp.PY_SOURCE, imp.PKG_DIRECTORY): # NB: use __import__(modname) rather than # loader.load_module(modname) to prevent # inappropriate double-execution of module code __import__(modname) module = sys.modules[modname] for name, ob in inspect.getmembers(module, None): invoke(name, ob)
def scan(self, package, categories=None): """ Scan a Python package and any of its subpackages. All top-level objects will be considered; those marked with venusian callback attributes related to ``category`` will be processed. The ``package`` argument should be a reference to a Python package or module object. The ``categories`` argument should be sequence of Venusian callback categories (each category usually a string) or the special value ``None`` which means all Venusian callback categories. The default is ``None``. """ def invoke(name, ob): category_keys = categories attached_categories = getattr(ob, ATTACH_ATTR, {}) if category_keys is None: category_keys = attached_categories.keys() category_keys.sort() for category in category_keys: callbacks = attached_categories.get(category, []) for callback in callbacks: callback(self, name, ob) for name, ob in inspect.getmembers(package): invoke(name, ob) if hasattr(package, '__path__'): # package, not module results = walk_packages(package.__path__, package.__name__+'.') for importer, modname, ispkg in results: loader = importer.find_module(modname) module_type = loader.etc[2] # only scrape members from non-orphaned source files # and package directories if module_type in (imp.PY_SOURCE, imp.PKG_DIRECTORY): # NB: use __import__(modname) rather than # loader.load_module(modname) to prevent # inappropriate double-execution of module code __import__(modname) module = sys.modules[modname] for name, ob in inspect.getmembers(module, None): invoke(name, ob)
def scan(self, package, categories=None): """ Scan a Python package and any of its subpackages. All top-level objects will be considered; those marked with venusian callback attributes related to ``category`` will be processed. The ``package`` argument should be a reference to a Python package or module object. The ``categories`` argument should be sequence of Venusian callback categories (each category usually a string) or the special value ``None`` which means all Venusian callback categories. The default is ``None``. """ seen = set() def invoke(name, ob): # in one scan, we only process each object once if id(ob) in seen: return seen.add(id(ob)) category_keys = categories try: # Some metaclasses do insane things when asked for an # ``ATTACH_ATTR``, like not raising an AttributeError but # some other arbitary exception. Some even shittier # introspected code lets us access ``ATTACH_ATTR`` far but # barfs on a second attribute access for ``attached_to`` # (still not raising an AttributeError, but some other # arbitrary exception). Finally, the shittiest code of all # allows the attribute access of the ``ATTACH_ATTR`` *and* # ``attached_to``, (say, both ``ob.__getattr__`` and # ``attached_categories.__getattr__`` returning a proxy for # any attribute access), which either a) isn't callable or b) # is callable, but, when called, shits its pants in an # potentially arbitrary way (although for b, only TypeError # has been seen in the wild, from PyMongo). Thus the # catchall except: return here, which in any other case would # be high treason. attached_categories = getattr(ob, ATTACH_ATTR) if not attached_categories.attached_to(ob): return except: return if category_keys is None: category_keys = attached_categories.keys() category_keys.sort() for category in category_keys: callbacks = attached_categories.get(category, []) for callback in callbacks: callback(self, name, ob) for name, ob in inspect.getmembers(package): invoke(name, ob) if hasattr(package, '__path__'): # package, not module results = walk_packages(package.__path__, package.__name__+'.') for importer, modname, ispkg in results: loader = importer.find_module(modname) if loader is not None: # happens on pypy with orphaned pyc module_type = loader.etc[2] # only scrape members from non-orphaned source files # and package directories if module_type in (imp.PY_SOURCE, imp.PKG_DIRECTORY): # NB: use __import__(modname) rather than # loader.load_module(modname) to prevent # inappropriate double-execution of module code __import__(modname) module = sys.modules[modname] for name, ob in inspect.getmembers(module, None): invoke(name, ob)
def scan(self, package, categories=None, onerror=None): """ Scan a Python package and any of its subpackages. All top-level objects will be considered; those marked with venusian callback attributes related to ``category`` will be processed. The ``package`` argument should be a reference to a Python package or module object. The ``categories`` argument should be sequence of Venusian callback categories (each category usually a string) or the special value ``None`` which means all Venusian callback categories. The default is ``None``. The ``onerror`` argument should either be ``None`` or a callback function which behaves the same way as the ``onerror`` callback function described in http://docs.python.org/library/pkgutil.html#pkgutil.walk_packages . By default, during a scan, Venusian will propagate all errors that happen during its code importing process, including :exc:`ImportError`. If you use a custom ``onerror`` callback, you can change this behavior. Here's an example ``onerror`` callback that ignores :exc:`ImportError`:: import sys def onerror(name): if not issubclass(sys.exc_info()[0], ImportError): raise # reraise the last exception The ``name`` passed to ``onerror`` is the module or package dotted name that could not be imported due to an exception. .. note:: the ``onerror`` callback is new as of Venusian 1.0. """ if onerror is None: # by default, propagate all errors (for bw compat purposes) def onerror(name): raise seen = set() def invoke(name, ob): # in one scan, we only process each object once if id(ob) in seen: return seen.add(id(ob)) category_keys = categories try: # Some metaclasses do insane things when asked for an # ``ATTACH_ATTR``, like not raising an AttributeError but # some other arbitary exception. Some even shittier # introspected code lets us access ``ATTACH_ATTR`` far but # barfs on a second attribute access for ``attached_to`` # (still not raising an AttributeError, but some other # arbitrary exception). Finally, the shittiest code of all # allows the attribute access of the ``ATTACH_ATTR`` *and* # ``attached_to``, (say, both ``ob.__getattr__`` and # ``attached_categories.__getattr__`` returning a proxy for # any attribute access), which either a) isn't callable or b) # is callable, but, when called, shits its pants in an # potentially arbitrary way (although for b, only TypeError # has been seen in the wild, from PyMongo). Thus the # catchall except: return here, which in any other case would # be high treason. attached_categories = getattr(ob, ATTACH_ATTR) if not attached_categories.attached_to(ob): return except: return if category_keys is None: category_keys = list(attached_categories.keys()) category_keys.sort() for category in category_keys: callbacks = attached_categories.get(category, []) for callback in callbacks: callback(self, name, ob) for name, ob in inspect.getmembers(package): invoke(name, ob) if hasattr(package, '__path__'): # package, not module results = walk_packages(package.__path__, package.__name__ + '.', onerror=onerror) for importer, modname, ispkg in results: loader = importer.find_module(modname) if loader is not None: # happens on pypy with orphaned pyc try: module_type = loader.etc[2] # only scrape members from non-orphaned source files # and package directories if module_type in (imp.PY_SOURCE, imp.PKG_DIRECTORY): # NB: use __import__(modname) rather than # loader.load_module(modname) to prevent # inappropriate double-execution of module code try: __import__(modname) except Exception: onerror(modname) module = sys.modules.get(modname) if module is not None: for name, ob in inspect.getmembers( module, None): invoke(name, ob) finally: if (hasattr(loader, 'file') and hasattr(loader.file, 'close')): loader.file.close()
def scan(self, package, categories=None, onerror=None): """ Scan a Python package and any of its subpackages. All top-level objects will be considered; those marked with venusian callback attributes related to ``category`` will be processed. The ``package`` argument should be a reference to a Python package or module object. The ``categories`` argument should be sequence of Venusian callback categories (each category usually a string) or the special value ``None`` which means all Venusian callback categories. The default is ``None``. The ``onerror`` argument should either be ``None`` or a callback function which behaves the same way as the ``onerror`` callback function described in http://docs.python.org/library/pkgutil.html#pkgutil.walk_packages . By default, during a scan, Venusian will propagate all errors that happen during its code importing process, including :exc:`ImportError`. If you use a custom ``onerror`` callback, you can change this behavior. Here's an example ``onerror`` callback that ignores :exc:`ImportError`:: import sys def onerror(name): if not isinstance(sys.exc_info()[0], ImportError): raise # reraise the last exception The ``name`` passed to ``onerror`` is the module or package dotted name that could not be imported due to an exception. """ if onerror is None: # by default, propagate all errors (for bw compat purposes) def onerror(name): raise seen = set() def invoke(name, ob): # in one scan, we only process each object once if id(ob) in seen: return seen.add(id(ob)) category_keys = categories try: # Some metaclasses do insane things when asked for an # ``ATTACH_ATTR``, like not raising an AttributeError but # some other arbitary exception. Some even shittier # introspected code lets us access ``ATTACH_ATTR`` far but # barfs on a second attribute access for ``attached_to`` # (still not raising an AttributeError, but some other # arbitrary exception). Finally, the shittiest code of all # allows the attribute access of the ``ATTACH_ATTR`` *and* # ``attached_to``, (say, both ``ob.__getattr__`` and # ``attached_categories.__getattr__`` returning a proxy for # any attribute access), which either a) isn't callable or b) # is callable, but, when called, shits its pants in an # potentially arbitrary way (although for b, only TypeError # has been seen in the wild, from PyMongo). Thus the # catchall except: return here, which in any other case would # be high treason. attached_categories = getattr(ob, ATTACH_ATTR) if not attached_categories.attached_to(ob): return except: return if category_keys is None: category_keys = list(attached_categories.keys()) category_keys.sort() for category in category_keys: callbacks = attached_categories.get(category, []) for callback in callbacks: callback(self, name, ob) for name, ob in inspect.getmembers(package): invoke(name, ob) if hasattr(package, '__path__'): # package, not module results = walk_packages(package.__path__, package.__name__+'.', onerror=onerror) for importer, modname, ispkg in results: loader = importer.find_module(modname) if loader is not None: # happens on pypy with orphaned pyc module_type = loader.etc[2] # only scrape members from non-orphaned source files # and package directories if module_type in (imp.PY_SOURCE, imp.PKG_DIRECTORY): # NB: use __import__(modname) rather than # loader.load_module(modname) to prevent # inappropriate double-execution of module code try: __import__(modname) except Exception: onerror(modname) module = sys.modules.get(modname) if module is not None: for name, ob in inspect.getmembers(module, None): invoke(name, ob)