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.
Files changed (99) hide show
  1. omlish/__about__.py +2 -2
  2. omlish/asyncs/bluelet/core.py +2 -2
  3. omlish/asyncs/bluelet/events.py +4 -3
  4. omlish/asyncs/bluelet/files.py +2 -2
  5. omlish/asyncs/bluelet/sockets.py +2 -2
  6. omlish/collections/frozen.py +1 -1
  7. omlish/collections/kv/base.py +1 -1
  8. omlish/collections/multimaps.py +1 -1
  9. omlish/collections/persistent/treapmap.py +2 -1
  10. omlish/concurrent/threadlets.py +2 -2
  11. omlish/configs/formats.py +5 -4
  12. omlish/configs/processing/flattening.py +2 -1
  13. omlish/configs/processing/rewriting.py +2 -2
  14. omlish/configs/shadow.py +3 -2
  15. omlish/daemons/spawning.py +2 -2
  16. omlish/daemons/targets.py +1 -1
  17. omlish/daemons/waiting.py +2 -1
  18. omlish/dataclasses/impl/generation/base.py +3 -2
  19. omlish/dataclasses/impl/generation/compilation.py +2 -1
  20. omlish/dataclasses/impl/generation/ops.py +2 -2
  21. omlish/dataclasses/impl/generation/processor.py +1 -1
  22. omlish/dataclasses/metaclass/meta.py +1 -0
  23. omlish/dataclasses/tools/static.py +5 -1
  24. omlish/formats/dotenv.py +3 -1
  25. omlish/formats/logfmt.py +111 -0
  26. omlish/formats/yaml.py +1 -1
  27. omlish/funcs/builders.py +2 -1
  28. omlish/funcs/match.py +1 -1
  29. omlish/funcs/pairs.py +41 -23
  30. omlish/funcs/pipes.py +3 -1
  31. omlish/http/asgi.py +2 -1
  32. omlish/http/coro/client/io.py +3 -2
  33. omlish/http/coro/server/server.py +2 -2
  34. omlish/http/handlers.py +2 -1
  35. omlish/http/jwt.py +1 -1
  36. omlish/http/parsing.py +2 -2
  37. omlish/io/compress/base.py +3 -2
  38. omlish/io/coro/readers.py +4 -3
  39. omlish/io/fdio/handlers.py +3 -2
  40. omlish/io/fdio/pollers.py +3 -1
  41. omlish/lang/__init__.py +20 -8
  42. omlish/lang/attrs.py +3 -1
  43. omlish/lang/casing.py +3 -1
  44. omlish/lang/classes/abstract.py +35 -96
  45. omlish/lang/classes/virtual.py +2 -2
  46. omlish/lang/contextmanagers.py +6 -4
  47. omlish/lang/generators.py +2 -2
  48. omlish/lang/iterables.py +6 -6
  49. omlish/lang/objects.py +0 -58
  50. omlish/lang/outcomes.py +3 -1
  51. omlish/lang/typing.py +5 -24
  52. omlish/lite/abstract.py +119 -0
  53. omlish/lite/contextmanagers.py +4 -1
  54. omlish/lite/inject.py +9 -9
  55. omlish/lite/marshal.py +230 -114
  56. omlish/lite/maybes.py +3 -1
  57. omlish/lite/maysync.py +4 -2
  58. omlish/lite/objects.py +81 -0
  59. omlish/lite/reflect.py +0 -15
  60. omlish/lite/timeouts.py +3 -1
  61. omlish/lite/wrappers.py +23 -0
  62. omlish/logs/abc.py +21 -5
  63. omlish/logs/all.py +14 -3
  64. omlish/logs/callers.py +23 -4
  65. omlish/logs/configs.py +13 -10
  66. omlish/logs/levels.py +7 -0
  67. omlish/logs/protocol.py +84 -42
  68. omlish/logs/typed/__init__.py +0 -0
  69. omlish/logs/typed/bindings.py +537 -0
  70. omlish/logs/typed/contexts.py +138 -0
  71. omlish/logs/typed/types.py +484 -0
  72. omlish/logs/typed/values.py +114 -0
  73. omlish/marshal/__init__.py +1 -0
  74. omlish/os/atomics.py +3 -2
  75. omlish/os/deathpacts/base.py +4 -2
  76. omlish/os/forkhooks.py +2 -2
  77. omlish/os/pidfiles/pinning.py +2 -1
  78. omlish/reflect/types.py +4 -3
  79. omlish/secrets/crypto.py +1 -1
  80. omlish/sockets/bind.py +2 -1
  81. omlish/sockets/handlers.py +3 -2
  82. omlish/sockets/server/handlers.py +2 -1
  83. omlish/sockets/server/server.py +4 -3
  84. omlish/sql/api/base.py +2 -2
  85. omlish/subprocesses/asyncs.py +2 -1
  86. omlish/subprocesses/base.py +2 -2
  87. omlish/subprocesses/maysync.py +1 -2
  88. omlish/subprocesses/run.py +2 -1
  89. omlish/subprocesses/sync.py +2 -1
  90. omlish/term/coloring.py +3 -1
  91. omlish/testing/pytest/plugins/asyncs/backends/base.py +3 -1
  92. omlish/testing/unittest/loading.py +2 -2
  93. omlish/text/asdl.py +4 -3
  94. {omlish-0.0.0.dev420.dist-info → omlish-0.0.0.dev422.dist-info}/METADATA +1 -1
  95. {omlish-0.0.0.dev420.dist-info → omlish-0.0.0.dev422.dist-info}/RECORD +99 -89
  96. {omlish-0.0.0.dev420.dist-info → omlish-0.0.0.dev422.dist-info}/WHEEL +0 -0
  97. {omlish-0.0.0.dev420.dist-info → omlish-0.0.0.dev422.dist-info}/entry_points.txt +0 -0
  98. {omlish-0.0.0.dev420.dist-info → omlish-0.0.0.dev422.dist-info}/licenses/LICENSE +0 -0
  99. {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(abc.ABC, ta.Generic[F, T]):
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.fp
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
- forward: ta.Callable[[F], T] # type: ignore
52
- backward: ta.Callable[[T], F] # type: ignore
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.fp.backward(f)
80
+ return self._fp.backward(f)
69
81
 
70
82
  def backward(self, t: T) -> F:
71
- return self.fp.forward(t)
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.children:
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.children):
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.fp.forward(f)
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.fp.backward(t)
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.fmt, *f)
210
+ return _struct.pack(self._fmt, *f)
193
211
 
194
212
  def backward(self, t: bytes) -> tuple:
195
- return _struct.unpack(self.fmt, t)
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(abc.ABC, ta.Generic[T]):
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_(abc.ABC): # noqa
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
@@ -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(abc.ABC): # noqa
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(abc.ABC): # noqa
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_(abc.ABC): # noqa
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
@@ -34,7 +34,7 @@ def base64url_encode(b: bytes) -> bytes:
34
34
  ##
35
35
 
36
36
 
37
- class Algorithm(abc.ABC):
37
+ class Algorithm(lang.Abstract):
38
38
  @property
39
39
  @abc.abstractmethod
40
40
  def name(self) -> str:
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(abc.ABC): # noqa
57
+ class ParseHttpRequestResult(Abstract):
58
58
  __slots__ = (
59
59
  'server_version',
60
60
  'request_line',
@@ -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(abc.ABC):
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(abc.ABC):
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(abc.ABC, ta.Generic[T]):
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], abc.ABC):
137
+ class BufferedCoroReader(PrependableCoroReader[AnyT], lang.Abstract):
137
138
  def __init__(
138
139
  self,
139
140
  buffer_size: int = DEFAULT_BUFFER_SIZE,
@@ -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(abc.ABC):
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, abc.ABC):
50
+ class SocketFdioHandler(FdioHandler, Abstract):
50
51
  def __init__(
51
52
  self,
52
53
  addr: SocketAddress,
omlish/io/fdio/pollers.py CHANGED
@@ -5,11 +5,13 @@ import errno
5
5
  import select
6
6
  import typing as ta
7
7
 
8
+ from ...lite.abstract import Abstract
9
+
8
10
 
9
11
  ##
10
12
 
11
13
 
12
- class FdioPoller(abc.ABC):
14
+ class FdioPoller(Abstract):
13
15
  def __init__(self) -> None:
14
16
  super().__init__()
15
17
 
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
- Abstract,
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
- exhaust,
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(abc.ABC):
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(abc.ABC):
36
+ class StringCasing(Abstract):
35
37
  @abc.abstractmethod
36
38
  def match(self, s: str) -> bool:
37
39
  raise NotImplementedError
@@ -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
- def get_abstract_methods(cls: type, *, include_internal: bool = False) -> frozenset[str]:
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
- def unabstract_class(
102
- members: ta.Iterable[str | tuple[str, ta.Any]],
103
- ): # -> ta.Callable[[type[T]], type[T]]:
104
- def inner(cls):
105
- if isinstance(members, str):
106
- raise TypeError(members)
107
-
108
- ams = getattr(cls, _ABSTRACT_METHODS_ATTR)
109
-
110
- names: set[str] = set()
111
- for m in members:
112
- if isinstance(m, str):
113
- if m not in ams:
114
- raise NameError(m)
115
- getattr(cls, m)
116
- names.add(m)
117
-
118
- elif isinstance(m, tuple):
119
- name, impl = m
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
@@ -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