omlish 0.0.0.dev6__py3-none-any.whl → 0.0.0.dev7__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.
Potentially problematic release.
This version of omlish might be problematic. Click here for more details.
- 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 +52 -0
- omlish/lite/marshal.py +316 -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/configs.py +15 -2
- omlish/logs/formatters.py +7 -2
- 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.dev7.dist-info/METADATA +50 -0
- {omlish-0.0.0.dev6.dist-info → omlish-0.0.0.dev7.dist-info}/RECORD +104 -76
- {omlish-0.0.0.dev6.dist-info → omlish-0.0.0.dev7.dist-info}/WHEEL +1 -1
- 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.dev7.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev6.dist-info → omlish-0.0.0.dev7.dist-info}/top_level.txt +0 -0
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)
|
omlish/reflect/ops.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import typing as ta
|
|
2
|
+
|
|
3
|
+
from .types import Annotated
|
|
4
|
+
from .types import Any
|
|
5
|
+
from .types import Generic
|
|
6
|
+
from .types import NewType
|
|
7
|
+
from .types import Type
|
|
8
|
+
from .types import Union
|
|
9
|
+
from .types import type_
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def strip_objs(ty: Type) -> Type:
|
|
13
|
+
if isinstance(ty, (type, ta.TypeVar, NewType, Any)):
|
|
14
|
+
return ty
|
|
15
|
+
|
|
16
|
+
if isinstance(ty, Union):
|
|
17
|
+
return Union(frozenset(map(strip_objs, ty.args)))
|
|
18
|
+
|
|
19
|
+
if isinstance(ty, Generic):
|
|
20
|
+
return Generic(ty.cls, tuple(map(strip_objs, ty.args)), ty.params, None)
|
|
21
|
+
|
|
22
|
+
if isinstance(ty, Annotated):
|
|
23
|
+
return Annotated(strip_objs(ty.ty), ty.md, None)
|
|
24
|
+
|
|
25
|
+
raise TypeError(ty)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def strip_annotations(ty: Type) -> Type:
|
|
29
|
+
if isinstance(ty, (type, ta.TypeVar, NewType, Any)):
|
|
30
|
+
return ty
|
|
31
|
+
|
|
32
|
+
if isinstance(ty, Union):
|
|
33
|
+
return Union(frozenset(map(strip_annotations, ty.args)))
|
|
34
|
+
|
|
35
|
+
if isinstance(ty, Generic):
|
|
36
|
+
return Generic(ty.cls, tuple(map(strip_annotations, ty.args)), ty.params, ty.obj)
|
|
37
|
+
|
|
38
|
+
if isinstance(ty, Annotated):
|
|
39
|
+
return strip_annotations(ty.ty)
|
|
40
|
+
|
|
41
|
+
raise TypeError(ty)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def types_equivalent(l: Type, r: Type) -> bool:
|
|
45
|
+
if isinstance(l, Generic) and isinstance(r, Generic):
|
|
46
|
+
return l.cls == r.cls and l.args == r.args
|
|
47
|
+
|
|
48
|
+
return l == r
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def get_underlying(nt: NewType) -> Type:
|
|
52
|
+
return type_(nt.obj.__supertype__) # noqa
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def get_concrete_type(ty: Type) -> type | None:
|
|
56
|
+
if isinstance(ty, type):
|
|
57
|
+
return ty
|
|
58
|
+
|
|
59
|
+
if isinstance(ty, Generic):
|
|
60
|
+
return ty.cls
|
|
61
|
+
|
|
62
|
+
if isinstance(ty, NewType):
|
|
63
|
+
return get_concrete_type(get_underlying(ty))
|
|
64
|
+
|
|
65
|
+
if isinstance(ty, (Union, ta.TypeVar, Any)):
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
raise TypeError(ty)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def to_annotation(ty: Type) -> ta.Any:
|
|
72
|
+
if isinstance(ty, Generic):
|
|
73
|
+
return ty.obj if ty.obj is not None else ty.cls
|
|
74
|
+
|
|
75
|
+
if isinstance(ty, Union):
|
|
76
|
+
return ta.Union[*tuple(to_annotation(e) for e in ty.args)]
|
|
77
|
+
|
|
78
|
+
if isinstance(ty, (type, ta.TypeVar, NewType)):
|
|
79
|
+
return ty
|
|
80
|
+
|
|
81
|
+
if isinstance(ty, Any):
|
|
82
|
+
return ta.Any
|
|
83
|
+
|
|
84
|
+
raise TypeError(ty)
|
omlish/reflect/subst.py
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import dataclasses as dc
|
|
2
|
+
import types
|
|
3
|
+
import typing as ta
|
|
4
|
+
|
|
5
|
+
from .. import c3
|
|
6
|
+
from .. import lang
|
|
7
|
+
from .ops import get_concrete_type
|
|
8
|
+
from .ops import to_annotation
|
|
9
|
+
from .types import Any
|
|
10
|
+
from .types import Generic
|
|
11
|
+
from .types import NewType
|
|
12
|
+
from .types import Type
|
|
13
|
+
from .types import Union
|
|
14
|
+
from .types import get_params
|
|
15
|
+
from .types import type_
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
if ta.TYPE_CHECKING:
|
|
19
|
+
from ..collections import cache
|
|
20
|
+
else:
|
|
21
|
+
cache = lang.proxy_import('.collections.cache', __package__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def get_type_var_replacements(ty: Type) -> ta.Mapping[ta.TypeVar, Type]:
|
|
25
|
+
if isinstance(ty, Generic):
|
|
26
|
+
return dict(zip(ty.params, ty.args))
|
|
27
|
+
|
|
28
|
+
return {}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def replace_type_vars(
|
|
32
|
+
ty: Type,
|
|
33
|
+
rpl: ta.Mapping[ta.TypeVar, Type],
|
|
34
|
+
*,
|
|
35
|
+
update_aliases: bool = False,
|
|
36
|
+
) -> Type:
|
|
37
|
+
def rec(cur):
|
|
38
|
+
if isinstance(cur, (type, NewType, Any)):
|
|
39
|
+
return cur
|
|
40
|
+
|
|
41
|
+
if isinstance(cur, Generic):
|
|
42
|
+
args = tuple(rec(a) for a in cur.args)
|
|
43
|
+
if update_aliases:
|
|
44
|
+
obj = cur.obj
|
|
45
|
+
if (ops := get_params(obj)):
|
|
46
|
+
nargs = [to_annotation(rpl[p]) for p in ops]
|
|
47
|
+
if ta.get_origin(obj) is ta.Generic:
|
|
48
|
+
# FIXME: None? filter_typing_generic in get_generic_bases?
|
|
49
|
+
pass
|
|
50
|
+
else:
|
|
51
|
+
obj = cur.obj[*nargs]
|
|
52
|
+
else:
|
|
53
|
+
obj = None
|
|
54
|
+
return dc.replace(cur, args=args, obj=obj)
|
|
55
|
+
|
|
56
|
+
if isinstance(cur, Union):
|
|
57
|
+
return Union(frozenset(rec(e) for e in cur.args))
|
|
58
|
+
|
|
59
|
+
if isinstance(cur, ta.TypeVar):
|
|
60
|
+
return rpl[cur]
|
|
61
|
+
|
|
62
|
+
raise TypeError(cur)
|
|
63
|
+
|
|
64
|
+
return rec(ty)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class GenericSubstitution:
|
|
68
|
+
def __init__(
|
|
69
|
+
self,
|
|
70
|
+
*,
|
|
71
|
+
update_aliases: bool = False,
|
|
72
|
+
cache_size: int = 0, # FIXME: ta.Generic isn't weakrefable..
|
|
73
|
+
) -> None:
|
|
74
|
+
super().__init__()
|
|
75
|
+
|
|
76
|
+
self._update_aliases = update_aliases
|
|
77
|
+
|
|
78
|
+
if cache_size > 0:
|
|
79
|
+
self.get_generic_bases = cache.cache(weak_keys=True, max_size=cache_size)(self.get_generic_bases) # type: ignore # noqa
|
|
80
|
+
self.generic_mro = cache.cache(weak_keys=True, max_size=cache_size)(self.generic_mro) # type: ignore
|
|
81
|
+
|
|
82
|
+
def get_generic_bases(self, ty: Type) -> tuple[Type, ...]:
|
|
83
|
+
if (cty := get_concrete_type(ty)) is not None:
|
|
84
|
+
rpl = get_type_var_replacements(ty)
|
|
85
|
+
ret: list[Type] = []
|
|
86
|
+
for b in types.get_original_bases(cty):
|
|
87
|
+
bty = type_(b)
|
|
88
|
+
if isinstance(bty, Generic) and isinstance(b, type):
|
|
89
|
+
# FIXME: throws away relative types, but can't use original vars as they're class-contextual
|
|
90
|
+
bty = type_(b[*((ta.Any,) * len(bty.params))]) # type: ignore
|
|
91
|
+
rty = replace_type_vars(bty, rpl, update_aliases=self._update_aliases)
|
|
92
|
+
ret.append(rty)
|
|
93
|
+
return tuple(ret)
|
|
94
|
+
return ()
|
|
95
|
+
|
|
96
|
+
def generic_mro(self, obj: ta.Any) -> list[Type]:
|
|
97
|
+
mro = c3.mro(
|
|
98
|
+
type_(obj),
|
|
99
|
+
get_bases=lambda t: self.get_generic_bases(t),
|
|
100
|
+
is_subclass=lambda l, r: issubclass(get_concrete_type(l), get_concrete_type(r)), # type: ignore
|
|
101
|
+
)
|
|
102
|
+
return [ty for ty in mro if get_concrete_type(ty) is not ta.Generic]
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
DEFAULT_GENERIC_SUBSTITUTION = GenericSubstitution()
|
|
106
|
+
|
|
107
|
+
get_generic_bases = DEFAULT_GENERIC_SUBSTITUTION.get_generic_bases
|
|
108
|
+
generic_mro = DEFAULT_GENERIC_SUBSTITUTION.generic_mro
|
|
109
|
+
|
|
110
|
+
ALIAS_UPDATING_GENERIC_SUBSTITUTION = GenericSubstitution(update_aliases=True)
|