|
2 | 2 | This module initializes the `python_utils` package by importing various |
3 | 3 | submodules and functions. |
4 | 4 |
|
| 5 | +Imports are performed lazily (PEP 562): nothing is imported when you ``import |
| 6 | +python_utils``; each submodule/function is loaded on first access. This keeps |
| 7 | +``import python_utils`` cheap and, in particular, avoids eagerly importing |
| 8 | +``asyncio`` (via the async helpers) for consumers that only need the |
| 9 | +synchronous utilities. |
| 10 | +
|
5 | 11 | Submodules: |
6 | 12 | aio |
7 | 13 | converters |
|
49 | 55 | LoggerBase |
50 | 56 | """ |
51 | 57 |
|
52 | | -from . import ( |
53 | | - aio, |
54 | | - converters, |
55 | | - decorators, |
56 | | - formatters, |
57 | | - generators, |
58 | | - import_, |
59 | | - logger, |
60 | | - terminal, |
61 | | - time, |
62 | | - types, |
63 | | -) |
64 | | -from .aio import acount |
65 | | -from .containers import CastedDict, LazyCastedDict, UniqueList |
66 | | -from .converters import remap, scale_1024, to_float, to_int, to_str, to_unicode |
67 | | -from .decorators import listify, set_attributes |
68 | | -from .exceptions import raise_exception, reraise |
69 | | -from .formatters import camel_to_underscore, timesince |
70 | | -from .generators import abatcher, batcher |
71 | | -from .import_ import import_global |
72 | | -from .logger import Logged, LoggerBase |
73 | | -from .terminal import get_terminal_size |
74 | | -from .time import ( |
75 | | - aio_generator_timeout_detector, |
76 | | - aio_generator_timeout_detector_decorator, |
77 | | - aio_timeout_generator, |
78 | | - delta_to_seconds, |
79 | | - delta_to_seconds_or_none, |
80 | | - format_time, |
81 | | - timedelta_to_seconds, |
82 | | - timeout_generator, |
| 58 | +import importlib |
| 59 | +import typing |
| 60 | + |
| 61 | +if typing.TYPE_CHECKING: |
| 62 | + # Eager imports for type checkers only; the runtime equivalents are loaded |
| 63 | + # lazily by ``__getattr__`` below. Names appear in ``__all__`` so they are |
| 64 | + # treated as re-exports (not unused imports). |
| 65 | + from . import ( |
| 66 | + aio, |
| 67 | + converters, |
| 68 | + decorators, |
| 69 | + formatters, |
| 70 | + generators, |
| 71 | + import_, |
| 72 | + logger, |
| 73 | + terminal, |
| 74 | + time, |
| 75 | + types, |
| 76 | + ) |
| 77 | + from .aio import acount |
| 78 | + from .containers import CastedDict, LazyCastedDict, UniqueList |
| 79 | + from .converters import ( |
| 80 | + remap, |
| 81 | + scale_1024, |
| 82 | + to_float, |
| 83 | + to_int, |
| 84 | + to_str, |
| 85 | + to_unicode, |
| 86 | + ) |
| 87 | + from .decorators import listify, set_attributes |
| 88 | + from .exceptions import raise_exception, reraise |
| 89 | + from .formatters import camel_to_underscore, timesince |
| 90 | + from .generators import abatcher, batcher |
| 91 | + from .import_ import import_global |
| 92 | + from .logger import Logged, LoggerBase |
| 93 | + from .terminal import get_terminal_size |
| 94 | + from .time import ( |
| 95 | + aio_generator_timeout_detector, |
| 96 | + aio_generator_timeout_detector_decorator, |
| 97 | + aio_timeout_generator, |
| 98 | + delta_to_seconds, |
| 99 | + delta_to_seconds_or_none, |
| 100 | + format_time, |
| 101 | + timedelta_to_seconds, |
| 102 | + timeout_generator, |
| 103 | + ) |
| 104 | + |
| 105 | +#: Submodules that can be accessed as ``python_utils.<name>``. |
| 106 | +_SUBMODULES: frozenset[str] = frozenset( |
| 107 | + { |
| 108 | + 'aio', |
| 109 | + 'containers', |
| 110 | + 'converters', |
| 111 | + 'decorators', |
| 112 | + 'exceptions', |
| 113 | + 'formatters', |
| 114 | + 'generators', |
| 115 | + 'import_', |
| 116 | + 'logger', |
| 117 | + 'terminal', |
| 118 | + 'time', |
| 119 | + 'types', |
| 120 | + } |
83 | 121 | ) |
84 | 122 |
|
| 123 | +#: Exported name -> submodule it lives in. |
| 124 | +_NAME_TO_MODULE: dict[str, str] = { |
| 125 | + 'acount': 'aio', |
| 126 | + 'CastedDict': 'containers', |
| 127 | + 'LazyCastedDict': 'containers', |
| 128 | + 'UniqueList': 'containers', |
| 129 | + 'remap': 'converters', |
| 130 | + 'scale_1024': 'converters', |
| 131 | + 'to_float': 'converters', |
| 132 | + 'to_int': 'converters', |
| 133 | + 'to_str': 'converters', |
| 134 | + 'to_unicode': 'converters', |
| 135 | + 'listify': 'decorators', |
| 136 | + 'set_attributes': 'decorators', |
| 137 | + 'raise_exception': 'exceptions', |
| 138 | + 'reraise': 'exceptions', |
| 139 | + 'camel_to_underscore': 'formatters', |
| 140 | + 'timesince': 'formatters', |
| 141 | + 'abatcher': 'generators', |
| 142 | + 'batcher': 'generators', |
| 143 | + 'import_global': 'import_', |
| 144 | + 'Logged': 'logger', |
| 145 | + 'LoggerBase': 'logger', |
| 146 | + 'get_terminal_size': 'terminal', |
| 147 | + 'aio_generator_timeout_detector': 'time', |
| 148 | + 'aio_generator_timeout_detector_decorator': 'time', |
| 149 | + 'aio_timeout_generator': 'time', |
| 150 | + 'delta_to_seconds': 'time', |
| 151 | + 'delta_to_seconds_or_none': 'time', |
| 152 | + 'format_time': 'time', |
| 153 | + 'timedelta_to_seconds': 'time', |
| 154 | + 'timeout_generator': 'time', |
| 155 | +} |
| 156 | + |
| 157 | + |
| 158 | +def __getattr__(name: str) -> typing.Any: |
| 159 | + """Lazily import submodules and their exported names on first access.""" |
| 160 | + if name in _SUBMODULES: |
| 161 | + module = importlib.import_module(f'.{name}', __name__) |
| 162 | + elif name in _NAME_TO_MODULE: |
| 163 | + module = importlib.import_module(f'.{_NAME_TO_MODULE[name]}', __name__) |
| 164 | + value = getattr(module, name) |
| 165 | + globals()[name] = value # cache so __getattr__ runs only once |
| 166 | + return value |
| 167 | + else: |
| 168 | + raise AttributeError(f'module {__name__!r} has no attribute {name!r}') |
| 169 | + |
| 170 | + globals()[name] = module |
| 171 | + return module |
| 172 | + |
| 173 | + |
| 174 | +def __dir__() -> list[str]: |
| 175 | + return sorted(set(globals()) | set(__all__)) |
| 176 | + |
| 177 | + |
85 | 178 | __all__ = [ |
86 | 179 | 'CastedDict', |
87 | 180 | 'LazyCastedDict', |
|
0 commit comments