1# This Source Code Form is subject to the terms of the Mozilla Public
2# License, v. 2.0. If a copy of the MPL was not distributed with this
3# file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5from __future__ import absolute_import, print_function, unicode_literals
6
7import inspect
8import os
9
10
11def find_object(path):
12    """
13    Find a Python object given a path of the form <modulepath>:<objectpath>.
14    Conceptually equivalent to
15
16        def find_object(modulepath, objectpath):
17            import <modulepath> as mod
18            return mod.<objectpath>
19    """
20    if path.count(':') != 1:
21        raise ValueError(
22            'python path {!r} does not have the form "module:object"'.format(path))
23
24    modulepath, objectpath = path.split(':')
25    obj = __import__(modulepath)
26    for a in modulepath.split('.')[1:]:
27        obj = getattr(obj, a)
28    for a in objectpath.split('.'):
29        obj = getattr(obj, a)
30    return obj
31
32
33def import_sibling_modules(exceptions=None):
34    """
35    Import all Python modules that are siblings of the calling module.
36
37    Args:
38        exceptions (list): A list of file names to exclude (caller and
39            __init__.py are implicitly excluded).
40    """
41    frame = inspect.stack()[1]
42    mod = inspect.getmodule(frame[0])
43
44    name = os.path.basename(mod.__file__)
45    excs = set(['__init__.py', name])
46    if exceptions:
47        excs.update(exceptions)
48
49    modpath = mod.__name__
50    if not name.startswith('__init__.py'):
51        modpath = modpath.rsplit('.', 1)[0]
52
53    for f in os.listdir(os.path.dirname(mod.__file__)):
54        if f.endswith('.py') and f not in excs:
55            __import__(modpath + '.' + f[:-3])
56