omlish 0.0.0.dev132__py3-none-any.whl → 0.0.0.dev177__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (210) hide show
  1. omlish/.manifests.json +265 -7
  2. omlish/__about__.py +7 -5
  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.dev132.dist-info → omlish-0.0.0.dev177.dist-info}/METADATA +13 -11
  188. {omlish-0.0.0.dev132.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.dev132.dist-info → omlish-0.0.0.dev177.dist-info}/LICENSE +0 -0
  208. {omlish-0.0.0.dev132.dist-info → omlish-0.0.0.dev177.dist-info}/WHEEL +0 -0
  209. {omlish-0.0.0.dev132.dist-info → omlish-0.0.0.dev177.dist-info}/entry_points.txt +0 -0
  210. {omlish-0.0.0.dev132.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)