omlish 0.0.0.dev283__py3-none-any.whl → 0.0.0.dev285__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.
Files changed (121) hide show
  1. omlish/__about__.py +4 -4
  2. omlish/dataclasses/__init__.py +58 -60
  3. omlish/dataclasses/api/__init__.py +25 -0
  4. omlish/dataclasses/api/classes/__init__.py +0 -0
  5. omlish/dataclasses/api/classes/conversion.py +30 -0
  6. omlish/dataclasses/api/classes/decorator.py +145 -0
  7. omlish/dataclasses/api/classes/make.py +109 -0
  8. omlish/dataclasses/api/classes/metadata.py +133 -0
  9. omlish/dataclasses/api/classes/params.py +78 -0
  10. omlish/dataclasses/api/fields/__init__.py +0 -0
  11. omlish/dataclasses/api/fields/building.py +120 -0
  12. omlish/dataclasses/api/fields/constructor.py +56 -0
  13. omlish/dataclasses/api/fields/conversion.py +191 -0
  14. omlish/dataclasses/api/fields/metadata.py +94 -0
  15. omlish/dataclasses/concerns/__init__.py +17 -0
  16. omlish/dataclasses/concerns/abc.py +15 -0
  17. omlish/dataclasses/concerns/copy.py +63 -0
  18. omlish/dataclasses/concerns/doc.py +53 -0
  19. omlish/dataclasses/concerns/eq.py +60 -0
  20. omlish/dataclasses/concerns/fields.py +119 -0
  21. omlish/dataclasses/concerns/frozen.py +133 -0
  22. omlish/dataclasses/concerns/hash.py +165 -0
  23. omlish/dataclasses/concerns/init.py +453 -0
  24. omlish/dataclasses/concerns/matchargs.py +27 -0
  25. omlish/dataclasses/concerns/mro.py +16 -0
  26. omlish/dataclasses/concerns/order.py +87 -0
  27. omlish/dataclasses/concerns/override.py +98 -0
  28. omlish/dataclasses/concerns/params.py +14 -0
  29. omlish/dataclasses/concerns/replace.py +48 -0
  30. omlish/dataclasses/concerns/repr.py +95 -0
  31. omlish/dataclasses/{impl → concerns}/slots.py +25 -1
  32. omlish/dataclasses/debug.py +2 -0
  33. omlish/dataclasses/errors.py +115 -0
  34. omlish/dataclasses/generation/__init__.py +0 -0
  35. omlish/dataclasses/generation/base.py +38 -0
  36. omlish/dataclasses/generation/compilation.py +258 -0
  37. omlish/dataclasses/generation/execution.py +195 -0
  38. omlish/dataclasses/generation/globals.py +83 -0
  39. omlish/dataclasses/generation/idents.py +6 -0
  40. omlish/dataclasses/generation/mangling.py +18 -0
  41. omlish/dataclasses/generation/manifests.py +20 -0
  42. omlish/dataclasses/generation/ops.py +97 -0
  43. omlish/dataclasses/generation/plans.py +35 -0
  44. omlish/dataclasses/generation/processor.py +174 -0
  45. omlish/dataclasses/generation/registry.py +42 -0
  46. omlish/dataclasses/generation/utils.py +83 -0
  47. omlish/dataclasses/{impl/reflect.py → inspect.py} +53 -90
  48. omlish/dataclasses/{impl/internals.py → internals.py} +26 -32
  49. omlish/dataclasses/metaclass/__init__.py +0 -0
  50. omlish/dataclasses/metaclass/bases.py +69 -0
  51. omlish/dataclasses/metaclass/confer.py +65 -0
  52. omlish/dataclasses/metaclass/meta.py +115 -0
  53. omlish/dataclasses/metaclass/specs.py +38 -0
  54. omlish/dataclasses/processing/__init__.py +0 -0
  55. omlish/dataclasses/processing/base.py +83 -0
  56. omlish/dataclasses/processing/driving.py +45 -0
  57. omlish/dataclasses/processing/priority.py +13 -0
  58. omlish/dataclasses/processing/registry.py +81 -0
  59. omlish/dataclasses/reflection.py +81 -0
  60. omlish/dataclasses/specs.py +224 -0
  61. omlish/dataclasses/tools/__init__.py +0 -0
  62. omlish/dataclasses/{impl → tools}/as_.py +23 -8
  63. omlish/dataclasses/tools/iter.py +27 -0
  64. omlish/dataclasses/tools/modifiers.py +52 -0
  65. omlish/dataclasses/tools/replace.py +17 -0
  66. omlish/dataclasses/tools/repr.py +12 -0
  67. omlish/dataclasses/{static.py → tools/static.py} +25 -4
  68. omlish/dataclasses/utils.py +54 -109
  69. omlish/diag/__init__.py +4 -4
  70. omlish/inject/bindings.py +1 -1
  71. omlish/inject/eagers.py +1 -1
  72. omlish/inject/impl/bindings.py +1 -1
  73. omlish/inject/impl/origins.py +1 -1
  74. omlish/inject/keys.py +1 -1
  75. omlish/inject/listeners.py +1 -1
  76. omlish/inject/multis.py +4 -4
  77. omlish/inject/origins.py +2 -2
  78. omlish/inject/overrides.py +1 -1
  79. omlish/inject/privates.py +2 -2
  80. omlish/inject/providers.py +4 -4
  81. omlish/inject/scopes.py +3 -3
  82. omlish/lang/__init__.py +6 -2
  83. omlish/lang/cached/function.py +13 -2
  84. omlish/lang/cached/property.py +3 -1
  85. omlish/lang/imports.py +68 -10
  86. omlish/lang/objects.py +0 -46
  87. omlish/lite/reprs.py +84 -0
  88. omlish/marshal/objects/dataclasses.py +5 -9
  89. omlish/marshal/objects/helpers.py +3 -3
  90. omlish/secrets/marshal.py +1 -1
  91. omlish/secrets/secrets.py +1 -1
  92. omlish/sql/queries/base.py +4 -4
  93. omlish/text/mangle.py +66 -7
  94. omlish/typedvalues/marshal.py +2 -2
  95. omlish/typedvalues/values.py +1 -1
  96. {omlish-0.0.0.dev283.dist-info → omlish-0.0.0.dev285.dist-info}/METADATA +3 -3
  97. {omlish-0.0.0.dev283.dist-info → omlish-0.0.0.dev285.dist-info}/RECORD +101 -60
  98. omlish/dataclasses/impl/LICENSE +0 -279
  99. omlish/dataclasses/impl/__init__.py +0 -33
  100. omlish/dataclasses/impl/api.py +0 -278
  101. omlish/dataclasses/impl/copy.py +0 -30
  102. omlish/dataclasses/impl/errors.py +0 -53
  103. omlish/dataclasses/impl/fields.py +0 -245
  104. omlish/dataclasses/impl/frozen.py +0 -93
  105. omlish/dataclasses/impl/hashing.py +0 -86
  106. omlish/dataclasses/impl/init.py +0 -199
  107. omlish/dataclasses/impl/main.py +0 -93
  108. omlish/dataclasses/impl/metaclass.py +0 -235
  109. omlish/dataclasses/impl/metadata.py +0 -75
  110. omlish/dataclasses/impl/order.py +0 -47
  111. omlish/dataclasses/impl/overrides.py +0 -53
  112. omlish/dataclasses/impl/params.py +0 -128
  113. omlish/dataclasses/impl/processing.py +0 -24
  114. omlish/dataclasses/impl/replace.py +0 -40
  115. omlish/dataclasses/impl/repr.py +0 -66
  116. omlish/dataclasses/impl/simple.py +0 -50
  117. omlish/dataclasses/impl/utils.py +0 -167
  118. {omlish-0.0.0.dev283.dist-info → omlish-0.0.0.dev285.dist-info}/WHEEL +0 -0
  119. {omlish-0.0.0.dev283.dist-info → omlish-0.0.0.dev285.dist-info}/entry_points.txt +0 -0
  120. {omlish-0.0.0.dev283.dist-info → omlish-0.0.0.dev285.dist-info}/licenses/LICENSE +0 -0
  121. {omlish-0.0.0.dev283.dist-info → omlish-0.0.0.dev285.dist-info}/top_level.txt +0 -0
@@ -8,9 +8,9 @@ import copy
8
8
  import dataclasses as dc
9
9
  import typing as ta
10
10
 
11
- from .. import lang
12
- from ..lite.dataclasses import is_immediate_dataclass
13
- from .impl.api import dataclass
11
+ from ... import lang
12
+ from ...lite.dataclasses import is_immediate_dataclass
13
+ from ..api.classes.decorator import dataclass
14
14
 
15
15
 
16
16
  ##
@@ -122,6 +122,27 @@ class Static(lang.Abstract):
122
122
  # Use a default_factory to allow unsafe (mutable) values.
123
123
  new_fld.default_factory = (lambda v2: lambda: v2)(v) # noqa
124
124
 
125
+ # FIXME
126
+ from ..api.fields.metadata import _ExtraFieldParamsMetadata # noqa
127
+ from ..specs import FieldSpec
128
+ try:
129
+ x_fs = fld.metadata[FieldSpec]
130
+ except KeyError:
131
+ pass
132
+ else:
133
+ n_md = {
134
+ k: v
135
+ for k, v in fld.metadata.items()
136
+ if k not in (FieldSpec, _ExtraFieldParamsMetadata)
137
+ }
138
+ n_md[_ExtraFieldParamsMetadata] = {
139
+ fs_f.name: getattr(x_fs, fs_f.name)
140
+ for fs_f in dc.fields(FieldSpec) # noqa
141
+ if fs_f not in dc.Field.__slots__ # type: ignore[attr-defined]
142
+ and fs_f.name not in ('default', 'default_factory')
143
+ }
144
+ new_fld.metadata = n_md # type: ignore[assignment]
145
+
125
146
  setattr(cls, fld.name, new_fld)
126
147
  new_anns[fld.name] = fld.type
127
148
 
@@ -144,7 +165,7 @@ class Static(lang.Abstract):
144
165
  )
145
166
 
146
167
  # Explicitly forbid dc transforms that rebuild the class, such as slots.
147
- if (dc_cls := dataclass(cls, frozen=True)) is not cls:
168
+ if (dc_cls := dataclass(frozen=True)(cls)) is not cls:
148
169
  raise TypeError(dc_cls)
149
170
 
150
171
  dc_flds = dc.fields(cls) # type: ignore[arg-type] # noqa
@@ -1,153 +1,98 @@
1
+ import ast
1
2
  import collections
2
- import dataclasses as dc
3
+ import functools
3
4
  import types
4
5
  import typing as ta
5
6
 
6
7
  from .. import check
7
- from .impl.metadata import METADATA_ATTR
8
- from .impl.metadata import UserMetadata
9
- from .impl.params import DEFAULT_FIELD_EXTRAS
10
- from .impl.params import FieldExtras
11
- from .impl.params import get_field_extras
12
8
 
13
9
 
14
10
  T = ta.TypeVar('T')
15
11
 
16
-
17
- ##
18
-
19
-
20
- def opt_repr(o: ta.Any) -> str | None:
21
- return repr(o) if o is not None else None
22
-
23
-
24
- def truthy_repr(o: ta.Any) -> str | None:
25
- return repr(o) if o else None
12
+ K = ta.TypeVar('K')
13
+ V = ta.TypeVar('V')
26
14
 
27
15
 
28
16
  ##
29
17
 
30
18
 
31
- def fields_dict(cls_or_instance: ta.Any) -> dict[str, dc.Field]:
32
- return {f.name: f for f in dc.fields(cls_or_instance)}
19
+ def repr_round_trip_value(v: T) -> T:
20
+ r = repr(v)
21
+ v2 = ast.literal_eval(r)
22
+ if v != v2:
23
+ raise ValueError(v)
24
+ return v2
33
25
 
34
26
 
35
27
  ##
36
28
 
37
29
 
38
- class field_modifier: # noqa
39
- def __init__(self, fn: ta.Callable[[dc.Field], dc.Field]) -> None:
40
- super().__init__()
41
- self.fn = fn
30
+ def set_qualname(cls: type, value: T) -> T:
31
+ if isinstance(value, types.FunctionType):
32
+ value.__qualname__ = f'{cls.__qualname__}.{value.__name__}'
33
+ return value
42
34
 
43
- def __ror__(self, other: T) -> T:
44
- return self(other)
45
35
 
46
- def __call__(self, f: T) -> T:
47
- return check.isinstance(self.fn(check.isinstance(f, dc.Field)), dc.Field) # type: ignore
36
+ def set_new_attribute(cls: type, name: str, value: ta.Any) -> bool:
37
+ if name in cls.__dict__:
38
+ return True
39
+ set_qualname(cls, value)
40
+ setattr(cls, name, value)
41
+ return False
48
42
 
49
43
 
50
- def chain_metadata(*mds: ta.Mapping) -> types.MappingProxyType:
51
- return types.MappingProxyType(collections.ChainMap(*mds)) # type: ignore # noqa
44
+ ##
52
45
 
53
46
 
54
- def update_class_metadata(cls: type[T], *args: ta.Any) -> type[T]:
55
- check.isinstance(cls, type)
56
- setattr(cls, METADATA_ATTR, md := getattr(cls, METADATA_ATTR, {}))
57
- md.setdefault(UserMetadata, []).extend(args)
58
- return cls
47
+ class SealableRegistry(ta.Generic[K, V]):
48
+ def __init__(self) -> None:
49
+ super().__init__()
59
50
 
51
+ self._dct: dict[K, V] = {}
52
+ self._sealed = False
60
53
 
61
- def update_field_metadata(f: dc.Field, nmd: ta.Mapping) -> dc.Field:
62
- check.isinstance(f, dc.Field)
63
- f.metadata = chain_metadata(nmd, f.metadata)
64
- return f
54
+ def seal(self) -> None:
55
+ self._sealed = True
65
56
 
57
+ def __setitem__(self, k: K, v: V) -> None:
58
+ check.state(not self._sealed)
59
+ check.not_in(k, self._dct)
60
+ self._dct[k] = v
66
61
 
67
- def update_field_extras(f: dc.Field, *, unless_non_default: bool = False, **kwargs: ta.Any) -> dc.Field:
68
- fe = get_field_extras(f)
69
- return update_field_metadata(f, {
70
- FieldExtras: dc.replace(fe, **{
71
- k: v
72
- for k, v in kwargs.items()
73
- if not unless_non_default or v != getattr(DEFAULT_FIELD_EXTRAS, k)
74
- }),
75
- })
62
+ def __getitem__(self, k: K) -> V:
63
+ self.seal()
64
+ return self._dct[k]
76
65
 
66
+ def items(self) -> ta.Iterator[tuple[K, V]]:
67
+ self.seal()
68
+ return iter(self._dct.items())
77
69
 
78
- def update_fields(
79
- fn: ta.Callable[[str, dc.Field], dc.Field],
80
- fields: ta.Iterable[str] | None = None,
81
- ) -> ta.Callable[[type[T]], type[T]]:
82
- def inner(cls):
83
- if fields is None:
84
- for a, v in list(cls.__dict__.items()):
85
- if isinstance(v, dc.Field):
86
- setattr(cls, a, fn(a, v))
87
70
 
88
- else:
89
- for a in fields:
90
- try:
91
- v = cls.__dict__[a]
92
- except KeyError:
93
- v = dc.field()
94
- else:
95
- if not isinstance(v, dc.Field):
96
- v = dc.field(default=v)
97
- setattr(cls, a, fn(a, v))
71
+ ##
98
72
 
99
- return cls
100
73
 
101
- check.not_isinstance(fields, str)
74
+ def class_decorator(fn):
75
+ @functools.wraps(fn)
76
+ def inner(cls=None, *args, **kwargs):
77
+ if cls is None:
78
+ return lambda cls: fn(cls, *args, **kwargs) # noqa
79
+ return fn(cls, *args, **kwargs)
102
80
  return inner
103
81
 
104
82
 
105
- def update_fields_metadata(
106
- nmd: ta.Mapping,
107
- fields: ta.Iterable[str] | None = None,
108
- ) -> ta.Callable[[type[T]], type[T]]:
109
- def inner(a: str, f: dc.Field) -> dc.Field:
110
- return update_field_metadata(f, nmd)
111
-
112
- return update_fields(inner, fields)
113
-
114
-
115
83
  ##
116
84
 
117
85
 
118
- def shallow_astuple(o: ta.Any) -> tuple[ta.Any, ...]:
119
- return tuple(getattr(o, f.name) for f in dc.fields(o))
86
+ _EMPTY_MAPPING_PROXY: ta.Mapping = types.MappingProxyType({})
120
87
 
121
88
 
122
- def shallow_asdict(o: ta.Any) -> dict[str, ta.Any]:
123
- return {f.name: getattr(o, f.name) for f in dc.fields(o)}
124
-
125
-
126
- ##
127
-
128
-
129
- def deep_replace(o: T, *args: str | ta.Callable[[ta.Any], ta.Mapping[str, ta.Any]]) -> T:
130
- if not args:
131
- return o
132
- elif len(args) == 1:
133
- return dc.replace(o, **args[0](o)) # type: ignore
89
+ def chain_mapping_proxy(*ms: ta.Mapping) -> types.MappingProxyType:
90
+ m: ta.Any
91
+ if len(ms) > 1:
92
+ m = collections.ChainMap(*ms) # type: ignore[arg-type]
93
+ elif ms:
94
+ [m] = ms
134
95
  else:
135
- return dc.replace(o, **{args[0]: deep_replace(getattr(o, args[0]), *args[1:])}) # type: ignore
136
-
137
-
138
- ##
139
-
140
-
141
- def iter_items(obj: ta.Any) -> ta.Iterator[tuple[str, ta.Any]]:
142
- for f in dc.fields(obj):
143
- yield (f.name, getattr(obj, f.name))
144
-
145
-
146
- def iter_keys(obj: ta.Any) -> ta.Iterator[str]:
147
- for f in dc.fields(obj):
148
- yield f.name
149
-
96
+ m = _EMPTY_MAPPING_PROXY
150
97
 
151
- def iter_values(obj: ta.Any) -> ta.Iterator[ta.Any]:
152
- for f in dc.fields(obj):
153
- yield getattr(obj, f.name)
98
+ return types.MappingProxyType(m)
omlish/diag/__init__.py CHANGED
@@ -17,10 +17,10 @@ Debuggers
17
17
 
18
18
  CPU Profilers
19
19
  - cProfile - https://docs.python.org/3/library/profile.html
20
- - pyinstrument - https://github.com/joerick/pyinstrument
21
- - py-pspy - https://github.com/benfred/py-spy
22
- - austin-dist - https://github.com/P403n1x87/austin
23
- - yappi - https://github.com/sumerc/yappi
20
+ - yappi - https://github.com/sumerc/yappi - tracing
21
+ - pyinstrument - https://github.com/joerick/pyinstrument - pretty
22
+ - py-spy - https://github.com/benfred/py-spy - busted on mac
23
+ - austin-dist - https://github.com/P403n1x87/austin - old
24
24
 
25
25
  Memory Profilers
26
26
  - tracemalloc - https://docs.python.org/3/library/tracemalloc.html
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.extra_params(cache_hash=True)
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
@@ -13,6 +13,6 @@ from .keys import Key
13
13
 
14
14
 
15
15
  @dc.dataclass(frozen=True)
16
- @dc.extra_params(cache_hash=True)
16
+ @dc.extra_class_params(cache_hash=True)
17
17
  class Eager(Element, lang.Final):
18
18
  key: Key = dc.xfield(coerce=check.of_isinstance(Key))
@@ -11,7 +11,7 @@ from .providers import ProviderImpl
11
11
 
12
12
 
13
13
  @dc.dataclass(frozen=True, eq=False)
14
- @dc.extra_params(cache_hash=True)
14
+ @dc.extra_class_params(cache_hash=True)
15
15
  class BindingImpl(lang.Final):
16
16
  key: Key
17
17
  provider: ProviderImpl
@@ -26,7 +26,7 @@ ORIGIN_IGNORED_PACKAGES = frozenset([
26
26
  lang.functions.__name__,
27
27
 
28
28
  dc.__name__,
29
- dc.impl.__name__, # noqa
29
+ *[m for m in sys.modules if m.startswith(dc.__name__ + '.')],
30
30
  ])
31
31
 
32
32
 
omlish/inject/keys.py CHANGED
@@ -14,7 +14,7 @@ T = ta.TypeVar('T')
14
14
 
15
15
 
16
16
  @dc.dataclass(frozen=True)
17
- @dc.extra_params(cache_hash=True)
17
+ @dc.extra_class_params(cache_hash=True)
18
18
  class Key(lang.Final, ta.Generic[T]):
19
19
  ty: rfl.Type = dc.xfield(coerce=rfl.type_)
20
20
 
@@ -20,7 +20,7 @@ ProvisionListener: ta.TypeAlias = ta.Callable[[
20
20
 
21
21
 
22
22
  @dc.dataclass(frozen=True)
23
- @dc.extra_params(cache_hash=True)
23
+ @dc.extra_class_params(cache_hash=True)
24
24
  class ProvisionListenerBinding(Element, lang.Final):
25
25
  listener: ProvisionListener
26
26
 
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.extra_params(cache_hash=True)
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.extra_params(cache_hash=True)
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.extra_params(cache_hash=True)
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.extra_params(cache_hash=True)
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.extra_params(cache_hash=True)
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.extra_params(cache_hash=True)
21
+ @dc.extra_class_params(cache_hash=True)
22
22
  class Origins:
23
23
  lst: ta.Sequence[Origin]
24
24
 
@@ -12,7 +12,7 @@ from .elements import as_elements
12
12
 
13
13
 
14
14
  @dc.dataclass(frozen=True)
15
- @dc.extra_params(cache_hash=True)
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.extra_params(cache_hash=True)
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.extra_params(cache_hash=True)
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
 
@@ -18,24 +18,24 @@ class Provider(lang.Abstract):
18
18
 
19
19
 
20
20
  @dc.dataclass(frozen=True)
21
- @dc.extra_params(cache_hash=True)
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.extra_params(cache_hash=True)
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.extra_params(cache_hash=True)
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.extra_params(cache_hash=True)
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.extra_params(cache_hash=True)
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.extra_params(cache_hash=True)
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.extra_params(cache_hash=True)
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
@@ -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,
@@ -201,11 +202,9 @@ from .maybes import ( # noqa
201
202
  )
202
203
 
203
204
  from .objects import ( # noqa
204
- AttrRepr,
205
205
  SimpleProxy,
206
206
  anon_object,
207
207
  arg_repr,
208
- attr_repr,
209
208
  can_weakref,
210
209
  deep_subclasses,
211
210
  dir_dict,
@@ -304,6 +303,11 @@ from ..lite.imports import ( # noqa
304
303
  import_module_attr,
305
304
  )
306
305
 
306
+ from ..lite.reprs import ( # noqa
307
+ AttrRepr,
308
+ attr_repr,
309
+ )
310
+
307
311
  from ..lite.timeouts import ( # noqa
308
312
  DeadlineTimeout,
309
313
  InfiniteTimeout,
@@ -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
@@ -146,11 +151,12 @@ def _make_cache_key_maker(
146
151
 
147
152
 
148
153
  class _CachedFunction(ta.Generic[T], Abstract):
149
- @dc.dataclass(frozen=True)
154
+ @dc.dataclass(frozen=True, kw_only=True)
150
155
  class Opts:
151
156
  map_maker: ta.Callable[[], ta.MutableMapping] = dict
152
157
  lock: DefaultLockable = None
153
158
  transient: bool = False
159
+ no_wrapper_update: bool = False
154
160
 
155
161
  def __init__(
156
162
  self,
@@ -170,7 +176,8 @@ class _CachedFunction(ta.Generic[T], Abstract):
170
176
  self._lock = default_lock(opts.lock, False)() if opts.lock is not None else None
171
177
  self._values = values if values is not None else opts.map_maker()
172
178
  self._value_fn = value_fn if value_fn is not None else fn
173
- functools.update_wrapper(self, fn)
179
+ if not self._opts.no_wrapper_update:
180
+ functools.update_wrapper(self, fn)
174
181
 
175
182
  @property
176
183
  def _fn(self):
@@ -353,10 +360,14 @@ class _DescriptorCachedFunction(_CachedFunction[T]):
353
360
  def cached_function(fn=None, **kwargs): # noqa
354
361
  if fn is None:
355
362
  return functools.partial(cached_function, **kwargs)
363
+
356
364
  opts = _CachedFunction.Opts(**kwargs)
365
+
357
366
  if isinstance(fn, staticmethod):
358
367
  return _FreeCachedFunction(fn, opts=opts, value_fn=unwrap_func(fn))
368
+
359
369
  scope = classmethod if isinstance(fn, classmethod) else None
370
+
360
371
  return _DescriptorCachedFunction(fn, scope, opts=opts)
361
372
 
362
373
 
@@ -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
- if transient:
115
+
116
+ elif transient:
116
117
  return _TransientCachedProperty(fn, **kwargs)
118
+
117
119
  else:
118
120
  return _DictCachedProperty(fn, **kwargs)