omlish 0.0.0.dev133__py3-none-any.whl → 0.0.0.dev177__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 (210) hide show
  1. omlish/.manifests.json +265 -7
  2. omlish/__about__.py +5 -3
  3. omlish/antlr/_runtime/__init__.py +0 -22
  4. omlish/antlr/_runtime/_all.py +24 -0
  5. omlish/antlr/_runtime/atn/ParserATNSimulator.py +1 -1
  6. omlish/antlr/_runtime/dfa/DFASerializer.py +1 -1
  7. omlish/antlr/_runtime/error/DiagnosticErrorListener.py +2 -1
  8. omlish/antlr/_runtime/xpath/XPath.py +7 -1
  9. omlish/antlr/_runtime/xpath/XPathLexer.py +1 -1
  10. omlish/antlr/delimit.py +106 -0
  11. omlish/antlr/dot.py +31 -0
  12. omlish/antlr/errors.py +11 -0
  13. omlish/antlr/input.py +96 -0
  14. omlish/antlr/parsing.py +19 -0
  15. omlish/antlr/runtime.py +102 -0
  16. omlish/antlr/utils.py +38 -0
  17. omlish/argparse/all.py +45 -0
  18. omlish/{argparse.py → argparse/cli.py} +112 -107
  19. omlish/asyncs/__init__.py +0 -35
  20. omlish/asyncs/all.py +35 -0
  21. omlish/asyncs/asyncio/all.py +7 -0
  22. omlish/asyncs/asyncio/channels.py +40 -0
  23. omlish/asyncs/asyncio/streams.py +45 -0
  24. omlish/asyncs/asyncio/subprocesses.py +238 -0
  25. omlish/asyncs/asyncio/timeouts.py +16 -0
  26. omlish/asyncs/bluelet/LICENSE +6 -0
  27. omlish/asyncs/bluelet/all.py +67 -0
  28. omlish/asyncs/bluelet/api.py +23 -0
  29. omlish/asyncs/bluelet/core.py +178 -0
  30. omlish/asyncs/bluelet/events.py +78 -0
  31. omlish/asyncs/bluelet/files.py +80 -0
  32. omlish/asyncs/bluelet/runner.py +416 -0
  33. omlish/asyncs/bluelet/sockets.py +214 -0
  34. omlish/bootstrap/sys.py +3 -3
  35. omlish/cached.py +2 -2
  36. omlish/check.py +49 -460
  37. omlish/codecs/__init__.py +72 -0
  38. omlish/codecs/base.py +106 -0
  39. omlish/codecs/bytes.py +119 -0
  40. omlish/codecs/chain.py +23 -0
  41. omlish/codecs/funcs.py +39 -0
  42. omlish/codecs/registry.py +139 -0
  43. omlish/codecs/standard.py +4 -0
  44. omlish/codecs/text.py +217 -0
  45. omlish/collections/cache/impl.py +50 -57
  46. omlish/collections/coerce.py +1 -0
  47. omlish/collections/mappings.py +1 -1
  48. omlish/configs/flattening.py +1 -1
  49. omlish/defs.py +1 -1
  50. omlish/diag/_pycharm/runhack.py +8 -2
  51. omlish/diag/procfs.py +8 -8
  52. omlish/docker/__init__.py +0 -36
  53. omlish/docker/all.py +31 -0
  54. omlish/docker/consts.py +4 -0
  55. omlish/{lite/docker.py → docker/detect.py} +18 -0
  56. omlish/docker/{helpers.py → timebomb.py} +0 -21
  57. omlish/formats/cbor.py +31 -0
  58. omlish/formats/cloudpickle.py +31 -0
  59. omlish/formats/codecs.py +93 -0
  60. omlish/formats/json/codecs.py +29 -0
  61. omlish/formats/json/delimted.py +4 -0
  62. omlish/formats/json/stream/errors.py +2 -0
  63. omlish/formats/json/stream/lex.py +12 -6
  64. omlish/formats/json/stream/parse.py +38 -22
  65. omlish/formats/json5.py +31 -0
  66. omlish/formats/pickle.py +31 -0
  67. omlish/formats/repr.py +25 -0
  68. omlish/formats/toml.py +17 -0
  69. omlish/formats/yaml.py +25 -0
  70. omlish/funcs/__init__.py +0 -0
  71. omlish/{genmachine.py → funcs/genmachine.py} +5 -4
  72. omlish/{matchfns.py → funcs/match.py} +1 -1
  73. omlish/funcs/pairs.py +215 -0
  74. omlish/http/__init__.py +0 -48
  75. omlish/http/all.py +48 -0
  76. omlish/http/coro/__init__.py +0 -0
  77. omlish/{lite/fdio/corohttp.py → http/coro/fdio.py} +21 -19
  78. omlish/{lite/http/coroserver.py → http/coro/server.py} +20 -21
  79. omlish/{lite/http → http}/handlers.py +3 -2
  80. omlish/{lite/http → http}/parsing.py +1 -0
  81. omlish/http/sessions.py +1 -1
  82. omlish/{lite/http → http}/versions.py +1 -0
  83. omlish/inject/managed.py +2 -2
  84. omlish/io/__init__.py +0 -3
  85. omlish/{lite/io.py → io/buffers.py} +8 -9
  86. omlish/io/compress/__init__.py +9 -0
  87. omlish/io/compress/abc.py +104 -0
  88. omlish/io/compress/adapters.py +148 -0
  89. omlish/io/compress/base.py +24 -0
  90. omlish/io/compress/brotli.py +47 -0
  91. omlish/io/compress/bz2.py +61 -0
  92. omlish/io/compress/codecs.py +78 -0
  93. omlish/io/compress/gzip.py +350 -0
  94. omlish/io/compress/lz4.py +91 -0
  95. omlish/io/compress/lzma.py +81 -0
  96. omlish/io/compress/snappy.py +34 -0
  97. omlish/io/compress/zlib.py +74 -0
  98. omlish/io/compress/zstd.py +44 -0
  99. omlish/io/fdio/__init__.py +1 -0
  100. omlish/{lite → io}/fdio/handlers.py +5 -5
  101. omlish/{lite → io}/fdio/kqueue.py +8 -8
  102. omlish/{lite → io}/fdio/manager.py +7 -7
  103. omlish/{lite → io}/fdio/pollers.py +13 -13
  104. omlish/io/generators/__init__.py +56 -0
  105. omlish/io/generators/consts.py +1 -0
  106. omlish/io/generators/direct.py +13 -0
  107. omlish/io/generators/readers.py +189 -0
  108. omlish/io/generators/stepped.py +191 -0
  109. omlish/io/pyio.py +5 -2
  110. omlish/iterators/__init__.py +24 -0
  111. omlish/iterators/iterators.py +132 -0
  112. omlish/iterators/recipes.py +18 -0
  113. omlish/iterators/tools.py +96 -0
  114. omlish/iterators/unique.py +67 -0
  115. omlish/lang/__init__.py +13 -1
  116. omlish/lang/functions.py +11 -2
  117. omlish/lang/generators.py +243 -0
  118. omlish/lang/iterables.py +46 -49
  119. omlish/lang/maybes.py +4 -4
  120. omlish/lite/cached.py +39 -6
  121. omlish/lite/check.py +438 -75
  122. omlish/lite/contextmanagers.py +17 -4
  123. omlish/lite/dataclasses.py +42 -0
  124. omlish/lite/inject.py +28 -45
  125. omlish/lite/logs.py +0 -270
  126. omlish/lite/marshal.py +309 -144
  127. omlish/lite/pycharm.py +47 -0
  128. omlish/lite/reflect.py +33 -0
  129. omlish/lite/resources.py +8 -0
  130. omlish/lite/runtime.py +4 -4
  131. omlish/lite/shlex.py +12 -0
  132. omlish/lite/socketserver.py +2 -2
  133. omlish/lite/strings.py +31 -0
  134. omlish/logs/__init__.py +0 -32
  135. omlish/logs/{_abc.py → abc.py} +0 -1
  136. omlish/logs/all.py +37 -0
  137. omlish/logs/{formatters.py → color.py} +1 -2
  138. omlish/logs/configs.py +7 -38
  139. omlish/logs/filters.py +10 -0
  140. omlish/logs/handlers.py +4 -1
  141. omlish/logs/json.py +56 -0
  142. omlish/logs/proxy.py +99 -0
  143. omlish/logs/standard.py +128 -0
  144. omlish/logs/utils.py +2 -2
  145. omlish/manifests/__init__.py +2 -0
  146. omlish/manifests/load.py +209 -0
  147. omlish/manifests/types.py +17 -0
  148. omlish/marshal/base.py +1 -1
  149. omlish/marshal/factories.py +1 -1
  150. omlish/marshal/forbidden.py +1 -1
  151. omlish/marshal/iterables.py +1 -1
  152. omlish/marshal/literals.py +50 -0
  153. omlish/marshal/mappings.py +1 -1
  154. omlish/marshal/maybes.py +1 -1
  155. omlish/marshal/standard.py +5 -1
  156. omlish/marshal/unions.py +1 -1
  157. omlish/os/__init__.py +0 -0
  158. omlish/os/atomics.py +205 -0
  159. omlish/os/deathsig.py +23 -0
  160. omlish/{os.py → os/files.py} +0 -9
  161. omlish/{lite → os}/journald.py +2 -1
  162. omlish/os/linux.py +484 -0
  163. omlish/os/paths.py +36 -0
  164. omlish/{lite → os}/pidfile.py +1 -0
  165. omlish/os/sizes.py +9 -0
  166. omlish/reflect/__init__.py +3 -0
  167. omlish/reflect/subst.py +2 -1
  168. omlish/reflect/types.py +126 -44
  169. omlish/secrets/pwhash.py +1 -1
  170. omlish/secrets/subprocesses.py +3 -1
  171. omlish/specs/jsonrpc/marshal.py +1 -1
  172. omlish/specs/openapi/marshal.py +1 -1
  173. omlish/sql/alchemy/asyncs.py +1 -1
  174. omlish/sql/queries/__init__.py +9 -1
  175. omlish/sql/queries/building.py +3 -0
  176. omlish/sql/queries/exprs.py +10 -27
  177. omlish/sql/queries/idents.py +48 -10
  178. omlish/sql/queries/names.py +80 -13
  179. omlish/sql/queries/params.py +64 -0
  180. omlish/sql/queries/rendering.py +1 -1
  181. omlish/subprocesses.py +340 -0
  182. omlish/term.py +29 -14
  183. omlish/testing/pytest/marks.py +2 -2
  184. omlish/testing/pytest/plugins/asyncs.py +6 -1
  185. omlish/testing/pytest/plugins/logging.py +1 -1
  186. omlish/testing/pytest/plugins/switches.py +1 -1
  187. {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/METADATA +7 -5
  188. {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/RECORD +200 -117
  189. omlish/fnpairs.py +0 -496
  190. omlish/formats/json/cli/__main__.py +0 -11
  191. omlish/formats/json/cli/cli.py +0 -298
  192. omlish/formats/json/cli/formats.py +0 -71
  193. omlish/formats/json/cli/io.py +0 -74
  194. omlish/formats/json/cli/parsing.py +0 -82
  195. omlish/formats/json/cli/processing.py +0 -48
  196. omlish/formats/json/cli/rendering.py +0 -92
  197. omlish/iterators.py +0 -300
  198. omlish/lite/subprocesses.py +0 -130
  199. /omlish/{formats/json/cli → argparse}/__init__.py +0 -0
  200. /omlish/{lite/fdio → asyncs/asyncio}/__init__.py +0 -0
  201. /omlish/asyncs/{asyncio.py → asyncio/asyncio.py} +0 -0
  202. /omlish/{lite/http → asyncs/bluelet}/__init__.py +0 -0
  203. /omlish/collections/{_abc.py → abc.py} +0 -0
  204. /omlish/{fnpipes.py → funcs/pipes.py} +0 -0
  205. /omlish/io/{_abc.py → abc.py} +0 -0
  206. /omlish/sql/{_abc.py → abc.py} +0 -0
  207. {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/LICENSE +0 -0
  208. {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/WHEEL +0 -0
  209. {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/entry_points.txt +0 -0
  210. {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,243 @@
1
+ import abc
2
+ import functools
3
+ import typing as ta
4
+
5
+ from .maybes import Maybe
6
+ from .maybes import empty
7
+ from .maybes import just
8
+
9
+
10
+ T = ta.TypeVar('T')
11
+ I = ta.TypeVar('I')
12
+ O = ta.TypeVar('O')
13
+ R = ta.TypeVar('R')
14
+ I_contra = ta.TypeVar('I_contra', contravariant=True)
15
+ O_co = ta.TypeVar('O_co', covariant=True)
16
+ R_co = ta.TypeVar('R_co', covariant=True)
17
+
18
+
19
+ ##
20
+
21
+
22
+ def nextgen(g: T) -> T:
23
+ next(g) # type: ignore
24
+ return g
25
+
26
+
27
+ def autostart(fn):
28
+ @functools.wraps(fn)
29
+ def inner(*args, **kwargs):
30
+ g = fn(*args, **kwargs)
31
+ if (o := next(g)) is not None:
32
+ raise TypeError(o)
33
+ return g
34
+ return inner
35
+
36
+
37
+ ##
38
+
39
+
40
+ @ta.runtime_checkable
41
+ class GeneratorLike(ta.Protocol[O_co, I_contra, R_co]):
42
+ def send(self, i: I_contra) -> O_co: # Raises[StopIteration[R_co]]
43
+ ...
44
+
45
+ def close(self) -> None:
46
+ ...
47
+
48
+
49
+ class GeneratorLike_(abc.ABC, ta.Generic[O, I, R]): # noqa
50
+ @abc.abstractmethod
51
+ def send(self, i: I) -> O: # Raises[StopIteration[R]]
52
+ raise NotImplementedError
53
+
54
+ def close(self) -> None:
55
+ pass
56
+
57
+
58
+ @ta.overload
59
+ def adapt_generator_like(gl: GeneratorLike_[O, I, R]) -> ta.Generator[O, I, R]:
60
+ ...
61
+
62
+
63
+ @ta.overload
64
+ def adapt_generator_like(gl: GeneratorLike[O, I, R]) -> ta.Generator[O, I, R]:
65
+ ...
66
+
67
+
68
+ def adapt_generator_like(gl):
69
+ try:
70
+ i = yield
71
+ while True:
72
+ i = yield gl.send(i)
73
+ except StopIteration as e:
74
+ return e.value
75
+ finally:
76
+ gl.close()
77
+
78
+
79
+ ##
80
+
81
+
82
+ class Generator(ta.Generator[O, I, R]):
83
+ def __init__(self, g: ta.Generator[O, I, R]) -> None:
84
+ super().__init__()
85
+ self._g = g
86
+
87
+ @property
88
+ def g(self) -> ta.Generator[O, I, R]:
89
+ return self._g
90
+
91
+ value: R
92
+
93
+ def __iter__(self):
94
+ return self
95
+
96
+ def __next__(self):
97
+ try:
98
+ return next(self._g)
99
+ except StopIteration as e:
100
+ self.value = e.value
101
+ raise
102
+
103
+ def send(self, v):
104
+ try:
105
+ return self._g.send(v)
106
+ except StopIteration as e:
107
+ self.value = e.value
108
+ raise
109
+
110
+ def throw(self, *args):
111
+ try:
112
+ return self._g.throw(*args)
113
+ except StopIteration as e:
114
+ self.value = e.value
115
+ raise
116
+
117
+ def close(self):
118
+ self._g.close()
119
+
120
+
121
+ ##
122
+
123
+
124
+ class CoroutineGenerator(ta.Generic[O, I, R]):
125
+ def __init__(self, g: ta.Generator[O, I, R]) -> None:
126
+ super().__init__()
127
+ self._g = g
128
+
129
+ @property
130
+ def g(self) -> ta.Generator[O, I, R]:
131
+ return self._g
132
+
133
+ #
134
+
135
+ def close(self) -> None:
136
+ self._g.close()
137
+
138
+ def __enter__(self) -> ta.Self:
139
+ return self
140
+
141
+ def __exit__(self, exc_type, exc_val, exc_tb):
142
+ self._g.close()
143
+
144
+ #
145
+
146
+ class Output(ta.NamedTuple, ta.Generic[T]):
147
+ v: T
148
+
149
+ @property
150
+ def is_return(self) -> bool:
151
+ raise NotImplementedError
152
+
153
+ class Yield(Output[T]):
154
+ @property
155
+ def is_return(self) -> bool:
156
+ return False
157
+
158
+ class Return(Output[T]):
159
+ @property
160
+ def is_return(self) -> bool:
161
+ return True
162
+
163
+ class Nothing:
164
+ def __new__(cls):
165
+ raise TypeError
166
+
167
+ #
168
+
169
+ def send(self, /, v: I | type[Nothing] = Nothing) -> Yield[O] | Return[R]:
170
+ try:
171
+ if v is self.Nothing:
172
+ o = next(self._g)
173
+ else:
174
+ o = self._g.send(v) # type: ignore[arg-type]
175
+ except StopIteration as e:
176
+ return self.Return(e.value)
177
+ else:
178
+ return self.Yield(o)
179
+
180
+ def send_opt(self, v: I | None) -> Yield[O] | Return[R]:
181
+ return self.send(v if v is not None else self.Nothing)
182
+
183
+ def send_maybe(self, v: Maybe[I]) -> Yield[O] | Return[R]:
184
+ return self.send(v.or_else(self.Nothing))
185
+
186
+ def throw(self, v: BaseException) -> Yield[O] | Return[R]:
187
+ try:
188
+ o = self._g.throw(v)
189
+ except StopIteration as e:
190
+ return self.Return(e.value)
191
+ else:
192
+ return self.Yield(o)
193
+
194
+
195
+ corogen = CoroutineGenerator
196
+
197
+
198
+ ##
199
+
200
+
201
+ class GeneratorMappedIterator(ta.Generic[O, I, R]):
202
+ """
203
+ Like a `map` iterator but takes a generator instead of a function. Provided generator *must* yield outputs 1:1 with
204
+ inputs.
205
+
206
+ Generator return value will be captured on `value` property - if present generator stopped, it absent iterator
207
+ stopped.
208
+ """
209
+
210
+ def __init__(self, g: ta.Generator[O, I, R], it: ta.Iterator[I]) -> None:
211
+ super().__init__()
212
+
213
+ self._g = g
214
+ self._it = it
215
+ self._value: Maybe[R] = empty()
216
+
217
+ @property
218
+ def g(self) -> ta.Generator[O, I, R]:
219
+ return self._g
220
+
221
+ @property
222
+ def it(self) -> ta.Iterator[I]:
223
+ return self._it
224
+
225
+ @property
226
+ def value(self) -> Maybe[R]:
227
+ return self._value
228
+
229
+ def __iter__(self) -> ta.Iterator[O]:
230
+ return self
231
+
232
+ def __next__(self) -> O:
233
+ i = next(self._it)
234
+ try:
235
+ o = self._g.send(i)
236
+ except StopIteration as e:
237
+ self._value = just(e.value)
238
+ raise StopIteration from e
239
+ return o
240
+
241
+
242
+ def genmap(g: ta.Generator[O, I, R], it: ta.Iterable[I]) -> GeneratorMappedIterator[O, I, R]:
243
+ return GeneratorMappedIterator(g, iter(it))
omlish/lang/iterables.py CHANGED
@@ -1,10 +1,10 @@
1
1
  import dataclasses as dc
2
+ import functools
2
3
  import itertools
3
4
  import typing as ta
4
5
 
5
6
 
6
7
  T = ta.TypeVar('T')
7
- S = ta.TypeVar('S')
8
8
  R = ta.TypeVar('R')
9
9
 
10
10
 
@@ -15,6 +15,9 @@ BUILTIN_SCALAR_ITERABLE_TYPES: tuple[type, ...] = (
15
15
  )
16
16
 
17
17
 
18
+ ##
19
+
20
+
18
21
  def ilen(it: ta.Iterable) -> int:
19
22
  c = 0
20
23
  for _ in it:
@@ -44,40 +47,36 @@ def interleave(vs: ta.Iterable[T], d: T) -> ta.Iterable[T]:
44
47
  yield v
45
48
 
46
49
 
47
- Rangeable: ta.TypeAlias = ta.Union[ # noqa
48
- int,
49
- tuple[int],
50
- tuple[int, int],
51
- tuple[int, int, int],
52
- ta.Iterable[int],
53
- ]
50
+ @ta.overload
51
+ def readiter(f: ta.TextIO, sz: int) -> ta.Iterator[str]:
52
+ ...
54
53
 
55
54
 
56
- def asrange(i: Rangeable) -> ta.Iterable[int]:
57
- if isinstance(i, int):
58
- return range(i)
59
- elif isinstance(i, tuple):
60
- return range(*i)
61
- elif isinstance(i, ta.Iterable):
62
- return i
63
- else:
64
- raise TypeError(i)
55
+ @ta.overload
56
+ def readiter(f: ta.BinaryIO, sz: int) -> ta.Iterator[bytes]:
57
+ ...
65
58
 
66
59
 
67
- def prodrange(*dims: Rangeable) -> ta.Iterable[ta.Sequence[int]]:
68
- if not dims:
69
- return []
70
- return itertools.product(*map(asrange, dims))
60
+ @ta.overload
61
+ def readiter(f: ta.IO, sz: int) -> ta.Iterator[ta.AnyStr]:
62
+ ...
63
+
64
+
65
+ def readiter(f, sz):
66
+ return iter(functools.partial(f.read, sz), None)
71
67
 
72
68
 
73
69
  @dc.dataclass(frozen=True)
74
- class itergen(ta.Generic[T]): # noqa
70
+ class IterGen(ta.Generic[T]):
75
71
  fn: ta.Callable[[], ta.Iterable[T]]
76
72
 
77
73
  def __iter__(self):
78
74
  return iter(self.fn())
79
75
 
80
76
 
77
+ itergen = IterGen
78
+
79
+
81
80
  def renumerate(it: ta.Iterable[T]) -> ta.Iterable[tuple[T, int]]:
82
81
  return ((e, i) for i, e in enumerate(it))
83
82
 
@@ -89,32 +88,30 @@ def flatmap(fn: ta.Callable[[T], ta.Iterable[R]], it: ta.Iterable[T]) -> ta.Iter
89
88
  return flatten(map(fn, it))
90
89
 
91
90
 
92
- class Generator(ta.Generator[T, S, R]):
93
- def __init__(self, gen: ta.Generator[T, S, R]) -> None:
94
- super().__init__()
95
- self.gen = gen
91
+ ##
96
92
 
97
- value: R
98
93
 
99
- def __iter__(self):
100
- return self
101
-
102
- def __next__(self):
103
- return self.send(None)
104
-
105
- def send(self, v):
106
- try:
107
- return self.gen.send(v)
108
- except StopIteration as e:
109
- self.value = e.value
110
- raise
111
-
112
- def throw(self, *args):
113
- try:
114
- return self.gen.throw(*args)
115
- except StopIteration as e:
116
- self.value = e.value
117
- raise
118
-
119
- def close(self):
120
- self.gen.close()
94
+ Rangeable: ta.TypeAlias = ta.Union[ # noqa
95
+ int,
96
+ tuple[int],
97
+ tuple[int, int],
98
+ tuple[int, int, int],
99
+ ta.Iterable[int],
100
+ ]
101
+
102
+
103
+ def asrange(i: Rangeable) -> ta.Iterable[int]:
104
+ if isinstance(i, int):
105
+ return range(i)
106
+ elif isinstance(i, tuple):
107
+ return range(*i)
108
+ elif isinstance(i, ta.Iterable):
109
+ return i
110
+ else:
111
+ raise TypeError(i)
112
+
113
+
114
+ def prodrange(*dims: Rangeable) -> ta.Iterable[ta.Sequence[int]]:
115
+ if not dims:
116
+ return []
117
+ return itertools.product(*map(asrange, dims))
omlish/lang/maybes.py CHANGED
@@ -50,11 +50,11 @@ class Maybe(abc.ABC, ta.Generic[T]):
50
50
  raise NotImplementedError
51
51
 
52
52
  @abc.abstractmethod
53
- def or_else(self, other: T) -> T:
53
+ def or_else(self, other: T | U) -> T | U:
54
54
  raise NotImplementedError
55
55
 
56
56
  @abc.abstractmethod
57
- def or_else_get(self, supplier: ta.Callable[[], T]) -> T:
57
+ def or_else_get(self, supplier: ta.Callable[[], U]) -> T | U:
58
58
  raise NotImplementedError
59
59
 
60
60
  @abc.abstractmethod
@@ -109,10 +109,10 @@ class _Maybe(Maybe[T], tuple):
109
109
  return value
110
110
  return _empty # noqa
111
111
 
112
- def or_else(self, other: T) -> T:
112
+ def or_else(self, other: T | U) -> T | U:
113
113
  return self.must() if self else other
114
114
 
115
- def or_else_get(self, supplier: ta.Callable[[], T]) -> T:
115
+ def or_else_get(self, supplier: ta.Callable[[], T | U]) -> T | U:
116
116
  return self.must() if self else supplier()
117
117
 
118
118
  def or_else_raise(self, exception_supplier: ta.Callable[[], Exception]) -> T:
omlish/lite/cached.py CHANGED
@@ -4,8 +4,13 @@ import typing as ta
4
4
 
5
5
  T = ta.TypeVar('T')
6
6
 
7
+ CallableT = ta.TypeVar('CallableT', bound=ta.Callable)
7
8
 
8
- class _cached_nullary: # noqa
9
+
10
+ ##
11
+
12
+
13
+ class _AbstractCachedNullary:
9
14
  def __init__(self, fn):
10
15
  super().__init__()
11
16
  self._fn = fn
@@ -13,14 +18,42 @@ class _cached_nullary: # noqa
13
18
  functools.update_wrapper(self, fn)
14
19
 
15
20
  def __call__(self, *args, **kwargs): # noqa
16
- if self._value is self._missing:
17
- self._value = self._fn()
18
- return self._value
21
+ raise TypeError
19
22
 
20
23
  def __get__(self, instance, owner): # noqa
21
24
  bound = instance.__dict__[self._fn.__name__] = self.__class__(self._fn.__get__(instance, owner))
22
25
  return bound
23
26
 
24
27
 
25
- def cached_nullary(fn): # ta.Callable[..., T]) -> ta.Callable[..., T]:
26
- return _cached_nullary(fn)
28
+ ##
29
+
30
+
31
+ class _CachedNullary(_AbstractCachedNullary):
32
+ def __call__(self, *args, **kwargs): # noqa
33
+ if self._value is self._missing:
34
+ self._value = self._fn()
35
+ return self._value
36
+
37
+
38
+ def cached_nullary(fn: CallableT) -> CallableT:
39
+ return _CachedNullary(fn) # type: ignore
40
+
41
+
42
+ def static_init(fn: CallableT) -> CallableT:
43
+ fn = cached_nullary(fn)
44
+ fn()
45
+ return fn
46
+
47
+
48
+ ##
49
+
50
+
51
+ class _AsyncCachedNullary(_AbstractCachedNullary):
52
+ async def __call__(self, *args, **kwargs):
53
+ if self._value is self._missing:
54
+ self._value = await self._fn()
55
+ return self._value
56
+
57
+
58
+ def async_cached_nullary(fn): # ta.Callable[..., T]) -> ta.Callable[..., T]:
59
+ return _AsyncCachedNullary(fn)