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.
- omlish/.manifests.json +265 -7
- omlish/__about__.py +5 -3
- omlish/antlr/_runtime/__init__.py +0 -22
- omlish/antlr/_runtime/_all.py +24 -0
- omlish/antlr/_runtime/atn/ParserATNSimulator.py +1 -1
- omlish/antlr/_runtime/dfa/DFASerializer.py +1 -1
- omlish/antlr/_runtime/error/DiagnosticErrorListener.py +2 -1
- omlish/antlr/_runtime/xpath/XPath.py +7 -1
- omlish/antlr/_runtime/xpath/XPathLexer.py +1 -1
- omlish/antlr/delimit.py +106 -0
- omlish/antlr/dot.py +31 -0
- omlish/antlr/errors.py +11 -0
- omlish/antlr/input.py +96 -0
- omlish/antlr/parsing.py +19 -0
- omlish/antlr/runtime.py +102 -0
- omlish/antlr/utils.py +38 -0
- omlish/argparse/all.py +45 -0
- omlish/{argparse.py → argparse/cli.py} +112 -107
- omlish/asyncs/__init__.py +0 -35
- omlish/asyncs/all.py +35 -0
- omlish/asyncs/asyncio/all.py +7 -0
- omlish/asyncs/asyncio/channels.py +40 -0
- omlish/asyncs/asyncio/streams.py +45 -0
- omlish/asyncs/asyncio/subprocesses.py +238 -0
- omlish/asyncs/asyncio/timeouts.py +16 -0
- omlish/asyncs/bluelet/LICENSE +6 -0
- omlish/asyncs/bluelet/all.py +67 -0
- omlish/asyncs/bluelet/api.py +23 -0
- omlish/asyncs/bluelet/core.py +178 -0
- omlish/asyncs/bluelet/events.py +78 -0
- omlish/asyncs/bluelet/files.py +80 -0
- omlish/asyncs/bluelet/runner.py +416 -0
- omlish/asyncs/bluelet/sockets.py +214 -0
- omlish/bootstrap/sys.py +3 -3
- omlish/cached.py +2 -2
- omlish/check.py +49 -460
- omlish/codecs/__init__.py +72 -0
- omlish/codecs/base.py +106 -0
- omlish/codecs/bytes.py +119 -0
- omlish/codecs/chain.py +23 -0
- omlish/codecs/funcs.py +39 -0
- omlish/codecs/registry.py +139 -0
- omlish/codecs/standard.py +4 -0
- omlish/codecs/text.py +217 -0
- omlish/collections/cache/impl.py +50 -57
- omlish/collections/coerce.py +1 -0
- omlish/collections/mappings.py +1 -1
- omlish/configs/flattening.py +1 -1
- omlish/defs.py +1 -1
- omlish/diag/_pycharm/runhack.py +8 -2
- omlish/diag/procfs.py +8 -8
- omlish/docker/__init__.py +0 -36
- omlish/docker/all.py +31 -0
- omlish/docker/consts.py +4 -0
- omlish/{lite/docker.py → docker/detect.py} +18 -0
- omlish/docker/{helpers.py → timebomb.py} +0 -21
- omlish/formats/cbor.py +31 -0
- omlish/formats/cloudpickle.py +31 -0
- omlish/formats/codecs.py +93 -0
- omlish/formats/json/codecs.py +29 -0
- omlish/formats/json/delimted.py +4 -0
- omlish/formats/json/stream/errors.py +2 -0
- omlish/formats/json/stream/lex.py +12 -6
- omlish/formats/json/stream/parse.py +38 -22
- omlish/formats/json5.py +31 -0
- omlish/formats/pickle.py +31 -0
- omlish/formats/repr.py +25 -0
- omlish/formats/toml.py +17 -0
- omlish/formats/yaml.py +25 -0
- omlish/funcs/__init__.py +0 -0
- omlish/{genmachine.py → funcs/genmachine.py} +5 -4
- omlish/{matchfns.py → funcs/match.py} +1 -1
- omlish/funcs/pairs.py +215 -0
- omlish/http/__init__.py +0 -48
- omlish/http/all.py +48 -0
- omlish/http/coro/__init__.py +0 -0
- omlish/{lite/fdio/corohttp.py → http/coro/fdio.py} +21 -19
- omlish/{lite/http/coroserver.py → http/coro/server.py} +20 -21
- omlish/{lite/http → http}/handlers.py +3 -2
- omlish/{lite/http → http}/parsing.py +1 -0
- omlish/http/sessions.py +1 -1
- omlish/{lite/http → http}/versions.py +1 -0
- omlish/inject/managed.py +2 -2
- omlish/io/__init__.py +0 -3
- omlish/{lite/io.py → io/buffers.py} +8 -9
- omlish/io/compress/__init__.py +9 -0
- omlish/io/compress/abc.py +104 -0
- omlish/io/compress/adapters.py +148 -0
- omlish/io/compress/base.py +24 -0
- omlish/io/compress/brotli.py +47 -0
- omlish/io/compress/bz2.py +61 -0
- omlish/io/compress/codecs.py +78 -0
- omlish/io/compress/gzip.py +350 -0
- omlish/io/compress/lz4.py +91 -0
- omlish/io/compress/lzma.py +81 -0
- omlish/io/compress/snappy.py +34 -0
- omlish/io/compress/zlib.py +74 -0
- omlish/io/compress/zstd.py +44 -0
- omlish/io/fdio/__init__.py +1 -0
- omlish/{lite → io}/fdio/handlers.py +5 -5
- omlish/{lite → io}/fdio/kqueue.py +8 -8
- omlish/{lite → io}/fdio/manager.py +7 -7
- omlish/{lite → io}/fdio/pollers.py +13 -13
- omlish/io/generators/__init__.py +56 -0
- omlish/io/generators/consts.py +1 -0
- omlish/io/generators/direct.py +13 -0
- omlish/io/generators/readers.py +189 -0
- omlish/io/generators/stepped.py +191 -0
- omlish/io/pyio.py +5 -2
- omlish/iterators/__init__.py +24 -0
- omlish/iterators/iterators.py +132 -0
- omlish/iterators/recipes.py +18 -0
- omlish/iterators/tools.py +96 -0
- omlish/iterators/unique.py +67 -0
- omlish/lang/__init__.py +13 -1
- omlish/lang/functions.py +11 -2
- omlish/lang/generators.py +243 -0
- omlish/lang/iterables.py +46 -49
- omlish/lang/maybes.py +4 -4
- omlish/lite/cached.py +39 -6
- omlish/lite/check.py +438 -75
- omlish/lite/contextmanagers.py +17 -4
- omlish/lite/dataclasses.py +42 -0
- omlish/lite/inject.py +28 -45
- omlish/lite/logs.py +0 -270
- omlish/lite/marshal.py +309 -144
- omlish/lite/pycharm.py +47 -0
- omlish/lite/reflect.py +33 -0
- omlish/lite/resources.py +8 -0
- omlish/lite/runtime.py +4 -4
- omlish/lite/shlex.py +12 -0
- omlish/lite/socketserver.py +2 -2
- omlish/lite/strings.py +31 -0
- omlish/logs/__init__.py +0 -32
- omlish/logs/{_abc.py → abc.py} +0 -1
- omlish/logs/all.py +37 -0
- omlish/logs/{formatters.py → color.py} +1 -2
- omlish/logs/configs.py +7 -38
- omlish/logs/filters.py +10 -0
- omlish/logs/handlers.py +4 -1
- omlish/logs/json.py +56 -0
- omlish/logs/proxy.py +99 -0
- omlish/logs/standard.py +128 -0
- omlish/logs/utils.py +2 -2
- omlish/manifests/__init__.py +2 -0
- omlish/manifests/load.py +209 -0
- omlish/manifests/types.py +17 -0
- omlish/marshal/base.py +1 -1
- omlish/marshal/factories.py +1 -1
- omlish/marshal/forbidden.py +1 -1
- omlish/marshal/iterables.py +1 -1
- omlish/marshal/literals.py +50 -0
- omlish/marshal/mappings.py +1 -1
- omlish/marshal/maybes.py +1 -1
- omlish/marshal/standard.py +5 -1
- omlish/marshal/unions.py +1 -1
- omlish/os/__init__.py +0 -0
- omlish/os/atomics.py +205 -0
- omlish/os/deathsig.py +23 -0
- omlish/{os.py → os/files.py} +0 -9
- omlish/{lite → os}/journald.py +2 -1
- omlish/os/linux.py +484 -0
- omlish/os/paths.py +36 -0
- omlish/{lite → os}/pidfile.py +1 -0
- omlish/os/sizes.py +9 -0
- omlish/reflect/__init__.py +3 -0
- omlish/reflect/subst.py +2 -1
- omlish/reflect/types.py +126 -44
- omlish/secrets/pwhash.py +1 -1
- omlish/secrets/subprocesses.py +3 -1
- omlish/specs/jsonrpc/marshal.py +1 -1
- omlish/specs/openapi/marshal.py +1 -1
- omlish/sql/alchemy/asyncs.py +1 -1
- omlish/sql/queries/__init__.py +9 -1
- omlish/sql/queries/building.py +3 -0
- omlish/sql/queries/exprs.py +10 -27
- omlish/sql/queries/idents.py +48 -10
- omlish/sql/queries/names.py +80 -13
- omlish/sql/queries/params.py +64 -0
- omlish/sql/queries/rendering.py +1 -1
- omlish/subprocesses.py +340 -0
- omlish/term.py +29 -14
- omlish/testing/pytest/marks.py +2 -2
- omlish/testing/pytest/plugins/asyncs.py +6 -1
- omlish/testing/pytest/plugins/logging.py +1 -1
- omlish/testing/pytest/plugins/switches.py +1 -1
- {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/METADATA +7 -5
- {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/RECORD +200 -117
- omlish/fnpairs.py +0 -496
- omlish/formats/json/cli/__main__.py +0 -11
- omlish/formats/json/cli/cli.py +0 -298
- omlish/formats/json/cli/formats.py +0 -71
- omlish/formats/json/cli/io.py +0 -74
- omlish/formats/json/cli/parsing.py +0 -82
- omlish/formats/json/cli/processing.py +0 -48
- omlish/formats/json/cli/rendering.py +0 -92
- omlish/iterators.py +0 -300
- omlish/lite/subprocesses.py +0 -130
- /omlish/{formats/json/cli → argparse}/__init__.py +0 -0
- /omlish/{lite/fdio → asyncs/asyncio}/__init__.py +0 -0
- /omlish/asyncs/{asyncio.py → asyncio/asyncio.py} +0 -0
- /omlish/{lite/http → asyncs/bluelet}/__init__.py +0 -0
- /omlish/collections/{_abc.py → abc.py} +0 -0
- /omlish/{fnpipes.py → funcs/pipes.py} +0 -0
- /omlish/io/{_abc.py → abc.py} +0 -0
- /omlish/sql/{_abc.py → abc.py} +0 -0
- {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev133.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
|
-
|
22
|
-
|
23
|
-
|
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
|
-
|
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
|
33
|
-
|
34
|
-
|
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
|
39
|
-
|
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
|
-
|
45
|
-
|
46
|
-
|
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
|
-
|
50
|
-
|
51
|
-
|
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
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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()
|
omlish/lite/contextmanagers.py
CHANGED
@@ -2,8 +2,7 @@
|
|
2
2
|
import contextlib
|
3
3
|
import typing as ta
|
4
4
|
|
5
|
-
from .check import
|
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
|
-
|
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 =
|
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
|