File: import_.py

package info (click to toggle)
python-utils 3.9.1-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 396 kB
  • sloc: python: 2,135; makefile: 19; sh: 5
file content (115 lines) | stat: -rw-r--r-- 3,838 bytes parent folder | download
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
"""
This module provides utilities for importing modules and handling exceptions.

Classes:
    DummyError(Exception):
        A custom exception class used as a default for exception handling.

Functions:
    import_global(name, modules=None, exceptions=DummyError, locals_=None,
        globals_=None, level=-1):
        Imports the requested items into the global scope, with support for
        relative imports and custom exception handling.
"""

from . import types


class DummyError(Exception):
    """A custom exception class used as a default for exception handling."""


# Legacy alias for DummyError
DummyException = DummyError


def import_global(  # noqa: C901
    name: str,
    modules: types.Optional[types.List[str]] = None,
    exceptions: types.ExceptionsType = DummyError,
    locals_: types.OptionalScope = None,
    globals_: types.OptionalScope = None,
    level: int = -1,
) -> types.Any:  # sourcery skip: hoist-if-from-if
    """Import the requested items into the global scope.

    WARNING! this method _will_ overwrite your global scope
    If you have a variable named `path` and you call `import_global('sys')`
    it will be overwritten with `sys.path`

    Args:
        name (str): the name of the module to import, e.g. sys
        modules (str): the modules to import, use None for everything
        exceptions (Exception): the exception to catch, e.g. ImportError
        locals_: the `locals()` method (in case you need a different scope)
        globals_: the `globals()` method (in case you need a different scope)
        level (int): the level to import from, this can be used for
        relative imports
    """
    frame = None
    name_parts: types.List[str] = name.split('.')
    modules_set: types.Set[str] = set()
    try:
        # If locals_ or globals_ are not given, autodetect them by inspecting
        # the current stack
        if locals_ is None or globals_ is None:
            import inspect

            frame = inspect.stack()[1][0]

            if locals_ is None:
                locals_ = frame.f_locals

            if globals_ is None:
                globals_ = frame.f_globals

        try:
            # Relative imports are supported (from .spam import eggs)
            if not name_parts[0]:
                name_parts = name_parts[1:]
                level = 1

            # raise IOError((name, level))
            module = __import__(
                name=name_parts[0] or '.',
                globals=globals_,
                locals=locals_,
                fromlist=name_parts[1:],
                level=max(level, 0),
            )

            # Make sure we get the right part of a dotted import (i.e.
            # spam.eggs should return eggs, not spam)
            try:
                for attr in name_parts[1:]:
                    module = getattr(module, attr)
            except AttributeError as e:
                raise ImportError(
                    'No module named ' + '.'.join(name_parts)
                ) from e

            # If no list of modules is given, autodetect from either __all__
            # or a dir() of the module
            if not modules:
                modules_set = set(getattr(module, '__all__', dir(module)))
            else:
                modules_set = set(modules).intersection(dir(module))

            # Add all items in modules to the global scope
            for k in set(dir(module)).intersection(modules_set):
                if k and k[0] != '_':
                    globals_[k] = getattr(module, k)
        except exceptions as e:
            return e
    finally:
        # Clean up, just to be sure
        del (
            name,
            name_parts,
            modules,
            modules_set,
            exceptions,
            locals_,
            globals_,
            frame,
        )