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.

Files changed (36) hide show
  1. omlish/.manifests.json +1 -1
  2. omlish/__about__.py +3 -3
  3. omlish/argparse/cli.py +1 -0
  4. omlish/codecs/registry.py +6 -2
  5. omlish/dataclasses/_internals.py +12 -5
  6. omlish/dataclasses/impl/api/classes/make.py +67 -3
  7. omlish/dataclasses/impl/api/fields/constructor.py +7 -0
  8. omlish/dataclasses/impl/api/fields/conversion.py +14 -0
  9. omlish/dataclasses/impl/concerns/doc.py +19 -1
  10. omlish/dataclasses/impl/concerns/slots.py +37 -11
  11. omlish/dataclasses/metaclass/meta.py +22 -1
  12. omlish/dataclasses/specs.py +1 -1
  13. omlish/dataclasses/tools/static.py +1 -0
  14. omlish/dispatch/impls.py +1 -2
  15. omlish/dispatch/methods.py +0 -6
  16. omlish/inject/impl/proxy.py +2 -0
  17. omlish/inject/impl/scopes.py +4 -0
  18. omlish/io/compress/zstd.py +16 -8
  19. omlish/lang/__init__.py +2 -0
  20. omlish/lang/objects.py +1 -0
  21. omlish/lang/typing.py +32 -2
  22. omlish/lite/marshal.py +24 -24
  23. omlish/lite/reflect.py +22 -5
  24. omlish/lite/typing.py +11 -0
  25. omlish/manifests/loading.py +10 -7
  26. omlish/reflect/__init__.py +1 -0
  27. omlish/reflect/inspect.py +10 -0
  28. omlish/testing/__init__.py +1 -0
  29. omlish/testing/pytest/skip.py +20 -5
  30. omlish/testing/testing.py +30 -0
  31. {omlish-0.0.0.dev402.dist-info → omlish-0.0.0.dev404.dist-info}/METADATA +3 -3
  32. {omlish-0.0.0.dev402.dist-info → omlish-0.0.0.dev404.dist-info}/RECORD +36 -36
  33. {omlish-0.0.0.dev402.dist-info → omlish-0.0.0.dev404.dist-info}/WHEEL +0 -0
  34. {omlish-0.0.0.dev402.dist-info → omlish-0.0.0.dev404.dist-info}/entry_points.txt +0 -0
  35. {omlish-0.0.0.dev402.dist-info → omlish-0.0.0.dev404.dist-info}/licenses/LICENSE +0 -0
  36. {omlish-0.0.0.dev402.dist-info → omlish-0.0.0.dev404.dist-info}/top_level.txt +0 -0
omlish/.manifests.json CHANGED
@@ -311,7 +311,7 @@
311
311
  "module": ".io.compress.zstd",
312
312
  "attr": "_ZSTD_LAZY_CODEC",
313
313
  "file": "omlish/io/compress/zstd.py",
314
- "line": 43,
314
+ "line": 51,
315
315
  "value": {
316
316
  "$.codecs.base.LazyLoadedCodec": {
317
317
  "mod_name": "omlish.io.compress.zstd",
omlish/__about__.py CHANGED
@@ -1,5 +1,5 @@
1
- __version__ = '0.0.0.dev402'
2
- __revision__ = '923dbb60edd6e046587daf400ee931ae9ec619de'
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
- for cb in self._late_load_callbacks:
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
 
@@ -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 (isinstance(ty, str) and dc._is_type(ty, cls, ta, ta.ClassVar, dc._is_classvar)) # type: ignore # noqa
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
- and any(
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
- and any(
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
- tp = 'typing.Any'
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
- ns['__annotations__'] = annotations
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
- return dataclass(
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 = str(inspect.signature(cls)).replace(' -> None', '')
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
- # The slots for our class. Remove slots from our base classes. Add '__weakref__' if weakref_slot was given, unless
87
- # it is already present.
88
- cls_dict['__slots__'] = tuple(
89
- itertools.filterfalse(
90
- inherited_slots.__contains__,
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.get('__annotations__', []) if a not 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
  #
@@ -65,7 +65,7 @@ class FieldSpec(lang.Final):
65
65
 
66
66
  kw_only: bool | None = None
67
67
 
68
- # doc: ta.Any = None
68
+ doc: ta.Any = None
69
69
 
70
70
  ##
71
71
  # ext
@@ -155,6 +155,7 @@ class Static(lang.Abstract):
155
155
  if k not in new_anns
156
156
  })
157
157
 
158
+ # FIXME: 3.14
158
159
  cls.__annotations__ = new_anns
159
160
 
160
161
  else:
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
- ann = getattr(func, '__annotations__', {})
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):
@@ -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
@@ -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
 
@@ -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)
@@ -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 ta.TYPE_CHECKING:
11
- import zstandard
12
- else:
13
- zstandard = lang.proxy_import('zstandard')
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 zstandard.compress(
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 zstandard.decompress(
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
@@ -446,4 +446,6 @@ from ..lite.typing import ( # noqa
446
446
  Func1,
447
447
  Func2,
448
448
  Func3,
449
+
450
+ typing_annotations_attr,
449
451
  )
omlish/lang/objects.py CHANGED
@@ -72,6 +72,7 @@ def super_meta(
72
72
  **kwargs: ta.Any,
73
73
  ) -> type:
74
74
  """Per types.new_class"""
75
+
75
76
  resolved_bases = types.resolve_bases(bases)
76
77
  if resolved_bases is not bases:
77
78
  if '__orig_bases__' in namespace: