omlish 0.0.0.dev447__py3-none-any.whl → 0.0.0.dev484__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/.omlish-manifests.json +12 -0
- omlish/__about__.py +15 -15
- omlish/argparse/all.py +17 -9
- omlish/argparse/cli.py +16 -3
- omlish/argparse/utils.py +21 -0
- omlish/asyncs/asyncio/rlock.py +110 -0
- omlish/asyncs/asyncio/sync.py +43 -0
- omlish/asyncs/asyncio/utils.py +2 -0
- omlish/asyncs/sync.py +25 -0
- omlish/bootstrap/_marshal.py +1 -1
- omlish/bootstrap/diag.py +12 -21
- omlish/bootstrap/main.py +2 -5
- omlish/bootstrap/sys.py +27 -28
- omlish/cexts/__init__.py +0 -0
- omlish/cexts/include/omlish/omlish.hh +1 -0
- omlish/collections/__init__.py +13 -1
- omlish/collections/attrregistry.py +210 -0
- omlish/collections/cache/impl.py +1 -0
- omlish/collections/identity.py +1 -0
- omlish/collections/mappings.py +28 -0
- omlish/collections/trie.py +5 -1
- omlish/collections/utils.py +77 -0
- omlish/concurrent/all.py +2 -1
- omlish/concurrent/futures.py +25 -0
- omlish/concurrent/threadlets.py +1 -1
- omlish/daemons/reparent.py +2 -3
- omlish/daemons/spawning.py +2 -3
- omlish/dataclasses/__init__.py +2 -0
- omlish/dataclasses/impl/api/classes/decorator.py +3 -0
- omlish/dataclasses/impl/api/classes/make.py +3 -0
- omlish/dataclasses/impl/concerns/repr.py +15 -2
- omlish/dataclasses/impl/configs.py +97 -37
- omlish/dataclasses/impl/generation/compilation.py +21 -19
- omlish/dataclasses/impl/generation/globals.py +1 -0
- omlish/dataclasses/impl/generation/ops.py +1 -0
- omlish/dataclasses/impl/generation/processor.py +105 -24
- omlish/dataclasses/impl/processing/base.py +8 -0
- omlish/dataclasses/impl/processing/driving.py +8 -8
- omlish/dataclasses/specs.py +1 -0
- omlish/dataclasses/tools/modifiers.py +5 -0
- omlish/diag/cmds/__init__.py +0 -0
- omlish/diag/{lslocks.py → cmds/lslocks.py} +6 -6
- omlish/diag/{lsof.py → cmds/lsof.py} +6 -6
- omlish/diag/{ps.py → cmds/ps.py} +6 -6
- omlish/diag/pycharm.py +16 -2
- omlish/diag/pydevd.py +58 -40
- omlish/diag/replserver/console.py +1 -1
- omlish/dispatch/__init__.py +18 -12
- omlish/dispatch/methods.py +50 -140
- omlish/dom/rendering.py +1 -1
- omlish/formats/dotenv.py +1 -1
- omlish/formats/json/stream/__init__.py +13 -0
- omlish/funcs/guard.py +225 -0
- omlish/graphs/dot/rendering.py +1 -1
- omlish/http/all.py +44 -4
- omlish/http/clients/asyncs.py +33 -27
- omlish/http/clients/base.py +17 -1
- omlish/http/clients/coro/__init__.py +0 -0
- omlish/http/clients/coro/sync.py +171 -0
- omlish/http/clients/default.py +208 -29
- omlish/http/clients/executor.py +56 -0
- omlish/http/clients/httpx.py +72 -11
- omlish/http/clients/middleware.py +181 -0
- omlish/http/clients/sync.py +33 -27
- omlish/http/clients/syncasync.py +49 -0
- omlish/http/clients/urllib.py +6 -3
- omlish/http/coro/client/connection.py +15 -6
- omlish/http/coro/io.py +2 -0
- omlish/http/flasky/__init__.py +40 -0
- omlish/http/flasky/_compat.py +2 -0
- omlish/http/flasky/api.py +82 -0
- omlish/http/flasky/app.py +203 -0
- omlish/http/flasky/cvs.py +59 -0
- omlish/http/flasky/requests.py +20 -0
- omlish/http/flasky/responses.py +23 -0
- omlish/http/flasky/routes.py +23 -0
- omlish/http/flasky/types.py +15 -0
- omlish/http/urls.py +67 -0
- omlish/inject/__init__.py +38 -18
- omlish/inject/_dataclasses.py +4986 -0
- omlish/inject/binder.py +4 -48
- omlish/inject/elements.py +27 -0
- omlish/inject/helpers/__init__.py +0 -0
- omlish/inject/{utils.py → helpers/constfn.py} +3 -3
- omlish/inject/{tags.py → helpers/id.py} +2 -2
- omlish/inject/helpers/multis.py +143 -0
- omlish/inject/helpers/wrappers.py +54 -0
- omlish/inject/impl/elements.py +47 -17
- omlish/inject/impl/injector.py +20 -19
- omlish/inject/impl/inspect.py +10 -1
- omlish/inject/impl/maysync.py +3 -4
- omlish/inject/impl/multis.py +3 -0
- omlish/inject/impl/sync.py +3 -4
- omlish/inject/injector.py +31 -2
- omlish/inject/inspect.py +35 -0
- omlish/inject/maysync.py +2 -4
- omlish/inject/multis.py +8 -0
- omlish/inject/overrides.py +3 -3
- omlish/inject/privates.py +6 -0
- omlish/inject/providers.py +3 -2
- omlish/inject/sync.py +5 -4
- omlish/io/buffers.py +118 -0
- omlish/io/readers.py +29 -0
- omlish/iterators/transforms.py +2 -2
- omlish/lang/__init__.py +178 -97
- omlish/lang/_asyncs.cc +186 -0
- omlish/lang/asyncs.py +17 -0
- omlish/lang/casing.py +11 -0
- omlish/lang/contextmanagers.py +28 -4
- omlish/lang/functions.py +31 -22
- omlish/lang/imports/_capture.cc +11 -9
- omlish/lang/imports/capture.py +363 -170
- omlish/lang/imports/proxy.py +455 -152
- omlish/lang/lazyglobals.py +22 -9
- omlish/lang/params.py +17 -0
- omlish/lang/recursion.py +0 -1
- omlish/lang/sequences.py +124 -0
- omlish/lite/abstract.py +54 -24
- omlish/lite/asyncs.py +2 -2
- omlish/lite/attrops.py +2 -0
- omlish/lite/contextmanagers.py +4 -4
- omlish/lite/dataclasses.py +44 -0
- omlish/lite/maybes.py +8 -0
- omlish/lite/maysync.py +1 -5
- omlish/lite/pycharm.py +1 -1
- omlish/lite/typing.py +6 -0
- omlish/logs/all.py +1 -1
- omlish/logs/utils.py +1 -1
- omlish/manifests/loading.py +2 -1
- omlish/marshal/__init__.py +33 -13
- omlish/marshal/_dataclasses.py +2774 -0
- omlish/marshal/base/configs.py +12 -0
- omlish/marshal/base/contexts.py +36 -21
- omlish/marshal/base/funcs.py +8 -11
- omlish/marshal/base/options.py +8 -0
- omlish/marshal/base/registries.py +146 -44
- omlish/marshal/base/types.py +40 -16
- omlish/marshal/composite/iterables.py +33 -20
- omlish/marshal/composite/literals.py +20 -18
- omlish/marshal/composite/mappings.py +36 -23
- omlish/marshal/composite/maybes.py +29 -19
- omlish/marshal/composite/newtypes.py +16 -16
- omlish/marshal/composite/optionals.py +14 -14
- omlish/marshal/composite/special.py +15 -15
- omlish/marshal/composite/unions/__init__.py +0 -0
- omlish/marshal/composite/unions/literals.py +93 -0
- omlish/marshal/composite/unions/primitives.py +103 -0
- omlish/marshal/factories/invalidate.py +18 -68
- omlish/marshal/factories/method.py +26 -0
- omlish/marshal/factories/moduleimport/factories.py +22 -65
- omlish/marshal/factories/multi.py +13 -25
- omlish/marshal/factories/recursive.py +42 -56
- omlish/marshal/factories/typecache.py +29 -74
- omlish/marshal/factories/typemap.py +42 -43
- omlish/marshal/objects/dataclasses.py +129 -106
- omlish/marshal/objects/marshal.py +18 -14
- omlish/marshal/objects/namedtuples.py +48 -42
- omlish/marshal/objects/unmarshal.py +19 -15
- omlish/marshal/polymorphism/marshal.py +9 -11
- omlish/marshal/polymorphism/metadata.py +16 -5
- omlish/marshal/polymorphism/standard.py +13 -1
- omlish/marshal/polymorphism/unions.py +15 -105
- omlish/marshal/polymorphism/unmarshal.py +9 -10
- omlish/marshal/singular/enums.py +14 -18
- omlish/marshal/standard.py +10 -6
- omlish/marshal/trivial/any.py +1 -1
- omlish/marshal/trivial/forbidden.py +21 -26
- omlish/metadata.py +23 -1
- omlish/os/forkhooks.py +4 -0
- omlish/os/pidfiles/pinning.py +2 -2
- omlish/reflect/types.py +1 -0
- omlish/secrets/marshal.py +1 -1
- omlish/specs/jmespath/__init__.py +12 -3
- omlish/specs/jmespath/_dataclasses.py +2893 -0
- omlish/specs/jmespath/ast.py +1 -1
- omlish/specs/jsonrpc/__init__.py +13 -0
- omlish/specs/jsonrpc/_marshal.py +32 -23
- omlish/specs/jsonrpc/conns.py +10 -7
- omlish/specs/jsonschema/_marshal.py +1 -1
- omlish/specs/jsonschema/keywords/__init__.py +7 -0
- omlish/specs/jsonschema/keywords/_dataclasses.py +1644 -0
- omlish/specs/openapi/_marshal.py +31 -22
- omlish/sql/{tabledefs/alchemy.py → alchemy/tabledefs.py} +2 -2
- omlish/sql/queries/_marshal.py +2 -2
- omlish/sql/queries/rendering.py +1 -1
- omlish/sql/tabledefs/_marshal.py +1 -1
- omlish/subprocesses/base.py +4 -0
- omlish/subprocesses/editor.py +1 -1
- omlish/sync.py +155 -21
- omlish/term/alt.py +60 -0
- omlish/term/confirm.py +8 -8
- omlish/term/pager.py +235 -0
- omlish/term/terminfo.py +935 -0
- omlish/term/termstate.py +67 -0
- omlish/term/vt100/terminal.py +0 -3
- omlish/testing/pytest/plugins/asyncs/fixtures.py +4 -1
- omlish/testing/pytest/plugins/skips.py +2 -1
- omlish/testing/unittest/main.py +3 -3
- omlish/text/docwrap/__init__.py +3 -0
- omlish/text/docwrap/__main__.py +11 -0
- omlish/text/docwrap/api.py +83 -0
- omlish/text/docwrap/cli.py +86 -0
- omlish/text/docwrap/groups.py +84 -0
- omlish/text/docwrap/lists.py +167 -0
- omlish/text/docwrap/parts.py +146 -0
- omlish/text/docwrap/reflowing.py +106 -0
- omlish/text/docwrap/rendering.py +151 -0
- omlish/text/docwrap/utils.py +11 -0
- omlish/text/docwrap/wrapping.py +59 -0
- omlish/text/filecache.py +2 -2
- omlish/text/lorem.py +6 -0
- omlish/text/parts.py +2 -2
- omlish/text/textwrap.py +51 -0
- omlish/typedvalues/marshal.py +85 -59
- omlish/typedvalues/values.py +2 -1
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/METADATA +29 -28
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/RECORD +222 -171
- omlish/dataclasses/impl/generation/mangling.py +0 -18
- omlish/funcs/match.py +0 -227
- omlish/marshal/factories/match.py +0 -34
- omlish/marshal/factories/simple.py +0 -28
- /omlish/inject/impl/{providers2.py → providersmap.py} +0 -0
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/top_level.txt +0 -0
omlish/lang/lazyglobals.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import threading
|
|
1
2
|
import typing as ta
|
|
2
3
|
|
|
3
4
|
|
|
@@ -15,6 +16,10 @@ class AmbiguousLazyGlobalsFallbackError(Exception):
|
|
|
15
16
|
return f'{self.__class__.__name__}({self.attr!r}, {self.fallbacks!r})'
|
|
16
17
|
|
|
17
18
|
|
|
19
|
+
_LAZY_GLOBALS_LOCK = threading.RLock()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@ta.final
|
|
18
23
|
class LazyGlobals:
|
|
19
24
|
def __init__(
|
|
20
25
|
self,
|
|
@@ -22,8 +27,6 @@ class LazyGlobals:
|
|
|
22
27
|
globals: ta.MutableMapping[str, ta.Any] | None = None, # noqa
|
|
23
28
|
update_globals: bool = False,
|
|
24
29
|
) -> None:
|
|
25
|
-
super().__init__()
|
|
26
|
-
|
|
27
30
|
self._globals = globals
|
|
28
31
|
self._update_globals = update_globals
|
|
29
32
|
|
|
@@ -37,18 +40,28 @@ class LazyGlobals:
|
|
|
37
40
|
except KeyError:
|
|
38
41
|
pass
|
|
39
42
|
else:
|
|
40
|
-
if not
|
|
43
|
+
if xga.__class__ is not cls:
|
|
41
44
|
raise RuntimeError(f'Module already has __getattr__ hook: {xga}') # noqa
|
|
42
45
|
return xga
|
|
43
46
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
with _LAZY_GLOBALS_LOCK:
|
|
48
|
+
try:
|
|
49
|
+
xga = globals['__getattr__']
|
|
50
|
+
except KeyError:
|
|
51
|
+
pass
|
|
52
|
+
else:
|
|
53
|
+
if xga.__class__ is not cls:
|
|
54
|
+
raise RuntimeError(f'Module already has __getattr__ hook: {xga}') # noqa
|
|
55
|
+
return xga
|
|
56
|
+
|
|
57
|
+
lm = cls(
|
|
58
|
+
globals=globals,
|
|
59
|
+
update_globals=True,
|
|
60
|
+
)
|
|
48
61
|
|
|
49
|
-
|
|
62
|
+
globals['__getattr__'] = lm
|
|
50
63
|
|
|
51
|
-
|
|
64
|
+
return lm
|
|
52
65
|
|
|
53
66
|
def set_fn(self, attr: str, fn: ta.Callable[[], ta.Any]) -> 'LazyGlobals':
|
|
54
67
|
self._attr_fns[attr] = fn
|
omlish/lang/params.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
TODO:
|
|
3
3
|
- check validity
|
|
4
|
+
- signature vs getfullargspec - diff unwrapping + 'self' handling
|
|
4
5
|
"""
|
|
5
6
|
import dataclasses as dc
|
|
6
7
|
import enum
|
|
@@ -16,6 +17,13 @@ from .classes.restrict import Sealed
|
|
|
16
17
|
T = ta.TypeVar('T')
|
|
17
18
|
|
|
18
19
|
|
|
20
|
+
CanParamSpec: ta.TypeAlias = ta.Union[
|
|
21
|
+
'ParamSpec',
|
|
22
|
+
inspect.Signature,
|
|
23
|
+
ta.Callable,
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
|
|
19
27
|
##
|
|
20
28
|
|
|
21
29
|
|
|
@@ -101,6 +109,15 @@ class ParamSpec(ta.Sequence[Param], Final):
|
|
|
101
109
|
|
|
102
110
|
#
|
|
103
111
|
|
|
112
|
+
@classmethod
|
|
113
|
+
def of(cls, obj: CanParamSpec) -> 'ParamSpec':
|
|
114
|
+
if isinstance(obj, ParamSpec):
|
|
115
|
+
return obj
|
|
116
|
+
elif isinstance(obj, inspect.Signature):
|
|
117
|
+
return cls.of_signature(obj)
|
|
118
|
+
else:
|
|
119
|
+
return cls.inspect(obj)
|
|
120
|
+
|
|
104
121
|
@classmethod
|
|
105
122
|
def of_signature(
|
|
106
123
|
cls,
|
omlish/lang/recursion.py
CHANGED
omlish/lang/sequences.py
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TODO:
|
|
3
|
+
- StrView, BytesView - in lieu of hkt lol
|
|
4
|
+
- cext? even necessary?
|
|
5
|
+
- __eq__, cmp, __hash__
|
|
6
|
+
- __buffer__
|
|
7
|
+
- optimize `slice(None)`, keep as SeqView but fast path ops
|
|
8
|
+
- shorter repr if __len__ > some threshold
|
|
9
|
+
- use materialize()?
|
|
10
|
+
"""
|
|
11
|
+
import typing as ta
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
T = ta.TypeVar('T')
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
##
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def iterslice(
|
|
21
|
+
seq: ta.Sequence[T],
|
|
22
|
+
slc: slice,
|
|
23
|
+
) -> ta.Iterator[T]:
|
|
24
|
+
return map(seq.__getitem__, range(*slc.indices(len(seq))))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def iterrange(
|
|
28
|
+
seq: ta.Sequence[T],
|
|
29
|
+
start: int | None = None,
|
|
30
|
+
stop: int | None = None,
|
|
31
|
+
step: int | None = None,
|
|
32
|
+
) -> ta.Iterator[T]:
|
|
33
|
+
return iterslice(seq, slice(start, stop, step))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
##
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@ta.final
|
|
40
|
+
class SeqView(ta.Sequence[T]):
|
|
41
|
+
def __init__(self, data: ta.Sequence[T], slice_: slice = slice(None)) -> None:
|
|
42
|
+
if data.__class__ is SeqView:
|
|
43
|
+
self._data = data._data # type: ignore[attr-defined] # noqa
|
|
44
|
+
self._range = data._range[slice_] # type: ignore[attr-defined] # noqa
|
|
45
|
+
else:
|
|
46
|
+
self._data = data
|
|
47
|
+
self._range = range(*slice_.indices(len(data)))
|
|
48
|
+
|
|
49
|
+
def __init_subclass__(cls, **kwargs):
|
|
50
|
+
raise TypeError
|
|
51
|
+
|
|
52
|
+
_data: ta.Sequence[T]
|
|
53
|
+
_range: range
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
def _from_range(cls, base: ta.Sequence[T], rng: range) -> 'SeqView[T]':
|
|
57
|
+
self = object.__new__(cls)
|
|
58
|
+
self._data = base
|
|
59
|
+
self._range = rng
|
|
60
|
+
return self
|
|
61
|
+
|
|
62
|
+
def __repr__(self) -> str:
|
|
63
|
+
return f'{self.__class__.__name__}({self._data!r}, {self.slice!r})'
|
|
64
|
+
|
|
65
|
+
#
|
|
66
|
+
|
|
67
|
+
def __len__(self) -> int:
|
|
68
|
+
return len(self._range)
|
|
69
|
+
|
|
70
|
+
def __getitem__(self, key: int | slice) -> ta.Union[T, 'SeqView[T]']: # type: ignore[override]
|
|
71
|
+
if isinstance(key, slice):
|
|
72
|
+
nr = self._range[key]
|
|
73
|
+
return SeqView._from_range(self._data, nr)
|
|
74
|
+
return self._data[self._range[key]]
|
|
75
|
+
|
|
76
|
+
def __iter__(self) -> ta.Iterator[T]:
|
|
77
|
+
return map(self._data.__getitem__, self._range)
|
|
78
|
+
|
|
79
|
+
def __reversed__(self) -> ta.Iterator[T]:
|
|
80
|
+
return map(self._data.__getitem__, reversed(self._range))
|
|
81
|
+
|
|
82
|
+
def count(self, value: ta.Any) -> int:
|
|
83
|
+
c = 0
|
|
84
|
+
for i in self._range:
|
|
85
|
+
if self._data[i] == value:
|
|
86
|
+
c += 1
|
|
87
|
+
return c
|
|
88
|
+
|
|
89
|
+
def index(self, value: ta.Any, start: int = 0, stop: int | None = None) -> int:
|
|
90
|
+
sub = self._range[slice(start, stop, 1)]
|
|
91
|
+
for off, i in enumerate(sub):
|
|
92
|
+
if self._data[i] == value:
|
|
93
|
+
return off
|
|
94
|
+
raise ValueError(f'{value!r} is not in view')
|
|
95
|
+
|
|
96
|
+
#
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def data(self) -> ta.Sequence[T]:
|
|
100
|
+
return self._data
|
|
101
|
+
|
|
102
|
+
_slice: slice
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def slice(self) -> slice:
|
|
106
|
+
try:
|
|
107
|
+
return self._slice
|
|
108
|
+
except AttributeError:
|
|
109
|
+
pass
|
|
110
|
+
|
|
111
|
+
step = self._range.step
|
|
112
|
+
start = self._range.start
|
|
113
|
+
if len(self._range) == 0:
|
|
114
|
+
stop = start
|
|
115
|
+
else:
|
|
116
|
+
last = start + (len(self._range) - 1) * step
|
|
117
|
+
stop = last + (1 if step > 0 else -1)
|
|
118
|
+
slc = slice(start, stop, step)
|
|
119
|
+
|
|
120
|
+
self._slice = slc
|
|
121
|
+
return slc
|
|
122
|
+
|
|
123
|
+
def materialize(self) -> ta.Sequence[T]:
|
|
124
|
+
return self._data[self.slice]
|
omlish/lite/abstract.py
CHANGED
|
@@ -3,6 +3,9 @@ import abc
|
|
|
3
3
|
import typing as ta
|
|
4
4
|
|
|
5
5
|
|
|
6
|
+
T = ta.TypeVar('T')
|
|
7
|
+
|
|
8
|
+
|
|
6
9
|
##
|
|
7
10
|
|
|
8
11
|
|
|
@@ -14,25 +17,49 @@ def is_abstract_method(obj: ta.Any) -> bool:
|
|
|
14
17
|
return bool(getattr(obj, _IS_ABSTRACT_METHOD_ATTR, False))
|
|
15
18
|
|
|
16
19
|
|
|
17
|
-
def
|
|
20
|
+
def compute_abstract_methods(cls: type) -> ta.FrozenSet[str]:
|
|
21
|
+
# ~> https://github.com/python/cpython/blob/f3476c6507381ca860eec0989f53647b13517423/Modules/_abc.c#L358
|
|
22
|
+
|
|
23
|
+
# Stage 1: direct abstract methods
|
|
24
|
+
|
|
25
|
+
abstracts = {
|
|
26
|
+
a
|
|
27
|
+
# Get items as a list to avoid mutation issues during iteration
|
|
28
|
+
for a, v in list(cls.__dict__.items())
|
|
29
|
+
if is_abstract_method(v)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# Stage 2: inherited abstract methods
|
|
33
|
+
|
|
34
|
+
for base in cls.__bases__:
|
|
35
|
+
# Get __abstractmethods__ from base if it exists
|
|
36
|
+
if (base_abstracts := getattr(base, _ABSTRACT_METHODS_ATTR, None)) is None:
|
|
37
|
+
continue
|
|
38
|
+
|
|
39
|
+
# Iterate over abstract methods in base
|
|
40
|
+
for key in base_abstracts:
|
|
41
|
+
# Check if this class has an attribute with this name
|
|
42
|
+
try:
|
|
43
|
+
value = getattr(cls, key)
|
|
44
|
+
except AttributeError:
|
|
45
|
+
# Attribute not found in this class, skip
|
|
46
|
+
continue
|
|
47
|
+
|
|
48
|
+
# Check if it's still abstract
|
|
49
|
+
if is_abstract_method(value):
|
|
50
|
+
abstracts.add(key)
|
|
51
|
+
|
|
52
|
+
return frozenset(abstracts)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def update_abstracts(cls: ta.Type[T], *, force: bool = False) -> ta.Type[T]:
|
|
18
56
|
if not force and not hasattr(cls, _ABSTRACT_METHODS_ATTR):
|
|
19
57
|
# Per stdlib: We check for __abstractmethods__ here because cls might by a C implementation or a python
|
|
20
58
|
# implementation (especially during testing), and we want to handle both cases.
|
|
21
59
|
return cls
|
|
22
60
|
|
|
23
|
-
abstracts
|
|
24
|
-
|
|
25
|
-
for scls in cls.__bases__:
|
|
26
|
-
for name in getattr(scls, _ABSTRACT_METHODS_ATTR, ()):
|
|
27
|
-
value = getattr(cls, name, None)
|
|
28
|
-
if getattr(value, _IS_ABSTRACT_METHOD_ATTR, False):
|
|
29
|
-
abstracts.add(name)
|
|
30
|
-
|
|
31
|
-
for name, value in cls.__dict__.items():
|
|
32
|
-
if getattr(value, _IS_ABSTRACT_METHOD_ATTR, False):
|
|
33
|
-
abstracts.add(name)
|
|
34
|
-
|
|
35
|
-
setattr(cls, _ABSTRACT_METHODS_ATTR, frozenset(abstracts))
|
|
61
|
+
abstracts = compute_abstract_methods(cls)
|
|
62
|
+
setattr(cls, _ABSTRACT_METHODS_ATTR, abstracts)
|
|
36
63
|
return cls
|
|
37
64
|
|
|
38
65
|
|
|
@@ -86,23 +113,26 @@ class Abstract:
|
|
|
86
113
|
super().__init_subclass__(**kwargs)
|
|
87
114
|
|
|
88
115
|
if not (Abstract in cls.__bases__ or abc.ABC in cls.__bases__):
|
|
89
|
-
ams
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
116
|
+
if ams := compute_abstract_methods(cls):
|
|
117
|
+
amd = {
|
|
118
|
+
a: mcls
|
|
119
|
+
for mcls in cls.__mro__[::-1]
|
|
120
|
+
for a in ams
|
|
121
|
+
if a in mcls.__dict__
|
|
122
|
+
}
|
|
95
123
|
|
|
96
|
-
if ams:
|
|
97
124
|
raise AbstractTypeError(
|
|
98
125
|
f'Cannot subclass abstract class {cls.__name__} with abstract methods: ' +
|
|
99
126
|
', '.join(sorted([
|
|
100
127
|
'.'.join([
|
|
101
|
-
*([
|
|
102
|
-
|
|
128
|
+
*([
|
|
129
|
+
*([m] if (m := getattr(c, '__module__')) else []),
|
|
130
|
+
getattr(c, '__qualname__', getattr(c, '__name__')),
|
|
131
|
+
] if c is not None else '?'),
|
|
103
132
|
a,
|
|
104
133
|
])
|
|
105
|
-
for a
|
|
134
|
+
for a in ams
|
|
135
|
+
for c in [amd.get(a)]
|
|
106
136
|
])),
|
|
107
137
|
)
|
|
108
138
|
|
omlish/lite/asyncs.py
CHANGED
omlish/lite/attrops.py
CHANGED
|
@@ -6,6 +6,8 @@ TODO:
|
|
|
6
6
|
- per-attr repr transform / filter
|
|
7
7
|
- __ne__ ? cases where it still matters
|
|
8
8
|
- ordering ?
|
|
9
|
+
- repr_filter: ta.Union[ta.Callable[[ta.Any], ta.Optional[str]], ta.Literal['not_none', 'truthy']]] ?
|
|
10
|
+
- unify repr/repr_fn/repr_filter
|
|
9
11
|
"""
|
|
10
12
|
import types # noqa
|
|
11
13
|
import typing as ta
|
omlish/lite/contextmanagers.py
CHANGED
|
@@ -54,7 +54,7 @@ class ExitStacked:
|
|
|
54
54
|
es.__enter__()
|
|
55
55
|
try:
|
|
56
56
|
self._enter_contexts()
|
|
57
|
-
except
|
|
57
|
+
except BaseException: # noqa
|
|
58
58
|
es.__exit__(*sys.exc_info())
|
|
59
59
|
raise
|
|
60
60
|
return self
|
|
@@ -65,7 +65,7 @@ class ExitStacked:
|
|
|
65
65
|
return None
|
|
66
66
|
try:
|
|
67
67
|
self._exit_contexts()
|
|
68
|
-
except
|
|
68
|
+
except BaseException: # noqa
|
|
69
69
|
es.__exit__(*sys.exc_info())
|
|
70
70
|
raise
|
|
71
71
|
return es.__exit__(exc_type, exc_val, exc_tb)
|
|
@@ -113,7 +113,7 @@ class AsyncExitStacked:
|
|
|
113
113
|
await es.__aenter__()
|
|
114
114
|
try:
|
|
115
115
|
await self._async_enter_contexts()
|
|
116
|
-
except
|
|
116
|
+
except BaseException: # noqa
|
|
117
117
|
await es.__aexit__(*sys.exc_info())
|
|
118
118
|
raise
|
|
119
119
|
return self
|
|
@@ -124,7 +124,7 @@ class AsyncExitStacked:
|
|
|
124
124
|
return None
|
|
125
125
|
try:
|
|
126
126
|
await self._async_exit_contexts()
|
|
127
|
-
except
|
|
127
|
+
except BaseException: # noqa
|
|
128
128
|
await es.__aexit__(*sys.exc_info())
|
|
129
129
|
raise
|
|
130
130
|
return await es.__aexit__(exc_type, exc_val, exc_tb)
|
omlish/lite/dataclasses.py
CHANGED
|
@@ -100,6 +100,36 @@ def dataclass_repr_omit_falsey(obj: ta.Any) -> str:
|
|
|
100
100
|
##
|
|
101
101
|
|
|
102
102
|
|
|
103
|
+
def dataclass_descriptor_method(*bind_attrs: str, bind_owner: bool = False) -> ta.Callable:
|
|
104
|
+
if not bind_attrs:
|
|
105
|
+
def __get__(self, instance, owner=None): # noqa
|
|
106
|
+
return self
|
|
107
|
+
|
|
108
|
+
elif bind_owner:
|
|
109
|
+
def __get__(self, instance, owner=None): # noqa
|
|
110
|
+
# Guaranteed to return a new instance even with no attrs
|
|
111
|
+
return dc.replace(self, **{
|
|
112
|
+
a: v.__get__(instance, owner) if (v := getattr(self, a)) is not None else None
|
|
113
|
+
for a in bind_attrs
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
else:
|
|
117
|
+
def __get__(self, instance, owner=None): # noqa
|
|
118
|
+
if instance is None:
|
|
119
|
+
return self
|
|
120
|
+
|
|
121
|
+
# Guaranteed to return a new instance even with no attrs
|
|
122
|
+
return dc.replace(self, **{
|
|
123
|
+
a: v.__get__(instance, owner) if (v := getattr(self, a)) is not None else None
|
|
124
|
+
for a in bind_attrs
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
return __get__
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
##
|
|
131
|
+
|
|
132
|
+
|
|
103
133
|
def dataclass_kw_only_init():
|
|
104
134
|
def inner(cls):
|
|
105
135
|
if not isinstance(cls, type) and dc.is_dataclass(cls):
|
|
@@ -161,3 +191,17 @@ def dataclass_kw_only_init():
|
|
|
161
191
|
return cls
|
|
162
192
|
|
|
163
193
|
return inner
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
##
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@dc.dataclass()
|
|
200
|
+
class DataclassFieldRequiredError(Exception):
|
|
201
|
+
name: str
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def dataclass_field_required(name: str) -> ta.Callable[[], ta.Any]:
|
|
205
|
+
def inner() -> ta.NoReturn:
|
|
206
|
+
raise DataclassFieldRequiredError(name)
|
|
207
|
+
return inner
|
omlish/lite/maybes.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# ruff: noqa: UP007 UP045
|
|
2
2
|
import abc
|
|
3
3
|
import functools
|
|
4
|
+
import operator
|
|
4
5
|
import typing as ta
|
|
5
6
|
|
|
6
7
|
from .abstract import Abstract
|
|
@@ -208,3 +209,10 @@ class _EmptyMaybe(_Maybe[T]):
|
|
|
208
209
|
|
|
209
210
|
|
|
210
211
|
Maybe._empty = _EmptyMaybe() # noqa
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
##
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
setattr(Maybe, 'just', _JustMaybe) # noqa
|
|
218
|
+
setattr(Maybe, 'empty', functools.partial(operator.attrgetter('_empty'), Maybe))
|
omlish/lite/maysync.py
CHANGED
|
@@ -25,6 +25,7 @@ Internally, it's not really correct to say that there is 'no event loop' in the
|
|
|
25
25
|
===
|
|
26
26
|
|
|
27
27
|
TODO:
|
|
28
|
+
- ! impl iterators not just generators !
|
|
28
29
|
- __del__
|
|
29
30
|
- (test) maysync context managers
|
|
30
31
|
- CancelledError
|
|
@@ -33,11 +34,6 @@ TODO:
|
|
|
33
34
|
- works down to 3.8
|
|
34
35
|
- make_maysync_from_sync can run with asyncio.run_in_thread
|
|
35
36
|
- make_maysync_from_async can run with asyncio.run_soon
|
|
36
|
-
|
|
37
|
-
TODO OVERHAUL:
|
|
38
|
-
- no more sync/async context, just one Context, and it means sync
|
|
39
|
-
- make FpMaywaitable *not reusable*
|
|
40
|
-
- `cannot reuse already awaited coroutine`
|
|
41
37
|
"""
|
|
42
38
|
import abc
|
|
43
39
|
import inspect
|
omlish/lite/pycharm.py
CHANGED
omlish/lite/typing.py
CHANGED
|
@@ -12,6 +12,12 @@ A2 = ta.TypeVar('A2')
|
|
|
12
12
|
|
|
13
13
|
##
|
|
14
14
|
# A workaround for typing deficiencies (like `Argument 2 to NewType(...) must be subclassable`).
|
|
15
|
+
#
|
|
16
|
+
# Note that this problem doesn't happen at runtime - it happens in mypy:
|
|
17
|
+
#
|
|
18
|
+
# mypy <(echo "import typing as ta; MyCallback = ta.NewType('MyCallback', ta.Callable[[], None])")
|
|
19
|
+
# /dev/fd/11:1:22: error: Argument 2 to NewType(...) must be subclassable (got "Callable[[], None]") [valid-newtype]
|
|
20
|
+
#
|
|
15
21
|
|
|
16
22
|
|
|
17
23
|
@dc.dataclass(frozen=True)
|
omlish/logs/all.py
CHANGED
omlish/logs/utils.py
CHANGED
omlish/manifests/loading.py
CHANGED
|
@@ -13,7 +13,7 @@ TODO:
|
|
|
13
13
|
scan_root_dirs
|
|
14
14
|
- currently the cli cant subprocess itself and keep manifests working
|
|
15
15
|
- EnvVar cls is already lite
|
|
16
|
-
|
|
16
|
+
- can discover_packages deadlock with concurrent / multithreaded imports?
|
|
17
17
|
"""
|
|
18
18
|
import dataclasses as dc
|
|
19
19
|
import importlib.machinery
|
|
@@ -417,6 +417,7 @@ class ManifestLoader:
|
|
|
417
417
|
def _read_package_file_text(cls, package_name: str, file_name: str) -> ta.Optional[str]:
|
|
418
418
|
# importlib.resources.files actually imports the package - to avoid this, if possible, the file is read straight
|
|
419
419
|
# off the filesystem.
|
|
420
|
+
# FIXME: find_spec *still* imports the parent package to get __path__ to feed to _find_spec...
|
|
420
421
|
spec = importlib.util.find_spec(package_name)
|
|
421
422
|
if (
|
|
422
423
|
spec is not None and
|
omlish/marshal/__init__.py
CHANGED
|
@@ -49,11 +49,17 @@ with _lang.auto_proxy_init(globals()):
|
|
|
49
49
|
|
|
50
50
|
from .base.configs import ( # noqa
|
|
51
51
|
Config,
|
|
52
|
+
Configs,
|
|
53
|
+
|
|
52
54
|
ConfigRegistry,
|
|
53
55
|
)
|
|
54
56
|
|
|
55
57
|
from .base.contexts import ( # noqa
|
|
56
58
|
BaseContext,
|
|
59
|
+
|
|
60
|
+
MarshalFactoryContext,
|
|
61
|
+
UnmarshalFactoryContext,
|
|
62
|
+
|
|
57
63
|
MarshalContext,
|
|
58
64
|
UnmarshalContext,
|
|
59
65
|
)
|
|
@@ -108,19 +114,35 @@ with _lang.auto_proxy_init(globals()):
|
|
|
108
114
|
IterableUnmarshaler,
|
|
109
115
|
)
|
|
110
116
|
|
|
117
|
+
from .composite.optionals import ( # noqa
|
|
118
|
+
OptionalMarshaler,
|
|
119
|
+
OptionalUnmarshaler,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
from .composite.unions.literals import ( # noqa
|
|
123
|
+
LITERAL_UNION_TYPES,
|
|
124
|
+
LiteralUnionMarshaler,
|
|
125
|
+
LiteralUnionMarshalerFactory,
|
|
126
|
+
LiteralUnionUnmarshaler,
|
|
127
|
+
LiteralUnionUnmarshalerFactory,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
from .composite.unions.primitives import ( # noqa
|
|
131
|
+
PRIMITIVE_UNION_TYPES,
|
|
132
|
+
PrimitiveUnionMarshaler,
|
|
133
|
+
PrimitiveUnionMarshalerFactory,
|
|
134
|
+
PrimitiveUnionUnmarshaler,
|
|
135
|
+
PrimitiveUnionUnmarshalerFactory,
|
|
136
|
+
)
|
|
137
|
+
|
|
111
138
|
from .composite.wrapped import ( # noqa
|
|
112
139
|
WrappedMarshaler,
|
|
113
140
|
WrappedUnmarshaler,
|
|
114
141
|
)
|
|
115
142
|
|
|
116
|
-
from .factories.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
from .factories.match import ( # noqa
|
|
122
|
-
MarshalerFactoryMatchClass,
|
|
123
|
-
UnmarshalerFactoryMatchClass,
|
|
143
|
+
from .factories.method import ( # noqa
|
|
144
|
+
MarshalerFactoryMethodClass,
|
|
145
|
+
UnmarshalerFactoryMethodClass,
|
|
124
146
|
)
|
|
125
147
|
|
|
126
148
|
from .factories.moduleimport.configs import ( # noqa
|
|
@@ -192,6 +214,7 @@ with _lang.auto_proxy_init(globals()):
|
|
|
192
214
|
)
|
|
193
215
|
|
|
194
216
|
from .polymorphism.metadata import ( # noqa
|
|
217
|
+
AutoStripSuffix,
|
|
195
218
|
FieldTypeTagging,
|
|
196
219
|
Impl,
|
|
197
220
|
Impls,
|
|
@@ -207,13 +230,8 @@ with _lang.auto_proxy_init(globals()):
|
|
|
207
230
|
)
|
|
208
231
|
|
|
209
232
|
from .polymorphism.unions import ( # noqa
|
|
210
|
-
PRIMITIVE_UNION_TYPES,
|
|
211
233
|
PolymorphismUnionMarshalerFactory,
|
|
212
234
|
PolymorphismUnionUnmarshalerFactory,
|
|
213
|
-
PrimitiveUnionMarshaler,
|
|
214
|
-
PrimitiveUnionMarshalerFactory,
|
|
215
|
-
PrimitiveUnionUnmarshaler,
|
|
216
|
-
PrimitiveUnionUnmarshalerFactory,
|
|
217
235
|
)
|
|
218
236
|
|
|
219
237
|
from .polymorphism.unmarshal import ( # noqa
|
|
@@ -225,6 +243,7 @@ with _lang.auto_proxy_init(globals()):
|
|
|
225
243
|
from .singular.base64 import ( # noqa
|
|
226
244
|
BASE64_MARSHALER_FACTORY,
|
|
227
245
|
BASE64_UNMARSHALER_FACTORY,
|
|
246
|
+
Base64MarshalerUnmarshaler,
|
|
228
247
|
)
|
|
229
248
|
|
|
230
249
|
from .singular.primitives import ( # noqa
|
|
@@ -233,6 +252,7 @@ with _lang.auto_proxy_init(globals()):
|
|
|
233
252
|
|
|
234
253
|
from .trivial.forbidden import ( # noqa
|
|
235
254
|
ForbiddenTypeMarshalerFactory,
|
|
255
|
+
ForbiddenTypeMarshalerFactoryUnmarshalerFactory,
|
|
236
256
|
ForbiddenTypeUnmarshalerFactory,
|
|
237
257
|
)
|
|
238
258
|
|