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
omlish/lite/check.py CHANGED
@@ -1,99 +1,462 @@
1
1
  # ruff: noqa: UP006 UP007
2
+ """
3
+ TODO:
4
+ - def maybe(v: lang.Maybe[T])
5
+ - def not_ ?
6
+ - ** class @dataclass Raise - user message should be able to be an exception type or instance or factory
7
+ """
8
+ import collections
9
+ import threading
2
10
  import typing as ta
3
11
 
4
12
 
5
13
  T = ta.TypeVar('T')
6
14
  SizedT = ta.TypeVar('SizedT', bound=ta.Sized)
7
15
 
16
+ CheckMessage = ta.Union[str, ta.Callable[..., ta.Optional[str]], None] # ta.TypeAlias
17
+ CheckLateConfigureFn = ta.Callable[['Checks'], None] # ta.TypeAlias
18
+ CheckOnRaiseFn = ta.Callable[[Exception], None] # ta.TypeAlias
19
+ CheckExceptionFactory = ta.Callable[..., Exception] # ta.TypeAlias
20
+ CheckArgsRenderer = ta.Callable[..., ta.Optional[str]] # ta.TypeAlias
8
21
 
9
- def check_isinstance(v: ta.Any, spec: ta.Union[ta.Type[T], tuple]) -> T:
10
- if not isinstance(v, spec):
11
- raise TypeError(v)
12
- return v
13
22
 
23
+ ##
14
24
 
15
- def check_not_isinstance(v: T, spec: ta.Union[type, tuple]) -> T:
16
- if isinstance(v, spec):
17
- raise TypeError(v)
18
- return v
19
25
 
26
+ class Checks:
27
+ def __init__(self) -> None:
28
+ super().__init__()
20
29
 
21
- def check_none(v: T) -> None:
22
- if v is not None:
23
- raise ValueError(v)
30
+ self._config_lock = threading.RLock()
31
+ self._on_raise_fns: ta.Sequence[CheckOnRaiseFn] = []
32
+ self._exception_factory: CheckExceptionFactory = Checks.default_exception_factory
33
+ self._args_renderer: ta.Optional[CheckArgsRenderer] = None
34
+ self._late_configure_fns: ta.Sequence[CheckLateConfigureFn] = []
24
35
 
36
+ @staticmethod
37
+ def default_exception_factory(exc_cls: ta.Type[Exception], *args, **kwargs) -> Exception:
38
+ return exc_cls(*args, **kwargs) # noqa
25
39
 
26
- def check_not_none(v: ta.Optional[T]) -> T:
27
- if v is None:
28
- raise ValueError
29
- return v
40
+ #
30
41
 
42
+ def register_on_raise(self, fn: CheckOnRaiseFn) -> None:
43
+ with self._config_lock:
44
+ self._on_raise_fns = [*self._on_raise_fns, fn]
31
45
 
32
- def check_not(v: ta.Any) -> None:
33
- if v:
34
- raise ValueError(v)
35
- return v
46
+ def unregister_on_raise(self, fn: CheckOnRaiseFn) -> None:
47
+ with self._config_lock:
48
+ self._on_raise_fns = [e for e in self._on_raise_fns if e != fn]
36
49
 
50
+ #
37
51
 
38
- def check_non_empty_str(v: ta.Optional[str]) -> str:
39
- if not v:
40
- raise ValueError
41
- return v
52
+ def set_exception_factory(self, factory: CheckExceptionFactory) -> None:
53
+ self._exception_factory = factory
42
54
 
55
+ def set_args_renderer(self, renderer: ta.Optional[CheckArgsRenderer]) -> None:
56
+ self._args_renderer = renderer
43
57
 
44
- def check_state(v: bool, msg: str = 'Illegal state') -> None:
45
- if not v:
46
- raise ValueError(msg)
58
+ #
59
+
60
+ def register_late_configure(self, fn: CheckLateConfigureFn) -> None:
61
+ with self._config_lock:
62
+ self._late_configure_fns = [*self._late_configure_fns, fn]
47
63
 
64
+ def _late_configure(self) -> None:
65
+ if not self._late_configure_fns:
66
+ return
48
67
 
49
- def check_equal(l: T, r: T) -> T:
50
- if l != r:
51
- raise ValueError(l, r)
52
- return l
68
+ with self._config_lock:
69
+ if not (lc := self._late_configure_fns):
70
+ return
53
71
 
72
+ for fn in lc:
73
+ fn(self)
74
+
75
+ self._late_configure_fns = []
76
+
77
+ #
78
+
79
+ class _ArgsKwargs:
80
+ def __init__(self, *args, **kwargs):
81
+ self.args = args
82
+ self.kwargs = kwargs
83
+
84
+ def _raise(
85
+ self,
86
+ exception_type: ta.Type[Exception],
87
+ default_message: str,
88
+ message: CheckMessage,
89
+ ak: _ArgsKwargs = _ArgsKwargs(),
90
+ *,
91
+ render_fmt: ta.Optional[str] = None,
92
+ ) -> ta.NoReturn:
93
+ exc_args = ()
94
+ if callable(message):
95
+ message = ta.cast(ta.Callable, message)(*ak.args, **ak.kwargs)
96
+ if isinstance(message, tuple):
97
+ message, *exc_args = message # type: ignore
98
+
99
+ if message is None:
100
+ message = default_message
54
101
 
55
- def check_not_equal(l: T, r: T) -> T:
56
- if l == r:
57
- raise ValueError(l, r)
58
- return l
59
-
60
-
61
- def check_is(l: T, r: T) -> T:
62
- if l is not r:
63
- raise ValueError(l, r)
64
- return l
65
-
66
-
67
- def check_is_not(l: T, r: ta.Any) -> T:
68
- if l is r:
69
- raise ValueError(l, r)
70
- return l
71
-
72
-
73
- def check_in(v: T, c: ta.Container[T]) -> T:
74
- if v not in c:
75
- raise ValueError(v, c)
76
- return v
77
-
78
-
79
- def check_not_in(v: T, c: ta.Container[T]) -> T:
80
- if v in c:
81
- raise ValueError(v, c)
82
- return v
83
-
84
-
85
- def check_single(vs: ta.Iterable[T]) -> T:
86
- [v] = vs
87
- return v
88
-
89
-
90
- def check_empty(v: SizedT) -> SizedT:
91
- if len(v):
92
- raise ValueError(v)
93
- return v
94
-
95
-
96
- def check_non_empty(v: SizedT) -> SizedT:
97
- if not len(v):
98
- raise ValueError(v)
99
- return v
102
+ self._late_configure()
103
+
104
+ if render_fmt is not None and (af := self._args_renderer) is not None:
105
+ rendered_args = af(render_fmt, *ak.args)
106
+ if rendered_args is not None:
107
+ message = f'{message} : {rendered_args}'
108
+
109
+ exc = self._exception_factory(
110
+ exception_type,
111
+ message,
112
+ *exc_args,
113
+ *ak.args,
114
+ **ak.kwargs,
115
+ )
116
+
117
+ for fn in self._on_raise_fns:
118
+ fn(exc)
119
+
120
+ raise exc
121
+
122
+ #
123
+
124
+ def _unpack_isinstance_spec(self, spec: ta.Any) -> tuple:
125
+ if isinstance(spec, type):
126
+ return (spec,)
127
+ if not isinstance(spec, tuple):
128
+ spec = (spec,)
129
+ if None in spec:
130
+ spec = tuple(filter(None, spec)) + (None.__class__,) # noqa
131
+ if ta.Any in spec:
132
+ spec = (object,)
133
+ return spec
134
+
135
+ def isinstance(self, v: ta.Any, spec: ta.Union[ta.Type[T], tuple], msg: CheckMessage = None) -> T: # noqa
136
+ if not isinstance(v, self._unpack_isinstance_spec(spec)):
137
+ self._raise(
138
+ TypeError,
139
+ 'Must be instance',
140
+ msg,
141
+ Checks._ArgsKwargs(v, spec),
142
+ render_fmt='not isinstance(%s, %s)',
143
+ )
144
+
145
+ return v
146
+
147
+ def of_isinstance(self, spec: ta.Union[ta.Type[T], tuple], msg: CheckMessage = None) -> ta.Callable[[ta.Any], T]:
148
+ def inner(v):
149
+ return self.isinstance(v, self._unpack_isinstance_spec(spec), msg)
150
+
151
+ return inner
152
+
153
+ def cast(self, v: ta.Any, cls: ta.Type[T], msg: CheckMessage = None) -> T: # noqa
154
+ if not isinstance(v, cls):
155
+ self._raise(
156
+ TypeError,
157
+ 'Must be instance',
158
+ msg,
159
+ Checks._ArgsKwargs(v, cls),
160
+ )
161
+
162
+ return v
163
+
164
+ def of_cast(self, cls: ta.Type[T], msg: CheckMessage = None) -> ta.Callable[[T], T]:
165
+ def inner(v):
166
+ return self.cast(v, cls, msg)
167
+
168
+ return inner
169
+
170
+ def not_isinstance(self, v: T, spec: ta.Any, msg: CheckMessage = None) -> T: # noqa
171
+ if isinstance(v, self._unpack_isinstance_spec(spec)):
172
+ self._raise(
173
+ TypeError,
174
+ 'Must not be instance',
175
+ msg,
176
+ Checks._ArgsKwargs(v, spec),
177
+ render_fmt='isinstance(%s, %s)',
178
+ )
179
+
180
+ return v
181
+
182
+ def of_not_isinstance(self, spec: ta.Any, msg: CheckMessage = None) -> ta.Callable[[T], T]:
183
+ def inner(v):
184
+ return self.not_isinstance(v, self._unpack_isinstance_spec(spec), msg)
185
+
186
+ return inner
187
+
188
+ ##
189
+
190
+ def issubclass(self, v: ta.Type[T], spec: ta.Any, msg: CheckMessage = None) -> ta.Type[T]: # noqa
191
+ if not issubclass(v, spec):
192
+ self._raise(
193
+ TypeError,
194
+ 'Must be subclass',
195
+ msg,
196
+ Checks._ArgsKwargs(v, spec),
197
+ render_fmt='not issubclass(%s, %s)',
198
+ )
199
+
200
+ return v
201
+
202
+ def not_issubclass(self, v: ta.Type[T], spec: ta.Any, msg: CheckMessage = None) -> ta.Type[T]: # noqa
203
+ if issubclass(v, spec):
204
+ self._raise(
205
+ TypeError,
206
+ 'Must not be subclass',
207
+ msg,
208
+ Checks._ArgsKwargs(v, spec),
209
+ render_fmt='issubclass(%s, %s)',
210
+ )
211
+
212
+ return v
213
+
214
+ #
215
+
216
+ def in_(self, v: T, c: ta.Container[T], msg: CheckMessage = None) -> T:
217
+ if v not in c:
218
+ self._raise(
219
+ ValueError,
220
+ 'Must be in',
221
+ msg,
222
+ Checks._ArgsKwargs(v, c),
223
+ render_fmt='%s not in %s',
224
+ )
225
+
226
+ return v
227
+
228
+ def not_in(self, v: T, c: ta.Container[T], msg: CheckMessage = None) -> T:
229
+ if v in c:
230
+ self._raise(
231
+ ValueError,
232
+ 'Must not be in',
233
+ msg,
234
+ Checks._ArgsKwargs(v, c),
235
+ render_fmt='%s in %s',
236
+ )
237
+
238
+ return v
239
+
240
+ def empty(self, v: SizedT, msg: CheckMessage = None) -> SizedT:
241
+ if len(v) != 0:
242
+ self._raise(
243
+ ValueError,
244
+ 'Must be empty',
245
+ msg,
246
+ Checks._ArgsKwargs(v),
247
+ render_fmt='%s',
248
+ )
249
+
250
+ return v
251
+
252
+ def iterempty(self, v: ta.Iterable[T], msg: CheckMessage = None) -> ta.Iterable[T]:
253
+ it = iter(v)
254
+ try:
255
+ next(it)
256
+ except StopIteration:
257
+ pass
258
+ else:
259
+ self._raise(
260
+ ValueError,
261
+ 'Must be empty',
262
+ msg,
263
+ Checks._ArgsKwargs(v),
264
+ render_fmt='%s',
265
+ )
266
+
267
+ return v
268
+
269
+ def not_empty(self, v: SizedT, msg: CheckMessage = None) -> SizedT:
270
+ if len(v) == 0:
271
+ self._raise(
272
+ ValueError,
273
+ 'Must not be empty',
274
+ msg,
275
+ Checks._ArgsKwargs(v),
276
+ render_fmt='%s',
277
+ )
278
+
279
+ return v
280
+
281
+ def unique(self, it: ta.Iterable[T], msg: CheckMessage = None) -> ta.Iterable[T]:
282
+ dupes = [e for e, c in collections.Counter(it).items() if c > 1]
283
+ if dupes:
284
+ self._raise(
285
+ ValueError,
286
+ 'Must be unique',
287
+ msg,
288
+ Checks._ArgsKwargs(it, dupes),
289
+ )
290
+
291
+ return it
292
+
293
+ def single(self, obj: ta.Iterable[T], message: CheckMessage = None) -> T:
294
+ try:
295
+ [value] = obj
296
+ except ValueError:
297
+ self._raise(
298
+ ValueError,
299
+ 'Must be single',
300
+ message,
301
+ Checks._ArgsKwargs(obj),
302
+ render_fmt='%s',
303
+ )
304
+
305
+ return value
306
+
307
+ def opt_single(self, obj: ta.Iterable[T], message: CheckMessage = None) -> ta.Optional[T]:
308
+ it = iter(obj)
309
+ try:
310
+ value = next(it)
311
+ except StopIteration:
312
+ return None
313
+
314
+ try:
315
+ next(it)
316
+ except StopIteration:
317
+ return value # noqa
318
+
319
+ self._raise(
320
+ ValueError,
321
+ 'Must be empty or single',
322
+ message,
323
+ Checks._ArgsKwargs(obj),
324
+ render_fmt='%s',
325
+ )
326
+
327
+ raise RuntimeError # noqa
328
+
329
+ #
330
+
331
+ def none(self, v: ta.Any, msg: CheckMessage = None) -> None:
332
+ if v is not None:
333
+ self._raise(
334
+ ValueError,
335
+ 'Must be None',
336
+ msg,
337
+ Checks._ArgsKwargs(v),
338
+ render_fmt='%s',
339
+ )
340
+
341
+ def not_none(self, v: ta.Optional[T], msg: CheckMessage = None) -> T:
342
+ if v is None:
343
+ self._raise(
344
+ ValueError,
345
+ 'Must not be None',
346
+ msg,
347
+ Checks._ArgsKwargs(v),
348
+ render_fmt='%s',
349
+ )
350
+
351
+ return v
352
+
353
+ #
354
+
355
+ def equal(self, v: T, o: ta.Any, msg: CheckMessage = None) -> T:
356
+ if o != v:
357
+ self._raise(
358
+ ValueError,
359
+ 'Must be equal',
360
+ msg,
361
+ Checks._ArgsKwargs(v, o),
362
+ render_fmt='%s != %s',
363
+ )
364
+
365
+ return v
366
+
367
+ def is_(self, v: T, o: ta.Any, msg: CheckMessage = None) -> T:
368
+ if o is not v:
369
+ self._raise(
370
+ ValueError,
371
+ 'Must be the same',
372
+ msg,
373
+ Checks._ArgsKwargs(v, o),
374
+ render_fmt='%s is not %s',
375
+ )
376
+
377
+ return v
378
+
379
+ def is_not(self, v: T, o: ta.Any, msg: CheckMessage = None) -> T:
380
+ if o is v:
381
+ self._raise(
382
+ ValueError,
383
+ 'Must not be the same',
384
+ msg,
385
+ Checks._ArgsKwargs(v, o),
386
+ render_fmt='%s is %s',
387
+ )
388
+
389
+ return v
390
+
391
+ def callable(self, v: T, msg: CheckMessage = None) -> T: # noqa
392
+ if not callable(v):
393
+ self._raise(
394
+ TypeError,
395
+ 'Must be callable',
396
+ msg,
397
+ Checks._ArgsKwargs(v),
398
+ render_fmt='%s',
399
+ )
400
+
401
+ return v # type: ignore
402
+
403
+ def non_empty_str(self, v: ta.Optional[str], msg: CheckMessage = None) -> str:
404
+ if not isinstance(v, str) or not v:
405
+ self._raise(
406
+ ValueError,
407
+ 'Must be non-empty str',
408
+ msg,
409
+ Checks._ArgsKwargs(v),
410
+ render_fmt='%s',
411
+ )
412
+
413
+ return v
414
+
415
+ def replacing(self, expected: ta.Any, old: ta.Any, new: T, msg: CheckMessage = None) -> T:
416
+ if old != expected:
417
+ self._raise(
418
+ ValueError,
419
+ 'Must be replacing',
420
+ msg,
421
+ Checks._ArgsKwargs(expected, old, new),
422
+ render_fmt='%s -> %s -> %s',
423
+ )
424
+
425
+ return new
426
+
427
+ def replacing_none(self, old: ta.Any, new: T, msg: CheckMessage = None) -> T:
428
+ if old is not None:
429
+ self._raise(
430
+ ValueError,
431
+ 'Must be replacing None',
432
+ msg,
433
+ Checks._ArgsKwargs(old, new),
434
+ render_fmt='%s -> %s',
435
+ )
436
+
437
+ return new
438
+
439
+ #
440
+
441
+ def arg(self, v: bool, msg: CheckMessage = None) -> None:
442
+ if not v:
443
+ self._raise(
444
+ RuntimeError,
445
+ 'Argument condition not met',
446
+ msg,
447
+ Checks._ArgsKwargs(v),
448
+ render_fmt='%s',
449
+ )
450
+
451
+ def state(self, v: bool, msg: CheckMessage = None) -> None:
452
+ if not v:
453
+ self._raise(
454
+ RuntimeError,
455
+ 'State condition not met',
456
+ msg,
457
+ Checks._ArgsKwargs(v),
458
+ render_fmt='%s',
459
+ )
460
+
461
+
462
+ check = Checks()
@@ -2,8 +2,7 @@
2
2
  import contextlib
3
3
  import typing as ta
4
4
 
5
- from .check import check_not_none
6
- from .check import check_state
5
+ from .check import check
7
6
 
8
7
 
9
8
  T = ta.TypeVar('T')
@@ -17,7 +16,7 @@ class ExitStacked:
17
16
  _exit_stack: ta.Optional[contextlib.ExitStack] = None
18
17
 
19
18
  def __enter__(self: ExitStackedT) -> ExitStackedT:
20
- check_state(self._exit_stack is None)
19
+ check.state(self._exit_stack is None)
21
20
  es = self._exit_stack = contextlib.ExitStack()
22
21
  es.__enter__()
23
22
  return self
@@ -32,7 +31,7 @@ class ExitStacked:
32
31
  pass
33
32
 
34
33
  def _enter_context(self, cm: ta.ContextManager[T]) -> T:
35
- es = check_not_none(self._exit_stack)
34
+ es = check.not_none(self._exit_stack)
36
35
  return es.enter_context(cm)
37
36
 
38
37
 
@@ -62,3 +61,17 @@ def attr_setting(obj, attr, val, *, default=None): # noqa
62
61
  delattr(obj, attr)
63
62
  else:
64
63
  setattr(obj, attr, orig)
64
+
65
+
66
+ ##
67
+
68
+
69
+ class aclosing(contextlib.AbstractAsyncContextManager): # noqa
70
+ def __init__(self, thing):
71
+ self.thing = thing
72
+
73
+ async def __aenter__(self):
74
+ return self.thing
75
+
76
+ async def __aexit__(self, *exc_info):
77
+ await self.thing.aclose()
@@ -0,0 +1,42 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import dataclasses as dc
3
+ import typing as ta
4
+
5
+
6
+ def dataclass_cache_hash(
7
+ *,
8
+ cached_hash_attr: str = '__dataclass_hash__',
9
+ ):
10
+ def inner(cls):
11
+ if not isinstance(cls, type) and dc.is_dataclass(cls):
12
+ raise TypeError(cls)
13
+
14
+ if (
15
+ cls.__hash__ is object.__hash__ or
16
+ '__hash__' not in cls.__dict__
17
+ ):
18
+ raise TypeError(cls)
19
+
20
+ real_hash = cls.__hash__
21
+
22
+ def cached_hash(self) -> int:
23
+ try:
24
+ return object.__getattribute__(self, cached_hash_attr)
25
+ except AttributeError:
26
+ object.__setattr__(self, cached_hash_attr, h := real_hash(self)) # type: ignore[call-arg]
27
+ return h
28
+
29
+ cls.__hash__ = cached_hash # type: ignore[method-assign]
30
+
31
+ return cls
32
+
33
+ return inner
34
+
35
+
36
+ def dataclass_maybe_post_init(sup: ta.Any) -> bool:
37
+ try:
38
+ fn = sup.__post_init__
39
+ except AttributeError:
40
+ return False
41
+ fn()
42
+ return True