Source code for tea.utils

__author__ = 'Viktor Kerkez <alefnula@gmail.com>'
__date__ = '27 November 2009'
__copyright__ = 'Copyright (c) 2009 Viktor Kerkez'

import six
import sys
import types
import logging
import pkgutil
import importlib
import traceback


logger = logging.getLogger(__name__)


def _package(module):
    """This is a hack for python2 since it does not have __package__ always set
    up correctly

    Args:
        module: Module for which we want the package name
    Returns:
        str: Model's package name
    """
    return (
        module.__name__ if module.__package__ is None else module.__package__
    )


[docs]def get_object(path='', obj=None): """Returns an object from a dot path. Path can either be a full path, in which case the `get_object` function will try to import the modules in the path and follow it to the final object. Or it can be a path relative to the object passed in as the second argument. Args: path (str): Full or relative dot path to the desired object obj (object): Starting object. Dot path is calculated relatively to this object. Returns: object: Object at the end of the path, or list of non hidden objects if we use the star query. Example for full paths:: >>> get_object('os.path.join') <function join at 0x1002d9ed8> >>> get_object('tea.process') <module 'tea.process' from 'tea/process/__init__.pyc'> Example for relative paths when an object is passed in:: >>> import os >>> get_object('path.join', os) <function join at 0x1002d9ed8> Example for a star query. (Star query can be used only as the last element of the path:: >>> get_object('tea.dsa.*') [] >>> get_object('tea.dsa.singleton.*') [<class 'tea.dsa.singleton.Singleton'>, <class 'tea.dsa.singleton.SingletonMetaclass'> <module 'six' from '...'>] >>> get_object('tea.dsa.*') [<module 'tea.dsa.singleton' from '...'>] # Since we imported it """ if not path: return obj path = path.split('.') if obj is None: obj = importlib.import_module(path[0]) path = path[1:] for item in path: if item == '*': # This is the star query, returns non hidden objects return [ getattr(obj, name) for name in dir(obj) if not name.startswith('__') ] if isinstance(obj, types.ModuleType): submodule = '{}.{}'.format(_package(obj), item) try: obj = importlib.import_module(submodule) except Exception as import_error: try: obj = getattr(obj, item) except: # FIXME: I know I should probably merge the errors, but # it's easier just to throw the import error since # it's most probably the one user wants to see. # Create a new LoadingError and throw a combination # of the import error and attribute error. raise import_error else: obj = getattr(obj, item) return obj
[docs]class Loader(object): """Module loader class loads recursively a module and all it's submodules. Loaded modules will be stored in the ``modules`` attribute of the loader as a dictionary of {module_path: module} key, value pairs. Errors accounted during the loading process will not stop the loading process. They will be stored in the ``errors`` attribute of the loader as a dictionary of {module_path: exception} key, value pairs. Usage:: loader = Loader() loader.load('foo') loader.load('baz.bar', 'boo') import baz loader.load(baz) """ def __init__(self): self.modules = {} self.errors = {}
[docs] def load(self, *modules): """Load one or modre modules. :param modules: Either a string full path to a module or an actual module object. """ for module in modules: if isinstance(module, six.string_types): try: module = get_object(module) except Exception as e: self.errors[module] = e continue self.modules[module.__package__] = module for (loader, module_name, is_pkg) in pkgutil.walk_packages( module.__path__): full_name = '{}.{}'.format(_package(module), module_name) try: self.modules[full_name] = get_object(full_name) if is_pkg: self.load(self.modules[full_name]) except Exception as e: self.errors[full_name] = e
[docs]def load_subclasses(klass, modules=None): """Load recursively all submodules of the modules and return all the subclasses of the provided class Args: klass (str or list of str): Class whose subclasses we want to load. modules: List of additional modules or module names that should be recursively imported in order to find all the subclasses of the desired class. Default: None FIXME: This function is kept only for backward compatibility reasons, it should not be used. Deprecation warning should be raised and it should be replaces by the ``Loader`` class. """ if modules: if isinstance(modules, six.string_types): modules = [modules] loader = Loader() loader.load(*modules) return klass.__subclasses__()
[docs]def get_exception(): """Returns full formatted traceback as a string.""" trace = '' exception = '' exc_list = traceback.format_exception_only(sys.exc_info()[0], sys.exc_info()[1]) for entry in exc_list: exception += entry tb_list = traceback.format_tb(sys.exc_info()[2]) for entry in tb_list: trace += entry return '%s\n%s' % (exception, trace)
[docs]def cmp(x, y): """Compare function from python2""" return (x > y) - (x < y)