omlish 0.0.0.dev282__py3-none-any.whl → 0.0.0.dev284__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- omlish/__about__.py +4 -4
- omlish/collections/__init__.py +1 -1
- omlish/collections/mappings.py +9 -0
- omlish/collections/utils.py +10 -1
- omlish/dataclasses/__init__.py +2 -2
- omlish/dataclasses/impl/api.py +1 -1
- omlish/dataclasses/impl/{exceptions.py → errors.py} +3 -0
- omlish/dataclasses/impl/fields.py +1 -1
- omlish/dataclasses/impl/init.py +1 -1
- omlish/dataclasses/impl/order.py +11 -1
- omlish/inject/__init__.py +1 -1
- omlish/inject/bindings.py +1 -1
- omlish/inject/eagers.py +1 -1
- omlish/inject/impl/bindings.py +1 -1
- omlish/inject/impl/elements.py +2 -2
- omlish/inject/impl/injector.py +2 -2
- omlish/inject/impl/inspect.py +1 -1
- omlish/inject/impl/scopes.py +2 -2
- omlish/inject/keys.py +1 -1
- omlish/inject/listeners.py +1 -1
- omlish/inject/multis.py +4 -4
- omlish/inject/origins.py +2 -2
- omlish/inject/overrides.py +1 -1
- omlish/inject/privates.py +2 -2
- omlish/inject/providers.py +4 -4
- omlish/inject/scopes.py +3 -3
- omlish/lang/__init__.py +8 -3
- omlish/lang/cached/function.py +9 -0
- omlish/lang/cached/property.py +3 -1
- omlish/lang/comparison.py +3 -0
- omlish/lang/enums.py +8 -0
- omlish/lang/imports.py +68 -10
- omlish/lang/objects.py +0 -17
- omlish/lite/reprs.py +84 -0
- omlish/marshal/__init__.py +1 -1
- omlish/marshal/base.py +1 -1
- omlish/marshal/{exceptions.py → errors.py} +3 -0
- omlish/marshal/objects/dataclasses.py +4 -4
- omlish/marshal/trivial/forbidden.py +1 -1
- omlish/specs/jmespath/__init__.py +1 -1
- omlish/specs/jmespath/cli.py +5 -5
- omlish/specs/jmespath/functions.py +6 -6
- omlish/specs/jmespath/lexer.py +2 -2
- omlish/specs/jmespath/parser.py +3 -3
- omlish/specs/jmespath/visitor.py +1 -1
- omlish/sql/queries/base.py +3 -3
- omlish/text/mangle.py +66 -7
- omlish/typedvalues/values.py +1 -1
- {omlish-0.0.0.dev282.dist-info → omlish-0.0.0.dev284.dist-info}/METADATA +3 -3
- {omlish-0.0.0.dev282.dist-info → omlish-0.0.0.dev284.dist-info}/RECORD +57 -57
- omlish/dataclasses/impl/descriptors.py +0 -93
- omlish/lang/exceptions.py +0 -2
- /omlish/collections/{exceptions.py → errors.py} +0 -0
- /omlish/inject/{exceptions.py → errors.py} +0 -0
- /omlish/specs/jmespath/{exceptions.py → errors.py} +0 -0
- {omlish-0.0.0.dev282.dist-info → omlish-0.0.0.dev284.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev282.dist-info → omlish-0.0.0.dev284.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev282.dist-info → omlish-0.0.0.dev284.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev282.dist-info → omlish-0.0.0.dev284.dist-info}/top_level.txt +0 -0
omlish/__about__.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
__version__ = '0.0.0.
|
2
|
-
__revision__ = '
|
1
|
+
__version__ = '0.0.0.dev284'
|
2
|
+
__revision__ = 'e10caaddb37a1fedcbe1bf3e711062e82a8cc7cb'
|
3
3
|
|
4
4
|
|
5
5
|
#
|
@@ -35,7 +35,7 @@ class Project(ProjectBase):
|
|
35
35
|
'anyio ~= 4.9',
|
36
36
|
'sniffio ~= 1.3',
|
37
37
|
|
38
|
-
'greenlet ~= 3.
|
38
|
+
'greenlet ~= 3.2',
|
39
39
|
|
40
40
|
'trio ~= 0.29',
|
41
41
|
'trio-asyncio ~= 0.15',
|
@@ -92,7 +92,7 @@ class Project(ProjectBase):
|
|
92
92
|
# 'psycopg ~= 3.2',
|
93
93
|
|
94
94
|
'pymysql ~= 1.1',
|
95
|
-
# 'mysql-connector-python ~= 9.
|
95
|
+
# 'mysql-connector-python ~= 9.3',
|
96
96
|
# 'mysqlclient ~= 2.2',
|
97
97
|
|
98
98
|
'aiomysql ~= 0.2',
|
omlish/collections/__init__.py
CHANGED
omlish/collections/mappings.py
CHANGED
@@ -23,6 +23,9 @@ def multikey_dict(dct: ta.Mapping[ta.Iterable[K] | K, V], *, deep: bool = False)
|
|
23
23
|
return ret
|
24
24
|
|
25
25
|
|
26
|
+
##
|
27
|
+
|
28
|
+
|
26
29
|
def guarded_map_update(
|
27
30
|
dst: ta.MutableMapping[ta.Any, ta.Any],
|
28
31
|
*srcs: ta.Mapping[ta.Any, ta.Any],
|
@@ -35,6 +38,9 @@ def guarded_map_update(
|
|
35
38
|
return dst
|
36
39
|
|
37
40
|
|
41
|
+
##
|
42
|
+
|
43
|
+
|
38
44
|
class TypeMap(ta.Generic[T]):
|
39
45
|
def __init__(self, items: ta.Iterable[T] = ()) -> None:
|
40
46
|
super().__init__()
|
@@ -99,6 +105,9 @@ class DynamicTypeMap(ta.Generic[V]):
|
|
99
105
|
return ret
|
100
106
|
|
101
107
|
|
108
|
+
##
|
109
|
+
|
110
|
+
|
102
111
|
class MissingDict(dict[K, V]):
|
103
112
|
def __init__(self, missing_fn: ta.Callable[[K], V]) -> None:
|
104
113
|
if not callable(missing_fn):
|
omlish/collections/utils.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import typing as ta
|
2
2
|
|
3
3
|
from .. import lang
|
4
|
-
from .
|
4
|
+
from .errors import DuplicateKeyError
|
5
5
|
from .identity import IdentityKeyDict
|
6
6
|
from .identity import IdentitySet
|
7
7
|
|
@@ -30,6 +30,9 @@ def partition(items: ta.Iterable[T], pred: ta.Callable[[T], bool]) -> PartitionR
|
|
30
30
|
return PartitionResult(t, f)
|
31
31
|
|
32
32
|
|
33
|
+
##
|
34
|
+
|
35
|
+
|
33
36
|
def unique(
|
34
37
|
it: ta.Iterable[T],
|
35
38
|
*,
|
@@ -52,6 +55,9 @@ def unique(
|
|
52
55
|
return ret
|
53
56
|
|
54
57
|
|
58
|
+
##
|
59
|
+
|
60
|
+
|
55
61
|
def make_map(
|
56
62
|
kvs: ta.Iterable[tuple[K, V]],
|
57
63
|
*,
|
@@ -82,6 +88,9 @@ def make_map_by(
|
|
82
88
|
)
|
83
89
|
|
84
90
|
|
91
|
+
##
|
92
|
+
|
93
|
+
|
85
94
|
def multi_map(kvs: ta.Iterable[tuple[K, V]], *, identity: bool = False) -> ta.MutableMapping[K, list[V]]:
|
86
95
|
d: ta.MutableMapping[K, list[V]] = IdentityKeyDict() if identity else {}
|
87
96
|
l: list[V]
|
omlish/dataclasses/__init__.py
CHANGED
@@ -28,7 +28,7 @@ from .impl.api import ( # noqa
|
|
28
28
|
dataclass as xdataclass,
|
29
29
|
make_dataclass as xmake_dataclass,
|
30
30
|
|
31
|
-
|
31
|
+
extra_class_params,
|
32
32
|
)
|
33
33
|
|
34
34
|
from .impl.as_ import ( # noqa
|
@@ -65,7 +65,7 @@ globals()['make_dataclass'] = xmake_dataclass
|
|
65
65
|
##
|
66
66
|
|
67
67
|
|
68
|
-
from .impl.
|
68
|
+
from .impl.errors import ( # noqa
|
69
69
|
FieldValidationError,
|
70
70
|
ValidationError,
|
71
71
|
)
|
omlish/dataclasses/impl/api.py
CHANGED
@@ -8,7 +8,7 @@ import typing as ta
|
|
8
8
|
|
9
9
|
from ... import check as check_
|
10
10
|
from ... import lang
|
11
|
-
from .
|
11
|
+
from .errors import FieldValidationError
|
12
12
|
from .internals import FIELDS_ATTR
|
13
13
|
from .internals import FieldType
|
14
14
|
from .internals import is_classvar
|
omlish/dataclasses/impl/init.py
CHANGED
omlish/dataclasses/impl/order.py
CHANGED
@@ -40,7 +40,17 @@ class OrderProcessor(Processor):
|
|
40
40
|
('__gt__', '>'),
|
41
41
|
('__ge__', '>='),
|
42
42
|
]:
|
43
|
-
if set_new_attribute(
|
43
|
+
if set_new_attribute(
|
44
|
+
self._cls, # noqa
|
45
|
+
name,
|
46
|
+
cmp_fn(
|
47
|
+
name,
|
48
|
+
op,
|
49
|
+
self_tuple,
|
50
|
+
other_tuple,
|
51
|
+
globals=self._info.globals,
|
52
|
+
),
|
53
|
+
):
|
44
54
|
raise TypeError(
|
45
55
|
f'Cannot overwrite attribute {name} in class {self._cls.__name__}. '
|
46
56
|
f'Consider using functools.total_ordering',
|
omlish/inject/__init__.py
CHANGED
omlish/inject/bindings.py
CHANGED
@@ -12,7 +12,7 @@ from .types import Unscoped
|
|
12
12
|
|
13
13
|
|
14
14
|
@dc.dataclass(frozen=True)
|
15
|
-
@dc.
|
15
|
+
@dc.extra_class_params(cache_hash=True)
|
16
16
|
class Binding(Element, lang.Final):
|
17
17
|
key: Key = dc.xfield(coerce=check.of_isinstance(Key))
|
18
18
|
provider: Provider = dc.xfield(coerce=check.of_isinstance(Provider))
|
omlish/inject/eagers.py
CHANGED
omlish/inject/impl/bindings.py
CHANGED
omlish/inject/impl/elements.py
CHANGED
@@ -29,8 +29,8 @@ from ..bindings import Binding
|
|
29
29
|
from ..eagers import Eager
|
30
30
|
from ..elements import Element
|
31
31
|
from ..elements import Elements
|
32
|
-
from ..
|
33
|
-
from ..
|
32
|
+
from ..errors import ConflictingKeyError
|
33
|
+
from ..errors import UnboundKeyError
|
34
34
|
from ..keys import Key
|
35
35
|
from ..listeners import ProvisionListenerBinding
|
36
36
|
from ..multis import MapBinding
|
omlish/inject/impl/injector.py
CHANGED
@@ -23,8 +23,8 @@ import weakref
|
|
23
23
|
from ... import check
|
24
24
|
from ... import lang
|
25
25
|
from ..elements import Elements
|
26
|
-
from ..
|
27
|
-
from ..
|
26
|
+
from ..errors import CyclicDependencyError
|
27
|
+
from ..errors import UnboundKeyError
|
28
28
|
from ..injector import Injector
|
29
29
|
from ..inspect import KwargsTarget
|
30
30
|
from ..keys import Key
|
omlish/inject/impl/inspect.py
CHANGED
@@ -12,7 +12,7 @@ import weakref
|
|
12
12
|
|
13
13
|
from ... import check
|
14
14
|
from ... import reflect as rfl
|
15
|
-
from ..
|
15
|
+
from ..errors import ConflictingKeyError
|
16
16
|
from ..inspect import Kwarg
|
17
17
|
from ..inspect import KwargsTarget
|
18
18
|
from ..keys import Key
|
omlish/inject/impl/scopes.py
CHANGED
@@ -15,8 +15,8 @@ from ... import lang
|
|
15
15
|
from ..bindings import Binding
|
16
16
|
from ..elements import Elements
|
17
17
|
from ..elements import as_elements
|
18
|
-
from ..
|
19
|
-
from ..
|
18
|
+
from ..errors import ScopeAlreadyOpenError
|
19
|
+
from ..errors import ScopeNotOpenError
|
20
20
|
from ..injector import Injector
|
21
21
|
from ..keys import Key
|
22
22
|
from ..providers import FnProvider
|
omlish/inject/keys.py
CHANGED
omlish/inject/listeners.py
CHANGED
omlish/inject/multis.py
CHANGED
@@ -30,14 +30,14 @@ def _check_set_multi_key(mk: Key) -> bool:
|
|
30
30
|
|
31
31
|
|
32
32
|
@dc.dataclass(frozen=True)
|
33
|
-
@dc.
|
33
|
+
@dc.extra_class_params(cache_hash=True)
|
34
34
|
class SetBinding(Element, lang.Final):
|
35
35
|
multi_key: Key = dc.xfield(validate=_check_set_multi_key)
|
36
36
|
dst: Key = dc.xfield(coerce=check.of_isinstance(Key))
|
37
37
|
|
38
38
|
|
39
39
|
@dc.dataclass(frozen=True)
|
40
|
-
@dc.
|
40
|
+
@dc.extra_class_params(cache_hash=True)
|
41
41
|
class SetProvider(Provider):
|
42
42
|
multi_key: Key = dc.xfield(validate=_check_set_multi_key)
|
43
43
|
|
@@ -50,7 +50,7 @@ def _check_map_multi_key(mk: Key) -> bool:
|
|
50
50
|
|
51
51
|
|
52
52
|
@dc.dataclass(frozen=True)
|
53
|
-
@dc.
|
53
|
+
@dc.extra_class_params(cache_hash=True)
|
54
54
|
class MapBinding(Element, lang.Final):
|
55
55
|
multi_key: Key = dc.xfield(validate=_check_map_multi_key)
|
56
56
|
map_key: ta.Any = dc.xfield()
|
@@ -58,7 +58,7 @@ class MapBinding(Element, lang.Final):
|
|
58
58
|
|
59
59
|
|
60
60
|
@dc.dataclass(frozen=True)
|
61
|
-
@dc.
|
61
|
+
@dc.extra_class_params(cache_hash=True)
|
62
62
|
class MapProvider(Provider):
|
63
63
|
multi_key: Key = dc.xfield(validate=_check_map_multi_key)
|
64
64
|
|
omlish/inject/origins.py
CHANGED
@@ -12,13 +12,13 @@ T = ta.TypeVar('T')
|
|
12
12
|
|
13
13
|
|
14
14
|
@dc.dataclass(frozen=True)
|
15
|
-
@dc.
|
15
|
+
@dc.extra_class_params(cache_hash=True)
|
16
16
|
class Origin:
|
17
17
|
lst: ta.Sequence[str]
|
18
18
|
|
19
19
|
|
20
20
|
@dc.dataclass(frozen=True)
|
21
|
-
@dc.
|
21
|
+
@dc.extra_class_params(cache_hash=True)
|
22
22
|
class Origins:
|
23
23
|
lst: ta.Sequence[Origin]
|
24
24
|
|
omlish/inject/overrides.py
CHANGED
@@ -12,7 +12,7 @@ from .elements import as_elements
|
|
12
12
|
|
13
13
|
|
14
14
|
@dc.dataclass(frozen=True)
|
15
|
-
@dc.
|
15
|
+
@dc.extra_class_params(cache_hash=True)
|
16
16
|
class Overrides(Element, lang.Final):
|
17
17
|
ovr: Elements = dc.xfield(coerce=check.of_isinstance(Elements))
|
18
18
|
src: Elements = dc.xfield(coerce=check.of_isinstance(Elements))
|
omlish/inject/privates.py
CHANGED
@@ -13,13 +13,13 @@ from .keys import as_key
|
|
13
13
|
|
14
14
|
|
15
15
|
@dc.dataclass(frozen=True)
|
16
|
-
@dc.
|
16
|
+
@dc.extra_class_params(cache_hash=True)
|
17
17
|
class Expose(Element, lang.Final):
|
18
18
|
key: Key = dc.xfield(coerce=as_key)
|
19
19
|
|
20
20
|
|
21
21
|
@dc.dataclass(frozen=True)
|
22
|
-
@dc.
|
22
|
+
@dc.extra_class_params(cache_hash=True)
|
23
23
|
class Private(Element, lang.Final):
|
24
24
|
elements: Elements = dc.xfield(coerce=check.of_isinstance(Elements))
|
25
25
|
|
omlish/inject/providers.py
CHANGED
@@ -18,24 +18,24 @@ class Provider(lang.Abstract):
|
|
18
18
|
|
19
19
|
|
20
20
|
@dc.dataclass(frozen=True)
|
21
|
-
@dc.
|
21
|
+
@dc.extra_class_params(cache_hash=True)
|
22
22
|
class FnProvider(Provider):
|
23
23
|
fn: ta.Any = dc.xfield(validate=callable)
|
24
24
|
|
25
25
|
|
26
26
|
@dc.dataclass(frozen=True)
|
27
|
-
@dc.
|
27
|
+
@dc.extra_class_params(cache_hash=True)
|
28
28
|
class CtorProvider(Provider):
|
29
29
|
ty: type = dc.xfield(coerce=check.of_isinstance(type))
|
30
30
|
|
31
31
|
|
32
32
|
@dc.dataclass(frozen=True)
|
33
|
-
@dc.
|
33
|
+
@dc.extra_class_params(cache_hash=True)
|
34
34
|
class ConstProvider(Provider):
|
35
35
|
v: ta.Any
|
36
36
|
|
37
37
|
|
38
38
|
@dc.dataclass(frozen=True)
|
39
|
-
@dc.
|
39
|
+
@dc.extra_class_params(cache_hash=True)
|
40
40
|
class LinkProvider(Provider):
|
41
41
|
k: Key = dc.xfield(coerce=check.of_isinstance(Key))
|
omlish/inject/scopes.py
CHANGED
@@ -27,7 +27,7 @@ SCOPE_ALIASES: dict[str, Scope] = {}
|
|
27
27
|
|
28
28
|
|
29
29
|
@dc.dataclass(frozen=True)
|
30
|
-
@dc.
|
30
|
+
@dc.extra_class_params(cache_hash=True)
|
31
31
|
class ScopeBinding(Element, lang.Final):
|
32
32
|
scope: Scope = dc.xfield(coerce=check.of_isinstance(Scope))
|
33
33
|
|
@@ -60,7 +60,7 @@ SCOPE_ALIASES['thread'] = ThreadScope()
|
|
60
60
|
|
61
61
|
|
62
62
|
@dc.dataclass(frozen=True)
|
63
|
-
@dc.
|
63
|
+
@dc.extra_class_params(cache_hash=True)
|
64
64
|
class SeededScope(Scope, lang.Final):
|
65
65
|
tag: ta.Any = dc.xfield(coerce=check.not_none)
|
66
66
|
|
@@ -71,7 +71,7 @@ class SeededScope(Scope, lang.Final):
|
|
71
71
|
|
72
72
|
|
73
73
|
@dc.dataclass(frozen=True)
|
74
|
-
@dc.
|
74
|
+
@dc.extra_class_params(cache_hash=True)
|
75
75
|
class ScopeSeededProvider(Provider):
|
76
76
|
ss: SeededScope = dc.xfield(coerce=check.of_isinstance(SeededScope))
|
77
77
|
key: Key = dc.xfield(coerce=check.of_isinstance(Key))
|
omlish/lang/__init__.py
CHANGED
@@ -122,8 +122,8 @@ from .descriptors import ( # noqa
|
|
122
122
|
update_wrapper,
|
123
123
|
)
|
124
124
|
|
125
|
-
from .
|
126
|
-
|
125
|
+
from .enums import ( # noqa
|
126
|
+
enum_name_repr,
|
127
127
|
)
|
128
128
|
|
129
129
|
from .functions import ( # noqa
|
@@ -166,6 +166,7 @@ from .generators import ( # noqa
|
|
166
166
|
)
|
167
167
|
|
168
168
|
from .imports import ( # noqa
|
169
|
+
LazyGlobals,
|
169
170
|
can_import,
|
170
171
|
get_real_module_name,
|
171
172
|
import_all,
|
@@ -204,7 +205,6 @@ from .objects import ( # noqa
|
|
204
205
|
SimpleProxy,
|
205
206
|
anon_object,
|
206
207
|
arg_repr,
|
207
|
-
attr_repr,
|
208
208
|
can_weakref,
|
209
209
|
deep_subclasses,
|
210
210
|
dir_dict,
|
@@ -303,6 +303,11 @@ from ..lite.imports import ( # noqa
|
|
303
303
|
import_module_attr,
|
304
304
|
)
|
305
305
|
|
306
|
+
from ..lite.reprs import ( # noqa
|
307
|
+
AttrRepr,
|
308
|
+
attr_repr,
|
309
|
+
)
|
310
|
+
|
306
311
|
from ..lite.timeouts import ( # noqa
|
307
312
|
DeadlineTimeout,
|
308
313
|
InfiniteTimeout,
|
omlish/lang/cached/function.py
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
"""
|
2
2
|
TODO:
|
3
|
+
- !!! lighter weight bound methods
|
4
|
+
- keymaker overhead less important than not rebuilding a whole dc every __get__ on a new instance
|
3
5
|
- !! specialize nullary, explicit kwarg
|
6
|
+
- !! use c-backed functools.cache if possible
|
7
|
+
- also just riic
|
4
8
|
- !! reconcile A().f() with A.f(A())
|
5
9
|
- unbound descriptor *should* still hit instance cache
|
6
10
|
- integrate / expose with collections.cache
|
@@ -10,6 +14,7 @@ TODO:
|
|
10
14
|
- 'staticmethod' or effective equiv - which must resolve to the shared instance
|
11
15
|
- and must be transient?
|
12
16
|
- use __transient_dict__ to support common state nuking
|
17
|
+
- use __set_name__ ?
|
13
18
|
"""
|
14
19
|
import dataclasses as dc
|
15
20
|
import functools
|
@@ -353,10 +358,14 @@ class _DescriptorCachedFunction(_CachedFunction[T]):
|
|
353
358
|
def cached_function(fn=None, **kwargs): # noqa
|
354
359
|
if fn is None:
|
355
360
|
return functools.partial(cached_function, **kwargs)
|
361
|
+
|
356
362
|
opts = _CachedFunction.Opts(**kwargs)
|
363
|
+
|
357
364
|
if isinstance(fn, staticmethod):
|
358
365
|
return _FreeCachedFunction(fn, opts=opts, value_fn=unwrap_func(fn))
|
366
|
+
|
359
367
|
scope = classmethod if isinstance(fn, classmethod) else None
|
368
|
+
|
360
369
|
return _DescriptorCachedFunction(fn, scope, opts=opts)
|
361
370
|
|
362
371
|
|
omlish/lang/cached/property.py
CHANGED
@@ -112,7 +112,9 @@ class _TransientCachedProperty(_CachedProperty):
|
|
112
112
|
def cached_property(fn=None, *, transient=False, **kwargs): # noqa
|
113
113
|
if fn is None:
|
114
114
|
return functools.partial(cached_property, transient=transient, **kwargs)
|
115
|
-
|
115
|
+
|
116
|
+
elif transient:
|
116
117
|
return _TransientCachedProperty(fn, **kwargs)
|
118
|
+
|
117
119
|
else:
|
118
120
|
return _DictCachedProperty(fn, **kwargs)
|
omlish/lang/comparison.py
CHANGED
omlish/lang/enums.py
ADDED
omlish/lang/imports.py
CHANGED
@@ -3,6 +3,7 @@ TODO:
|
|
3
3
|
- proxy_init 'as' alias support - attrs of (src, dst)
|
4
4
|
"""
|
5
5
|
import contextlib
|
6
|
+
import functools
|
6
7
|
import importlib.util
|
7
8
|
import sys
|
8
9
|
import types
|
@@ -254,6 +255,64 @@ def _trigger_conditional_imports(package: str) -> None:
|
|
254
255
|
##
|
255
256
|
|
256
257
|
|
258
|
+
class LazyGlobals:
|
259
|
+
def __init__(
|
260
|
+
self,
|
261
|
+
*,
|
262
|
+
globals: ta.MutableMapping[str, ta.Any] | None = None, # noqa
|
263
|
+
update_globals: bool = False,
|
264
|
+
) -> None:
|
265
|
+
super().__init__()
|
266
|
+
|
267
|
+
self._globals = globals
|
268
|
+
self._update_globals = update_globals
|
269
|
+
|
270
|
+
self._attr_fns: dict[str, ta.Callable[[], ta.Any]] = {}
|
271
|
+
|
272
|
+
@classmethod
|
273
|
+
def install(cls, globals: ta.MutableMapping[str, ta.Any]) -> 'LazyGlobals': # noqa
|
274
|
+
try:
|
275
|
+
xga = globals['__getattr__']
|
276
|
+
except KeyError:
|
277
|
+
pass
|
278
|
+
else:
|
279
|
+
if not isinstance(xga, cls):
|
280
|
+
raise RuntimeError(f'Module already has __getattr__ hook: {xga}') # noqa
|
281
|
+
return xga
|
282
|
+
|
283
|
+
lm = cls(
|
284
|
+
globals=globals,
|
285
|
+
update_globals=True,
|
286
|
+
)
|
287
|
+
|
288
|
+
globals['__getattr__'] = lm
|
289
|
+
|
290
|
+
return lm
|
291
|
+
|
292
|
+
def set_fn(self, attr: str, fn: ta.Callable[[], ta.Any]) -> 'LazyGlobals':
|
293
|
+
self._attr_fns[attr] = fn
|
294
|
+
return self
|
295
|
+
|
296
|
+
def get(self, attr: str) -> ta.Any:
|
297
|
+
try:
|
298
|
+
fn = self._attr_fns[attr]
|
299
|
+
except KeyError:
|
300
|
+
raise AttributeError(attr) from None
|
301
|
+
|
302
|
+
val = fn()
|
303
|
+
|
304
|
+
if self._update_globals and self._globals is not None:
|
305
|
+
self._globals[attr] = val
|
306
|
+
|
307
|
+
return val
|
308
|
+
|
309
|
+
def __call__(self, attr: str) -> ta.Any:
|
310
|
+
return self.get(attr)
|
311
|
+
|
312
|
+
|
313
|
+
##
|
314
|
+
|
315
|
+
|
257
316
|
class NamePackage(ta.NamedTuple):
|
258
317
|
name: str
|
259
318
|
package: str
|
@@ -266,16 +325,13 @@ class _ProxyInit:
|
|
266
325
|
|
267
326
|
def __init__(
|
268
327
|
self,
|
328
|
+
lazy_globals: LazyGlobals,
|
269
329
|
name_package: NamePackage,
|
270
|
-
*,
|
271
|
-
globals: ta.MutableMapping[str, ta.Any] | None = None, # noqa
|
272
|
-
update_globals: bool = False,
|
273
330
|
) -> None:
|
274
331
|
super().__init__()
|
275
332
|
|
333
|
+
self._lazy_globals = lazy_globals
|
276
334
|
self._name_package = name_package
|
277
|
-
self._globals = globals
|
278
|
-
self._update_globals = update_globals
|
279
335
|
|
280
336
|
self._imps_by_attr: dict[str, _ProxyInit._Import] = {}
|
281
337
|
self._mods_by_pkgs: dict[str, ta.Any] = {}
|
@@ -287,13 +343,17 @@ class _ProxyInit:
|
|
287
343
|
def add(self, package: str, attrs: ta.Iterable[str | tuple[str, str]]) -> None:
|
288
344
|
if isinstance(attrs, str):
|
289
345
|
raise TypeError(attrs)
|
346
|
+
|
290
347
|
for attr in attrs:
|
291
348
|
if isinstance(attr, tuple):
|
292
349
|
imp_attr, attr = attr
|
293
350
|
else:
|
294
351
|
imp_attr = attr
|
352
|
+
|
295
353
|
self._imps_by_attr[attr] = self._Import(package, imp_attr)
|
296
354
|
|
355
|
+
self._lazy_globals.set_fn(attr, functools.partial(self.get, attr))
|
356
|
+
|
297
357
|
def get(self, attr: str) -> ta.Any:
|
298
358
|
try:
|
299
359
|
imp = self._imps_by_attr[attr]
|
@@ -307,9 +367,6 @@ class _ProxyInit:
|
|
307
367
|
|
308
368
|
val = getattr(mod, imp.attr)
|
309
369
|
|
310
|
-
if self._update_globals and self._globals is not None:
|
311
|
-
self._globals[attr] = val
|
312
|
-
|
313
370
|
return val
|
314
371
|
|
315
372
|
|
@@ -329,13 +386,14 @@ def proxy_init(
|
|
329
386
|
pi: _ProxyInit
|
330
387
|
try:
|
331
388
|
pi = globals['__proxy_init__']
|
389
|
+
|
332
390
|
except KeyError:
|
333
391
|
pi = _ProxyInit(
|
392
|
+
LazyGlobals.install(globals),
|
334
393
|
init_name_package,
|
335
|
-
globals=globals,
|
336
394
|
)
|
337
395
|
globals['__proxy_init__'] = pi
|
338
|
-
|
396
|
+
|
339
397
|
else:
|
340
398
|
if pi.name_package != init_name_package:
|
341
399
|
raise Exception(f'Wrong init name: {pi.name_package=} != {init_name_package=}')
|