omlish 0.0.0.dev394__py3-none-any.whl → 0.0.0.dev396__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 +3 -3
- omlish/collections/__init__.py +4 -0
- omlish/collections/trie.py +156 -0
- omlish/dataclasses/__init__.py +6 -6
- omlish/dataclasses/{api → impl/api}/classes/conversion.py +4 -4
- omlish/dataclasses/{api → impl/api}/classes/decorator.py +5 -5
- omlish/dataclasses/{api → impl/api}/classes/metadata.py +4 -4
- omlish/dataclasses/{api → impl/api}/classes/params.py +4 -4
- omlish/dataclasses/{api → impl/api}/fields/building.py +7 -7
- omlish/dataclasses/{api → impl/api}/fields/constructor.py +4 -4
- omlish/dataclasses/{api → impl/api}/fields/conversion.py +8 -8
- omlish/dataclasses/{api → impl/api}/fields/metadata.py +5 -5
- omlish/dataclasses/{concerns → impl/concerns}/copy.py +1 -1
- omlish/dataclasses/{concerns → impl/concerns}/fields.py +6 -6
- omlish/dataclasses/{concerns → impl/concerns}/frozen.py +3 -3
- omlish/dataclasses/{concerns → impl/concerns}/hash.py +1 -1
- omlish/dataclasses/{concerns → impl/concerns}/init.py +9 -9
- omlish/dataclasses/{concerns → impl/concerns}/matchargs.py +1 -1
- omlish/dataclasses/{concerns → impl/concerns}/mro.py +1 -1
- omlish/dataclasses/{concerns → impl/concerns}/params.py +2 -2
- omlish/dataclasses/{concerns → impl/concerns}/replace.py +4 -4
- omlish/dataclasses/{concerns → impl/concerns}/repr.py +3 -3
- omlish/dataclasses/impl/configs.py +12 -0
- omlish/dataclasses/{generation → impl/generation}/compilation.py +1 -1
- omlish/dataclasses/{generation → impl/generation}/execution.py +1 -1
- omlish/dataclasses/{generation → impl/generation}/globals.py +3 -3
- omlish/dataclasses/{generation → impl/generation}/mangling.py +1 -1
- omlish/dataclasses/{generation → impl/generation}/plans.py +1 -1
- omlish/dataclasses/{generation → impl/generation}/processor.py +41 -14
- omlish/dataclasses/{generation → impl/generation}/registry.py +2 -2
- omlish/dataclasses/impl/processing/__init__.py +0 -0
- omlish/dataclasses/{processing → impl/processing}/base.py +3 -3
- omlish/dataclasses/{processing → impl/processing}/driving.py +24 -2
- omlish/dataclasses/{processing → impl/processing}/registry.py +2 -2
- omlish/dataclasses/{utils.py → impl/utils.py} +1 -1
- omlish/dataclasses/inspect.py +4 -4
- omlish/dataclasses/metaclass/bases.py +2 -2
- omlish/dataclasses/metaclass/confer.py +1 -1
- omlish/dataclasses/metaclass/meta.py +1 -1
- omlish/dataclasses/metaclass/specs.py +1 -1
- omlish/dataclasses/reflection.py +9 -9
- omlish/dataclasses/tools/as_.py +2 -2
- omlish/dataclasses/tools/static.py +2 -2
- omlish/diag/pydevd.py +2 -2
- omlish/diag/timers.py +184 -0
- omlish/inject/impl/injector.py +1 -1
- omlish/inject/listeners.py +1 -1
- omlish/lang/__init__.py +1 -0
- omlish/lang/cached/function.py +10 -0
- omlish/lang/contextmanagers.py +27 -0
- omlish/reflect/subst.py +43 -2
- {omlish-0.0.0.dev394.dist-info → omlish-0.0.0.dev396.dist-info}/METADATA +3 -3
- {omlish-0.0.0.dev394.dist-info → omlish-0.0.0.dev396.dist-info}/RECORD +77 -73
- /omlish/dataclasses/{internals.py → _internals.py} +0 -0
- /omlish/dataclasses/{api/classes → impl}/__init__.py +0 -0
- /omlish/dataclasses/{api → impl/api}/__init__.py +0 -0
- /omlish/dataclasses/{api/fields → impl/api/classes}/__init__.py +0 -0
- /omlish/dataclasses/{api → impl/api}/classes/make.py +0 -0
- /omlish/dataclasses/{generation → impl/api/fields}/__init__.py +0 -0
- /omlish/dataclasses/{concerns → impl/concerns}/__init__.py +0 -0
- /omlish/dataclasses/{concerns → impl/concerns}/abc.py +0 -0
- /omlish/dataclasses/{concerns → impl/concerns}/doc.py +0 -0
- /omlish/dataclasses/{concerns → impl/concerns}/eq.py +0 -0
- /omlish/dataclasses/{concerns → impl/concerns}/order.py +0 -0
- /omlish/dataclasses/{concerns → impl/concerns}/override.py +0 -0
- /omlish/dataclasses/{concerns → impl/concerns}/slots.py +0 -0
- /omlish/dataclasses/{processing → impl/generation}/__init__.py +0 -0
- /omlish/dataclasses/{generation → impl/generation}/base.py +0 -0
- /omlish/dataclasses/{generation → impl/generation}/idents.py +0 -0
- /omlish/dataclasses/{generation → impl/generation}/manifests.py +0 -0
- /omlish/dataclasses/{generation → impl/generation}/ops.py +0 -0
- /omlish/dataclasses/{generation → impl/generation}/utils.py +0 -0
- /omlish/dataclasses/{processing → impl/processing}/priority.py +0 -0
- {omlish-0.0.0.dev394.dist-info → omlish-0.0.0.dev396.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev394.dist-info → omlish-0.0.0.dev396.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev394.dist-info → omlish-0.0.0.dev396.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev394.dist-info → omlish-0.0.0.dev396.dist-info}/top_level.txt +0 -0
@@ -8,8 +8,8 @@ import dataclasses as dc
|
|
8
8
|
import sys
|
9
9
|
import typing as ta
|
10
10
|
|
11
|
-
from
|
12
|
-
from
|
11
|
+
from .... import check
|
12
|
+
from .... import lang
|
13
13
|
from ..processing.base import ProcessingContext
|
14
14
|
from ..processing.base import ProcessingOption
|
15
15
|
from ..processing.base import Processor
|
@@ -43,6 +43,21 @@ class Verbose(ProcessingOption):
|
|
43
43
|
b: bool
|
44
44
|
|
45
45
|
|
46
|
+
class CompileCallback(ta.Protocol):
|
47
|
+
def __call__(
|
48
|
+
self,
|
49
|
+
ctx: ProcessingContext,
|
50
|
+
prepared: 'GeneratorProcessor.Prepared',
|
51
|
+
comp: OpCompiler.CompileResult,
|
52
|
+
) -> None:
|
53
|
+
...
|
54
|
+
|
55
|
+
|
56
|
+
@dc.dataclass(frozen=True)
|
57
|
+
class Codegen(ProcessingOption):
|
58
|
+
callback: CompileCallback
|
59
|
+
|
60
|
+
|
46
61
|
@register_processor_type(priority=ProcessorPriority.GENERATION)
|
47
62
|
class GeneratorProcessor(Processor):
|
48
63
|
class Mode(abc.ABC):
|
@@ -61,6 +76,15 @@ class GeneratorProcessor(Processor):
|
|
61
76
|
opx.execute(op)
|
62
77
|
|
63
78
|
class CompilerMode(Mode):
|
79
|
+
def __init__(
|
80
|
+
self,
|
81
|
+
*,
|
82
|
+
codegen: Codegen,
|
83
|
+
) -> None:
|
84
|
+
super().__init__()
|
85
|
+
|
86
|
+
self._codegen = codegen
|
87
|
+
|
64
88
|
def _process(self, gp: 'GeneratorProcessor', cls: type) -> None:
|
65
89
|
compiler = OpCompiler(
|
66
90
|
OpCompiler.AotStyle(),
|
@@ -115,18 +139,14 @@ class GeneratorProcessor(Processor):
|
|
115
139
|
|
116
140
|
fn(**kw)
|
117
141
|
|
118
|
-
|
142
|
+
if (cg := self._codegen) is not None and (cb := cg.callback) is not None:
|
143
|
+
cb(
|
144
|
+
gp._ctx, # noqa
|
145
|
+
gp.prepare(),
|
146
|
+
comp,
|
147
|
+
)
|
119
148
|
|
120
|
-
|
121
|
-
self,
|
122
|
-
ctx: ProcessingContext,
|
123
|
-
*,
|
124
|
-
mode: Mode = ExecutorMode(),
|
125
|
-
# mode: Mode = CompilerMode(),
|
126
|
-
) -> None:
|
127
|
-
super().__init__(ctx)
|
128
|
-
|
129
|
-
self._mode = mode
|
149
|
+
#
|
130
150
|
|
131
151
|
@dc.dataclass(frozen=True)
|
132
152
|
class Prepared:
|
@@ -177,6 +197,13 @@ class GeneratorProcessor(Processor):
|
|
177
197
|
def process(self, cls: type) -> type:
|
178
198
|
if (po := self._ctx.option(PlanOnly)) is not None and po.b:
|
179
199
|
self.prepare()
|
200
|
+
return cls
|
201
|
+
|
202
|
+
mode: GeneratorProcessor.Mode
|
203
|
+
if (cg := self._ctx.option(Codegen)) is not None: # noqa
|
204
|
+
mode = GeneratorProcessor.CompilerMode(codegen=cg)
|
180
205
|
else:
|
181
|
-
|
206
|
+
mode = GeneratorProcessor.ExecutorMode()
|
207
|
+
|
208
|
+
mode._process(self, cls) # noqa
|
182
209
|
return cls
|
File without changes
|
@@ -1,7 +1,11 @@
|
|
1
|
+
import contextlib
|
2
|
+
import contextvars
|
3
|
+
import typing as ta
|
1
4
|
|
5
|
+
from .... import lang
|
6
|
+
from ...specs import ClassSpec
|
2
7
|
from .. import concerns as _concerns # noqa # imported for registration
|
3
8
|
from ..generation import processor as gp
|
4
|
-
from ..specs import ClassSpec
|
5
9
|
from .base import ProcessingContext
|
6
10
|
from .base import ProcessingOption
|
7
11
|
from .base import Processor
|
@@ -12,6 +16,24 @@ from .registry import ordered_processor_types
|
|
12
16
|
##
|
13
17
|
|
14
18
|
|
19
|
+
_OPTIONS_CONTEXT_VAR: contextvars.ContextVar[ta.Sequence[ProcessingOption]] = contextvars.ContextVar(
|
20
|
+
f'{__name__}._OPTIONS_CONTEXT_VAR',
|
21
|
+
default=(),
|
22
|
+
)
|
23
|
+
|
24
|
+
|
25
|
+
@contextlib.contextmanager
|
26
|
+
def processing_options_context(*opts: ProcessingOption) -> ta.Iterator[None]:
|
27
|
+
with lang.context_var_setting(
|
28
|
+
_OPTIONS_CONTEXT_VAR,
|
29
|
+
(*_OPTIONS_CONTEXT_VAR.get(), *opts),
|
30
|
+
):
|
31
|
+
yield
|
32
|
+
|
33
|
+
|
34
|
+
##
|
35
|
+
|
36
|
+
|
15
37
|
def drive_cls_processing(
|
16
38
|
cls: type,
|
17
39
|
cs: ClassSpec,
|
@@ -19,7 +41,7 @@ def drive_cls_processing(
|
|
19
41
|
plan_only: bool = False,
|
20
42
|
verbose: bool = False,
|
21
43
|
) -> type:
|
22
|
-
options: list[ProcessingOption] =
|
44
|
+
options: list[ProcessingOption] = list(_OPTIONS_CONTEXT_VAR.get())
|
23
45
|
if plan_only:
|
24
46
|
options.append(gp.PlanOnly(True))
|
25
47
|
if verbose:
|
@@ -1,8 +1,8 @@
|
|
1
1
|
import dataclasses as dc
|
2
2
|
import typing as ta
|
3
3
|
|
4
|
-
from
|
5
|
-
from
|
4
|
+
from .... import check
|
5
|
+
from .... import lang
|
6
6
|
from ..utils import SealableRegistry
|
7
7
|
from .base import ProcessingContextItemFactory
|
8
8
|
from .base import Processor
|
omlish/dataclasses/inspect.py
CHANGED
@@ -7,10 +7,10 @@ from .. import check
|
|
7
7
|
from .. import collections as col
|
8
8
|
from .. import lang
|
9
9
|
from .. import reflect as rfl
|
10
|
-
from .
|
11
|
-
from .
|
12
|
-
from .
|
13
|
-
from .
|
10
|
+
from ._internals import STD_FIELDS_ATTR
|
11
|
+
from ._internals import StdFieldType
|
12
|
+
from ._internals import std_field_type
|
13
|
+
from ._internals import std_is_kw_only
|
14
14
|
|
15
15
|
|
16
16
|
ClassAnnotations: ta.TypeAlias = ta.Mapping[str, ta.Any]
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import typing as ta
|
2
2
|
|
3
|
-
from ..api.classes.decorator import dataclass
|
4
|
-
from ..concerns.frozen import unchecked_frozen_base
|
3
|
+
from ..impl.api.classes.decorator import dataclass
|
4
|
+
from ..impl.concerns.frozen import unchecked_frozen_base
|
5
5
|
from .meta import DataMeta
|
6
6
|
from .specs import get_metaclass_spec
|
7
7
|
|
@@ -8,7 +8,7 @@ import dataclasses as dc
|
|
8
8
|
import typing as ta
|
9
9
|
|
10
10
|
from ... import lang
|
11
|
-
from ..api.classes.decorator import dataclass
|
11
|
+
from ..impl.api.classes.decorator import dataclass
|
12
12
|
from .confer import CONFER_METACLASS_PARAMS
|
13
13
|
from .confer import confer_kwargs
|
14
14
|
from .specs import MetaclassSpec
|
omlish/dataclasses/reflection.py
CHANGED
@@ -2,17 +2,17 @@ import dataclasses as dc
|
|
2
2
|
import typing as ta
|
3
3
|
|
4
4
|
from .. import lang
|
5
|
-
from .
|
6
|
-
from .
|
7
|
-
from .
|
8
|
-
from .api.
|
9
|
-
from .
|
10
|
-
from .
|
5
|
+
from ._internals import STD_PARAMS_ATTR
|
6
|
+
from ._internals import StdFieldType
|
7
|
+
from ._internals import std_field_type
|
8
|
+
from .impl.api.classes.conversion import std_params_to_class_spec
|
9
|
+
from .impl.api.classes.metadata import extract_cls_metadata
|
10
|
+
from .impl.api.classes.params import get_class_spec
|
11
|
+
from .impl.api.fields.conversion import std_field_to_field_spec
|
12
|
+
from .impl.concerns.fields import InitFields
|
13
|
+
from .impl.concerns.fields import calc_init_fields
|
11
14
|
from .inspect import FieldsInspection
|
12
15
|
from .inspect import inspect_fields
|
13
|
-
from .internals import STD_PARAMS_ATTR
|
14
|
-
from .internals import StdFieldType
|
15
|
-
from .internals import std_field_type
|
16
16
|
from .specs import ClassSpec
|
17
17
|
|
18
18
|
|
omlish/dataclasses/tools/as_.py
CHANGED
@@ -10,7 +10,7 @@ import typing as ta
|
|
10
10
|
|
11
11
|
from ... import lang
|
12
12
|
from ...lite.dataclasses import is_immediate_dataclass
|
13
|
-
from ..api.classes.decorator import dataclass
|
13
|
+
from ..impl.api.classes.decorator import dataclass
|
14
14
|
|
15
15
|
|
16
16
|
##
|
@@ -123,7 +123,7 @@ class Static(lang.Abstract):
|
|
123
123
|
new_fld.default_factory = (lambda v2: lambda: v2)(v) # noqa
|
124
124
|
|
125
125
|
# FIXME
|
126
|
-
from ..api.fields.metadata import _ExtraFieldParamsMetadata # noqa
|
126
|
+
from ..impl.api.fields.metadata import _ExtraFieldParamsMetadata # noqa
|
127
127
|
from ..specs import FieldSpec
|
128
128
|
try:
|
129
129
|
x_fs = fld.metadata[FieldSpec]
|
omlish/diag/pydevd.py
CHANGED
@@ -124,7 +124,7 @@ def is_present() -> bool:
|
|
124
124
|
|
125
125
|
def get_setup() -> dict | None:
|
126
126
|
if is_present():
|
127
|
-
return _pydevd().SetupHolder.setup
|
127
|
+
return check.not_none(_pydevd()).SetupHolder.setup
|
128
128
|
else:
|
129
129
|
return None
|
130
130
|
|
@@ -160,7 +160,7 @@ ARGS_ENV_VAR = 'PYDEVD_ARGS'
|
|
160
160
|
def get_args() -> list[str]:
|
161
161
|
check.state(is_present())
|
162
162
|
setup: ta.Mapping[ta.Any, ta.Any] = check.isinstance(get_setup(), dict)
|
163
|
-
args = [_pydevd().__file__]
|
163
|
+
args = [check.not_none(check.not_none(_pydevd()).__file__)]
|
164
164
|
|
165
165
|
for k in [
|
166
166
|
'port',
|
omlish/diag/timers.py
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
import atexit
|
2
|
+
import contextlib
|
3
|
+
import functools
|
4
|
+
import sys
|
5
|
+
import threading
|
6
|
+
import time
|
7
|
+
import typing as ta
|
8
|
+
|
9
|
+
from .. import check
|
10
|
+
from .. import lang
|
11
|
+
|
12
|
+
|
13
|
+
T = ta.TypeVar('T')
|
14
|
+
|
15
|
+
|
16
|
+
##
|
17
|
+
|
18
|
+
|
19
|
+
class _GlobalTimer:
|
20
|
+
def __init__(
|
21
|
+
self,
|
22
|
+
registry: '_GlobalTimerRegistry',
|
23
|
+
name: str,
|
24
|
+
*,
|
25
|
+
clock: ta.Callable[[], float] | None = None,
|
26
|
+
report_at_exit: bool = False,
|
27
|
+
report_out: ta.Any | None = None,
|
28
|
+
) -> None:
|
29
|
+
super().__init__()
|
30
|
+
|
31
|
+
self._registry = registry
|
32
|
+
self._name = name
|
33
|
+
|
34
|
+
if clock is None:
|
35
|
+
clock = time.monotonic
|
36
|
+
self._clock = clock
|
37
|
+
self._report_out = report_out
|
38
|
+
|
39
|
+
self._lock = threading.Lock()
|
40
|
+
|
41
|
+
self._c = 0
|
42
|
+
self._t = 0.
|
43
|
+
|
44
|
+
if report_at_exit:
|
45
|
+
atexit.register(self.report)
|
46
|
+
|
47
|
+
def report(self) -> None:
|
48
|
+
msg = (
|
49
|
+
f'{self._registry.module_name}::{self._name}: '
|
50
|
+
f'{self._c} calls, '
|
51
|
+
f'{self._t:.3f}s total'
|
52
|
+
)
|
53
|
+
|
54
|
+
print(
|
55
|
+
msg,
|
56
|
+
file=self._report_out if self._report_out is not None else sys.stderr,
|
57
|
+
)
|
58
|
+
|
59
|
+
@contextlib.contextmanager
|
60
|
+
def __call__(self) -> ta.Iterator[None]:
|
61
|
+
start = self._clock()
|
62
|
+
|
63
|
+
try:
|
64
|
+
yield
|
65
|
+
|
66
|
+
finally:
|
67
|
+
end = self._clock()
|
68
|
+
took = end - start
|
69
|
+
|
70
|
+
with self._lock:
|
71
|
+
self._c += 1
|
72
|
+
self._t += took
|
73
|
+
|
74
|
+
|
75
|
+
#
|
76
|
+
|
77
|
+
|
78
|
+
class _GlobalTimerRegistry:
|
79
|
+
def __init__(
|
80
|
+
self,
|
81
|
+
module_name: str,
|
82
|
+
) -> None:
|
83
|
+
super().__init__()
|
84
|
+
|
85
|
+
self._module_name = module_name
|
86
|
+
|
87
|
+
self._lock = threading.Lock()
|
88
|
+
|
89
|
+
self._timers: dict[str, _GlobalTimer] = {}
|
90
|
+
|
91
|
+
@property
|
92
|
+
def module_name(self) -> str:
|
93
|
+
return self._module_name
|
94
|
+
|
95
|
+
def get_timer(
|
96
|
+
self,
|
97
|
+
name: str,
|
98
|
+
**kwargs: ta.Any,
|
99
|
+
) -> _GlobalTimer:
|
100
|
+
return lang.double_check_setdefault(
|
101
|
+
self._lock,
|
102
|
+
self._timers,
|
103
|
+
name,
|
104
|
+
lambda: _GlobalTimer(
|
105
|
+
self,
|
106
|
+
name,
|
107
|
+
**kwargs,
|
108
|
+
),
|
109
|
+
)
|
110
|
+
|
111
|
+
|
112
|
+
_GLOBAL_LOCK = threading.Lock()
|
113
|
+
_GLOBAL_ATTR = '__global_timers_registry__'
|
114
|
+
|
115
|
+
|
116
|
+
def _get_global_registry(
|
117
|
+
globals: ta.MutableMapping[str, ta.Any], # noqa
|
118
|
+
) -> _GlobalTimerRegistry:
|
119
|
+
reg = lang.double_check_setdefault(
|
120
|
+
_GLOBAL_LOCK,
|
121
|
+
globals,
|
122
|
+
_GLOBAL_ATTR,
|
123
|
+
lambda: _GlobalTimerRegistry(
|
124
|
+
globals['__name__'],
|
125
|
+
),
|
126
|
+
)
|
127
|
+
|
128
|
+
return check.isinstance(reg, _GlobalTimerRegistry)
|
129
|
+
|
130
|
+
|
131
|
+
#
|
132
|
+
|
133
|
+
|
134
|
+
@contextlib.contextmanager
|
135
|
+
def global_timer_context(
|
136
|
+
globals: ta.MutableMapping[str, ta.Any], # noqa
|
137
|
+
name: str,
|
138
|
+
**kwargs: ta.Any,
|
139
|
+
) -> ta.Iterator[None]:
|
140
|
+
reg = _get_global_registry(globals)
|
141
|
+
timer = reg.get_timer(name, **kwargs)
|
142
|
+
with timer():
|
143
|
+
yield
|
144
|
+
|
145
|
+
|
146
|
+
#
|
147
|
+
|
148
|
+
|
149
|
+
class _GlobalTimerContextWrapped:
|
150
|
+
def __init__(
|
151
|
+
self,
|
152
|
+
fn: ta.Any,
|
153
|
+
timer: _GlobalTimer,
|
154
|
+
) -> None:
|
155
|
+
super().__init__()
|
156
|
+
|
157
|
+
self._fn = fn
|
158
|
+
self._timer = timer
|
159
|
+
|
160
|
+
functools.update_wrapper(self, fn)
|
161
|
+
|
162
|
+
def __get__(self, instance, owner=None):
|
163
|
+
return self.__class__(
|
164
|
+
self._fn.__get__(instance, owner),
|
165
|
+
self._timer,
|
166
|
+
)
|
167
|
+
|
168
|
+
def __call__(self, *args, **kwargs):
|
169
|
+
with self._timer():
|
170
|
+
return self._fn(*args, **kwargs)
|
171
|
+
|
172
|
+
|
173
|
+
def global_timer_wrap(
|
174
|
+
globals: ta.MutableMapping[str, ta.Any], # noqa
|
175
|
+
name: str,
|
176
|
+
**kwargs: ta.Any,
|
177
|
+
) -> ta.Callable[[T], T]:
|
178
|
+
reg = _get_global_registry(globals)
|
179
|
+
timer = reg.get_timer(name, **kwargs)
|
180
|
+
|
181
|
+
def inner(fn):
|
182
|
+
return _GlobalTimerContextWrapped(fn, timer)
|
183
|
+
|
184
|
+
return inner
|
omlish/inject/impl/injector.py
CHANGED
@@ -69,7 +69,7 @@ class InjectorImpl(Injector, lang.Final):
|
|
69
69
|
self._bim = ec.binding_impl_map()
|
70
70
|
self._ekbs = ec.eager_keys_by_scope()
|
71
71
|
self._pls: tuple[ProvisionListener, ...] = tuple(
|
72
|
-
b.listener
|
72
|
+
b.listener # type: ignore[attr-defined]
|
73
73
|
for b in itertools.chain(
|
74
74
|
ec.elements_of_type(ProvisionListenerBinding),
|
75
75
|
(p._pls if p is not None else ()), # noqa
|
omlish/inject/listeners.py
CHANGED
omlish/lang/__init__.py
CHANGED
omlish/lang/cached/function.py
CHANGED
@@ -415,6 +415,16 @@ class _DescriptorCachedFunction(_CachedFunction[T]):
|
|
415
415
|
#
|
416
416
|
|
417
417
|
|
418
|
+
@ta.overload
|
419
|
+
def cached_function(fn: None = None, **kwargs: ta.Any) -> ta.Callable[[CallableT], CallableT]:
|
420
|
+
...
|
421
|
+
|
422
|
+
|
423
|
+
@ta.overload
|
424
|
+
def cached_function(fn: CallableT, **kwargs: ta.Any) -> CallableT:
|
425
|
+
...
|
426
|
+
|
427
|
+
|
418
428
|
def cached_function(fn=None, **kwargs): # noqa
|
419
429
|
if fn is None:
|
420
430
|
return functools.partial(cached_function, **kwargs)
|
omlish/lang/contextmanagers.py
CHANGED
@@ -13,6 +13,8 @@ import typing as ta
|
|
13
13
|
|
14
14
|
|
15
15
|
T = ta.TypeVar('T')
|
16
|
+
K = ta.TypeVar('K')
|
17
|
+
V = ta.TypeVar('V')
|
16
18
|
|
17
19
|
|
18
20
|
##
|
@@ -286,3 +288,28 @@ class Timer:
|
|
286
288
|
|
287
289
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
288
290
|
self._end = self._clock()
|
291
|
+
|
292
|
+
|
293
|
+
##
|
294
|
+
|
295
|
+
|
296
|
+
def double_check_setdefault(
|
297
|
+
cm: ta.ContextManager,
|
298
|
+
dct: ta.MutableMapping[K, V],
|
299
|
+
k: K,
|
300
|
+
fn: ta.Callable[[], V],
|
301
|
+
) -> V:
|
302
|
+
try:
|
303
|
+
return dct[k]
|
304
|
+
except KeyError:
|
305
|
+
pass
|
306
|
+
|
307
|
+
with cm:
|
308
|
+
try:
|
309
|
+
return dct[k]
|
310
|
+
except KeyError:
|
311
|
+
pass
|
312
|
+
|
313
|
+
v = fn()
|
314
|
+
dct[k] = v
|
315
|
+
return v
|
omlish/reflect/subst.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import abc
|
1
2
|
import dataclasses as dc
|
2
3
|
import types
|
3
4
|
import typing as ta
|
@@ -22,6 +23,9 @@ else:
|
|
22
23
|
cache = lang.proxy_import('.collections.cache', __package__)
|
23
24
|
|
24
25
|
|
26
|
+
GenericBasesMap: ta.TypeAlias = ta.Mapping[Type, tuple[Type, ...]]
|
27
|
+
|
28
|
+
|
25
29
|
##
|
26
30
|
|
27
31
|
|
@@ -69,22 +73,35 @@ def replace_type_vars(
|
|
69
73
|
return rec(ty)
|
70
74
|
|
71
75
|
|
76
|
+
##
|
77
|
+
|
78
|
+
|
79
|
+
_DEFAULT_SIMPLE_GENERIC_BASES: dict[Type, tuple[Type, ...]] = {}
|
80
|
+
DEFAULT_SIMPLE_GENERIC_BASES: GenericBasesMap = _DEFAULT_SIMPLE_GENERIC_BASES
|
81
|
+
|
82
|
+
|
72
83
|
class GenericSubstitution:
|
73
84
|
def __init__(
|
74
85
|
self,
|
75
86
|
*,
|
76
87
|
update_aliases: bool = False,
|
77
88
|
cache_size: int = 0, # FIXME: ta.Generic isn't weakrefable..
|
89
|
+
simple_generic_bases: GenericBasesMap | None = None,
|
78
90
|
) -> None:
|
79
91
|
super().__init__()
|
80
92
|
|
81
93
|
self._update_aliases = update_aliases
|
94
|
+
self._simple_generic_bases = simple_generic_bases
|
82
95
|
|
83
96
|
if cache_size > 0:
|
84
97
|
self.get_generic_bases = cache.cache(weak_keys=True, max_size=cache_size)(self.get_generic_bases) # type: ignore # noqa
|
85
98
|
self.generic_mro = cache.cache(weak_keys=True, max_size=cache_size)(self.generic_mro) # type: ignore
|
86
99
|
|
87
100
|
def get_generic_bases(self, ty: Type) -> tuple[Type, ...]:
|
101
|
+
if (sgm := self._simple_generic_bases) is not None:
|
102
|
+
if (sgb := sgm.get(ty)) is not None:
|
103
|
+
return sgb
|
104
|
+
|
88
105
|
if (cty := get_concrete_type(ty)) is not None:
|
89
106
|
rpl = get_type_var_replacements(ty)
|
90
107
|
ret: list[Type] = []
|
@@ -97,6 +114,7 @@ class GenericSubstitution:
|
|
97
114
|
rty = replace_type_vars(bty, rpl, update_aliases=self._update_aliases)
|
98
115
|
ret.append(rty)
|
99
116
|
return tuple(ret)
|
117
|
+
|
100
118
|
return ()
|
101
119
|
|
102
120
|
def generic_mro(self, obj: ta.Any) -> list[Type]:
|
@@ -108,9 +126,32 @@ class GenericSubstitution:
|
|
108
126
|
return [ty for ty in mro if get_concrete_type(ty) is not ta.Generic]
|
109
127
|
|
110
128
|
|
111
|
-
DEFAULT_GENERIC_SUBSTITUTION = GenericSubstitution(
|
129
|
+
DEFAULT_GENERIC_SUBSTITUTION = GenericSubstitution(
|
130
|
+
simple_generic_bases=DEFAULT_SIMPLE_GENERIC_BASES,
|
131
|
+
)
|
112
132
|
|
113
133
|
get_generic_bases = DEFAULT_GENERIC_SUBSTITUTION.get_generic_bases
|
114
134
|
generic_mro = DEFAULT_GENERIC_SUBSTITUTION.generic_mro
|
115
135
|
|
116
|
-
ALIAS_UPDATING_GENERIC_SUBSTITUTION = GenericSubstitution(
|
136
|
+
ALIAS_UPDATING_GENERIC_SUBSTITUTION = GenericSubstitution(
|
137
|
+
update_aliases=True,
|
138
|
+
simple_generic_bases=DEFAULT_SIMPLE_GENERIC_BASES,
|
139
|
+
)
|
140
|
+
|
141
|
+
|
142
|
+
##
|
143
|
+
|
144
|
+
|
145
|
+
_DEFAULT_SIMPLE_GENERIC_BASE_LIST: ta.Sequence[Type] = [
|
146
|
+
object,
|
147
|
+
abc.ABC,
|
148
|
+
lang.Abstract,
|
149
|
+
lang.Sealed,
|
150
|
+
lang.PackageSealed,
|
151
|
+
lang.Final,
|
152
|
+
]
|
153
|
+
|
154
|
+
_DEFAULT_SIMPLE_GENERIC_BASES.update({
|
155
|
+
b: get_generic_bases(b)
|
156
|
+
for b in _DEFAULT_SIMPLE_GENERIC_BASE_LIST
|
157
|
+
})
|