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/manifests/load.py
ADDED
@@ -0,0 +1,209 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
"""
|
3
|
+
Should be kept somewhat lightweight - used in cli entrypoints.
|
4
|
+
|
5
|
+
TODO:
|
6
|
+
- persisted caching support - {pkg_name: manifests}
|
7
|
+
"""
|
8
|
+
import dataclasses as dc
|
9
|
+
import importlib.machinery
|
10
|
+
import importlib.resources
|
11
|
+
import json
|
12
|
+
import threading
|
13
|
+
import typing as ta
|
14
|
+
|
15
|
+
from .types import Manifest
|
16
|
+
|
17
|
+
|
18
|
+
##
|
19
|
+
|
20
|
+
|
21
|
+
class ManifestLoader:
|
22
|
+
def __init__(
|
23
|
+
self,
|
24
|
+
*,
|
25
|
+
module_remap: ta.Optional[ta.Mapping[str, str]] = None,
|
26
|
+
) -> None:
|
27
|
+
super().__init__()
|
28
|
+
|
29
|
+
self._lock = threading.RLock()
|
30
|
+
|
31
|
+
self._module_remap = module_remap or {}
|
32
|
+
self._module_reverse_remap = {v: k for k, v in self._module_remap.items()}
|
33
|
+
|
34
|
+
self._cls_cache: ta.Dict[str, type] = {}
|
35
|
+
self._raw_cache: ta.Dict[str, ta.Optional[ta.Sequence[Manifest]]] = {}
|
36
|
+
|
37
|
+
#
|
38
|
+
|
39
|
+
@classmethod
|
40
|
+
def from_entry_point(
|
41
|
+
cls,
|
42
|
+
globals: ta.Mapping[str, ta.Any], # noqa
|
43
|
+
*,
|
44
|
+
module_remap: ta.Optional[ta.Mapping[str, str]] = None,
|
45
|
+
**kwargs: ta.Any,
|
46
|
+
) -> 'ManifestLoader':
|
47
|
+
rm: ta.Dict[str, str] = {}
|
48
|
+
|
49
|
+
if module_remap:
|
50
|
+
rm.update(module_remap)
|
51
|
+
|
52
|
+
if '__name__' in globals and '__spec__' in globals:
|
53
|
+
name: str = globals['__name__']
|
54
|
+
spec: importlib.machinery.ModuleSpec = globals['__spec__']
|
55
|
+
if '__main__' not in rm and name == '__main__':
|
56
|
+
rm[spec.name] = '__main__'
|
57
|
+
|
58
|
+
return cls(module_remap=rm, **kwargs)
|
59
|
+
|
60
|
+
#
|
61
|
+
|
62
|
+
def _load_cls(self, key: str) -> type:
|
63
|
+
try:
|
64
|
+
return self._cls_cache[key]
|
65
|
+
except KeyError:
|
66
|
+
pass
|
67
|
+
|
68
|
+
if not key.startswith('$'):
|
69
|
+
raise Exception(f'Bad key: {key}')
|
70
|
+
|
71
|
+
parts = key[1:].split('.')
|
72
|
+
pos = next(i for i, p in enumerate(parts) if p[0].isupper())
|
73
|
+
|
74
|
+
mod_name = '.'.join(parts[:pos])
|
75
|
+
mod_name = self._module_remap.get(mod_name, mod_name)
|
76
|
+
mod = importlib.import_module(mod_name)
|
77
|
+
|
78
|
+
obj: ta.Any = mod
|
79
|
+
for ca in parts[pos:]:
|
80
|
+
obj = getattr(obj, ca)
|
81
|
+
|
82
|
+
cls = obj
|
83
|
+
if not isinstance(cls, type):
|
84
|
+
raise TypeError(cls)
|
85
|
+
|
86
|
+
self._cls_cache[key] = cls
|
87
|
+
return cls
|
88
|
+
|
89
|
+
def load_cls(self, key: str) -> type:
|
90
|
+
with self._lock:
|
91
|
+
return self._load_cls(key)
|
92
|
+
|
93
|
+
#
|
94
|
+
|
95
|
+
def _load_contents(self, obj: ta.Any, pkg_name: str) -> ta.Sequence[Manifest]:
|
96
|
+
if not isinstance(obj, (list, tuple)):
|
97
|
+
raise TypeError(obj)
|
98
|
+
|
99
|
+
lst: ta.List[Manifest] = []
|
100
|
+
for e in obj:
|
101
|
+
m = Manifest(**e)
|
102
|
+
|
103
|
+
m = dc.replace(m, module=pkg_name + m.module)
|
104
|
+
|
105
|
+
[(key, value_dct)] = m.value.items()
|
106
|
+
if not key.startswith('$'):
|
107
|
+
raise Exception(f'Bad key: {key}')
|
108
|
+
if key.startswith('$.'):
|
109
|
+
key = f'${pkg_name}{key[1:]}'
|
110
|
+
m = dc.replace(m, value={key: value_dct})
|
111
|
+
|
112
|
+
lst.append(m)
|
113
|
+
|
114
|
+
return lst
|
115
|
+
|
116
|
+
def load_contents(self, obj: ta.Any, pkg_name: str) -> ta.Sequence[Manifest]:
|
117
|
+
with self._lock:
|
118
|
+
return self.load_contents(obj, pkg_name)
|
119
|
+
|
120
|
+
#
|
121
|
+
|
122
|
+
def _load_raw(self, pkg_name: str) -> ta.Optional[ta.Sequence[Manifest]]:
|
123
|
+
try:
|
124
|
+
return self._raw_cache[pkg_name]
|
125
|
+
except KeyError:
|
126
|
+
pass
|
127
|
+
|
128
|
+
t = importlib.resources.files(pkg_name).joinpath('.manifests.json')
|
129
|
+
if not t.is_file():
|
130
|
+
self._raw_cache[pkg_name] = None
|
131
|
+
return None
|
132
|
+
|
133
|
+
src = t.read_text('utf-8')
|
134
|
+
obj = json.loads(src)
|
135
|
+
if not isinstance(obj, (list, tuple)):
|
136
|
+
raise TypeError(obj)
|
137
|
+
|
138
|
+
lst = self._load_contents(obj, pkg_name)
|
139
|
+
|
140
|
+
self._raw_cache[pkg_name] = lst
|
141
|
+
return lst
|
142
|
+
|
143
|
+
def load_raw(self, pkg_name: str) -> ta.Optional[ta.Sequence[Manifest]]:
|
144
|
+
with self._lock:
|
145
|
+
return self._load_raw(pkg_name)
|
146
|
+
|
147
|
+
#
|
148
|
+
|
149
|
+
def _load(
|
150
|
+
self,
|
151
|
+
*pkg_names: str,
|
152
|
+
only: ta.Optional[ta.Iterable[type]] = None,
|
153
|
+
) -> ta.Sequence[Manifest]:
|
154
|
+
only_keys: ta.Optional[ta.Set]
|
155
|
+
if only is not None:
|
156
|
+
only_keys = set()
|
157
|
+
for cls in only:
|
158
|
+
if not (isinstance(cls, type) and dc.is_dataclass(cls)):
|
159
|
+
raise TypeError(cls)
|
160
|
+
mod_name = cls.__module__
|
161
|
+
mod_name = self._module_reverse_remap.get(mod_name, mod_name)
|
162
|
+
only_keys.add(f'${mod_name}.{cls.__qualname__}')
|
163
|
+
else:
|
164
|
+
only_keys = None
|
165
|
+
|
166
|
+
lst: ta.List[Manifest] = []
|
167
|
+
for pn in pkg_names:
|
168
|
+
for manifest in (self.load_raw(pn) or []):
|
169
|
+
[(key, value_dct)] = manifest.value.items()
|
170
|
+
if only_keys is not None and key not in only_keys:
|
171
|
+
continue
|
172
|
+
|
173
|
+
cls = self._load_cls(key)
|
174
|
+
value = cls(**value_dct)
|
175
|
+
|
176
|
+
manifest = dc.replace(manifest, value=value)
|
177
|
+
lst.append(manifest)
|
178
|
+
|
179
|
+
return lst
|
180
|
+
|
181
|
+
def load(
|
182
|
+
self,
|
183
|
+
*pkg_names: str,
|
184
|
+
only: ta.Optional[ta.Iterable[type]] = None,
|
185
|
+
) -> ta.Sequence[Manifest]:
|
186
|
+
with self._lock:
|
187
|
+
return self._load(
|
188
|
+
*pkg_names,
|
189
|
+
only=only,
|
190
|
+
)
|
191
|
+
|
192
|
+
#
|
193
|
+
|
194
|
+
ENTRY_POINT_GROUP = 'omlish.manifests'
|
195
|
+
|
196
|
+
def discover(self) -> ta.Sequence[str]:
|
197
|
+
# This is a fat dep so do it late.
|
198
|
+
import importlib.metadata
|
199
|
+
|
200
|
+
return [
|
201
|
+
ep.value
|
202
|
+
for ep in importlib.metadata.entry_points(group=self.ENTRY_POINT_GROUP)
|
203
|
+
]
|
204
|
+
|
205
|
+
|
206
|
+
##
|
207
|
+
|
208
|
+
|
209
|
+
MANIFEST_LOADER = ManifestLoader()
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
import dataclasses as dc
|
3
|
+
import typing as ta
|
4
|
+
|
5
|
+
|
6
|
+
@dc.dataclass(frozen=True)
|
7
|
+
class ManifestOrigin:
|
8
|
+
module: str
|
9
|
+
attr: str
|
10
|
+
|
11
|
+
file: str
|
12
|
+
line: int
|
13
|
+
|
14
|
+
|
15
|
+
@dc.dataclass(frozen=True)
|
16
|
+
class Manifest(ManifestOrigin):
|
17
|
+
value: ta.Any
|
omlish/marshal/base.py
CHANGED
@@ -89,8 +89,8 @@ from .. import check
|
|
89
89
|
from .. import collections as col
|
90
90
|
from .. import dataclasses as dc
|
91
91
|
from .. import lang
|
92
|
-
from .. import matchfns as mfs
|
93
92
|
from .. import reflect as rfl
|
93
|
+
from ..funcs import match as mfs
|
94
94
|
from .exceptions import UnhandledTypeError
|
95
95
|
from .factories import RecursiveTypeFactory
|
96
96
|
from .factories import TypeCacheFactory
|
omlish/marshal/factories.py
CHANGED
omlish/marshal/forbidden.py
CHANGED
omlish/marshal/iterables.py
CHANGED
@@ -8,8 +8,8 @@ import functools
|
|
8
8
|
import typing as ta
|
9
9
|
|
10
10
|
from .. import check
|
11
|
-
from .. import matchfns as mfs
|
12
11
|
from .. import reflect as rfl
|
12
|
+
from ..funcs import match as mfs
|
13
13
|
from .base import MarshalContext
|
14
14
|
from .base import Marshaler
|
15
15
|
from .base import MarshalerFactoryMatchClass
|
@@ -0,0 +1,50 @@
|
|
1
|
+
import dataclasses as dc
|
2
|
+
import typing as ta
|
3
|
+
|
4
|
+
from .. import check
|
5
|
+
from .. import reflect as rfl
|
6
|
+
from .base import MarshalContext
|
7
|
+
from .base import Marshaler
|
8
|
+
from .base import MarshalerFactory
|
9
|
+
from .base import UnmarshalContext
|
10
|
+
from .base import Unmarshaler
|
11
|
+
from .base import UnmarshalerFactory
|
12
|
+
from .values import Value
|
13
|
+
|
14
|
+
|
15
|
+
@dc.dataclass(frozen=True)
|
16
|
+
class LiteralMarshaler(Marshaler):
|
17
|
+
e: Marshaler
|
18
|
+
vs: frozenset
|
19
|
+
|
20
|
+
def marshal(self, ctx: MarshalContext, o: ta.Any | None) -> Value:
|
21
|
+
return self.e.marshal(ctx, check.in_(o, self.vs))
|
22
|
+
|
23
|
+
|
24
|
+
class LiteralMarshalerFactory(MarshalerFactory):
|
25
|
+
def guard(self, ctx: MarshalContext, rty: rfl.Type) -> bool:
|
26
|
+
return isinstance(rty, rfl.Literal)
|
27
|
+
|
28
|
+
def fn(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler:
|
29
|
+
lty = check.isinstance(rty, rfl.Literal)
|
30
|
+
ety = check.single(set(map(type, lty.args)))
|
31
|
+
return LiteralMarshaler(ctx.make(ety), frozenset(lty.args))
|
32
|
+
|
33
|
+
|
34
|
+
@dc.dataclass(frozen=True)
|
35
|
+
class LiteralUnmarshaler(Unmarshaler):
|
36
|
+
e: Unmarshaler
|
37
|
+
vs: frozenset
|
38
|
+
|
39
|
+
def unmarshal(self, ctx: UnmarshalContext, v: Value) -> ta.Any | None:
|
40
|
+
return check.in_(self.e.unmarshal(ctx, v), self.vs)
|
41
|
+
|
42
|
+
|
43
|
+
class LiteralUnmarshalerFactory(UnmarshalerFactory):
|
44
|
+
def guard(self, ctx: UnmarshalContext, rty: rfl.Type) -> bool:
|
45
|
+
return isinstance(rty, rfl.Literal)
|
46
|
+
|
47
|
+
def fn(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler:
|
48
|
+
lty = check.isinstance(rty, rfl.Literal)
|
49
|
+
ety = check.single(set(map(type, lty.args)))
|
50
|
+
return LiteralUnmarshaler(ctx.make(ety), frozenset(lty.args))
|
omlish/marshal/mappings.py
CHANGED
@@ -3,8 +3,8 @@ import dataclasses as dc
|
|
3
3
|
import typing as ta
|
4
4
|
|
5
5
|
from .. import check
|
6
|
-
from .. import matchfns as mfs
|
7
6
|
from .. import reflect as rfl
|
7
|
+
from ..funcs import match as mfs
|
8
8
|
from .base import MarshalContext
|
9
9
|
from .base import Marshaler
|
10
10
|
from .base import MarshalerFactoryMatchClass
|
omlish/marshal/maybes.py
CHANGED
@@ -7,8 +7,8 @@ import typing as ta
|
|
7
7
|
|
8
8
|
from .. import check
|
9
9
|
from .. import lang
|
10
|
-
from .. import matchfns as mfs
|
11
10
|
from .. import reflect as rfl
|
11
|
+
from ..funcs import match as mfs
|
12
12
|
from .base import MarshalContext
|
13
13
|
from .base import Marshaler
|
14
14
|
from .base import MarshalerFactoryMatchClass
|
omlish/marshal/standard.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
from .. import
|
1
|
+
from ..funcs import match as mfs
|
2
2
|
from .any import ANY_MARSHALER_FACTORY
|
3
3
|
from .any import ANY_UNMARSHALER_FACTORY
|
4
4
|
from .base import MarshalerFactory
|
@@ -17,6 +17,8 @@ from .enums import EnumMarshalerFactory
|
|
17
17
|
from .enums import EnumUnmarshalerFactory
|
18
18
|
from .iterables import IterableMarshalerFactory
|
19
19
|
from .iterables import IterableUnmarshalerFactory
|
20
|
+
from .literals import LiteralMarshalerFactory
|
21
|
+
from .literals import LiteralUnmarshalerFactory
|
20
22
|
from .mappings import MappingMarshalerFactory
|
21
23
|
from .mappings import MappingUnmarshalerFactory
|
22
24
|
from .maybes import MaybeMarshalerFactory
|
@@ -48,6 +50,7 @@ STANDARD_MARSHALER_FACTORIES: list[MarshalerFactory] = [
|
|
48
50
|
DataclassMarshalerFactory(),
|
49
51
|
NamedtupleMarshalerFactory(),
|
50
52
|
EnumMarshalerFactory(),
|
53
|
+
LiteralMarshalerFactory(),
|
51
54
|
NUMBERS_MARSHALER_FACTORY,
|
52
55
|
UUID_MARSHALER_FACTORY,
|
53
56
|
BASE64_MARSHALER_FACTORY,
|
@@ -80,6 +83,7 @@ STANDARD_UNMARSHALER_FACTORIES: list[UnmarshalerFactory] = [
|
|
80
83
|
DataclassUnmarshalerFactory(),
|
81
84
|
NamedtupleUnmarshalerFactory(),
|
82
85
|
EnumUnmarshalerFactory(),
|
86
|
+
LiteralUnmarshalerFactory(),
|
83
87
|
NUMBERS_UNMARSHALER_FACTORY,
|
84
88
|
UUID_UNMARSHALER_FACTORY,
|
85
89
|
BASE64_UNMARSHALER_FACTORY,
|
omlish/marshal/unions.py
CHANGED
@@ -4,8 +4,8 @@ from .. import cached
|
|
4
4
|
from .. import check
|
5
5
|
from .. import dataclasses as dc
|
6
6
|
from .. import lang
|
7
|
-
from .. import matchfns as mfs
|
8
7
|
from .. import reflect as rfl
|
8
|
+
from ..funcs import match as mfs
|
9
9
|
from .base import MarshalContext
|
10
10
|
from .base import Marshaler
|
11
11
|
from .base import MarshalerFactory
|
omlish/os/__init__.py
ADDED
File without changes
|
omlish/os/atomics.py
ADDED
@@ -0,0 +1,205 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
# @omlish-lite
|
3
|
+
import abc
|
4
|
+
import os
|
5
|
+
import shutil
|
6
|
+
import tempfile
|
7
|
+
import typing as ta
|
8
|
+
|
9
|
+
from omlish.lite.check import check
|
10
|
+
from omlish.lite.strings import attr_repr
|
11
|
+
|
12
|
+
|
13
|
+
AtomicPathSwapKind = ta.Literal['dir', 'file']
|
14
|
+
AtomicPathSwapState = ta.Literal['open', 'committed', 'aborted'] # ta.TypeAlias
|
15
|
+
|
16
|
+
|
17
|
+
##
|
18
|
+
|
19
|
+
|
20
|
+
class AtomicPathSwap(abc.ABC):
|
21
|
+
def __init__(
|
22
|
+
self,
|
23
|
+
kind: AtomicPathSwapKind,
|
24
|
+
dst_path: str,
|
25
|
+
*,
|
26
|
+
auto_commit: bool = False,
|
27
|
+
) -> None:
|
28
|
+
super().__init__()
|
29
|
+
|
30
|
+
self._kind = kind
|
31
|
+
self._dst_path = dst_path
|
32
|
+
self._auto_commit = auto_commit
|
33
|
+
|
34
|
+
self._state: AtomicPathSwapState = 'open'
|
35
|
+
|
36
|
+
def __repr__(self) -> str:
|
37
|
+
return attr_repr(self, 'kind', 'dst_path', 'tmp_path')
|
38
|
+
|
39
|
+
@property
|
40
|
+
def kind(self) -> AtomicPathSwapKind:
|
41
|
+
return self._kind
|
42
|
+
|
43
|
+
@property
|
44
|
+
def dst_path(self) -> str:
|
45
|
+
return self._dst_path
|
46
|
+
|
47
|
+
@property
|
48
|
+
@abc.abstractmethod
|
49
|
+
def tmp_path(self) -> str:
|
50
|
+
raise NotImplementedError
|
51
|
+
|
52
|
+
#
|
53
|
+
|
54
|
+
@property
|
55
|
+
def state(self) -> AtomicPathSwapState:
|
56
|
+
return self._state
|
57
|
+
|
58
|
+
def _check_state(self, *states: AtomicPathSwapState) -> None:
|
59
|
+
if self._state not in states:
|
60
|
+
raise RuntimeError(f'Atomic path swap not in correct state: {self._state}, {states}')
|
61
|
+
|
62
|
+
#
|
63
|
+
|
64
|
+
@abc.abstractmethod
|
65
|
+
def _commit(self) -> None:
|
66
|
+
raise NotImplementedError
|
67
|
+
|
68
|
+
def commit(self) -> None:
|
69
|
+
if self._state == 'committed':
|
70
|
+
return
|
71
|
+
self._check_state('open')
|
72
|
+
try:
|
73
|
+
self._commit()
|
74
|
+
except Exception: # noqa
|
75
|
+
self._abort()
|
76
|
+
raise
|
77
|
+
else:
|
78
|
+
self._state = 'committed'
|
79
|
+
|
80
|
+
#
|
81
|
+
|
82
|
+
@abc.abstractmethod
|
83
|
+
def _abort(self) -> None:
|
84
|
+
raise NotImplementedError
|
85
|
+
|
86
|
+
def abort(self) -> None:
|
87
|
+
if self._state == 'aborted':
|
88
|
+
return
|
89
|
+
self._abort()
|
90
|
+
self._state = 'aborted'
|
91
|
+
|
92
|
+
#
|
93
|
+
|
94
|
+
def __enter__(self) -> 'AtomicPathSwap':
|
95
|
+
return self
|
96
|
+
|
97
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
98
|
+
if (
|
99
|
+
exc_type is None and
|
100
|
+
self._auto_commit and
|
101
|
+
self._state == 'open'
|
102
|
+
):
|
103
|
+
self.commit()
|
104
|
+
else:
|
105
|
+
self.abort()
|
106
|
+
|
107
|
+
|
108
|
+
class AtomicPathSwapping(abc.ABC):
|
109
|
+
@abc.abstractmethod
|
110
|
+
def begin_atomic_path_swap(
|
111
|
+
self,
|
112
|
+
kind: AtomicPathSwapKind,
|
113
|
+
dst_path: str,
|
114
|
+
*,
|
115
|
+
name_hint: ta.Optional[str] = None,
|
116
|
+
make_dirs: bool = False,
|
117
|
+
**kwargs: ta.Any,
|
118
|
+
) -> AtomicPathSwap:
|
119
|
+
raise NotImplementedError
|
120
|
+
|
121
|
+
|
122
|
+
##
|
123
|
+
|
124
|
+
|
125
|
+
class OsReplaceAtomicPathSwap(AtomicPathSwap):
|
126
|
+
def __init__(
|
127
|
+
self,
|
128
|
+
kind: AtomicPathSwapKind,
|
129
|
+
dst_path: str,
|
130
|
+
tmp_path: str,
|
131
|
+
**kwargs: ta.Any,
|
132
|
+
) -> None:
|
133
|
+
if kind == 'dir':
|
134
|
+
check.state(os.path.isdir(tmp_path))
|
135
|
+
elif kind == 'file':
|
136
|
+
check.state(os.path.isfile(tmp_path))
|
137
|
+
else:
|
138
|
+
raise TypeError(kind)
|
139
|
+
|
140
|
+
super().__init__(
|
141
|
+
kind,
|
142
|
+
dst_path,
|
143
|
+
**kwargs,
|
144
|
+
)
|
145
|
+
|
146
|
+
self._tmp_path = tmp_path
|
147
|
+
|
148
|
+
@property
|
149
|
+
def tmp_path(self) -> str:
|
150
|
+
return self._tmp_path
|
151
|
+
|
152
|
+
def _commit(self) -> None:
|
153
|
+
os.replace(self._tmp_path, self._dst_path)
|
154
|
+
|
155
|
+
def _abort(self) -> None:
|
156
|
+
shutil.rmtree(self._tmp_path, ignore_errors=True)
|
157
|
+
|
158
|
+
|
159
|
+
class TempDirAtomicPathSwapping(AtomicPathSwapping):
|
160
|
+
def __init__(
|
161
|
+
self,
|
162
|
+
*,
|
163
|
+
temp_dir: ta.Optional[str] = None,
|
164
|
+
root_dir: ta.Optional[str] = None,
|
165
|
+
) -> None:
|
166
|
+
super().__init__()
|
167
|
+
|
168
|
+
if root_dir is not None:
|
169
|
+
root_dir = os.path.abspath(root_dir)
|
170
|
+
self._root_dir = root_dir
|
171
|
+
self._temp_dir = temp_dir
|
172
|
+
|
173
|
+
def begin_atomic_path_swap(
|
174
|
+
self,
|
175
|
+
kind: AtomicPathSwapKind,
|
176
|
+
dst_path: str,
|
177
|
+
*,
|
178
|
+
name_hint: ta.Optional[str] = None,
|
179
|
+
make_dirs: bool = False,
|
180
|
+
**kwargs: ta.Any,
|
181
|
+
) -> AtomicPathSwap:
|
182
|
+
dst_path = os.path.abspath(dst_path)
|
183
|
+
if self._root_dir is not None and not dst_path.startswith(check.non_empty_str(self._root_dir)):
|
184
|
+
raise RuntimeError(f'Atomic path swap dst must be in root dir: {dst_path}, {self._root_dir}')
|
185
|
+
|
186
|
+
dst_dir = os.path.dirname(dst_path)
|
187
|
+
if make_dirs:
|
188
|
+
os.makedirs(dst_dir, exist_ok=True)
|
189
|
+
if not os.path.isdir(dst_dir):
|
190
|
+
raise RuntimeError(f'Atomic path swap dst dir does not exist: {dst_dir}')
|
191
|
+
|
192
|
+
if kind == 'dir':
|
193
|
+
tmp_path = tempfile.mkdtemp(prefix=name_hint, dir=self._temp_dir)
|
194
|
+
elif kind == 'file':
|
195
|
+
fd, tmp_path = tempfile.mkstemp(prefix=name_hint, dir=self._temp_dir)
|
196
|
+
os.close(fd)
|
197
|
+
else:
|
198
|
+
raise TypeError(kind)
|
199
|
+
|
200
|
+
return OsReplaceAtomicPathSwap(
|
201
|
+
kind,
|
202
|
+
dst_path,
|
203
|
+
tmp_path,
|
204
|
+
**kwargs,
|
205
|
+
)
|
omlish/os/deathsig.py
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# @omlish-lite
|
2
|
+
import ctypes as ct
|
3
|
+
import sys
|
4
|
+
|
5
|
+
|
6
|
+
LINUX_PR_SET_PDEATHSIG = 1 # Second arg is a signal
|
7
|
+
LINUX_PR_GET_PDEATHSIG = 2 # Second arg is a ptr to return the signal
|
8
|
+
|
9
|
+
|
10
|
+
def set_process_deathsig(sig: int) -> bool:
|
11
|
+
if sys.platform == 'linux':
|
12
|
+
libc = ct.CDLL('libc.so.6')
|
13
|
+
|
14
|
+
# int prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);
|
15
|
+
libc.prctl.restype = ct.c_int
|
16
|
+
libc.prctl.argtypes = [ct.c_int, ct.c_ulong, ct.c_ulong, ct.c_ulong, ct.c_ulong]
|
17
|
+
|
18
|
+
libc.prctl(LINUX_PR_SET_PDEATHSIG, sig, 0, 0, 0, 0)
|
19
|
+
|
20
|
+
return True
|
21
|
+
|
22
|
+
else:
|
23
|
+
return False
|
omlish/{os.py → os/files.py}
RENAMED
@@ -1,19 +1,10 @@
|
|
1
1
|
import contextlib
|
2
2
|
import os
|
3
|
-
import resource
|
4
3
|
import shutil
|
5
4
|
import tempfile
|
6
5
|
import typing as ta
|
7
6
|
|
8
7
|
|
9
|
-
PAGE_SIZE = resource.getpagesize()
|
10
|
-
|
11
|
-
|
12
|
-
def round_to_page_size(sz: int) -> int:
|
13
|
-
sz += PAGE_SIZE - 1
|
14
|
-
return sz - (sz % PAGE_SIZE)
|
15
|
-
|
16
|
-
|
17
8
|
@contextlib.contextmanager
|
18
9
|
def tmp_dir(
|
19
10
|
root_dir: str | None = None,
|
omlish/{lite → os}/journald.py
RENAMED
@@ -1,4 +1,5 @@
|
|
1
1
|
# ruff: noqa: UP007 UP012
|
2
|
+
# @omlish-lite
|
2
3
|
import ctypes as ct
|
3
4
|
import logging
|
4
5
|
import sys
|
@@ -6,7 +7,7 @@ import syslog
|
|
6
7
|
import threading
|
7
8
|
import typing as ta
|
8
9
|
|
9
|
-
from .cached import cached_nullary
|
10
|
+
from ..lite.cached import cached_nullary
|
10
11
|
|
11
12
|
|
12
13
|
##
|