omlish 0.0.0.dev424__py3-none-any.whl → 0.0.0.dev426__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 (126) hide show
  1. omlish/__about__.py +3 -3
  2. omlish/c3.py +4 -1
  3. omlish/configs/processing/flattening.py +1 -1
  4. omlish/configs/processing/merging.py +8 -6
  5. omlish/dataclasses/impl/concerns/doc.py +1 -1
  6. omlish/dataclasses/impl/configs.py +2 -1
  7. omlish/diag/_pycharm/runhack.py +1 -1
  8. omlish/diag/procfs.py +2 -2
  9. omlish/formats/json/stream/lexing.py +69 -14
  10. omlish/formats/json/stream/parsing.py +1 -1
  11. omlish/formats/json/stream/utils.py +3 -0
  12. omlish/formats/json5/streams.py +22 -0
  13. omlish/formats/logfmt.py +8 -2
  14. omlish/funcs/genmachine.py +1 -1
  15. omlish/http/sse.py +1 -1
  16. omlish/inject/impl/injector.py +1 -1
  17. omlish/inject/impl/multis.py +2 -2
  18. omlish/inject/impl/providers.py +0 -4
  19. omlish/inject/impl/proxy.py +0 -2
  20. omlish/inject/scopes.py +0 -4
  21. omlish/io/buffers.py +1 -1
  22. omlish/lang/__init__.py +26 -14
  23. omlish/lang/asyncs.py +12 -0
  24. omlish/lang/{attrs.py → attrstorage.py} +15 -15
  25. omlish/lang/cached/property.py +2 -2
  26. omlish/lang/classes/simple.py +26 -4
  27. omlish/lang/collections.py +1 -1
  28. omlish/lang/functions.py +0 -11
  29. omlish/lang/iterables.py +2 -2
  30. omlish/lang/lazyglobals.py +27 -5
  31. omlish/lang/maysync.py +2 -2
  32. omlish/lifecycles/contextmanagers.py +1 -2
  33. omlish/lifecycles/controller.py +1 -2
  34. omlish/lite/asyncs.py +20 -0
  35. omlish/lite/attrops.py +332 -0
  36. omlish/lite/cached.py +1 -1
  37. omlish/lite/maybes.py +2 -0
  38. omlish/lite/strings.py +0 -7
  39. omlish/lite/timing.py +6 -3
  40. omlish/logs/all.py +25 -32
  41. omlish/logs/base.py +248 -0
  42. omlish/logs/callers.py +21 -15
  43. omlish/logs/infos.py +105 -0
  44. omlish/logs/levels.py +64 -0
  45. omlish/logs/modules.py +10 -0
  46. omlish/logs/protocols.py +31 -0
  47. omlish/logs/standard.py +12 -11
  48. omlish/logs/std/adapters.py +41 -0
  49. omlish/logs/std/configs.py +29 -0
  50. omlish/logs/{filters.py → std/filters.py} +1 -1
  51. omlish/logs/{handlers.py → std/handlers.py} +1 -1
  52. omlish/logs/{json.py → std/json.py} +2 -2
  53. omlish/logs/{proxy.py → std/proxy.py} +3 -3
  54. omlish/logs/std/records.py +286 -0
  55. omlish/logs/times.py +86 -0
  56. omlish/logs/typed/bindings.py +24 -0
  57. omlish/logs/utils.py +60 -4
  58. omlish/logs/warnings.py +8 -0
  59. omlish/manifests/loading.py +1 -1
  60. omlish/os/atomics.py +1 -1
  61. omlish/os/journald.py +3 -3
  62. omlish/reflect/types.py +22 -0
  63. omlish/testing/pytest/plugins/skips.py +0 -4
  64. {omlish-0.0.0.dev424.dist-info → omlish-0.0.0.dev426.dist-info}/METADATA +2 -2
  65. {omlish-0.0.0.dev424.dist-info → omlish-0.0.0.dev426.dist-info}/RECORD +72 -114
  66. omlish/defs.py +0 -216
  67. omlish/dispatch/_dispatch2.py +0 -69
  68. omlish/dispatch/_dispatch3.py +0 -108
  69. omlish/dynamic.py +0 -219
  70. omlish/formats/json/Json.g4 +0 -77
  71. omlish/formats/json/_antlr/JsonLexer.py +0 -109
  72. omlish/formats/json/_antlr/JsonListener.py +0 -61
  73. omlish/formats/json/_antlr/JsonParser.py +0 -457
  74. omlish/formats/json/_antlr/JsonVisitor.py +0 -42
  75. omlish/io/trampoline.py +0 -289
  76. omlish/lite/logs.py +0 -4
  77. omlish/lite/reprs.py +0 -85
  78. omlish/logs/abc.py +0 -319
  79. omlish/logs/color.py +0 -27
  80. omlish/logs/configs.py +0 -29
  81. omlish/logs/protocol.py +0 -218
  82. omlish/logs/timing.py +0 -58
  83. omlish/specs/irc/__init__.py +0 -0
  84. omlish/specs/irc/messages/__init__.py +0 -0
  85. omlish/specs/irc/messages/base.py +0 -49
  86. omlish/specs/irc/messages/formats.py +0 -92
  87. omlish/specs/irc/messages/messages.py +0 -774
  88. omlish/specs/irc/messages/parsing.py +0 -98
  89. omlish/specs/irc/numerics/__init__.py +0 -0
  90. omlish/specs/irc/numerics/formats.py +0 -97
  91. omlish/specs/irc/numerics/numerics.py +0 -865
  92. omlish/specs/irc/numerics/types.py +0 -59
  93. omlish/specs/irc/protocol/LICENSE +0 -11
  94. omlish/specs/irc/protocol/__init__.py +0 -61
  95. omlish/specs/irc/protocol/consts.py +0 -6
  96. omlish/specs/irc/protocol/errors.py +0 -30
  97. omlish/specs/irc/protocol/message.py +0 -21
  98. omlish/specs/irc/protocol/nuh.py +0 -55
  99. omlish/specs/irc/protocol/parsing.py +0 -158
  100. omlish/specs/irc/protocol/rendering.py +0 -153
  101. omlish/specs/irc/protocol/tags.py +0 -102
  102. omlish/specs/irc/protocol/utils.py +0 -30
  103. omlish/specs/proto/Protobuf3.g4 +0 -396
  104. omlish/specs/proto/__init__.py +0 -0
  105. omlish/specs/proto/_antlr/Protobuf3Lexer.py +0 -340
  106. omlish/specs/proto/_antlr/Protobuf3Listener.py +0 -448
  107. omlish/specs/proto/_antlr/Protobuf3Parser.py +0 -3909
  108. omlish/specs/proto/_antlr/Protobuf3Visitor.py +0 -257
  109. omlish/specs/proto/_antlr/__init__.py +0 -0
  110. omlish/specs/proto/nodes.py +0 -54
  111. omlish/specs/proto/parsing.py +0 -97
  112. omlish/sql/parsing/Minisql.g4 +0 -292
  113. omlish/sql/parsing/__init__.py +0 -0
  114. omlish/sql/parsing/_antlr/MinisqlLexer.py +0 -322
  115. omlish/sql/parsing/_antlr/MinisqlListener.py +0 -511
  116. omlish/sql/parsing/_antlr/MinisqlParser.py +0 -3763
  117. omlish/sql/parsing/_antlr/MinisqlVisitor.py +0 -292
  118. omlish/sql/parsing/_antlr/__init__.py +0 -0
  119. omlish/sql/parsing/parsing.py +0 -119
  120. /omlish/{.manifests.json → .omlish-manifests.json} +0 -0
  121. /omlish/{formats/json/_antlr → logs/std}/__init__.py +0 -0
  122. /omlish/logs/{noisy.py → std/noisy.py} +0 -0
  123. {omlish-0.0.0.dev424.dist-info → omlish-0.0.0.dev426.dist-info}/WHEEL +0 -0
  124. {omlish-0.0.0.dev424.dist-info → omlish-0.0.0.dev426.dist-info}/entry_points.txt +0 -0
  125. {omlish-0.0.0.dev424.dist-info → omlish-0.0.0.dev426.dist-info}/licenses/LICENSE +0 -0
  126. {omlish-0.0.0.dev424.dist-info → omlish-0.0.0.dev426.dist-info}/top_level.txt +0 -0
@@ -63,7 +63,6 @@ class Marker(NotInstantiable, metaclass=_MarkerMeta):
63
63
 
64
64
 
65
65
  _SINGLETON_INSTANCE_ATTR = '__singleton_instance__'
66
- _SINGLETON_LOCK = threading.RLock()
67
66
 
68
67
 
69
68
  def _set_singleton_instance(inst):
@@ -85,7 +84,24 @@ def _set_singleton_instance(inst):
85
84
  return inst
86
85
 
87
86
 
88
- class Singleton:
87
+ class _AnySingleton:
88
+ def __init__(self) -> None:
89
+ try:
90
+ type(self).__dict__[_SINGLETON_INSTANCE_ATTR]
91
+ except KeyError:
92
+ pass
93
+ else:
94
+ raise TypeError(f'Must not re-instantiate singleton {type(self)}')
95
+
96
+ super().__init__()
97
+
98
+ @ta.final
99
+ def __reduce__(self):
100
+ return (type(self), ())
101
+
102
+
103
+ class Singleton(_AnySingleton):
104
+ @ta.final
89
105
  def __new__(cls):
90
106
  return cls.__dict__[_SINGLETON_INSTANCE_ATTR]
91
107
 
@@ -95,15 +111,21 @@ class Singleton:
95
111
  _set_singleton_instance(super().__new__(cls)) # noqa
96
112
 
97
113
 
98
- class LazySingleton:
114
+ _LAZY_SINGLETON_LOCK = threading.RLock()
115
+
116
+
117
+ class LazySingleton(_AnySingleton):
118
+ @ta.final
99
119
  def __new__(cls):
100
120
  try:
101
121
  return cls.__dict__[_SINGLETON_INSTANCE_ATTR]
102
122
  except KeyError:
103
123
  pass
104
- with _SINGLETON_LOCK:
124
+
125
+ with _LAZY_SINGLETON_LOCK:
105
126
  try:
106
127
  return cls.__dict__[_SINGLETON_INSTANCE_ATTR]
107
128
  except KeyError:
108
129
  pass
130
+
109
131
  return _set_singleton_instance(super().__new__(cls))
@@ -9,7 +9,7 @@ V = ta.TypeVar('V')
9
9
  ##
10
10
 
11
11
 
12
- def yield_dict_init(*args: ta.Any, **kwargs: ta.Any) -> ta.Iterable[tuple[ta.Any, ta.Any]]:
12
+ def yield_dict_init(*args: ta.Any, **kwargs: ta.Any) -> ta.Iterator[tuple[ta.Any, ta.Any]]:
13
13
  if len(args) > 1:
14
14
  raise TypeError
15
15
 
omlish/lang/functions.py CHANGED
@@ -167,17 +167,6 @@ def void(*args: ta.Any, **kwargs: ta.Any) -> ta.NoReturn:
167
167
  ##
168
168
 
169
169
 
170
- def as_async(fn: ta.Callable[P, T]) -> ta.Callable[P, ta.Awaitable[T]]:
171
- @functools.wraps(fn)
172
- async def inner(*args, **kwargs):
173
- return fn(*args, **kwargs)
174
-
175
- return inner
176
-
177
-
178
- ##
179
-
180
-
181
170
  _MISSING = object()
182
171
 
183
172
 
omlish/lang/iterables.py CHANGED
@@ -46,14 +46,14 @@ def chunk(n: int, iterable: ta.Iterable[T], strict: bool = False) -> ta.Iterator
46
46
  return iterator
47
47
 
48
48
 
49
- def interleave(vs: ta.Iterable[T], d: T) -> ta.Iterable[T]:
49
+ def interleave(vs: ta.Iterable[T], d: T) -> ta.Iterator[T]:
50
50
  for i, v in enumerate(vs):
51
51
  if i:
52
52
  yield d
53
53
  yield v
54
54
 
55
55
 
56
- def renumerate(it: ta.Iterable[T]) -> ta.Iterable[tuple[T, int]]:
56
+ def renumerate(it: ta.Iterable[T]) -> ta.Iterator[tuple[T, int]]:
57
57
  return ((e, i) for i, e in enumerate(it))
58
58
 
59
59
 
@@ -4,6 +4,17 @@ import typing as ta
4
4
  ##
5
5
 
6
6
 
7
+ class AmbiguousLazyGlobalsFallbackError(Exception):
8
+ def __init__(self, attr: str, fallbacks: list[ta.Callable[[str], ta.Any]]) -> None:
9
+ super().__init__()
10
+
11
+ self.attr = attr
12
+ self.fallbacks = fallbacks
13
+
14
+ def __repr__(self) -> str:
15
+ return f'{self.__class__.__name__}({self.attr!r}, {self.fallbacks!r})'
16
+
17
+
7
18
  class LazyGlobals:
8
19
  def __init__(
9
20
  self,
@@ -17,6 +28,7 @@ class LazyGlobals:
17
28
  self._update_globals = update_globals
18
29
 
19
30
  self._attr_fns: dict[str, ta.Callable[[], ta.Any]] = {}
31
+ self._fallback_fns: list[ta.Callable[[str], ta.Callable[[], ta.Any]]] = []
20
32
 
21
33
  @classmethod
22
34
  def install(cls, globals: ta.MutableMapping[str, ta.Any]) -> 'LazyGlobals': # noqa
@@ -42,13 +54,23 @@ class LazyGlobals:
42
54
  self._attr_fns[attr] = fn
43
55
  return self
44
56
 
57
+ def add_fallback_fn(self, fn: ta.Callable[[str], ta.Callable[[], ta.Any]]) -> 'LazyGlobals':
58
+ self._fallback_fns.append(fn)
59
+ return self
60
+
45
61
  def get(self, attr: str) -> ta.Any:
46
- try:
47
- fn = self._attr_fns[attr]
48
- except KeyError:
49
- raise AttributeError(attr) from None
62
+ val: ta.Any
50
63
 
51
- val = fn()
64
+ if (attr_fn := self._attr_fns.get(attr)) is not None:
65
+ val = attr_fn()
66
+
67
+ elif (fallbacks := [(fb_fn, fb_fn(attr)) for fb_fn in self._fallback_fns]):
68
+ if len(fallbacks) > 1:
69
+ raise AmbiguousLazyGlobalsFallbackError(attr, [fb_fn for fb_fn, _ in fallbacks])
70
+ [val] = fallbacks
71
+
72
+ else:
73
+ raise AttributeError(attr)
52
74
 
53
75
  if self._update_globals and self._globals is not None:
54
76
  self._globals[attr] = val
omlish/lang/maysync.py CHANGED
@@ -3,7 +3,7 @@ import typing as ta
3
3
  from ..lite.maysync import MaysyncFn
4
4
  from ..lite.maysync import MaysyncGeneratorFn
5
5
  from ..lite.maysync import make_maysync as _make_maysync
6
- from .functions import as_async
6
+ from .asyncs import as_async
7
7
 
8
8
 
9
9
  T = ta.TypeVar('T')
@@ -68,5 +68,5 @@ def make_maysync_from_sync(
68
68
  ) -> ta.Callable[P, ta.Awaitable[T]]:
69
69
  return _make_maysync(
70
70
  s,
71
- a if a is not None else as_async(s),
71
+ a if a is not None else as_async(s, wrap=True),
72
72
  )
@@ -2,7 +2,6 @@ import types
2
2
  import typing as ta
3
3
 
4
4
  from .. import dataclasses as dc
5
- from .. import defs
6
5
  from .. import lang
7
6
  from .base import Lifecycle
8
7
  from .controller import LifecycleController
@@ -37,7 +36,7 @@ class LifecycleContextManager(ta.Generic[LifecycleT]):
37
36
  self._lifecycle = lifecycle
38
37
  self._controller = lifecycle if isinstance(lifecycle, LifecycleController) else LifecycleController(lifecycle)
39
38
 
40
- defs.repr('lifecycle', 'state')
39
+ __repr__ = lang.attr_ops('lifecycle', 'state').repr
41
40
 
42
41
  @property
43
42
  def lifecycle(self) -> LifecycleT:
@@ -2,7 +2,6 @@ import abc
2
2
  import typing as ta
3
3
 
4
4
  from .. import check
5
- from .. import defs
6
5
  from .. import lang
7
6
  from .base import AnyLifecycle # noqa
8
7
  from .base import Lifecycle # noqa
@@ -48,7 +47,7 @@ class AnyLifecycleController(AnyLifecycle[R], lang.Abstract, ta.Generic[AnyLifec
48
47
  self._state = LifecycleStates.NEW
49
48
  self._listeners: list[AnyLifecycleListener[AnyLifecycleT, R]] = []
50
49
 
51
- defs.repr('lifecycle', 'state')
50
+ __repr__ = lang.attr_ops('lifecycle', 'state').repr
52
51
 
53
52
  @property
54
53
  def lifecycle(self) -> AnyLifecycleT:
omlish/lite/asyncs.py ADDED
@@ -0,0 +1,20 @@
1
+ # ruff: noqa: UP045
2
+ import functools
3
+ import typing as ta
4
+
5
+
6
+ T = ta.TypeVar('T')
7
+
8
+
9
+ ##
10
+
11
+
12
+ async def opt_await(aw: ta.Optional[ta.Awaitable[T]]) -> ta.Optional[T]:
13
+ return (await aw if aw is not None else None)
14
+
15
+
16
+ def as_async(fn: ta.Callable[..., T], *, wrap: bool = False) -> ta.Callable[..., ta.Awaitable[T]]:
17
+ async def inner(*args, **kwargs):
18
+ return fn(*args, **kwargs)
19
+
20
+ return functools.wraps(fn)(inner) if wrap else inner
omlish/lite/attrops.py ADDED
@@ -0,0 +1,332 @@
1
+ # ruff: noqa: UP006 UP007 UP045
2
+ # @omlish-lite
3
+ """
4
+ TODO:
5
+ - dotted paths!
6
+ - per-attr repr transform / filter
7
+ - __ne__ ? cases where it still matters
8
+ - ordering ?
9
+ """
10
+ import types # noqa
11
+ import typing as ta
12
+
13
+
14
+ T = ta.TypeVar('T')
15
+
16
+
17
+ ##
18
+
19
+
20
+ @ta.final
21
+ class AttrOps(ta.Generic[T]):
22
+ @ta.final
23
+ class Attr:
24
+ def __init__(
25
+ self,
26
+ name: str,
27
+ *,
28
+ display: ta.Optional[str] = None,
29
+
30
+ repr: bool = True, # noqa
31
+ hash: bool = True, # noqa
32
+ eq: bool = True,
33
+ ) -> None:
34
+ if '.' in name:
35
+ raise NotImplementedError('Dotted paths not yet supported')
36
+ if not name.isidentifier() or name.startswith('__'):
37
+ raise AttributeError(f'Invalid attr: {name!r}')
38
+ self._name = name
39
+
40
+ if display is None:
41
+ display = name[1:] if name.startswith('_') and len(name) > 1 else name
42
+ self._display = display
43
+
44
+ self._repr = repr
45
+ self._hash = hash
46
+ self._eq = eq
47
+
48
+ @classmethod
49
+ def of(
50
+ cls,
51
+ o: ta.Union[
52
+ str,
53
+ ta.Tuple[str, str],
54
+ 'AttrOps.Attr',
55
+ ],
56
+ ) -> 'AttrOps.Attr':
57
+ if isinstance(o, AttrOps.Attr):
58
+ return o
59
+ elif isinstance(o, str):
60
+ return cls(o)
61
+ else:
62
+ name, disp = o
63
+ return cls(
64
+ name,
65
+ display=disp,
66
+ )
67
+
68
+ @property
69
+ def name(self) -> str:
70
+ return self._name
71
+
72
+ @property
73
+ def display(self) -> str:
74
+ return self._display
75
+
76
+ @property
77
+ def hash(self) -> bool:
78
+ return self._hash
79
+
80
+ @property
81
+ def eq(self) -> bool:
82
+ return self._eq
83
+
84
+ @ta.overload
85
+ def __init__(
86
+ self,
87
+ *attrs: ta.Sequence[ta.Union[
88
+ str,
89
+ ta.Tuple[str, str],
90
+ Attr,
91
+ ]],
92
+ with_module: bool = False,
93
+ use_qualname: bool = False,
94
+ with_id: bool = False,
95
+ repr_filter: ta.Optional[ta.Callable[[ta.Any], bool]] = None,
96
+ recursive: bool = False,
97
+ subtypes_eq: bool = False,
98
+ ) -> None:
99
+ ...
100
+
101
+ @ta.overload
102
+ def __init__(
103
+ self,
104
+ attrs_fn: ta.Callable[[T], ta.Tuple[ta.Union[
105
+ ta.Any,
106
+ ta.Tuple[str, ta.Any],
107
+ Attr,
108
+ ], ...]],
109
+ /,
110
+ *,
111
+ with_module: bool = False,
112
+ use_qualname: bool = False,
113
+ with_id: bool = False,
114
+ repr_filter: ta.Optional[ta.Callable[[ta.Any], bool]] = None,
115
+ recursive: bool = False,
116
+ subtypes_eq: bool = False,
117
+ ) -> None:
118
+ ...
119
+
120
+ def __init__(
121
+ self,
122
+ *args,
123
+ with_module=False,
124
+ use_qualname=False,
125
+ with_id=False,
126
+ repr_filter=None,
127
+ recursive=False,
128
+ subtypes_eq=False,
129
+ ) -> None:
130
+ if args and len(args) == 1 and callable(args[0]):
131
+ self._attrs: ta.Sequence[AttrOps.Attr] = self._capture_attrs(args[0])
132
+ else:
133
+ self._attrs = list(map(AttrOps.Attr.of, args))
134
+
135
+ self._with_module: bool = with_module
136
+ self._use_qualname: bool = use_qualname
137
+ self._with_id: bool = with_id
138
+ self._repr_filter: ta.Optional[ta.Callable[[ta.Any], bool]] = repr_filter
139
+ self._recursive: bool = recursive
140
+ self._subtypes_eq: bool = subtypes_eq
141
+
142
+ @property
143
+ def attrs(self) -> ta.Sequence[Attr]:
144
+ return self._attrs
145
+
146
+ #
147
+
148
+ @ta.final
149
+ class _AttrCapturer:
150
+ def __init__(self, fn):
151
+ self.__fn = fn
152
+
153
+ def __getattr__(self, attr):
154
+ return self.__fn(self, attr)
155
+
156
+ @classmethod
157
+ def _capture_attrs(cls, fn: ta.Callable) -> ta.Sequence[Attr]:
158
+ def access(parent, attr):
159
+ dct[(ret := cls._AttrCapturer(access))] = (parent, attr)
160
+ return ret
161
+
162
+ dct: dict = {}
163
+ raw = fn(root := cls._AttrCapturer(access))
164
+
165
+ def rec(cap): # noqa
166
+ if cap is root:
167
+ return
168
+ parent, attr = dct[cap]
169
+ yield from rec(parent)
170
+ yield attr
171
+
172
+ attrs: ta.List[AttrOps.Attr] = []
173
+ for o in raw:
174
+ if isinstance(o, AttrOps.Attr):
175
+ attrs.append(o)
176
+ continue
177
+
178
+ if isinstance(o, tuple):
179
+ disp, cap, = o
180
+ else:
181
+ disp, cap = None, o
182
+
183
+ path = tuple(rec(cap))
184
+
185
+ attrs.append(AttrOps.Attr(
186
+ '.'.join(path),
187
+ display=disp,
188
+ ))
189
+
190
+ return attrs
191
+
192
+ #
193
+
194
+ _repr: ta.Callable[[T], str]
195
+
196
+ @property
197
+ def repr(self) -> ta.Callable[[T], str]:
198
+ try:
199
+ return self._repr
200
+ except AttributeError:
201
+ pass
202
+
203
+ def _repr(o: T) -> str:
204
+ vs = ', '.join(
205
+ f'{a._display}={v!r}' # noqa
206
+ for a in self._attrs
207
+ if a._repr # noqa
208
+ for v in [getattr(o, a._name)] # noqa
209
+ if self._repr_filter is None or self._repr_filter(v)
210
+ )
211
+
212
+ return (
213
+ f'{o.__class__.__module__ + "." if self._with_module else ""}'
214
+ f'{o.__class__.__qualname__ if self._use_qualname else o.__class__.__name__}'
215
+ f'{("@" + hex(id(o))[2:]) if self._with_id else ""}'
216
+ f'({vs})'
217
+ )
218
+
219
+ if self._recursive:
220
+ _repr = self._reprlib().recursive_repr()(_repr)
221
+
222
+ self._repr = _repr
223
+ return _repr
224
+
225
+ _reprlib_: ta.ClassVar[ta.Any]
226
+
227
+ @classmethod
228
+ def _reprlib(cls) -> ta.Any:
229
+ try:
230
+ return cls._reprlib_
231
+ except AttributeError:
232
+ pass
233
+
234
+ import reprlib # noqa
235
+
236
+ cls._reprlib_ = reprlib
237
+ return reprlib
238
+
239
+ #
240
+
241
+ _hash: ta.Callable[[T], int]
242
+
243
+ @property
244
+ def hash(self) -> ta.Callable[[T], int]:
245
+ try:
246
+ return self._hash
247
+ except AttributeError:
248
+ pass
249
+
250
+ def _hash(o: T) -> int:
251
+ return hash(tuple(
252
+ getattr(o, a._name) # noqa
253
+ for a in self._attrs
254
+ if a._hash # noqa
255
+ ))
256
+
257
+ self._hash = _hash
258
+ return _hash
259
+
260
+ #
261
+
262
+ _eq: ta.Callable[[T, ta.Any], ta.Union[bool, 'types.NotImplementedType']]
263
+
264
+ @property
265
+ def eq(self) -> ta.Callable[[T, ta.Any], ta.Union[bool, 'types.NotImplementedType']]:
266
+ try:
267
+ return self._eq
268
+ except AttributeError:
269
+ pass
270
+
271
+ def _eq(o: T, x: ta.Any) -> 'ta.Union[bool, types.NotImplementedType]':
272
+ if self._subtypes_eq:
273
+ if not isinstance(x, type(o)):
274
+ return NotImplemented
275
+ else:
276
+ if type(x) is not type(o):
277
+ return NotImplemented
278
+
279
+ return all(
280
+ getattr(o, a._name) == getattr(x, a._name) # noqa
281
+ for a in self._attrs
282
+ if a._eq # noqa
283
+ )
284
+
285
+ self._eq = _eq
286
+ return _eq
287
+
288
+ #
289
+
290
+ @property
291
+ def hash_eq(self) -> ta.Tuple[
292
+ ta.Callable[[T], int],
293
+ ta.Callable[[T, ta.Any], ta.Union[bool, 'types.NotImplementedType']],
294
+ ]:
295
+ return (self.hash, self.eq)
296
+
297
+ @property
298
+ def repr_hash_eq(self) -> ta.Tuple[
299
+ ta.Callable[[T], str],
300
+ ta.Callable[[T], int],
301
+ ta.Callable[[T, ta.Any], ta.Union[bool, 'types.NotImplementedType']],
302
+ ]:
303
+ return (self.repr, self.hash, self.eq)
304
+
305
+ #
306
+
307
+ def install(
308
+ self,
309
+ locals_dct: ta.MutableMapping[str, ta.Any],
310
+ *,
311
+ all: bool = False, # noqa
312
+ repr: bool = False, # noqa
313
+ hash: bool = False, # noqa
314
+ eq: bool = False,
315
+ ) -> 'AttrOps[T]':
316
+ if repr or all:
317
+ locals_dct.update(__repr__=self.repr)
318
+ if hash or all:
319
+ locals_dct.update(__hash__=self.hash)
320
+ if eq or all:
321
+ locals_dct.update(__eq__=self.eq)
322
+ return self
323
+
324
+
325
+ attr_ops = AttrOps[ta.Any]
326
+
327
+
328
+ ##
329
+
330
+
331
+ def attr_repr(obj: ta.Any, *attrs: str, **kwargs: ta.Any) -> str:
332
+ return AttrOps(*attrs, **kwargs).repr(obj)
omlish/lite/cached.py CHANGED
@@ -21,7 +21,7 @@ class _AbstractCachedNullary:
21
21
  def __call__(self, *args, **kwargs): # noqa
22
22
  raise TypeError
23
23
 
24
- def __get__(self, instance, owner): # noqa
24
+ def __get__(self, instance, owner=None): # noqa
25
25
  bound = instance.__dict__[self._fn.__name__] = self.__class__(self._fn.__get__(instance, owner))
26
26
  return bound
27
27
 
omlish/lite/maybes.py CHANGED
@@ -148,6 +148,7 @@ class _Maybe(Maybe[T], Abstract):
148
148
  return op and not sp
149
149
 
150
150
 
151
+ @ta.final
151
152
  class _JustMaybe(_Maybe[T]):
152
153
  __slots__ = ('_v', '_hash')
153
154
 
@@ -185,6 +186,7 @@ class _JustMaybe(_Maybe[T]):
185
186
  )
186
187
 
187
188
 
189
+ @ta.final
188
190
  class _EmptyMaybe(_Maybe[T]):
189
191
  __slots__ = ()
190
192
 
omlish/lite/strings.py CHANGED
@@ -73,13 +73,6 @@ def split_keep_delimiter(s, d):
73
73
  ##
74
74
 
75
75
 
76
- def attr_repr(obj: ta.Any, *attrs: str) -> str:
77
- return f'{type(obj).__name__}({", ".join(f"{attr}={getattr(obj, attr)!r}" for attr in attrs)})'
78
-
79
-
80
- ##
81
-
82
-
83
76
  FORMAT_NUM_BYTES_SUFFIXES: ta.Sequence[str] = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB']
84
77
 
85
78
 
omlish/lite/timing.py CHANGED
@@ -1,6 +1,9 @@
1
- from ..logs.timing import LogTimingContext
2
- from ..logs.timing import log_timing_context
3
- from .logs import log
1
+ from ..logs.modules import get_module_logger
2
+ from ..logs.utils import LogTimingContext
3
+ from ..logs.utils import log_timing_context
4
+
5
+
6
+ log = get_module_logger(globals()) # noqa
4
7
 
5
8
 
6
9
  LogTimingContext.DEFAULT_LOG = log