omlish 0.0.0.dev420__py3-none-any.whl → 0.0.0.dev422__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 +2 -2
- omlish/asyncs/bluelet/core.py +2 -2
- omlish/asyncs/bluelet/events.py +4 -3
- omlish/asyncs/bluelet/files.py +2 -2
- omlish/asyncs/bluelet/sockets.py +2 -2
- omlish/collections/frozen.py +1 -1
- omlish/collections/kv/base.py +1 -1
- omlish/collections/multimaps.py +1 -1
- omlish/collections/persistent/treapmap.py +2 -1
- omlish/concurrent/threadlets.py +2 -2
- omlish/configs/formats.py +5 -4
- omlish/configs/processing/flattening.py +2 -1
- omlish/configs/processing/rewriting.py +2 -2
- omlish/configs/shadow.py +3 -2
- omlish/daemons/spawning.py +2 -2
- omlish/daemons/targets.py +1 -1
- omlish/daemons/waiting.py +2 -1
- omlish/dataclasses/impl/generation/base.py +3 -2
- omlish/dataclasses/impl/generation/compilation.py +2 -1
- omlish/dataclasses/impl/generation/ops.py +2 -2
- omlish/dataclasses/impl/generation/processor.py +1 -1
- omlish/dataclasses/metaclass/meta.py +1 -0
- omlish/dataclasses/tools/static.py +5 -1
- omlish/formats/dotenv.py +3 -1
- omlish/formats/logfmt.py +111 -0
- omlish/formats/yaml.py +1 -1
- omlish/funcs/builders.py +2 -1
- omlish/funcs/match.py +1 -1
- omlish/funcs/pairs.py +41 -23
- omlish/funcs/pipes.py +3 -1
- omlish/http/asgi.py +2 -1
- omlish/http/coro/client/io.py +3 -2
- omlish/http/coro/server/server.py +2 -2
- omlish/http/handlers.py +2 -1
- omlish/http/jwt.py +1 -1
- omlish/http/parsing.py +2 -2
- omlish/io/compress/base.py +3 -2
- omlish/io/coro/readers.py +4 -3
- omlish/io/fdio/handlers.py +3 -2
- omlish/io/fdio/pollers.py +3 -1
- omlish/lang/__init__.py +20 -8
- omlish/lang/attrs.py +3 -1
- omlish/lang/casing.py +3 -1
- omlish/lang/classes/abstract.py +35 -96
- omlish/lang/classes/virtual.py +2 -2
- omlish/lang/contextmanagers.py +6 -4
- omlish/lang/generators.py +2 -2
- omlish/lang/iterables.py +6 -6
- omlish/lang/objects.py +0 -58
- omlish/lang/outcomes.py +3 -1
- omlish/lang/typing.py +5 -24
- omlish/lite/abstract.py +119 -0
- omlish/lite/contextmanagers.py +4 -1
- omlish/lite/inject.py +9 -9
- omlish/lite/marshal.py +230 -114
- omlish/lite/maybes.py +3 -1
- omlish/lite/maysync.py +4 -2
- omlish/lite/objects.py +81 -0
- omlish/lite/reflect.py +0 -15
- omlish/lite/timeouts.py +3 -1
- omlish/lite/wrappers.py +23 -0
- omlish/logs/abc.py +21 -5
- omlish/logs/all.py +14 -3
- omlish/logs/callers.py +23 -4
- omlish/logs/configs.py +13 -10
- omlish/logs/levels.py +7 -0
- omlish/logs/protocol.py +84 -42
- omlish/logs/typed/__init__.py +0 -0
- omlish/logs/typed/bindings.py +537 -0
- omlish/logs/typed/contexts.py +138 -0
- omlish/logs/typed/types.py +484 -0
- omlish/logs/typed/values.py +114 -0
- omlish/marshal/__init__.py +1 -0
- omlish/os/atomics.py +3 -2
- omlish/os/deathpacts/base.py +4 -2
- omlish/os/forkhooks.py +2 -2
- omlish/os/pidfiles/pinning.py +2 -1
- omlish/reflect/types.py +4 -3
- omlish/secrets/crypto.py +1 -1
- omlish/sockets/bind.py +2 -1
- omlish/sockets/handlers.py +3 -2
- omlish/sockets/server/handlers.py +2 -1
- omlish/sockets/server/server.py +4 -3
- omlish/sql/api/base.py +2 -2
- omlish/subprocesses/asyncs.py +2 -1
- omlish/subprocesses/base.py +2 -2
- omlish/subprocesses/maysync.py +1 -2
- omlish/subprocesses/run.py +2 -1
- omlish/subprocesses/sync.py +2 -1
- omlish/term/coloring.py +3 -1
- omlish/testing/pytest/plugins/asyncs/backends/base.py +3 -1
- omlish/testing/unittest/loading.py +2 -2
- omlish/text/asdl.py +4 -3
- {omlish-0.0.0.dev420.dist-info → omlish-0.0.0.dev422.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev420.dist-info → omlish-0.0.0.dev422.dist-info}/RECORD +99 -89
- {omlish-0.0.0.dev420.dist-info → omlish-0.0.0.dev422.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev420.dist-info → omlish-0.0.0.dev422.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev420.dist-info → omlish-0.0.0.dev422.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev420.dist-info → omlish-0.0.0.dev422.dist-info}/top_level.txt +0 -0
omlish/funcs/pairs.py
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
import abc
|
2
|
-
import dataclasses as dc
|
3
2
|
import typing as ta
|
4
3
|
|
5
4
|
from .. import lang
|
@@ -19,7 +18,7 @@ T = ta.TypeVar('T')
|
|
19
18
|
U = ta.TypeVar('U')
|
20
19
|
|
21
20
|
|
22
|
-
class FnPair(
|
21
|
+
class FnPair(lang.Abstract, ta.Generic[F, T]):
|
23
22
|
@abc.abstractmethod
|
24
23
|
def forward(self, f: F) -> T:
|
25
24
|
raise NotImplementedError
|
@@ -35,7 +34,7 @@ class FnPair(abc.ABC, ta.Generic[F, T]):
|
|
35
34
|
|
36
35
|
def invert(self) -> 'FnPair[T, F]':
|
37
36
|
if isinstance(self, Inverted):
|
38
|
-
return self.
|
37
|
+
return self._fp
|
39
38
|
return Inverted(self)
|
40
39
|
|
41
40
|
def compose(self, nxt: 'FnPair[T, U]') -> 'FnPair[F, U]':
|
@@ -45,11 +44,22 @@ class FnPair(abc.ABC, ta.Generic[F, T]):
|
|
45
44
|
##
|
46
45
|
|
47
46
|
|
48
|
-
@lang.unabstract_class(['forward', 'backward'])
|
49
|
-
@dc.dataclass(frozen=True)
|
50
47
|
class Simple(FnPair[F, T]):
|
51
|
-
|
52
|
-
|
48
|
+
def __init__(
|
49
|
+
self,
|
50
|
+
forward: ta.Callable[[F], T],
|
51
|
+
backward: ta.Callable[[T], F],
|
52
|
+
) -> None:
|
53
|
+
super().__init__()
|
54
|
+
|
55
|
+
self._forward = self.forward = forward # type: ignore
|
56
|
+
self._backward = self.backward = backward # type: ignore
|
57
|
+
|
58
|
+
def forward(self, f: F) -> T:
|
59
|
+
return self._forward(f)
|
60
|
+
|
61
|
+
def backward(self, t: T) -> F:
|
62
|
+
return self._backward(t)
|
53
63
|
|
54
64
|
|
55
65
|
of = Simple
|
@@ -60,31 +70,35 @@ NOP: FnPair[ta.Any, ta.Any] = of(lang.identity, lang.identity)
|
|
60
70
|
##
|
61
71
|
|
62
72
|
|
63
|
-
@dc.dataclass(frozen=True)
|
64
73
|
class Inverted(FnPair[F, T]):
|
65
|
-
fp: FnPair[T, F]
|
74
|
+
def __init__(self, fp: FnPair[T, F]) -> None:
|
75
|
+
super().__init__()
|
76
|
+
|
77
|
+
self._fp = fp
|
66
78
|
|
67
79
|
def forward(self, f: F) -> T:
|
68
|
-
return self.
|
80
|
+
return self._fp.backward(f)
|
69
81
|
|
70
82
|
def backward(self, t: T) -> F:
|
71
|
-
return self.
|
83
|
+
return self._fp.forward(t)
|
72
84
|
|
73
85
|
|
74
86
|
##
|
75
87
|
|
76
88
|
|
77
|
-
@dc.dataclass(frozen=True)
|
78
89
|
class Composite(FnPair[F, T]):
|
79
|
-
children: ta.Sequence[FnPair]
|
90
|
+
def __init__(self, children: ta.Sequence[FnPair]) -> None:
|
91
|
+
super().__init__()
|
92
|
+
|
93
|
+
self._children = children
|
80
94
|
|
81
95
|
def forward(self, f: F) -> T:
|
82
|
-
for c in self.
|
96
|
+
for c in self._children:
|
83
97
|
f = c.forward(f)
|
84
98
|
return ta.cast(T, f)
|
85
99
|
|
86
100
|
def backward(self, t: T) -> F:
|
87
|
-
for c in reversed(self.
|
101
|
+
for c in reversed(self._children):
|
88
102
|
t = c.backward(t)
|
89
103
|
return ta.cast(F, t)
|
90
104
|
|
@@ -162,15 +176,17 @@ def compose(*ps):
|
|
162
176
|
##
|
163
177
|
|
164
178
|
|
165
|
-
@dc.dataclass(frozen=True)
|
166
179
|
class Optional(FnPair[F | None, T | None]):
|
167
|
-
fp: FnPair[F, T]
|
180
|
+
def __init__(self, fp: FnPair[F, T]) -> None:
|
181
|
+
super().__init__()
|
182
|
+
|
183
|
+
self._fp = fp
|
168
184
|
|
169
185
|
def forward(self, f: F | None) -> T | None:
|
170
|
-
return None if f is None else self.
|
186
|
+
return None if f is None else self._fp.forward(f)
|
171
187
|
|
172
188
|
def backward(self, t: T | None) -> F | None:
|
173
|
-
return None if t is None else self.
|
189
|
+
return None if t is None else self._fp.backward(t)
|
174
190
|
|
175
191
|
|
176
192
|
class Lines(FnPair[ta.Sequence[str], str]):
|
@@ -184,15 +200,17 @@ class Lines(FnPair[ta.Sequence[str], str]):
|
|
184
200
|
##
|
185
201
|
|
186
202
|
|
187
|
-
@dc.dataclass(frozen=True)
|
188
203
|
class Struct(FnPair[tuple, bytes]):
|
189
|
-
fmt: str
|
204
|
+
def __init__(self, fmt: str) -> None:
|
205
|
+
super().__init__()
|
206
|
+
|
207
|
+
self._fmt = fmt
|
190
208
|
|
191
209
|
def forward(self, f: tuple) -> bytes:
|
192
|
-
return _struct.pack(self.
|
210
|
+
return _struct.pack(self._fmt, *f)
|
193
211
|
|
194
212
|
def backward(self, t: bytes) -> tuple:
|
195
|
-
return _struct.unpack(self.
|
213
|
+
return _struct.unpack(self._fmt, t)
|
196
214
|
|
197
215
|
|
198
216
|
##
|
omlish/funcs/pipes.py
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
import abc
|
2
2
|
import typing as ta
|
3
3
|
|
4
|
+
from .. import lang
|
5
|
+
|
4
6
|
|
5
7
|
T = ta.TypeVar('T')
|
6
8
|
U = ta.TypeVar('U')
|
@@ -9,7 +11,7 @@ U = ta.TypeVar('U')
|
|
9
11
|
##
|
10
12
|
|
11
13
|
|
12
|
-
class Fn(
|
14
|
+
class Fn(lang.Abstract, ta.Generic[T]):
|
13
15
|
@abc.abstractmethod
|
14
16
|
def __call__(self, *args: ta.Any, **kwargs: ta.Any) -> T:
|
15
17
|
raise NotImplementedError
|
omlish/http/asgi.py
CHANGED
@@ -4,6 +4,7 @@ import typing as ta
|
|
4
4
|
import urllib.parse
|
5
5
|
|
6
6
|
from .. import check
|
7
|
+
from .. import lang
|
7
8
|
from . import consts
|
8
9
|
|
9
10
|
|
@@ -21,7 +22,7 @@ App: ta.TypeAlias = ta.Callable[[Scope, Recv, Send], ta.Awaitable[None]]
|
|
21
22
|
Wrapper: ta.TypeAlias = ta.Callable[[App, Scope, Recv, Send], ta.Awaitable[None]]
|
22
23
|
|
23
24
|
|
24
|
-
class App_(
|
25
|
+
class App_(lang.Abstract): # noqa
|
25
26
|
@abc.abstractmethod
|
26
27
|
async def __call__(self, scope: Scope, recv: Recv, send: Send) -> None:
|
27
28
|
raise NotImplementedError
|
omlish/http/coro/client/io.py
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
# @omlish-lite
|
2
2
|
# ruff: noqa: UP006 UP007 UP043 UP045
|
3
|
-
import abc
|
4
3
|
import dataclasses as dc
|
5
4
|
import typing as ta
|
6
5
|
|
6
|
+
from ....lite.abstract import Abstract
|
7
|
+
|
7
8
|
|
8
9
|
##
|
9
10
|
|
@@ -13,7 +14,7 @@ class CoroHttpClientIo:
|
|
13
14
|
|
14
15
|
#
|
15
16
|
|
16
|
-
class Io(
|
17
|
+
class Io(Abstract):
|
17
18
|
pass
|
18
19
|
|
19
20
|
#
|
@@ -52,7 +52,6 @@ curl -v -XPOST localhost:8000 -d 'foo' --next -XFOO localhost:8000 -d 'bar'
|
|
52
52
|
curl -v -XFOO localhost:8000 -d 'foo' --next -XPOST localhost:8000 -d 'bar'
|
53
53
|
curl -v -XFOO localhost:8000 -d 'foo' --next -XFOO localhost:8000 -d 'bar'
|
54
54
|
"""
|
55
|
-
import abc
|
56
55
|
import dataclasses as dc
|
57
56
|
import email.utils
|
58
57
|
import html
|
@@ -62,6 +61,7 @@ import textwrap
|
|
62
61
|
import time
|
63
62
|
import typing as ta
|
64
63
|
|
64
|
+
from ....lite.abstract import Abstract
|
65
65
|
from ....lite.check import check
|
66
66
|
from ....sockets.addresses import SocketAddress
|
67
67
|
from ...handlers import HttpHandler
|
@@ -380,7 +380,7 @@ class CoroHttpServer:
|
|
380
380
|
|
381
381
|
#
|
382
382
|
|
383
|
-
class Io(
|
383
|
+
class Io(Abstract):
|
384
384
|
pass
|
385
385
|
|
386
386
|
#
|
omlish/http/handlers.py
CHANGED
@@ -6,6 +6,7 @@ import http.server
|
|
6
6
|
import logging
|
7
7
|
import typing as ta
|
8
8
|
|
9
|
+
from ..lite.abstract import Abstract
|
9
10
|
from ..sockets.addresses import SocketAddress
|
10
11
|
from .parsing import HttpHeaders
|
11
12
|
|
@@ -57,7 +58,7 @@ class UnsupportedMethodHttpHandlerError(Exception):
|
|
57
58
|
pass
|
58
59
|
|
59
60
|
|
60
|
-
class HttpHandler_(
|
61
|
+
class HttpHandler_(Abstract): # noqa
|
61
62
|
@abc.abstractmethod
|
62
63
|
def __call__(self, req: HttpHandlerRequest) -> HttpHandlerResponse:
|
63
64
|
raise NotImplementedError
|
omlish/http/jwt.py
CHANGED
omlish/http/parsing.py
CHANGED
@@ -35,12 +35,12 @@
|
|
35
35
|
#
|
36
36
|
# 8. By copying, installing or otherwise using Python, Licensee agrees to be bound by the terms and conditions of this
|
37
37
|
# License Agreement.
|
38
|
-
import abc
|
39
38
|
import http.client
|
40
39
|
import http.server
|
41
40
|
import io
|
42
41
|
import typing as ta
|
43
42
|
|
43
|
+
from ..lite.abstract import Abstract
|
44
44
|
from .versions import HttpProtocolVersion
|
45
45
|
from .versions import HttpProtocolVersions
|
46
46
|
|
@@ -54,7 +54,7 @@ HttpHeaders = http.client.HTTPMessage # ta.TypeAlias
|
|
54
54
|
##
|
55
55
|
|
56
56
|
|
57
|
-
class ParseHttpRequestResult(
|
57
|
+
class ParseHttpRequestResult(Abstract):
|
58
58
|
__slots__ = (
|
59
59
|
'server_version',
|
60
60
|
'request_line',
|
omlish/io/compress/base.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import abc
|
2
2
|
|
3
|
+
from ... import lang
|
3
4
|
from ..coro import BytesSteppedCoro
|
4
5
|
from ..coro import BytesSteppedReaderCoro
|
5
6
|
|
@@ -7,7 +8,7 @@ from ..coro import BytesSteppedReaderCoro
|
|
7
8
|
##
|
8
9
|
|
9
10
|
|
10
|
-
class Compression(
|
11
|
+
class Compression(lang.Abstract):
|
11
12
|
@abc.abstractmethod
|
12
13
|
def compress(self, d: bytes) -> bytes:
|
13
14
|
raise NotImplementedError
|
@@ -17,7 +18,7 @@ class Compression(abc.ABC):
|
|
17
18
|
raise NotImplementedError
|
18
19
|
|
19
20
|
|
20
|
-
class IncrementalCompression(
|
21
|
+
class IncrementalCompression(lang.Abstract):
|
21
22
|
@abc.abstractmethod
|
22
23
|
def compress_incremental(self) -> BytesSteppedCoro[None]:
|
23
24
|
raise NotImplementedError
|
omlish/io/coro/readers.py
CHANGED
@@ -8,6 +8,7 @@ import abc
|
|
8
8
|
import typing as ta
|
9
9
|
|
10
10
|
from ... import check
|
11
|
+
from ... import lang
|
11
12
|
from .consts import DEFAULT_BUFFER_SIZE
|
12
13
|
|
13
14
|
|
@@ -50,7 +51,7 @@ class _StrJoiner:
|
|
50
51
|
##
|
51
52
|
|
52
53
|
|
53
|
-
class CoroReader(
|
54
|
+
class CoroReader(lang.Abstract, ta.Generic[T]):
|
54
55
|
@abc.abstractmethod
|
55
56
|
def read(self, sz: int | None) -> ReaderCoro[T, T]:
|
56
57
|
raise NotImplementedError
|
@@ -65,7 +66,7 @@ class CoroReader(abc.ABC, ta.Generic[T]):
|
|
65
66
|
##
|
66
67
|
|
67
68
|
|
68
|
-
class PrependableCoroReader(CoroReader[AnyT]):
|
69
|
+
class PrependableCoroReader(CoroReader[AnyT], lang.Abstract):
|
69
70
|
def __init__(self) -> None:
|
70
71
|
super().__init__()
|
71
72
|
|
@@ -133,7 +134,7 @@ prependable_str_coro_reader = PrependableStrCoroReader
|
|
133
134
|
##
|
134
135
|
|
135
136
|
|
136
|
-
class BufferedCoroReader(PrependableCoroReader[AnyT],
|
137
|
+
class BufferedCoroReader(PrependableCoroReader[AnyT], lang.Abstract):
|
137
138
|
def __init__(
|
138
139
|
self,
|
139
140
|
buffer_size: int = DEFAULT_BUFFER_SIZE,
|
omlish/io/fdio/handlers.py
CHANGED
@@ -3,6 +3,7 @@ import abc
|
|
3
3
|
import socket
|
4
4
|
import typing as ta
|
5
5
|
|
6
|
+
from ...lite.abstract import Abstract
|
6
7
|
from ...lite.check import check
|
7
8
|
from ...sockets.addresses import SocketAddress
|
8
9
|
|
@@ -10,7 +11,7 @@ from ...sockets.addresses import SocketAddress
|
|
10
11
|
##
|
11
12
|
|
12
13
|
|
13
|
-
class FdioHandler(
|
14
|
+
class FdioHandler(Abstract):
|
14
15
|
@abc.abstractmethod
|
15
16
|
def fd(self) -> int:
|
16
17
|
raise NotImplementedError
|
@@ -46,7 +47,7 @@ class FdioHandler(abc.ABC):
|
|
46
47
|
pass
|
47
48
|
|
48
49
|
|
49
|
-
class SocketFdioHandler(FdioHandler,
|
50
|
+
class SocketFdioHandler(FdioHandler, Abstract):
|
50
51
|
def __init__(
|
51
52
|
self,
|
52
53
|
addr: SocketAddress,
|
omlish/io/fdio/pollers.py
CHANGED
omlish/lang/__init__.py
CHANGED
@@ -68,14 +68,11 @@ with _auto_proxy_init(
|
|
68
68
|
)
|
69
69
|
|
70
70
|
from .classes.abstract import ( # noqa
|
71
|
-
|
72
|
-
AbstractTypeError,
|
73
|
-
get_abstract_methods,
|
71
|
+
get_abstracts,
|
74
72
|
is_abstract,
|
75
73
|
is_abstract_class,
|
76
74
|
is_abstract_method,
|
77
75
|
make_abstract,
|
78
|
-
unabstract_class,
|
79
76
|
)
|
80
77
|
|
81
78
|
from .classes.bindable import ( # noqa
|
@@ -295,7 +292,7 @@ with _auto_proxy_init(
|
|
295
292
|
asrange,
|
296
293
|
chunk,
|
297
294
|
common_prefix_len,
|
298
|
-
|
295
|
+
consume,
|
299
296
|
flatmap,
|
300
297
|
flatten,
|
301
298
|
ilen,
|
@@ -332,9 +329,6 @@ with _auto_proxy_init(
|
|
332
329
|
arg_repr,
|
333
330
|
can_weakref,
|
334
331
|
deep_subclasses,
|
335
|
-
dir_dict,
|
336
|
-
mro_dict,
|
337
|
-
mro_owner_dict,
|
338
332
|
new_type,
|
339
333
|
opt_repr,
|
340
334
|
super_meta,
|
@@ -437,6 +431,14 @@ with _auto_proxy_init(
|
|
437
431
|
|
438
432
|
##
|
439
433
|
|
434
|
+
from ..lite.abstract import ( # noqa
|
435
|
+
is_abstract_method,
|
436
|
+
update_abstracts,
|
437
|
+
|
438
|
+
AbstractTypeError,
|
439
|
+
Abstract,
|
440
|
+
)
|
441
|
+
|
440
442
|
from ..lite.args import ( # noqa
|
441
443
|
Args,
|
442
444
|
)
|
@@ -479,6 +481,12 @@ with _auto_proxy_init(
|
|
479
481
|
run_maysync,
|
480
482
|
)
|
481
483
|
|
484
|
+
from ..lite.objects import ( # noqa
|
485
|
+
mro_dict,
|
486
|
+
mro_owner_dict,
|
487
|
+
dir_dict,
|
488
|
+
)
|
489
|
+
|
482
490
|
from ..lite.reprs import ( # noqa
|
483
491
|
AttrRepr,
|
484
492
|
attr_repr,
|
@@ -504,3 +512,7 @@ with _auto_proxy_init(
|
|
504
512
|
|
505
513
|
typing_annotations_attr,
|
506
514
|
)
|
515
|
+
|
516
|
+
from ..lite.wrappers import ( # noqa
|
517
|
+
update_wrapper_no_annotations,
|
518
|
+
)
|
omlish/lang/attrs.py
CHANGED
@@ -3,6 +3,8 @@ import collections.abc
|
|
3
3
|
import functools
|
4
4
|
import typing as ta
|
5
5
|
|
6
|
+
from ..lite.abstract import Abstract
|
7
|
+
|
6
8
|
|
7
9
|
T = ta.TypeVar('T')
|
8
10
|
|
@@ -74,7 +76,7 @@ def set_attr(
|
|
74
76
|
##
|
75
77
|
|
76
78
|
|
77
|
-
class AttrOps(
|
79
|
+
class AttrOps(Abstract):
|
78
80
|
class NOT_SET: # noqa
|
79
81
|
def __new__(cls, *args, **kwargs): # noqa
|
80
82
|
raise TypeError
|
omlish/lang/casing.py
CHANGED
@@ -2,6 +2,8 @@ import abc
|
|
2
2
|
import re
|
3
3
|
import typing as ta
|
4
4
|
|
5
|
+
from ..lite.abstract import Abstract
|
6
|
+
|
5
7
|
|
6
8
|
##
|
7
9
|
|
@@ -31,7 +33,7 @@ def _check_all_lowercase(*ps: str) -> None:
|
|
31
33
|
##
|
32
34
|
|
33
35
|
|
34
|
-
class StringCasing(
|
36
|
+
class StringCasing(Abstract):
|
35
37
|
@abc.abstractmethod
|
36
38
|
def match(self, s: str) -> bool:
|
37
39
|
raise NotImplementedError
|
omlish/lang/classes/abstract.py
CHANGED
@@ -1,6 +1,12 @@
|
|
1
1
|
import abc
|
2
2
|
import typing as ta
|
3
3
|
|
4
|
+
from ...lite.abstract import _ABSTRACT_METHODS_ATTR # noqa
|
5
|
+
from ...lite.abstract import _FORCE_ABSTRACT_ATTR # noqa
|
6
|
+
from ...lite.abstract import _IS_ABSTRACT_METHOD_ATTR # noqa
|
7
|
+
from ...lite.abstract import Abstract
|
8
|
+
from ...lite.abstract import is_abstract_method
|
9
|
+
|
4
10
|
|
5
11
|
T = ta.TypeVar('T')
|
6
12
|
|
@@ -8,82 +14,20 @@ T = ta.TypeVar('T')
|
|
8
14
|
##
|
9
15
|
|
10
16
|
|
11
|
-
_DISABLE_CHECKS = False
|
12
|
-
|
13
|
-
_ABSTRACT_METHODS_ATTR = '__abstractmethods__'
|
14
|
-
_IS_ABSTRACT_METHOD_ATTR = '__isabstractmethod__'
|
15
|
-
_FORCE_ABSTRACT_ATTR = '__forceabstract__'
|
16
|
-
|
17
|
-
_INTERNAL_ABSTRACT_ATTRS = frozenset([_FORCE_ABSTRACT_ATTR])
|
18
|
-
|
19
|
-
|
20
|
-
def make_abstract(obj: T) -> T:
|
21
|
-
if callable(obj):
|
22
|
-
return abc.abstractmethod(obj)
|
23
|
-
elif isinstance(obj, property):
|
24
|
-
return ta.cast(T, property(
|
25
|
-
abc.abstractmethod(obj.fget) if obj.fget is not None else None,
|
26
|
-
abc.abstractmethod(obj.fset) if obj.fset is not None else None,
|
27
|
-
abc.abstractmethod(obj.fdel) if obj.fdel is not None else None,
|
28
|
-
))
|
29
|
-
elif isinstance(obj, (classmethod, staticmethod)):
|
30
|
-
return ta.cast(T, type(obj)(abc.abstractmethod(obj.__func__)))
|
31
|
-
else:
|
32
|
-
return obj
|
33
|
-
|
34
|
-
|
35
|
-
class AbstractTypeError(TypeError):
|
36
|
-
pass
|
37
|
-
|
38
|
-
|
39
|
-
class Abstract(abc.ABC): # noqa
|
40
|
-
__slots__ = ()
|
41
|
-
|
42
|
-
def __forceabstract__(self):
|
43
|
-
raise TypeError
|
44
|
-
|
45
|
-
setattr(__forceabstract__, _IS_ABSTRACT_METHOD_ATTR, True)
|
46
|
-
|
47
|
-
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
48
|
-
if Abstract in cls.__bases__:
|
49
|
-
setattr(cls, _FORCE_ABSTRACT_ATTR, getattr(Abstract, _FORCE_ABSTRACT_ATTR))
|
50
|
-
else:
|
51
|
-
setattr(cls, _FORCE_ABSTRACT_ATTR, False)
|
52
|
-
|
53
|
-
super().__init_subclass__(**kwargs)
|
54
|
-
|
55
|
-
if (
|
56
|
-
not _DISABLE_CHECKS and
|
57
|
-
Abstract not in cls.__bases__ and
|
58
|
-
abc.ABC not in cls.__bases__
|
59
|
-
):
|
60
|
-
ams = {a for a, o in cls.__dict__.items() if is_abstract_method(o)}
|
61
|
-
seen = set(cls.__dict__)
|
62
|
-
for b in cls.__bases__:
|
63
|
-
ams.update(set(getattr(b, _ABSTRACT_METHODS_ATTR, [])) - seen)
|
64
|
-
seen.update(dir(b))
|
65
|
-
if ams:
|
66
|
-
raise AbstractTypeError(
|
67
|
-
f'Cannot subclass abstract class {cls.__name__} with abstract methods: '
|
68
|
-
f'{", ".join(map(str, sorted(ams)))}',
|
69
|
-
)
|
70
|
-
|
71
|
-
|
72
|
-
def is_abstract_method(obj: ta.Any) -> bool:
|
73
|
-
return bool(getattr(obj, _IS_ABSTRACT_METHOD_ATTR, False))
|
74
|
-
|
75
|
-
|
76
17
|
def is_abstract_class(obj: ta.Any) -> bool:
|
77
18
|
if bool(getattr(obj, _ABSTRACT_METHODS_ATTR, [])):
|
78
19
|
return True
|
20
|
+
|
79
21
|
if isinstance(obj, type):
|
80
22
|
if Abstract in obj.__bases__:
|
81
23
|
return True
|
24
|
+
|
82
25
|
if (
|
83
26
|
Abstract in obj.__mro__
|
84
27
|
and getattr(obj.__dict__.get(_FORCE_ABSTRACT_ATTR, None), _IS_ABSTRACT_METHOD_ATTR, False)
|
85
28
|
):
|
86
29
|
return True
|
30
|
+
|
87
31
|
return False
|
88
32
|
|
89
33
|
|
@@ -91,40 +35,35 @@ def is_abstract(obj: ta.Any) -> bool:
|
|
91
35
|
return is_abstract_method(obj) or is_abstract_class(obj)
|
92
36
|
|
93
37
|
|
94
|
-
|
38
|
+
##
|
39
|
+
|
40
|
+
|
41
|
+
_INTERNAL_ABSTRACT_ATTRS = frozenset([_FORCE_ABSTRACT_ATTR])
|
42
|
+
|
43
|
+
|
44
|
+
def get_abstracts(cls: type, *, include_internal: bool = False) -> frozenset[str]:
|
95
45
|
ms = frozenset(getattr(cls, _ABSTRACT_METHODS_ATTR))
|
96
46
|
if not include_internal:
|
97
47
|
ms -= _INTERNAL_ABSTRACT_ATTRS
|
98
48
|
return ms
|
99
49
|
|
100
50
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
if
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
if name not in ams:
|
121
|
-
raise NameError(name)
|
122
|
-
if isinstance(impl, str):
|
123
|
-
impl = getattr(cls, impl)
|
124
|
-
setattr(cls, name, impl)
|
125
|
-
names.add(name)
|
126
|
-
|
127
|
-
setattr(cls, _ABSTRACT_METHODS_ATTR, ams - names)
|
128
|
-
return cls
|
129
|
-
|
130
|
-
return inner
|
51
|
+
##
|
52
|
+
|
53
|
+
|
54
|
+
def make_abstract(obj: T) -> T:
|
55
|
+
if callable(obj):
|
56
|
+
return abc.abstractmethod(obj)
|
57
|
+
|
58
|
+
elif isinstance(obj, property):
|
59
|
+
return ta.cast(T, property(
|
60
|
+
abc.abstractmethod(obj.fget) if obj.fget is not None else None,
|
61
|
+
abc.abstractmethod(obj.fset) if obj.fset is not None else None,
|
62
|
+
abc.abstractmethod(obj.fdel) if obj.fdel is not None else None,
|
63
|
+
))
|
64
|
+
|
65
|
+
elif isinstance(obj, (classmethod, staticmethod)):
|
66
|
+
return ta.cast(T, type(obj)(abc.abstractmethod(obj.__func__)))
|
67
|
+
|
68
|
+
else:
|
69
|
+
return obj
|
omlish/lang/classes/virtual.py
CHANGED
@@ -59,7 +59,7 @@ class _VirtualMeta(abc.ABCMeta):
|
|
59
59
|
if get_missing_reqs(subclass):
|
60
60
|
return False
|
61
61
|
if user_subclasshook is not None:
|
62
|
-
ret = user_subclasshook(cls, subclass)
|
62
|
+
ret = user_subclasshook(cls, subclass) # noqa
|
63
63
|
else:
|
64
64
|
ret = super(kls, cls).__subclasshook__(subclass) # type: ignore
|
65
65
|
return True if ret is NotImplemented else ret
|
@@ -113,7 +113,7 @@ class Picklable(Virtual):
|
|
113
113
|
##
|
114
114
|
|
115
115
|
|
116
|
-
class Callable(NotInstantiable, Final, ta.Generic[T]):
|
116
|
+
class Callable(NotInstantiable, Final, ta.Generic[T], metaclass=abc.ABCMeta):
|
117
117
|
def __call__(self, *args: ta.Any, **kwargs: ta.Any) -> T:
|
118
118
|
raise TypeError
|
119
119
|
|