omlish 0.0.0.dev402__py3-none-any.whl → 0.0.0.dev404__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.
Potentially problematic release.
This version of omlish might be problematic. Click here for more details.
- omlish/.manifests.json +1 -1
- omlish/__about__.py +3 -3
- omlish/argparse/cli.py +1 -0
- omlish/codecs/registry.py +6 -2
- omlish/dataclasses/_internals.py +12 -5
- omlish/dataclasses/impl/api/classes/make.py +67 -3
- omlish/dataclasses/impl/api/fields/constructor.py +7 -0
- omlish/dataclasses/impl/api/fields/conversion.py +14 -0
- omlish/dataclasses/impl/concerns/doc.py +19 -1
- omlish/dataclasses/impl/concerns/slots.py +37 -11
- omlish/dataclasses/metaclass/meta.py +22 -1
- omlish/dataclasses/specs.py +1 -1
- omlish/dataclasses/tools/static.py +1 -0
- omlish/dispatch/impls.py +1 -2
- omlish/dispatch/methods.py +0 -6
- omlish/inject/impl/proxy.py +2 -0
- omlish/inject/impl/scopes.py +4 -0
- omlish/io/compress/zstd.py +16 -8
- omlish/lang/__init__.py +2 -0
- omlish/lang/objects.py +1 -0
- omlish/lang/typing.py +32 -2
- omlish/lite/marshal.py +24 -24
- omlish/lite/reflect.py +22 -5
- omlish/lite/typing.py +11 -0
- omlish/manifests/loading.py +10 -7
- omlish/reflect/__init__.py +1 -0
- omlish/reflect/inspect.py +10 -0
- omlish/testing/__init__.py +1 -0
- omlish/testing/pytest/skip.py +20 -5
- omlish/testing/testing.py +30 -0
- {omlish-0.0.0.dev402.dist-info → omlish-0.0.0.dev404.dist-info}/METADATA +3 -3
- {omlish-0.0.0.dev402.dist-info → omlish-0.0.0.dev404.dist-info}/RECORD +36 -36
- {omlish-0.0.0.dev402.dist-info → omlish-0.0.0.dev404.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev402.dist-info → omlish-0.0.0.dev404.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev402.dist-info → omlish-0.0.0.dev404.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev402.dist-info → omlish-0.0.0.dev404.dist-info}/top_level.txt +0 -0
omlish/.manifests.json
CHANGED
omlish/__about__.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
__version__ = '0.0.0.
|
|
2
|
-
__revision__ = '
|
|
1
|
+
__version__ = '0.0.0.dev404'
|
|
2
|
+
__revision__ = 'bd964bf904e22ab46e4d3b4f7ec3028f9aed5c9a'
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
#
|
|
@@ -51,7 +51,7 @@ class Project(ProjectBase):
|
|
|
51
51
|
|
|
52
52
|
'python-snappy ~= 0.7',
|
|
53
53
|
|
|
54
|
-
'zstandard ~= 0.24',
|
|
54
|
+
'zstandard ~= 0.24; python_version < "3.14"',
|
|
55
55
|
|
|
56
56
|
'brotli ~= 1.1',
|
|
57
57
|
],
|
omlish/argparse/cli.py
CHANGED
|
@@ -140,6 +140,7 @@ def _get_argparse_arg_ann_kwargs(ann: ta.Any) -> ta.Mapping[str, ta.Any]:
|
|
|
140
140
|
class _ArgparseCliAnnotationBox:
|
|
141
141
|
def __init__(self, annotations: ta.Mapping[str, ta.Any]) -> None:
|
|
142
142
|
super().__init__()
|
|
143
|
+
|
|
143
144
|
self.__annotations__ = annotations # type: ignore
|
|
144
145
|
|
|
145
146
|
|
omlish/codecs/registry.py
CHANGED
|
@@ -27,6 +27,8 @@ class CodecRegistry:
|
|
|
27
27
|
) -> None:
|
|
28
28
|
super().__init__()
|
|
29
29
|
|
|
30
|
+
if late_load_callbacks is not None:
|
|
31
|
+
late_load_callbacks = list(late_load_callbacks)
|
|
30
32
|
self._late_load_callbacks = late_load_callbacks
|
|
31
33
|
|
|
32
34
|
self._lock = threading.RLock()
|
|
@@ -36,8 +38,10 @@ class CodecRegistry:
|
|
|
36
38
|
|
|
37
39
|
def _late_load(self) -> None:
|
|
38
40
|
if self._late_load_callbacks:
|
|
39
|
-
|
|
41
|
+
while self._late_load_callbacks:
|
|
42
|
+
cb = self._late_load_callbacks[0]
|
|
40
43
|
cb(self)
|
|
44
|
+
self._late_load_callbacks.pop(0)
|
|
41
45
|
self._late_load_callbacks = None
|
|
42
46
|
|
|
43
47
|
@contextlib.contextmanager
|
|
@@ -103,7 +107,7 @@ def _install_standard_codecs(registry: CodecRegistry) -> None:
|
|
|
103
107
|
@cached.function
|
|
104
108
|
def _build_manifest_lazy_loaded_codecs() -> ta.Sequence[LazyLoadedCodec]:
|
|
105
109
|
ldr = manifest_globals.GlobalManifestLoader.instance()
|
|
106
|
-
pkgs = {__package__.split('.')[0], *ldr.discover_packages()}
|
|
110
|
+
pkgs = {__package__.split('.')[0], *ldr.discover_packages()} # FIXME
|
|
107
111
|
mns = ldr.load(*pkgs, only=[LazyLoadedCodec])
|
|
108
112
|
return [m.value() for m in mns]
|
|
109
113
|
|
omlish/dataclasses/_internals.py
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
"""
|
|
2
|
+
For arcane import machinery compatibility with stdlib, this must be a direct child of the 'dataclasses' package - not
|
|
3
|
+
under 'impl'.
|
|
4
|
+
"""
|
|
1
5
|
import dataclasses as dc
|
|
2
6
|
import enum
|
|
3
7
|
import sys
|
|
@@ -101,7 +105,10 @@ def _self_module():
|
|
|
101
105
|
def std_is_classvar(cls: type, ty: ta.Any) -> bool:
|
|
102
106
|
return (
|
|
103
107
|
dc._is_classvar(ty, ta) # type: ignore # noqa
|
|
104
|
-
or (
|
|
108
|
+
or (
|
|
109
|
+
isinstance(ty, str) and
|
|
110
|
+
dc._is_type(ty, cls, ta, ta.ClassVar, dc._is_classvar) # type: ignore # noqa
|
|
111
|
+
)
|
|
105
112
|
)
|
|
106
113
|
|
|
107
114
|
|
|
@@ -109,8 +116,8 @@ def std_is_initvar(cls: type, ty: ta.Any) -> bool:
|
|
|
109
116
|
return (
|
|
110
117
|
dc._is_initvar(ty, dc) # type: ignore # noqa
|
|
111
118
|
or (
|
|
112
|
-
isinstance(ty, str)
|
|
113
|
-
|
|
119
|
+
isinstance(ty, str) and
|
|
120
|
+
any(
|
|
114
121
|
dc._is_type(ty, cls, mod, dc.InitVar, dc._is_initvar) # type: ignore # noqa
|
|
115
122
|
for mod in (dc, _self_module())
|
|
116
123
|
)
|
|
@@ -122,8 +129,8 @@ def std_is_kw_only(cls: type, ty: ta.Any) -> bool:
|
|
|
122
129
|
return (
|
|
123
130
|
dc._is_kw_only(ty, dc) # type: ignore # noqa
|
|
124
131
|
or (
|
|
125
|
-
isinstance(ty, str)
|
|
126
|
-
|
|
132
|
+
isinstance(ty, str) and
|
|
133
|
+
any(
|
|
127
134
|
dc._is_type(ty, cls, mod, dc.KW_ONLY, dc._is_kw_only) # type: ignore # noqa
|
|
128
135
|
for mod in (dc, _self_module())
|
|
129
136
|
)
|
|
@@ -10,6 +10,14 @@ from .decorator import dataclass
|
|
|
10
10
|
##
|
|
11
11
|
|
|
12
12
|
|
|
13
|
+
_IS_PY_3_14 = sys.version_info >= (3, 14)
|
|
14
|
+
|
|
15
|
+
if _IS_PY_3_14:
|
|
16
|
+
import annotationlib # noqa
|
|
17
|
+
|
|
18
|
+
_ANY_MARKER = object()
|
|
19
|
+
|
|
20
|
+
|
|
13
21
|
def make_dataclass( # noqa
|
|
14
22
|
cls_name,
|
|
15
23
|
fields,
|
|
@@ -31,6 +39,8 @@ def make_dataclass( # noqa
|
|
|
31
39
|
|
|
32
40
|
module=None,
|
|
33
41
|
|
|
42
|
+
decorator=dataclass,
|
|
43
|
+
|
|
34
44
|
#
|
|
35
45
|
|
|
36
46
|
metadata: ta.Sequence[ta.Any] | None = None,
|
|
@@ -46,6 +56,12 @@ def make_dataclass( # noqa
|
|
|
46
56
|
|
|
47
57
|
allow_redundant_decorator: bool | None = None,
|
|
48
58
|
):
|
|
59
|
+
if decorator is not dataclass:
|
|
60
|
+
raise TypeError(
|
|
61
|
+
f'The `decorator` kwarg, as added in https://github.com/python/cpython/pull/122723, is not supported. '
|
|
62
|
+
f'{decorator=}',
|
|
63
|
+
)
|
|
64
|
+
|
|
49
65
|
if namespace is None:
|
|
50
66
|
namespace = {}
|
|
51
67
|
|
|
@@ -55,7 +71,10 @@ def make_dataclass( # noqa
|
|
|
55
71
|
for item in fields:
|
|
56
72
|
if isinstance(item, str):
|
|
57
73
|
name = item
|
|
58
|
-
|
|
74
|
+
if _IS_PY_3_14:
|
|
75
|
+
tp = _ANY_MARKER
|
|
76
|
+
else:
|
|
77
|
+
tp = 'typing.Any'
|
|
59
78
|
elif len(item) == 2:
|
|
60
79
|
name, tp, = item
|
|
61
80
|
elif len(item) == 3:
|
|
@@ -73,13 +92,52 @@ def make_dataclass( # noqa
|
|
|
73
92
|
seen.add(name)
|
|
74
93
|
annotations[name] = tp
|
|
75
94
|
|
|
95
|
+
if _IS_PY_3_14:
|
|
96
|
+
# We initially block the VALUE format, because inside dataclass() we'll call get_annotations(), which will try
|
|
97
|
+
# the VALUE format first. If we don't block, that means we'd always end up eagerly importing typing here, which
|
|
98
|
+
# is what we're trying to avoid.
|
|
99
|
+
value_blocked = True
|
|
100
|
+
|
|
101
|
+
def annotate_method(format): # noqa
|
|
102
|
+
def get_any():
|
|
103
|
+
match format:
|
|
104
|
+
case annotationlib.Format.STRING:
|
|
105
|
+
return 'typing.Any'
|
|
106
|
+
case annotationlib.Format.FORWARDREF:
|
|
107
|
+
typing = sys.modules.get('typing')
|
|
108
|
+
if typing is None:
|
|
109
|
+
return annotationlib.ForwardRef('Any', module='typing')
|
|
110
|
+
else:
|
|
111
|
+
return typing.Any
|
|
112
|
+
case annotationlib.Format.VALUE:
|
|
113
|
+
if value_blocked:
|
|
114
|
+
raise NotImplementedError
|
|
115
|
+
from typing import Any
|
|
116
|
+
return Any
|
|
117
|
+
case _:
|
|
118
|
+
raise NotImplementedError
|
|
119
|
+
|
|
120
|
+
annos = {
|
|
121
|
+
ann: get_any() if t is _ANY_MARKER else t
|
|
122
|
+
for ann, t in annotations.items()
|
|
123
|
+
}
|
|
124
|
+
if format == annotationlib.Format.STRING:
|
|
125
|
+
return annotationlib.annotations_to_string(annos)
|
|
126
|
+
else:
|
|
127
|
+
return annos
|
|
128
|
+
|
|
76
129
|
def exec_body_callback(ns):
|
|
77
130
|
ns.update(namespace)
|
|
78
131
|
ns.update(defaults)
|
|
79
|
-
|
|
132
|
+
if not _IS_PY_3_14:
|
|
133
|
+
ns['__annotations__'] = annotations
|
|
80
134
|
|
|
81
135
|
cls = types.new_class(cls_name, bases, {}, exec_body_callback)
|
|
82
136
|
|
|
137
|
+
if _IS_PY_3_14:
|
|
138
|
+
# For now, set annotations including the _ANY_MARKER.
|
|
139
|
+
cls.__annotate__ = annotate_method # type: ignore
|
|
140
|
+
|
|
83
141
|
if module is None:
|
|
84
142
|
try:
|
|
85
143
|
module = sys._getframemodulename(1) or '__main__' # type: ignore # noqa
|
|
@@ -89,7 +147,7 @@ def make_dataclass( # noqa
|
|
|
89
147
|
if module is not None:
|
|
90
148
|
cls.__module__ = module
|
|
91
149
|
|
|
92
|
-
|
|
150
|
+
cls = decorator(
|
|
93
151
|
cls,
|
|
94
152
|
init=init,
|
|
95
153
|
repr=repr,
|
|
@@ -117,3 +175,9 @@ def make_dataclass( # noqa
|
|
|
117
175
|
|
|
118
176
|
allow_redundant_decorator=allow_redundant_decorator,
|
|
119
177
|
)
|
|
178
|
+
|
|
179
|
+
if _IS_PY_3_14:
|
|
180
|
+
# Now that the class is ready, allow the VALUE format.
|
|
181
|
+
value_blocked = False
|
|
182
|
+
|
|
183
|
+
return cls
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import collections
|
|
2
2
|
import dataclasses as dc
|
|
3
|
+
import sys
|
|
3
4
|
import typing as ta
|
|
4
5
|
|
|
5
6
|
from ..... import lang
|
|
@@ -13,6 +14,9 @@ from .metadata import extra_field_params
|
|
|
13
14
|
##
|
|
14
15
|
|
|
15
16
|
|
|
17
|
+
_IS_PY_3_14 = sys.version_info >= (3, 14)
|
|
18
|
+
|
|
19
|
+
|
|
16
20
|
def field(
|
|
17
21
|
default=dc.MISSING,
|
|
18
22
|
*,
|
|
@@ -23,6 +27,7 @@ def field(
|
|
|
23
27
|
compare=True,
|
|
24
28
|
metadata=None,
|
|
25
29
|
kw_only=dc.MISSING,
|
|
30
|
+
doc=None,
|
|
26
31
|
|
|
27
32
|
coerce: bool | CoerceFn | None = None,
|
|
28
33
|
validate: ValidateFn | None = None, # noqa
|
|
@@ -40,6 +45,7 @@ def field(
|
|
|
40
45
|
override=override,
|
|
41
46
|
repr_fn=repr_fn,
|
|
42
47
|
repr_priority=repr_priority,
|
|
48
|
+
doc=doc,
|
|
43
49
|
),
|
|
44
50
|
})
|
|
45
51
|
|
|
@@ -58,4 +64,5 @@ def field(
|
|
|
58
64
|
compare=compare,
|
|
59
65
|
metadata=md,
|
|
60
66
|
kw_only=kw_only,
|
|
67
|
+
**(dict(doc=doc) if _IS_PY_3_14 else {}),
|
|
61
68
|
)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import dataclasses as dc
|
|
2
|
+
import sys
|
|
2
3
|
import typing as ta
|
|
3
4
|
|
|
4
5
|
from ..... import check
|
|
@@ -16,6 +17,12 @@ from .metadata import set_field_spec_metadata
|
|
|
16
17
|
##
|
|
17
18
|
|
|
18
19
|
|
|
20
|
+
_IS_PY_3_14 = sys.version_info >= (3, 14)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
##
|
|
24
|
+
|
|
25
|
+
|
|
19
26
|
STD_FIELD_TYPE_BY_SPEC_FIELD_TYPE: ta.Mapping[FieldType, StdFieldType] = {
|
|
20
27
|
FieldType.INSTANCE: StdFieldType.INSTANCE,
|
|
21
28
|
FieldType.CLASS_VAR: StdFieldType.CLASS_VAR,
|
|
@@ -99,6 +106,10 @@ def std_field_to_field_spec(
|
|
|
99
106
|
metadata=f.metadata,
|
|
100
107
|
kw_only=None if f.kw_only is dc.MISSING else (check.isinstance(f.kw_only, bool) if DEBUG else f.kw_only),
|
|
101
108
|
|
|
109
|
+
**lang.opt_kw(
|
|
110
|
+
doc=extra_params.get('doc', f.doc if _IS_PY_3_14 else None), # type: ignore[attr-defined] # noqa
|
|
111
|
+
),
|
|
112
|
+
|
|
102
113
|
**lang.opt_kw(
|
|
103
114
|
coerce=extra_params.get('coerce'),
|
|
104
115
|
validate=extra_params.get('validate'),
|
|
@@ -133,6 +144,7 @@ def field_spec_to_std_field(fs: FieldSpec) -> dc.Field:
|
|
|
133
144
|
compare=fs.compare,
|
|
134
145
|
**lang.opt_kw(metadata=fs.metadata),
|
|
135
146
|
kw_only=dc.MISSING if fs.kw_only is None else fs.kw_only, # type: ignore[arg-type]
|
|
147
|
+
**(dict(doc=fs.doc) if _IS_PY_3_14 else {}),
|
|
136
148
|
)
|
|
137
149
|
|
|
138
150
|
f.name = fs.name
|
|
@@ -162,6 +174,7 @@ def check_field_spec_against_field(f: dc.Field, fs: FieldSpec) -> None:
|
|
|
162
174
|
'compare': f.compare,
|
|
163
175
|
# f.metadata,
|
|
164
176
|
'kw_only': f.kw_only if f.kw_only is not dc.MISSING else None,
|
|
177
|
+
**({'doc': f.doc} if _IS_PY_3_14 else {}), # type: ignore[attr-defined] # noqa
|
|
165
178
|
|
|
166
179
|
'std_field_type': f._field_type, # type: ignore[attr-defined] # noqa
|
|
167
180
|
}
|
|
@@ -178,6 +191,7 @@ def check_field_spec_against_field(f: dc.Field, fs: FieldSpec) -> None:
|
|
|
178
191
|
'compare': fs.compare,
|
|
179
192
|
# fs.metadata,
|
|
180
193
|
'kw_only': fs.kw_only,
|
|
194
|
+
**({'doc': fs.doc} if _IS_PY_3_14 else {}),
|
|
181
195
|
|
|
182
196
|
'std_field_type': STD_FIELD_TYPE_BY_SPEC_FIELD_TYPE[fs.field_type].value,
|
|
183
197
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import inspect
|
|
2
|
+
import sys
|
|
2
3
|
|
|
3
4
|
from ..processing.base import ProcessingContext
|
|
4
5
|
from ..processing.base import Processor
|
|
@@ -9,14 +10,31 @@ from ..processing.registry import register_processor_type
|
|
|
9
10
|
##
|
|
10
11
|
|
|
11
12
|
|
|
13
|
+
if sys.version_info >= (3, 14):
|
|
14
|
+
import annotationlib # noqa
|
|
15
|
+
|
|
16
|
+
def _raw_build_cls_sig(cls: type) -> str:
|
|
17
|
+
return str(inspect.signature(
|
|
18
|
+
cls,
|
|
19
|
+
annotation_format=annotationlib.Format.FORWARDREF, # noqa
|
|
20
|
+
)).replace(' -> None', '')
|
|
21
|
+
|
|
22
|
+
else:
|
|
23
|
+
def _raw_build_cls_sig(cls: type) -> str:
|
|
24
|
+
return str(inspect.signature(cls)).replace(' -> None', '')
|
|
25
|
+
|
|
26
|
+
|
|
12
27
|
def _build_cls_doc(cls: type) -> str:
|
|
13
28
|
try:
|
|
14
|
-
text_sig =
|
|
29
|
+
text_sig = _raw_build_cls_sig(cls)
|
|
15
30
|
except (TypeError, ValueError):
|
|
16
31
|
text_sig = ''
|
|
17
32
|
return cls.__name__ + text_sig
|
|
18
33
|
|
|
19
34
|
|
|
35
|
+
##
|
|
36
|
+
|
|
37
|
+
|
|
20
38
|
class _LazyClsDocDescriptor:
|
|
21
39
|
def __get__(self, instance, owner):
|
|
22
40
|
if instance is not None:
|
|
@@ -2,6 +2,7 @@ import dataclasses as dc
|
|
|
2
2
|
import inspect
|
|
3
3
|
import itertools
|
|
4
4
|
import types
|
|
5
|
+
import typing as ta
|
|
5
6
|
|
|
6
7
|
from ..processing.base import Processor
|
|
7
8
|
from ..processing.priority import ProcessorPriority
|
|
@@ -63,11 +64,41 @@ def _update_func_cell_for__class__(f, oldcls, newcls):
|
|
|
63
64
|
return False
|
|
64
65
|
|
|
65
66
|
|
|
67
|
+
def _create_slots(
|
|
68
|
+
defined_fields,
|
|
69
|
+
inherited_slots,
|
|
70
|
+
field_names,
|
|
71
|
+
weakref_slot,
|
|
72
|
+
):
|
|
73
|
+
# The slots for our class. Remove slots from our base classes. Add '__weakref__' if weakref_slot was given, unless
|
|
74
|
+
# it is already present.
|
|
75
|
+
seen_docs = False
|
|
76
|
+
slots = {}
|
|
77
|
+
for slot in itertools.filterfalse(
|
|
78
|
+
inherited_slots.__contains__,
|
|
79
|
+
itertools.chain(
|
|
80
|
+
# gh-93521: '__weakref__' also needs to be filtered out if already present in inherited_slots
|
|
81
|
+
field_names, ('__weakref__',) if weakref_slot else (),
|
|
82
|
+
),
|
|
83
|
+
):
|
|
84
|
+
doc = getattr(defined_fields.get(slot), 'doc', None)
|
|
85
|
+
if doc is not None:
|
|
86
|
+
seen_docs = True
|
|
87
|
+
slots[slot] = doc
|
|
88
|
+
|
|
89
|
+
# We only return dict if there's at least one doc member, otherwise we return tuple, which is the old default
|
|
90
|
+
# format.
|
|
91
|
+
if seen_docs:
|
|
92
|
+
return slots
|
|
93
|
+
return tuple(slots)
|
|
94
|
+
|
|
95
|
+
|
|
66
96
|
def add_slots(
|
|
67
97
|
cls: type,
|
|
68
98
|
*,
|
|
69
99
|
is_frozen: bool,
|
|
70
100
|
weakref_slot: bool,
|
|
101
|
+
defined_fields: ta.Mapping[str, ta.Any],
|
|
71
102
|
) -> type:
|
|
72
103
|
# Need to create a new class, since we can't set __slots__ after a class has been created, and the @dataclass
|
|
73
104
|
# decorator is called after the class is created.
|
|
@@ -83,17 +114,11 @@ def add_slots(
|
|
|
83
114
|
# Make sure slots don't overlap with those in base classes.
|
|
84
115
|
inherited_slots = set(itertools.chain.from_iterable(map(_get_slots, cls.__mro__[1:-1])))
|
|
85
116
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
itertools.chain(
|
|
92
|
-
field_names,
|
|
93
|
-
# gh-93521: '__weakref__' also needs to be filtered out if already present in inherited_slots
|
|
94
|
-
('__weakref__',) if weakref_slot else (),
|
|
95
|
-
),
|
|
96
|
-
),
|
|
117
|
+
cls_dict['__slots__'] = _create_slots(
|
|
118
|
+
defined_fields,
|
|
119
|
+
inherited_slots,
|
|
120
|
+
field_names,
|
|
121
|
+
weakref_slot,
|
|
97
122
|
)
|
|
98
123
|
|
|
99
124
|
for field_name in field_names:
|
|
@@ -156,4 +181,5 @@ class SlotsProcessor(Processor):
|
|
|
156
181
|
cls,
|
|
157
182
|
is_frozen=self._ctx.cs.frozen,
|
|
158
183
|
weakref_slot=self._ctx.cs.weakref_slot,
|
|
184
|
+
defined_fields=self._ctx.cs.fields_by_name,
|
|
159
185
|
)
|
|
@@ -5,6 +5,7 @@ TODO:
|
|
|
5
5
|
"""
|
|
6
6
|
import abc
|
|
7
7
|
import dataclasses as dc
|
|
8
|
+
import sys
|
|
8
9
|
import typing as ta
|
|
9
10
|
|
|
10
11
|
from ... import lang
|
|
@@ -21,6 +22,26 @@ T = ta.TypeVar('T')
|
|
|
21
22
|
##
|
|
22
23
|
|
|
23
24
|
|
|
25
|
+
if sys.version_info >= (3, 14):
|
|
26
|
+
annotationlib = __import__('annotationlib') # noqa
|
|
27
|
+
|
|
28
|
+
# See:
|
|
29
|
+
# - https://github.com/python/cpython/pull/132345
|
|
30
|
+
# - https://github.com/python/cpython/pull/132490
|
|
31
|
+
def _get_ns_annotation_names(ns: ta.Mapping[str, ta.Any]) -> ta.Sequence[str]:
|
|
32
|
+
if (fn := annotationlib.get_annotate_from_class_namespace(ns)) is not None: # noqa
|
|
33
|
+
return list(annotationlib.call_annotate_function(fn, annotationlib.Format.FORWARDREF)) # noqa
|
|
34
|
+
else:
|
|
35
|
+
return []
|
|
36
|
+
|
|
37
|
+
else:
|
|
38
|
+
def _get_ns_annotation_names(ns: ta.Mapping[str, ta.Any]) -> ta.Sequence[str]:
|
|
39
|
+
return list(ns.get('__annotations__', []))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
##
|
|
43
|
+
|
|
44
|
+
|
|
24
45
|
class DataMeta(abc.ABCMeta):
|
|
25
46
|
def __new__(
|
|
26
47
|
mcls,
|
|
@@ -88,7 +109,7 @@ class DataMeta(abc.ABCMeta):
|
|
|
88
109
|
|
|
89
110
|
ofs: set[str] = set()
|
|
90
111
|
if any(issubclass(b, lang.Abstract) for b in bases) and nkw.get('override'):
|
|
91
|
-
ofs.update(a for a in namespace
|
|
112
|
+
ofs.update(a for a in _get_ns_annotation_names(namespace) if a not in namespace)
|
|
92
113
|
namespace.update((a, dc.MISSING) for a in ofs)
|
|
93
114
|
|
|
94
115
|
#
|
omlish/dataclasses/specs.py
CHANGED
omlish/dispatch/impls.py
CHANGED
|
@@ -20,8 +20,7 @@ T = ta.TypeVar('T')
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
def get_impl_func_cls_set(func: ta.Callable, *, arg_offset: int = 0) -> frozenset[type]:
|
|
23
|
-
|
|
24
|
-
if not ann:
|
|
23
|
+
if not rfl.has_annotations(func):
|
|
25
24
|
raise TypeError(f'Invalid impl func: {func!r}')
|
|
26
25
|
|
|
27
26
|
def erase(a):
|
omlish/dispatch/methods.py
CHANGED
|
@@ -101,12 +101,6 @@ class Method(ta.Generic[P, R]):
|
|
|
101
101
|
return wrapper
|
|
102
102
|
|
|
103
103
|
def register(self, impl: T, cls_set: frozenset[type] | None = None) -> T:
|
|
104
|
-
# bpo-39679: in Python <= 3.9, classmethods and staticmethods don't inherit __annotations__ of the wrapped
|
|
105
|
-
# function (fixed in 3.10+ as a side-effect of bpo-43682) but we need that for annotation-derived
|
|
106
|
-
# singledispatches. So we add that just-in-time here.
|
|
107
|
-
if isinstance(impl, (staticmethod, classmethod)):
|
|
108
|
-
impl.__annotations__ = getattr(impl.__func__, '__annotations__', {})
|
|
109
|
-
|
|
110
104
|
check.callable(impl)
|
|
111
105
|
if impl not in self._impls:
|
|
112
106
|
self._impls[impl] = cls_set # type: ignore
|
omlish/inject/impl/proxy.py
CHANGED
|
@@ -15,6 +15,7 @@ def _cyclic_dependency_proxy() -> tuple[type, ta.Callable[[ta.Any, ta.Any], None
|
|
|
15
15
|
|
|
16
16
|
def __init__(self, cls: ta.Any) -> None:
|
|
17
17
|
super().__init__()
|
|
18
|
+
|
|
18
19
|
self.__cls = cls
|
|
19
20
|
|
|
20
21
|
def __repr__(self) -> str:
|
|
@@ -24,6 +25,7 @@ def _cyclic_dependency_proxy() -> tuple[type, ta.Callable[[ta.Any, ta.Any], None
|
|
|
24
25
|
|
|
25
26
|
def __init__(self, cls):
|
|
26
27
|
super().__init__(_CyclicDependencyPlaceholder(cls))
|
|
28
|
+
|
|
27
29
|
if isinstance(cls, type):
|
|
28
30
|
self._self__class__ = cls # noqa
|
|
29
31
|
|
omlish/inject/impl/scopes.py
CHANGED
|
@@ -67,6 +67,7 @@ class UnscopedScopeImpl(ScopeImpl, lang.Final):
|
|
|
67
67
|
class SingletonScopeImpl(ScopeImpl, lang.Final):
|
|
68
68
|
def __init__(self) -> None:
|
|
69
69
|
super().__init__()
|
|
70
|
+
|
|
70
71
|
self._dct: dict[BindingImpl, ta.Any] = {}
|
|
71
72
|
|
|
72
73
|
@property
|
|
@@ -86,6 +87,7 @@ class SingletonScopeImpl(ScopeImpl, lang.Final):
|
|
|
86
87
|
class ThreadScopeImpl(ScopeImpl, lang.Final):
|
|
87
88
|
def __init__(self) -> None:
|
|
88
89
|
super().__init__()
|
|
90
|
+
|
|
89
91
|
self._local = threading.local()
|
|
90
92
|
|
|
91
93
|
@property
|
|
@@ -135,6 +137,7 @@ class SeededScopeImpl(ScopeImpl):
|
|
|
135
137
|
|
|
136
138
|
def __init__(self, ss: SeededScope) -> None:
|
|
137
139
|
super().__init__()
|
|
140
|
+
|
|
138
141
|
self._ss = check.isinstance(ss, SeededScope)
|
|
139
142
|
self._st: SeededScopeImpl.State | None = None
|
|
140
143
|
|
|
@@ -150,6 +153,7 @@ class SeededScopeImpl(ScopeImpl):
|
|
|
150
153
|
class Manager(SeededScope.Manager, lang.Final):
|
|
151
154
|
def __init__(self, ss: SeededScope, i: Injector) -> None:
|
|
152
155
|
super().__init__()
|
|
156
|
+
|
|
153
157
|
self._ss = check.isinstance(ss, SeededScope)
|
|
154
158
|
self._ii = check.isinstance(i, injector_.InjectorImpl)
|
|
155
159
|
self._ssi = check.isinstance(self._ii.get_scope_impl(self._ss), SeededScopeImpl)
|
omlish/io/compress/zstd.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import dataclasses as dc
|
|
2
|
+
import sys
|
|
2
3
|
import typing as ta
|
|
3
4
|
|
|
4
5
|
from ... import lang
|
|
@@ -7,10 +8,17 @@ from .codecs import make_compression_codec
|
|
|
7
8
|
from .codecs import make_compression_lazy_loaded_codec
|
|
8
9
|
|
|
9
10
|
|
|
10
|
-
if
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
if sys.version_info >= (3, 14):
|
|
12
|
+
if ta.TYPE_CHECKING:
|
|
13
|
+
from compression import zstd # noqa
|
|
14
|
+
else:
|
|
15
|
+
zstd = lang.proxy_import('compression.zstd')
|
|
16
|
+
|
|
17
|
+
else: # noqa
|
|
18
|
+
if ta.TYPE_CHECKING:
|
|
19
|
+
import zstandard as zstd
|
|
20
|
+
else:
|
|
21
|
+
zstd = lang.proxy_import('zstandard')
|
|
14
22
|
|
|
15
23
|
|
|
16
24
|
##
|
|
@@ -20,18 +28,18 @@ else:
|
|
|
20
28
|
class ZstdCompression(Compression):
|
|
21
29
|
level: int | None = None
|
|
22
30
|
|
|
23
|
-
max_output_size: int = 0
|
|
31
|
+
# max_output_size: int = 0
|
|
24
32
|
|
|
25
33
|
def compress(self, d: bytes) -> bytes:
|
|
26
|
-
return
|
|
34
|
+
return zstd.compress(
|
|
27
35
|
d,
|
|
28
36
|
**(dict(level=self.level) if self.level is not None else {}),
|
|
29
37
|
)
|
|
30
38
|
|
|
31
39
|
def decompress(self, d: bytes) -> bytes:
|
|
32
|
-
return
|
|
40
|
+
return zstd.decompress(
|
|
33
41
|
d,
|
|
34
|
-
max_output_size=self.max_output_size,
|
|
42
|
+
# max_output_size=self.max_output_size,
|
|
35
43
|
)
|
|
36
44
|
|
|
37
45
|
|
omlish/lang/__init__.py
CHANGED