omlish 0.0.0.dev6__py3-none-any.whl → 0.0.0.dev8__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/__about__.py +109 -5
- omlish/__init__.py +0 -8
- omlish/asyncs/__init__.py +0 -9
- omlish/asyncs/anyio.py +40 -0
- omlish/bootstrap.py +737 -0
- omlish/check.py +1 -1
- omlish/collections/__init__.py +4 -0
- omlish/collections/exceptions.py +2 -0
- omlish/collections/utils.py +38 -9
- omlish/configs/strings.py +2 -0
- omlish/dataclasses/__init__.py +7 -0
- omlish/dataclasses/impl/descriptors.py +95 -0
- omlish/dataclasses/impl/reflect.py +1 -1
- omlish/dataclasses/utils.py +23 -0
- omlish/{lang/datetimes.py → datetimes.py} +8 -4
- omlish/diag/procfs.py +1 -1
- omlish/diag/threads.py +131 -48
- omlish/docker.py +16 -1
- omlish/fnpairs.py +0 -4
- omlish/{serde → formats}/dotenv.py +3 -0
- omlish/{serde → formats}/yaml.py +2 -2
- omlish/graphs/trees.py +1 -1
- omlish/http/consts.py +6 -0
- omlish/http/sessions.py +2 -2
- omlish/inject/__init__.py +4 -0
- omlish/inject/binder.py +3 -3
- omlish/inject/elements.py +1 -1
- omlish/inject/impl/injector.py +57 -27
- omlish/inject/impl/origins.py +2 -0
- omlish/inject/origins.py +3 -0
- omlish/inject/utils.py +18 -0
- omlish/iterators.py +69 -2
- omlish/lang/__init__.py +16 -7
- omlish/lang/classes/restrict.py +10 -0
- omlish/lang/contextmanagers.py +1 -1
- omlish/lang/descriptors.py +3 -3
- omlish/lang/imports.py +67 -0
- omlish/lang/iterables.py +40 -0
- omlish/lang/maybes.py +3 -0
- omlish/lang/objects.py +38 -0
- omlish/lang/strings.py +25 -0
- omlish/lang/sys.py +9 -0
- omlish/lang/typing.py +37 -0
- omlish/lite/__init__.py +1 -0
- omlish/lite/cached.py +18 -0
- omlish/lite/check.py +29 -0
- omlish/lite/contextmanagers.py +18 -0
- omlish/lite/json.py +30 -0
- omlish/lite/logs.py +121 -0
- omlish/lite/marshal.py +318 -0
- omlish/lite/reflect.py +49 -0
- omlish/lite/runtime.py +18 -0
- omlish/lite/secrets.py +19 -0
- omlish/lite/strings.py +25 -0
- omlish/lite/subprocesses.py +112 -0
- omlish/logs/__init__.py +13 -9
- omlish/logs/configs.py +17 -22
- omlish/logs/formatters.py +3 -48
- omlish/marshal/__init__.py +28 -0
- omlish/marshal/any.py +5 -5
- omlish/marshal/base.py +27 -11
- omlish/marshal/base64.py +24 -9
- omlish/marshal/dataclasses.py +34 -28
- omlish/marshal/datetimes.py +74 -18
- omlish/marshal/enums.py +14 -8
- omlish/marshal/exceptions.py +11 -1
- omlish/marshal/factories.py +59 -74
- omlish/marshal/forbidden.py +35 -0
- omlish/marshal/global_.py +11 -4
- omlish/marshal/iterables.py +21 -24
- omlish/marshal/mappings.py +23 -26
- omlish/marshal/numbers.py +51 -0
- omlish/marshal/optionals.py +11 -12
- omlish/marshal/polymorphism.py +86 -21
- omlish/marshal/primitives.py +4 -5
- omlish/marshal/standard.py +13 -8
- omlish/marshal/uuids.py +4 -5
- omlish/matchfns.py +218 -0
- omlish/os.py +64 -0
- omlish/reflect/__init__.py +39 -0
- omlish/reflect/isinstance.py +38 -0
- omlish/reflect/ops.py +84 -0
- omlish/reflect/subst.py +110 -0
- omlish/reflect/types.py +275 -0
- omlish/secrets/__init__.py +18 -2
- omlish/secrets/crypto.py +132 -0
- omlish/secrets/marshal.py +36 -7
- omlish/secrets/openssl.py +207 -0
- omlish/secrets/secrets.py +260 -8
- omlish/secrets/subprocesses.py +42 -0
- omlish/sql/dbs.py +6 -5
- omlish/sql/exprs.py +12 -0
- omlish/sql/secrets.py +10 -0
- omlish/term.py +1 -1
- omlish/testing/pytest/plugins/switches.py +54 -19
- omlish/text/glyphsplit.py +5 -0
- omlish-0.0.0.dev8.dist-info/METADATA +50 -0
- {omlish-0.0.0.dev6.dist-info → omlish-0.0.0.dev8.dist-info}/RECORD +105 -78
- {omlish-0.0.0.dev6.dist-info → omlish-0.0.0.dev8.dist-info}/WHEEL +1 -1
- omlish/logs/filters.py +0 -11
- omlish/reflect.py +0 -470
- omlish-0.0.0.dev6.dist-info/METADATA +0 -34
- /omlish/{asyncs/futures.py → concurrent.py} +0 -0
- /omlish/{serde → formats}/__init__.py +0 -0
- /omlish/{serde → formats}/json.py +0 -0
- /omlish/{serde → formats}/props.py +0 -0
- {omlish-0.0.0.dev6.dist-info → omlish-0.0.0.dev8.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev6.dist-info → omlish-0.0.0.dev8.dist-info}/top_level.txt +0 -0
omlish/marshal/polymorphism.py
CHANGED
|
@@ -19,12 +19,29 @@ from .base import Unmarshaler
|
|
|
19
19
|
from .base import UnmarshalerFactory
|
|
20
20
|
from .naming import Naming
|
|
21
21
|
from .naming import translate_name
|
|
22
|
+
from .registries import RegistryItem
|
|
22
23
|
from .values import Value
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
##
|
|
26
27
|
|
|
27
28
|
|
|
29
|
+
class TypeTagging(RegistryItem, lang.Abstract, lang.Sealed):
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class WrapperTypeTagging(TypeTagging, lang.Final):
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dc.dataclass(frozen=True)
|
|
38
|
+
class FieldTypeTagging(TypeTagging, lang.Final):
|
|
39
|
+
field: str
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
##
|
|
43
|
+
|
|
44
|
+
|
|
28
45
|
@dc.dataclass(frozen=True)
|
|
29
46
|
class Impl:
|
|
30
47
|
ty: type
|
|
@@ -33,7 +50,11 @@ class Impl:
|
|
|
33
50
|
|
|
34
51
|
|
|
35
52
|
class Polymorphism:
|
|
36
|
-
def __init__(
|
|
53
|
+
def __init__(
|
|
54
|
+
self,
|
|
55
|
+
ty: type,
|
|
56
|
+
impls: ta.Iterable[Impl],
|
|
57
|
+
) -> None:
|
|
37
58
|
super().__init__()
|
|
38
59
|
self._ty = ty
|
|
39
60
|
self._impls = list(impls)
|
|
@@ -72,7 +93,11 @@ class Polymorphism:
|
|
|
72
93
|
return self._by_tag
|
|
73
94
|
|
|
74
95
|
|
|
75
|
-
def polymorphism_from_subclasses(
|
|
96
|
+
def polymorphism_from_subclasses(
|
|
97
|
+
ty: type,
|
|
98
|
+
*,
|
|
99
|
+
naming: Naming | None = None,
|
|
100
|
+
) -> Polymorphism:
|
|
76
101
|
dct: dict[str, Impl] = {}
|
|
77
102
|
seen: set[type] = set()
|
|
78
103
|
todo: list[type] = [ty]
|
|
@@ -97,7 +122,7 @@ def polymorphism_from_subclasses(ty: type, *, naming: Naming | None = None) -> P
|
|
|
97
122
|
|
|
98
123
|
|
|
99
124
|
@dc.dataclass(frozen=True)
|
|
100
|
-
class
|
|
125
|
+
class WrapperPolymorphismMarshaler(Marshaler):
|
|
101
126
|
m: ta.Mapping[type, tuple[str, Marshaler]]
|
|
102
127
|
|
|
103
128
|
def marshal(self, ctx: MarshalContext, o: ta.Any | None) -> Value:
|
|
@@ -105,24 +130,43 @@ class DictKeyPolymorphismMarshaler(Marshaler):
|
|
|
105
130
|
return {tag: m.marshal(ctx, o)}
|
|
106
131
|
|
|
107
132
|
|
|
133
|
+
@dc.dataclass(frozen=True)
|
|
134
|
+
class FieldPolymorphismMarshaler(Marshaler):
|
|
135
|
+
m: ta.Mapping[type, tuple[str, Marshaler]]
|
|
136
|
+
tf: str
|
|
137
|
+
|
|
138
|
+
def marshal(self, ctx: MarshalContext, o: ta.Any | None) -> Value:
|
|
139
|
+
tag, m = self.m[type(o)]
|
|
140
|
+
return {self.tf: tag, **m.marshal(ctx, o)} # type: ignore
|
|
141
|
+
|
|
142
|
+
|
|
108
143
|
@dc.dataclass(frozen=True)
|
|
109
144
|
class PolymorphismMarshalerFactory(MarshalerFactory):
|
|
110
145
|
p: Polymorphism
|
|
146
|
+
tt: TypeTagging = WrapperTypeTagging()
|
|
111
147
|
|
|
112
|
-
def
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
148
|
+
def guard(self, ctx: MarshalContext, rty: rfl.Type) -> bool:
|
|
149
|
+
return rty is self.p.ty
|
|
150
|
+
|
|
151
|
+
def fn(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler:
|
|
152
|
+
check.is_(rty, self.p.ty)
|
|
153
|
+
m = {
|
|
154
|
+
i.ty: (i.tag, ctx.make(i.ty))
|
|
155
|
+
for i in self.p.impls
|
|
156
|
+
}
|
|
157
|
+
if isinstance(self.tt, WrapperTypeTagging):
|
|
158
|
+
return WrapperPolymorphismMarshaler(m)
|
|
159
|
+
elif isinstance(self.tt, FieldTypeTagging):
|
|
160
|
+
return FieldPolymorphismMarshaler(m, self.tt.field)
|
|
161
|
+
else:
|
|
162
|
+
raise TypeError(self.tt)
|
|
119
163
|
|
|
120
164
|
|
|
121
165
|
##
|
|
122
166
|
|
|
123
167
|
|
|
124
168
|
@dc.dataclass(frozen=True)
|
|
125
|
-
class
|
|
169
|
+
class WrapperPolymorphismUnmarshaler(Unmarshaler):
|
|
126
170
|
m: ta.Mapping[str, Unmarshaler]
|
|
127
171
|
|
|
128
172
|
def unmarshal(self, ctx: UnmarshalContext, v: Value) -> ta.Any | None:
|
|
@@ -132,16 +176,37 @@ class DictKeyPolymorphismUnmarshaler(Unmarshaler):
|
|
|
132
176
|
return u.unmarshal(ctx, iv) # type: ignore
|
|
133
177
|
|
|
134
178
|
|
|
179
|
+
@dc.dataclass(frozen=True)
|
|
180
|
+
class FieldPolymorphismUnmarshaler(Unmarshaler):
|
|
181
|
+
m: ta.Mapping[str, Unmarshaler]
|
|
182
|
+
tf: str
|
|
183
|
+
|
|
184
|
+
def unmarshal(self, ctx: UnmarshalContext, v: Value) -> ta.Any | None:
|
|
185
|
+
ma = dict(check.isinstance(v, collections.abc.Mapping))
|
|
186
|
+
tag = ma.pop(self.tf) # type: ignore
|
|
187
|
+
u = self.m[tag] # type: ignore
|
|
188
|
+
return u.unmarshal(ctx, ma)
|
|
189
|
+
|
|
190
|
+
|
|
135
191
|
@dc.dataclass(frozen=True)
|
|
136
192
|
class PolymorphismUnmarshalerFactory(UnmarshalerFactory):
|
|
137
193
|
p: Polymorphism
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
194
|
+
tt: TypeTagging = WrapperTypeTagging()
|
|
195
|
+
|
|
196
|
+
def guard(self, ctx: UnmarshalContext, rty: rfl.Type) -> bool:
|
|
197
|
+
return rty is self.p.ty
|
|
198
|
+
|
|
199
|
+
def fn(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler:
|
|
200
|
+
check.is_(rty, self.p.ty)
|
|
201
|
+
m = {
|
|
202
|
+
t: u
|
|
203
|
+
for i in self.p.impls
|
|
204
|
+
for u in [ctx.make(i.ty)]
|
|
205
|
+
for t in [i.tag, *i.alts]
|
|
206
|
+
}
|
|
207
|
+
if isinstance(self.tt, WrapperTypeTagging):
|
|
208
|
+
return WrapperPolymorphismUnmarshaler(m)
|
|
209
|
+
elif isinstance(self.tt, FieldTypeTagging):
|
|
210
|
+
return FieldPolymorphismUnmarshaler(m, self.tt.field)
|
|
211
|
+
else:
|
|
212
|
+
raise TypeError(self.tt)
|
omlish/marshal/primitives.py
CHANGED
|
@@ -2,11 +2,10 @@ import typing as ta
|
|
|
2
2
|
|
|
3
3
|
from .base import MarshalContext
|
|
4
4
|
from .base import Marshaler
|
|
5
|
-
from .base import
|
|
5
|
+
from .base import TypeMapMarshalerFactory
|
|
6
|
+
from .base import TypeMapUnmarshalerFactory
|
|
6
7
|
from .base import UnmarshalContext
|
|
7
8
|
from .base import Unmarshaler
|
|
8
|
-
from .base import UnmarshalerFactory
|
|
9
|
-
from .factories import TypeMapFactory
|
|
10
9
|
from .values import Value
|
|
11
10
|
|
|
12
11
|
|
|
@@ -34,10 +33,10 @@ class PrimitiveMarshalerUnmarshaler(Marshaler, Unmarshaler):
|
|
|
34
33
|
|
|
35
34
|
PRIMITIVE_MARSHALER_UNMARSHALER = PrimitiveMarshalerUnmarshaler()
|
|
36
35
|
|
|
37
|
-
PRIMITIVE_MARSHALER_FACTORY
|
|
36
|
+
PRIMITIVE_MARSHALER_FACTORY = TypeMapMarshalerFactory({ # noqa
|
|
38
37
|
t: PRIMITIVE_MARSHALER_UNMARSHALER for t in PRIMITIVE_TYPES
|
|
39
38
|
})
|
|
40
39
|
|
|
41
|
-
PRIMITIVE_UNMARSHALER_FACTORY
|
|
40
|
+
PRIMITIVE_UNMARSHALER_FACTORY = TypeMapUnmarshalerFactory({ # noqa
|
|
42
41
|
t: PRIMITIVE_MARSHALER_UNMARSHALER for t in PRIMITIVE_TYPES
|
|
43
42
|
})
|
omlish/marshal/standard.py
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
from .. import matchfns as mfs
|
|
1
2
|
from .any import ANY_MARSHALER_FACTORY
|
|
2
3
|
from .any import ANY_UNMARSHALER_FACTORY
|
|
3
4
|
from .base import MarshalerFactory
|
|
4
5
|
from .base import RecursiveMarshalerFactory
|
|
5
6
|
from .base import RecursiveUnmarshalerFactory
|
|
7
|
+
from .base import TypeCacheMarshalerFactory
|
|
8
|
+
from .base import TypeCacheUnmarshalerFactory
|
|
6
9
|
from .base import UnmarshalerFactory
|
|
7
10
|
from .base64 import BASE64_MARSHALER_FACTORY
|
|
8
11
|
from .base64 import BASE64_UNMARSHALER_FACTORY
|
|
@@ -12,12 +15,12 @@ from .datetimes import DATETIME_MARSHALER_FACTORY
|
|
|
12
15
|
from .datetimes import DATETIME_UNMARSHALER_FACTORY
|
|
13
16
|
from .enums import EnumMarshalerFactory
|
|
14
17
|
from .enums import EnumUnmarshalerFactory
|
|
15
|
-
from .factories import CompositeFactory
|
|
16
|
-
from .factories import TypeCacheFactory
|
|
17
18
|
from .iterables import IterableMarshalerFactory
|
|
18
19
|
from .iterables import IterableUnmarshalerFactory
|
|
19
20
|
from .mappings import MappingMarshalerFactory
|
|
20
21
|
from .mappings import MappingUnmarshalerFactory
|
|
22
|
+
from .numbers import NUMBERS_MARSHALER_FACTORY
|
|
23
|
+
from .numbers import NUMBERS_UNMARSHALER_FACTORY
|
|
21
24
|
from .optionals import OptionalMarshalerFactory
|
|
22
25
|
from .optionals import OptionalUnmarshalerFactory
|
|
23
26
|
from .primitives import PRIMITIVE_MARSHALER_FACTORY
|
|
@@ -34,6 +37,7 @@ STANDARD_MARSHALER_FACTORIES: list[MarshalerFactory] = [
|
|
|
34
37
|
OptionalMarshalerFactory(),
|
|
35
38
|
DataclassMarshalerFactory(),
|
|
36
39
|
EnumMarshalerFactory(),
|
|
40
|
+
NUMBERS_MARSHALER_FACTORY,
|
|
37
41
|
UUID_MARSHALER_FACTORY,
|
|
38
42
|
BASE64_MARSHALER_FACTORY,
|
|
39
43
|
DATETIME_MARSHALER_FACTORY,
|
|
@@ -44,10 +48,10 @@ STANDARD_MARSHALER_FACTORIES: list[MarshalerFactory] = [
|
|
|
44
48
|
|
|
45
49
|
|
|
46
50
|
def new_standard_marshaler_factory() -> MarshalerFactory:
|
|
47
|
-
return
|
|
51
|
+
return TypeCacheMarshalerFactory(
|
|
48
52
|
RecursiveMarshalerFactory(
|
|
49
|
-
|
|
50
|
-
|
|
53
|
+
mfs.MultiMatchFn(
|
|
54
|
+
list(STANDARD_MARSHALER_FACTORIES),
|
|
51
55
|
),
|
|
52
56
|
),
|
|
53
57
|
)
|
|
@@ -61,6 +65,7 @@ STANDARD_UNMARSHALER_FACTORIES: list[UnmarshalerFactory] = [
|
|
|
61
65
|
OptionalUnmarshalerFactory(),
|
|
62
66
|
DataclassUnmarshalerFactory(),
|
|
63
67
|
EnumUnmarshalerFactory(),
|
|
68
|
+
NUMBERS_UNMARSHALER_FACTORY,
|
|
64
69
|
UUID_UNMARSHALER_FACTORY,
|
|
65
70
|
BASE64_UNMARSHALER_FACTORY,
|
|
66
71
|
DATETIME_UNMARSHALER_FACTORY,
|
|
@@ -71,10 +76,10 @@ STANDARD_UNMARSHALER_FACTORIES: list[UnmarshalerFactory] = [
|
|
|
71
76
|
|
|
72
77
|
|
|
73
78
|
def new_standard_unmarshaler_factory() -> UnmarshalerFactory:
|
|
74
|
-
return
|
|
79
|
+
return TypeCacheUnmarshalerFactory(
|
|
75
80
|
RecursiveUnmarshalerFactory(
|
|
76
|
-
|
|
77
|
-
|
|
81
|
+
mfs.MultiMatchFn(
|
|
82
|
+
list(STANDARD_UNMARSHALER_FACTORIES),
|
|
78
83
|
),
|
|
79
84
|
),
|
|
80
85
|
)
|
omlish/marshal/uuids.py
CHANGED
|
@@ -4,11 +4,10 @@ import uuid
|
|
|
4
4
|
from .. import check
|
|
5
5
|
from .base import MarshalContext
|
|
6
6
|
from .base import Marshaler
|
|
7
|
-
from .base import
|
|
7
|
+
from .base import TypeMapMarshalerFactory
|
|
8
|
+
from .base import TypeMapUnmarshalerFactory
|
|
8
9
|
from .base import UnmarshalContext
|
|
9
10
|
from .base import Unmarshaler
|
|
10
|
-
from .base import UnmarshalerFactory
|
|
11
|
-
from .factories import TypeMapFactory
|
|
12
11
|
from .values import Value
|
|
13
12
|
|
|
14
13
|
|
|
@@ -25,5 +24,5 @@ class UuidMarshalerUnmarshaler(Marshaler, Unmarshaler):
|
|
|
25
24
|
|
|
26
25
|
UUID_MARSHALER_UNMARSHALER = UuidMarshalerUnmarshaler()
|
|
27
26
|
|
|
28
|
-
UUID_MARSHALER_FACTORY
|
|
29
|
-
UUID_UNMARSHALER_FACTORY
|
|
27
|
+
UUID_MARSHALER_FACTORY = TypeMapMarshalerFactory({uuid.UUID: UUID_MARSHALER_UNMARSHALER})
|
|
28
|
+
UUID_UNMARSHALER_FACTORY = TypeMapUnmarshalerFactory({uuid.UUID: UUID_MARSHALER_UNMARSHALER})
|
omlish/matchfns.py
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import dataclasses as dc
|
|
3
|
+
import typing as ta
|
|
4
|
+
|
|
5
|
+
from . import lang
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
T = ta.TypeVar('T')
|
|
9
|
+
P = ta.ParamSpec('P')
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
##
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class MatchGuardError(Exception):
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class MatchFn(abc.ABC, ta.Generic[P, T]):
|
|
20
|
+
@abc.abstractmethod
|
|
21
|
+
def guard(self, *args: P.args, **kwargs: P.kwargs) -> bool:
|
|
22
|
+
raise NotImplementedError
|
|
23
|
+
|
|
24
|
+
@abc.abstractmethod
|
|
25
|
+
def fn(self, *args: P.args, **kwargs: P.kwargs) -> T:
|
|
26
|
+
raise NotImplementedError
|
|
27
|
+
|
|
28
|
+
def __get__(self, instance, owner=None):
|
|
29
|
+
return self
|
|
30
|
+
|
|
31
|
+
@ta.final
|
|
32
|
+
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T:
|
|
33
|
+
if not self.guard(*args, **kwargs):
|
|
34
|
+
raise MatchGuardError(*args, **kwargs)
|
|
35
|
+
return self.fn(*args, **kwargs)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
##
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dc.dataclass(frozen=True)
|
|
42
|
+
class SimpleMatchFn(MatchFn[P, T]):
|
|
43
|
+
_guard: ta.Callable[P, bool]
|
|
44
|
+
_fn: ta.Callable[P, T]
|
|
45
|
+
|
|
46
|
+
def guard(self, *args: P.args, **kwargs: P.kwargs) -> bool:
|
|
47
|
+
return self._guard(*args, **kwargs)
|
|
48
|
+
|
|
49
|
+
def fn(self, *args: P.args, **kwargs: P.kwargs) -> T:
|
|
50
|
+
return self._fn(*args, **kwargs)
|
|
51
|
+
|
|
52
|
+
def __get__(self, instance, owner=None):
|
|
53
|
+
return self.__class__(
|
|
54
|
+
self._guard.__get__(instance, owner), # noqa
|
|
55
|
+
self._fn.__get__(instance, owner), # noqa
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@ta.overload
|
|
60
|
+
def simple(guard: ta.Callable[..., bool], fn: ta.Callable[P, T]) -> SimpleMatchFn[P, T]:
|
|
61
|
+
...
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@ta.overload
|
|
65
|
+
def simple(guard: ta.Callable[..., bool]) -> ta.Callable[[ta.Callable[P, T]], SimpleMatchFn[P, T]]:
|
|
66
|
+
...
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def simple(guard, fn=None):
|
|
70
|
+
def inner(fn): # noqa
|
|
71
|
+
return SimpleMatchFn(guard, fn)
|
|
72
|
+
if fn is not None:
|
|
73
|
+
return inner(fn)
|
|
74
|
+
else:
|
|
75
|
+
return inner
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
##
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class AmbiguousMatchesError(Exception):
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@dc.dataclass(frozen=True)
|
|
86
|
+
class MultiMatchFn(MatchFn[P, T]):
|
|
87
|
+
children: ta.Sequence[MatchFn[P, T]]
|
|
88
|
+
strict: bool = False
|
|
89
|
+
|
|
90
|
+
def _match(self, *args: P.args, **kwargs: P.kwargs) -> MatchFn[P, T] | None:
|
|
91
|
+
matches = []
|
|
92
|
+
for cur in self.children:
|
|
93
|
+
if cur.guard(*args, **kwargs):
|
|
94
|
+
if self.strict:
|
|
95
|
+
matches.append(cur)
|
|
96
|
+
else:
|
|
97
|
+
return cur
|
|
98
|
+
if not matches:
|
|
99
|
+
return None
|
|
100
|
+
elif len(matches) > 1:
|
|
101
|
+
raise AmbiguousMatchesError
|
|
102
|
+
else:
|
|
103
|
+
return matches[0]
|
|
104
|
+
|
|
105
|
+
def guard(self, *args: P.args, **kwargs: P.kwargs) -> bool:
|
|
106
|
+
return self._match(*args, **kwargs) is not None
|
|
107
|
+
|
|
108
|
+
def fn(self, *args: P.args, **kwargs: P.kwargs) -> T:
|
|
109
|
+
if (m := self._match(*args, **kwargs)) is None:
|
|
110
|
+
raise MatchGuardError(*args, **kwargs)
|
|
111
|
+
return m.fn(*args, **kwargs)
|
|
112
|
+
|
|
113
|
+
def __get__(self, instance, owner=None):
|
|
114
|
+
return self.__class__(
|
|
115
|
+
[c.__get__(instance, owner) for c in self.children],
|
|
116
|
+
strict=self.strict,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def multi(*children: MatchFn[P, T], strict: bool = False):
|
|
121
|
+
return MultiMatchFn(children, strict=strict) # noqa
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
##
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class CachedMultiFn(MatchFn[P, T]):
|
|
128
|
+
@staticmethod
|
|
129
|
+
def _default_key(*args, **kwargs):
|
|
130
|
+
return (args, tuple(sorted(kwargs.items(), key=lambda t: t[0])))
|
|
131
|
+
|
|
132
|
+
def __init__(
|
|
133
|
+
self,
|
|
134
|
+
f: MatchFn[P, T],
|
|
135
|
+
*,
|
|
136
|
+
key: ta.Callable[P, ta.Any] = _default_key,
|
|
137
|
+
lock: lang.DefaultLockable = None,
|
|
138
|
+
) -> None:
|
|
139
|
+
super().__init__()
|
|
140
|
+
self._f = f
|
|
141
|
+
self._key = key
|
|
142
|
+
self._lock = lock
|
|
143
|
+
self._lock_impl = lang.default_lock(lock)()
|
|
144
|
+
self._dct: dict[ta.Any, lang.Maybe[ta.Any]] = {}
|
|
145
|
+
|
|
146
|
+
def guard(self, *args: P.args, **kwargs: P.kwargs) -> bool:
|
|
147
|
+
with self._lock_impl:
|
|
148
|
+
k = self._key(*args, **kwargs)
|
|
149
|
+
try:
|
|
150
|
+
e = self._dct[k]
|
|
151
|
+
except KeyError:
|
|
152
|
+
if self._f.guard(*args, **kwargs):
|
|
153
|
+
return True
|
|
154
|
+
else:
|
|
155
|
+
self._dct[k] = lang.empty()
|
|
156
|
+
return False
|
|
157
|
+
else:
|
|
158
|
+
return e.present
|
|
159
|
+
|
|
160
|
+
def fn(self, *args: P.args, **kwargs: P.kwargs) -> T:
|
|
161
|
+
with self._lock_impl:
|
|
162
|
+
k = self._key(*args, **kwargs)
|
|
163
|
+
try:
|
|
164
|
+
e = self._dct[k]
|
|
165
|
+
except KeyError:
|
|
166
|
+
try:
|
|
167
|
+
ret = self._f(*args, **kwargs)
|
|
168
|
+
except MatchGuardError:
|
|
169
|
+
self._dct[k] = lang.empty()
|
|
170
|
+
raise
|
|
171
|
+
else:
|
|
172
|
+
self._dct[k] = lang.just(ret)
|
|
173
|
+
return ret
|
|
174
|
+
else:
|
|
175
|
+
if e.present:
|
|
176
|
+
return e.must()
|
|
177
|
+
else:
|
|
178
|
+
raise MatchGuardError(*args, **kwargs)
|
|
179
|
+
|
|
180
|
+
def __get__(self, instance, owner=None):
|
|
181
|
+
return self.__class__(self._f.__get__(instance, owner), key=self._key) # noqa
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
cached = CachedMultiFn
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
##
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
class MatchFnClass(MatchFn[P, T]):
|
|
191
|
+
_cls_match_fn: ta.ClassVar[MultiMatchFn]
|
|
192
|
+
|
|
193
|
+
def __init__(self) -> None:
|
|
194
|
+
super().__init__()
|
|
195
|
+
self.__match_fn: MatchFn[P, T] | None = None
|
|
196
|
+
|
|
197
|
+
@property
|
|
198
|
+
def _match_fn(self) -> MatchFn[P, T]:
|
|
199
|
+
if (mf := self.__match_fn) is None:
|
|
200
|
+
mf = self.__match_fn = self._cls_match_fn.__get__(self)
|
|
201
|
+
return mf
|
|
202
|
+
|
|
203
|
+
def __init_subclass__(cls, strict: bool = False, **kwargs):
|
|
204
|
+
super().__init_subclass__()
|
|
205
|
+
if '_cls_match_fn' in cls.__dict__:
|
|
206
|
+
raise AttributeError('_cls_match_fn')
|
|
207
|
+
d = {}
|
|
208
|
+
for c in cls.__mro__:
|
|
209
|
+
for a, o in c.__dict__.items():
|
|
210
|
+
if isinstance(o, MatchFn) and a not in d:
|
|
211
|
+
d[a] = o
|
|
212
|
+
cls._cls_match_fn = MultiMatchFn(list(d.values()), strict=strict)
|
|
213
|
+
|
|
214
|
+
def guard(self, *args: P.args, **kwargs: P.kwargs) -> bool:
|
|
215
|
+
return self._match_fn.guard(*args, **kwargs)
|
|
216
|
+
|
|
217
|
+
def fn(self, *args: P.args, **kwargs: P.kwargs) -> T:
|
|
218
|
+
return self._match_fn.fn(*args, **kwargs)
|
omlish/os.py
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import contextlib
|
|
2
|
+
import fcntl
|
|
3
|
+
import os
|
|
2
4
|
import resource
|
|
3
5
|
import shutil
|
|
6
|
+
import signal
|
|
4
7
|
import tempfile
|
|
5
8
|
import typing as ta
|
|
6
9
|
|
|
@@ -39,3 +42,64 @@ def tmp_file(
|
|
|
39
42
|
finally:
|
|
40
43
|
if cleanup:
|
|
41
44
|
shutil.rmtree(f.name, ignore_errors=True)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class Pidfile:
|
|
48
|
+
def __init__(self, path: str) -> None:
|
|
49
|
+
super().__init__()
|
|
50
|
+
self._path = path
|
|
51
|
+
|
|
52
|
+
_f: ta.TextIO
|
|
53
|
+
|
|
54
|
+
def __repr__(self) -> str:
|
|
55
|
+
return f'{self.__class__.__name__}({self._path!r})'
|
|
56
|
+
|
|
57
|
+
def __enter__(self) -> ta.Self:
|
|
58
|
+
fd = os.open(self._path, os.O_RDWR | os.O_CREAT, 0o600)
|
|
59
|
+
try:
|
|
60
|
+
os.set_inheritable(fd, True)
|
|
61
|
+
f = os.fdopen(fd, 'r+')
|
|
62
|
+
except Exception:
|
|
63
|
+
try:
|
|
64
|
+
os.close(fd)
|
|
65
|
+
except Exception: # noqa
|
|
66
|
+
pass
|
|
67
|
+
raise
|
|
68
|
+
self._f = f
|
|
69
|
+
return self
|
|
70
|
+
|
|
71
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
72
|
+
if self._f is not None:
|
|
73
|
+
self._f.close()
|
|
74
|
+
del self._f
|
|
75
|
+
|
|
76
|
+
def try_lock(self) -> bool:
|
|
77
|
+
try:
|
|
78
|
+
fcntl.flock(self._f, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
|
79
|
+
return True
|
|
80
|
+
except OSError:
|
|
81
|
+
return False
|
|
82
|
+
|
|
83
|
+
def write(self, pid: int | None = None) -> None:
|
|
84
|
+
if not self.try_lock():
|
|
85
|
+
raise RuntimeError('Could not get lock')
|
|
86
|
+
if pid is None:
|
|
87
|
+
pid = os.getpid()
|
|
88
|
+
self._f.write(f'{pid}\n')
|
|
89
|
+
self._f.flush()
|
|
90
|
+
|
|
91
|
+
def clear(self) -> None:
|
|
92
|
+
if not self.try_lock():
|
|
93
|
+
raise RuntimeError('Could not get lock')
|
|
94
|
+
self._f.seek(0)
|
|
95
|
+
self._f.truncate()
|
|
96
|
+
|
|
97
|
+
def read(self) -> int:
|
|
98
|
+
if self.try_lock():
|
|
99
|
+
raise RuntimeError('Got lock')
|
|
100
|
+
self._f.seek(0)
|
|
101
|
+
return int(self._f.read())
|
|
102
|
+
|
|
103
|
+
def kill(self, sig: int = signal.SIGTERM) -> None:
|
|
104
|
+
pid = self.read()
|
|
105
|
+
os.kill(pid, sig) # Still racy
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from .types import ( # noqa
|
|
2
|
+
ANY,
|
|
3
|
+
Annotated,
|
|
4
|
+
Any,
|
|
5
|
+
Generic,
|
|
6
|
+
NewType,
|
|
7
|
+
TYPES,
|
|
8
|
+
Type,
|
|
9
|
+
Union,
|
|
10
|
+
get_orig_class,
|
|
11
|
+
get_params,
|
|
12
|
+
is_type,
|
|
13
|
+
is_union_type,
|
|
14
|
+
type_,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
from .ops import ( # noqa
|
|
18
|
+
get_concrete_type,
|
|
19
|
+
get_underlying,
|
|
20
|
+
strip_annotations,
|
|
21
|
+
strip_objs,
|
|
22
|
+
to_annotation,
|
|
23
|
+
types_equivalent,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
from .isinstance import ( # noqa
|
|
27
|
+
KNOWN_ISINSTANCE_GENERICS,
|
|
28
|
+
isinstance_of,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
from .subst import ( # noqa
|
|
32
|
+
ALIAS_UPDATING_GENERIC_SUBSTITUTION,
|
|
33
|
+
DEFAULT_GENERIC_SUBSTITUTION,
|
|
34
|
+
GenericSubstitution,
|
|
35
|
+
generic_mro,
|
|
36
|
+
get_generic_bases,
|
|
37
|
+
get_type_var_replacements,
|
|
38
|
+
replace_type_vars,
|
|
39
|
+
)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import collections.abc
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
from .ops import get_underlying
|
|
5
|
+
from .types import Generic
|
|
6
|
+
from .types import NewType
|
|
7
|
+
from .types import Type
|
|
8
|
+
from .types import Union
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
KNOWN_ISINSTANCE_GENERICS: ta.AbstractSet[type] = frozenset([
|
|
12
|
+
collections.abc.Mapping,
|
|
13
|
+
collections.abc.Sequence,
|
|
14
|
+
collections.abc.Set,
|
|
15
|
+
])
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def isinstance_of(rfl: Type) -> ta.Callable[[ta.Any], bool]:
|
|
19
|
+
if isinstance(rfl, type):
|
|
20
|
+
return lambda o: isinstance(o, rfl)
|
|
21
|
+
|
|
22
|
+
if isinstance(rfl, NewType):
|
|
23
|
+
return isinstance_of(get_underlying(rfl))
|
|
24
|
+
|
|
25
|
+
if isinstance(rfl, Union):
|
|
26
|
+
fns = [isinstance_of(a) for a in rfl.args]
|
|
27
|
+
return lambda o: any(fn(o) for fn in fns)
|
|
28
|
+
|
|
29
|
+
if isinstance(rfl, Generic):
|
|
30
|
+
if rfl.cls in (collections.abc.Sequence, collections.abc.Set):
|
|
31
|
+
[efn] = map(isinstance_of, rfl.args)
|
|
32
|
+
return lambda o: isinstance(o, rfl.cls) and all(efn(e) for e in o) # type: ignore
|
|
33
|
+
|
|
34
|
+
if rfl.cls == collections.abc.Mapping:
|
|
35
|
+
kfn, vfn = map(isinstance_of, rfl.args)
|
|
36
|
+
return lambda o: isinstance(o, rfl.cls) and all(kfn(k) and vfn(v) for k, v in o.items()) # type: ignore
|
|
37
|
+
|
|
38
|
+
raise TypeError(rfl)
|