1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# -*- coding: utf-8 -*-
'''\
:mod:`baph.utils.importing` -- Import-related Utilities
=======================================================

.. moduleauthor:: Mark Lee <markl@evomediagroup.com>
'''

from django.conf import settings
from django.utils.importlib import import_module


def import_any_module(modules, raise_error=True):
    '''Imports the first module available from a list of modules.

    :param modules: The list of modules to look for.
    :type modules: a :class:`list` of :class:`str`
    :returns: A module or :const:`None`, if no module could be found and
              ``raise_error`` is :const:`False`.
    '''
    # This hack avoids ImproperlyConfigured errors due to usage of backends
    # that Django doesn't know about but SQLAlchemy does.
    old_default = settings.DATABASES['default']
    settings.DATABASES['default'] = {'ENGINE': ''}
    try:
        mod = None
        for module in modules:
            try:
                mod = import_module(module)
            except ImportError:
                pass
            else:
                break
        if mod is None and raise_error:
            raise ImportError('Could not import any of %s' % (modules,))
    finally:
        # Revert the hack from above
        settings.DATABASES['default'] = old_default

    return mod


def import_attr(modules, attr, raise_error=True):
    '''Imports one or more attributes from the first module in a list of
    available modules.

    :param modules: The list of modules to look for.
    :type modules: a :class:`list` of :class:`str`
    :param attr: One or more attributes to search for.
    :type attr: either a :class:`str` or a :class:`list` of :class:`str`
    :returns: The attribute, a :class:`tuple` of attributes, or :const:`None`,
              if no module/attribute could be found and ``raise_error`` is
              :const:`False`.
    '''
    if isinstance(attr, str):
        attrs = [attr]
    else:
        attrs = attr
    result = tuple()
    module = import_any_module(modules, raise_error)
    if module:
        for a in attrs:
            if raise_error:
                result += (getattr(module, a),)
            else:
                result += (getattr(module, a, None),)
    if len(result) == 0:
        result = None
    elif len(result) == 1:
        result = result[0]
    return result


def import_all_attrs(modules, use_all=True, raise_error=True):
    '''Like :func:`import_attr`, where ``attr`` is ``*``, i.e.,
    ``from foo import *``.

    :param modules: The list of modules to look for.
    :type modules: a :class:`list` of :class:`str`
    :param bool use_all: Whether to use the ``__all__`` attribute of a module,
                         if it exists.
    :returns: A :class:`dict` of zero or more attributes, or :const:`None`,
              if no module/attribute could be found and ``raise_error`` is
              :const:`False`.
    '''
    result = {}
    module = import_any_module(modules, raise_error)
    if module:
        attrs = None
        if use_all:
            attrs = getattr(module, '__all__', None)
        if attrs is None:
            attrs = [x for x in dir(module) if not x.startswith('__')]
        for a in attrs:
            if raise_error:
                result[a] = getattr(module, a)
            else:
                result[a] = getattr(module, a, None)
    else:
        result = None
    return result


def import_any_attr(modules, attr, raise_error=True):
    '''Imports an attribute from the first module that has said attribute.

    :param modules: The list of modules to look in.
    :type modules: a :class:`list` of :class:`str`
    :param attr: One or more attributes to search for.
    :type attr: either a :class:`str` or a :class:`list` of :class:`str`
    :returns: The attribute or :const:`None`, if no module/attribute could be
              found and ``raise_error`` is :const:`False`.
    '''
    result = None
    # This hack avoids ImproperlyConfigured errors due to usage of backends
    # that Django doesn't know about but SQLAlchemy does.
    old_default = settings.DATABASES['default']
    settings.DATABASES['default'] = {'ENGINE': ''}
    try:
        mod = None
        for module in modules:
            try:
                mod = import_module(module)
            except ImportError:
                pass
            else:
                if hasattr(mod, attr):
                    result = getattr(mod, attr)
                    break
        if mod is None and raise_error:
            raise ImportError('Could not import any of %s' % (modules,))
    finally:
        # Revert the hack from above
        settings.DATABASES['default'] = old_default
    if result is None and raise_error:
        raise AttributeError('Could not locate %s in any of %s' % \
                             (attr, modules))
    return result