omlish 0.0.0.dev1__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 +7 -0
- omlish/__init__.py +0 -0
- omlish/argparse.py +223 -0
- omlish/asyncs/__init__.py +17 -0
- omlish/asyncs/anyio.py +23 -0
- omlish/asyncs/asyncio.py +19 -0
- omlish/asyncs/asyncs.py +76 -0
- omlish/asyncs/futures.py +179 -0
- omlish/asyncs/trio.py +11 -0
- omlish/c3.py +173 -0
- omlish/cached.py +9 -0
- omlish/check.py +231 -0
- omlish/collections/__init__.py +63 -0
- omlish/collections/_abc.py +156 -0
- omlish/collections/_io_abc.py +78 -0
- omlish/collections/cache/__init__.py +11 -0
- omlish/collections/cache/descriptor.py +188 -0
- omlish/collections/cache/impl.py +485 -0
- omlish/collections/cache/types.py +37 -0
- omlish/collections/coerce.py +337 -0
- omlish/collections/frozen.py +148 -0
- omlish/collections/identity.py +106 -0
- omlish/collections/indexed.py +75 -0
- omlish/collections/mappings.py +127 -0
- omlish/collections/ordered.py +81 -0
- omlish/collections/persistent.py +36 -0
- omlish/collections/skiplist.py +193 -0
- omlish/collections/sorted.py +126 -0
- omlish/collections/treap.py +228 -0
- omlish/collections/treapmap.py +144 -0
- omlish/collections/unmodifiable.py +174 -0
- omlish/collections/utils.py +110 -0
- omlish/configs/__init__.py +0 -0
- omlish/configs/flattening.py +147 -0
- omlish/configs/props.py +64 -0
- omlish/dataclasses/__init__.py +83 -0
- omlish/dataclasses/impl/__init__.py +6 -0
- omlish/dataclasses/impl/api.py +260 -0
- omlish/dataclasses/impl/as_.py +76 -0
- omlish/dataclasses/impl/exceptions.py +2 -0
- omlish/dataclasses/impl/fields.py +148 -0
- omlish/dataclasses/impl/frozen.py +55 -0
- omlish/dataclasses/impl/hashing.py +85 -0
- omlish/dataclasses/impl/init.py +173 -0
- omlish/dataclasses/impl/internals.py +118 -0
- omlish/dataclasses/impl/main.py +150 -0
- omlish/dataclasses/impl/metaclass.py +126 -0
- omlish/dataclasses/impl/metadata.py +74 -0
- omlish/dataclasses/impl/order.py +47 -0
- omlish/dataclasses/impl/params.py +150 -0
- omlish/dataclasses/impl/processing.py +16 -0
- omlish/dataclasses/impl/reflect.py +173 -0
- omlish/dataclasses/impl/replace.py +40 -0
- omlish/dataclasses/impl/repr.py +34 -0
- omlish/dataclasses/impl/simple.py +92 -0
- omlish/dataclasses/impl/slots.py +80 -0
- omlish/dataclasses/impl/utils.py +167 -0
- omlish/defs.py +193 -0
- omlish/dispatch/__init__.py +3 -0
- omlish/dispatch/dispatch.py +137 -0
- omlish/dispatch/functions.py +52 -0
- omlish/dispatch/methods.py +162 -0
- omlish/docker.py +149 -0
- omlish/dynamic.py +220 -0
- omlish/graphs/__init__.py +0 -0
- omlish/graphs/dot/__init__.py +19 -0
- omlish/graphs/dot/items.py +162 -0
- omlish/graphs/dot/rendering.py +147 -0
- omlish/graphs/dot/utils.py +30 -0
- omlish/graphs/trees.py +249 -0
- omlish/http/__init__.py +0 -0
- omlish/http/consts.py +20 -0
- omlish/http/wsgi.py +34 -0
- omlish/inject/__init__.py +85 -0
- omlish/inject/binder.py +12 -0
- omlish/inject/bindings.py +49 -0
- omlish/inject/eagers.py +21 -0
- omlish/inject/elements.py +43 -0
- omlish/inject/exceptions.py +49 -0
- omlish/inject/impl/__init__.py +0 -0
- omlish/inject/impl/bindings.py +19 -0
- omlish/inject/impl/elements.py +154 -0
- omlish/inject/impl/injector.py +182 -0
- omlish/inject/impl/inspect.py +98 -0
- omlish/inject/impl/private.py +109 -0
- omlish/inject/impl/providers.py +132 -0
- omlish/inject/impl/scopes.py +198 -0
- omlish/inject/injector.py +40 -0
- omlish/inject/inspect.py +14 -0
- omlish/inject/keys.py +43 -0
- omlish/inject/managed.py +24 -0
- omlish/inject/overrides.py +18 -0
- omlish/inject/private.py +29 -0
- omlish/inject/providers.py +111 -0
- omlish/inject/proxy.py +48 -0
- omlish/inject/scopes.py +84 -0
- omlish/inject/types.py +21 -0
- omlish/iterators.py +184 -0
- omlish/json.py +194 -0
- omlish/lang/__init__.py +112 -0
- omlish/lang/cached.py +267 -0
- omlish/lang/classes/__init__.py +24 -0
- omlish/lang/classes/abstract.py +74 -0
- omlish/lang/classes/restrict.py +137 -0
- omlish/lang/classes/simple.py +120 -0
- omlish/lang/classes/test/__init__.py +0 -0
- omlish/lang/classes/test/test_abstract.py +89 -0
- omlish/lang/classes/test/test_restrict.py +71 -0
- omlish/lang/classes/test/test_simple.py +58 -0
- omlish/lang/classes/test/test_virtual.py +72 -0
- omlish/lang/classes/virtual.py +130 -0
- omlish/lang/clsdct.py +67 -0
- omlish/lang/cmp.py +63 -0
- omlish/lang/contextmanagers.py +249 -0
- omlish/lang/datetimes.py +67 -0
- omlish/lang/descriptors.py +52 -0
- omlish/lang/functions.py +126 -0
- omlish/lang/imports.py +153 -0
- omlish/lang/iterables.py +54 -0
- omlish/lang/maybes.py +136 -0
- omlish/lang/objects.py +103 -0
- omlish/lang/resolving.py +50 -0
- omlish/lang/strings.py +128 -0
- omlish/lang/typing.py +92 -0
- omlish/libc.py +532 -0
- omlish/logs/__init__.py +9 -0
- omlish/logs/_abc.py +247 -0
- omlish/logs/configs.py +62 -0
- omlish/logs/filters.py +9 -0
- omlish/logs/formatters.py +67 -0
- omlish/logs/utils.py +20 -0
- omlish/marshal/__init__.py +52 -0
- omlish/marshal/any.py +25 -0
- omlish/marshal/base.py +201 -0
- omlish/marshal/base64.py +25 -0
- omlish/marshal/dataclasses.py +115 -0
- omlish/marshal/datetimes.py +90 -0
- omlish/marshal/enums.py +43 -0
- omlish/marshal/exceptions.py +7 -0
- omlish/marshal/factories.py +129 -0
- omlish/marshal/global_.py +33 -0
- omlish/marshal/iterables.py +57 -0
- omlish/marshal/mappings.py +66 -0
- omlish/marshal/naming.py +17 -0
- omlish/marshal/objects.py +106 -0
- omlish/marshal/optionals.py +49 -0
- omlish/marshal/polymorphism.py +147 -0
- omlish/marshal/primitives.py +43 -0
- omlish/marshal/registries.py +57 -0
- omlish/marshal/standard.py +80 -0
- omlish/marshal/utils.py +23 -0
- omlish/marshal/uuids.py +29 -0
- omlish/marshal/values.py +30 -0
- omlish/math.py +184 -0
- omlish/os.py +32 -0
- omlish/reflect.py +359 -0
- omlish/replserver/__init__.py +5 -0
- omlish/replserver/__main__.py +4 -0
- omlish/replserver/console.py +247 -0
- omlish/replserver/server.py +146 -0
- omlish/runmodule.py +28 -0
- omlish/stats.py +342 -0
- omlish/term.py +222 -0
- omlish/testing/__init__.py +7 -0
- omlish/testing/pydevd.py +225 -0
- omlish/testing/pytest/__init__.py +8 -0
- omlish/testing/pytest/helpers.py +35 -0
- omlish/testing/pytest/inject/__init__.py +1 -0
- omlish/testing/pytest/inject/harness.py +159 -0
- omlish/testing/pytest/plugins/__init__.py +20 -0
- omlish/testing/pytest/plugins/_registry.py +6 -0
- omlish/testing/pytest/plugins/logging.py +13 -0
- omlish/testing/pytest/plugins/pycharm.py +54 -0
- omlish/testing/pytest/plugins/repeat.py +19 -0
- omlish/testing/pytest/plugins/skips.py +32 -0
- omlish/testing/pytest/plugins/spacing.py +19 -0
- omlish/testing/pytest/plugins/switches.py +70 -0
- omlish/testing/testing.py +102 -0
- omlish/text/__init__.py +0 -0
- omlish/text/delimit.py +171 -0
- omlish/text/indent.py +50 -0
- omlish/text/parts.py +265 -0
- omlish-0.0.0.dev1.dist-info/LICENSE +21 -0
- omlish-0.0.0.dev1.dist-info/METADATA +17 -0
- omlish-0.0.0.dev1.dist-info/RECORD +187 -0
- omlish-0.0.0.dev1.dist-info/WHEEL +5 -0
- omlish-0.0.0.dev1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import typing as ta
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from ..restrict import Final
|
|
6
|
+
from ..restrict import FinalException
|
|
7
|
+
from ..restrict import NoBool
|
|
8
|
+
from ..restrict import Sealed
|
|
9
|
+
from ..restrict import SealedException
|
|
10
|
+
from ..restrict import no_bool
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_final():
|
|
14
|
+
class A:
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
A()
|
|
18
|
+
|
|
19
|
+
class B(A, Final):
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
B()
|
|
23
|
+
|
|
24
|
+
with pytest.raises(FinalException):
|
|
25
|
+
class C(B):
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
T = ta.TypeVar('T')
|
|
29
|
+
|
|
30
|
+
class D(ta.Generic[T], Final):
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
D()
|
|
34
|
+
D[int] # noqa
|
|
35
|
+
|
|
36
|
+
with pytest.raises(FinalException):
|
|
37
|
+
class E(D[int]):
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_sealed():
|
|
42
|
+
class A(Sealed):
|
|
43
|
+
__module__ = 'a'
|
|
44
|
+
|
|
45
|
+
class B(A):
|
|
46
|
+
__module__ = 'a'
|
|
47
|
+
|
|
48
|
+
with pytest.raises(SealedException):
|
|
49
|
+
class C(A):
|
|
50
|
+
__module__ = 'c'
|
|
51
|
+
|
|
52
|
+
class D(B):
|
|
53
|
+
__module__ = 'd'
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_no_bool():
|
|
57
|
+
@no_bool
|
|
58
|
+
def f():
|
|
59
|
+
return 1
|
|
60
|
+
|
|
61
|
+
assert f() == 1
|
|
62
|
+
assert bool(f())
|
|
63
|
+
with pytest.raises(TypeError):
|
|
64
|
+
bool(f)
|
|
65
|
+
|
|
66
|
+
class C(NoBool):
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
assert C # type: ignore
|
|
70
|
+
with pytest.raises(TypeError):
|
|
71
|
+
bool(C())
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from ..restrict import FinalException
|
|
4
|
+
from ..simple import LazySingleton
|
|
5
|
+
from ..simple import Marker
|
|
6
|
+
from ..simple import Singleton
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def test_marker():
|
|
10
|
+
class M(Marker):
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
with pytest.raises(FinalException):
|
|
14
|
+
class N(M):
|
|
15
|
+
pass
|
|
16
|
+
with pytest.raises(TypeError):
|
|
17
|
+
M()
|
|
18
|
+
|
|
19
|
+
assert repr(M) == '<M>'
|
|
20
|
+
|
|
21
|
+
assert isinstance(M, M)
|
|
22
|
+
assert issubclass(M, M) # noqa
|
|
23
|
+
|
|
24
|
+
class O(Marker):
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
assert isinstance(O, O)
|
|
28
|
+
assert issubclass(O, O) # noqa
|
|
29
|
+
|
|
30
|
+
assert not isinstance(M, O)
|
|
31
|
+
assert not issubclass(M, O)
|
|
32
|
+
assert not isinstance(O, M)
|
|
33
|
+
assert not issubclass(O, M)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_singletons():
|
|
37
|
+
for bcls in [Singleton, LazySingleton]:
|
|
38
|
+
foo_init_calls = 0
|
|
39
|
+
foo2_init_calls = 0
|
|
40
|
+
|
|
41
|
+
class Foo(bcls): # type: ignore
|
|
42
|
+
def __init__(self):
|
|
43
|
+
super().__init__()
|
|
44
|
+
nonlocal foo_init_calls
|
|
45
|
+
foo_init_calls += 1
|
|
46
|
+
|
|
47
|
+
assert Foo() is Foo()
|
|
48
|
+
assert foo_init_calls == 1
|
|
49
|
+
|
|
50
|
+
class Foo2(Foo):
|
|
51
|
+
def __init__(self):
|
|
52
|
+
super().__init__()
|
|
53
|
+
nonlocal foo2_init_calls
|
|
54
|
+
foo2_init_calls += 1
|
|
55
|
+
|
|
56
|
+
assert Foo2() is Foo2()
|
|
57
|
+
assert foo2_init_calls == 1
|
|
58
|
+
assert foo_init_calls == 2
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from ..virtual import Callable
|
|
6
|
+
from ..virtual import Virtual
|
|
7
|
+
from ..virtual import virtual_check
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_virtual():
|
|
11
|
+
class P(Virtual):
|
|
12
|
+
@abc.abstractmethod
|
|
13
|
+
def f(self):
|
|
14
|
+
raise NotImplementedError
|
|
15
|
+
|
|
16
|
+
with pytest.raises(TypeError):
|
|
17
|
+
P() # type: ignore
|
|
18
|
+
|
|
19
|
+
class A:
|
|
20
|
+
def f(self):
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
class B:
|
|
24
|
+
def g(self):
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
A()
|
|
28
|
+
|
|
29
|
+
assert issubclass(A, P)
|
|
30
|
+
assert not issubclass(B, P)
|
|
31
|
+
assert isinstance(A(), P)
|
|
32
|
+
assert not isinstance(B(), P)
|
|
33
|
+
|
|
34
|
+
class C(P):
|
|
35
|
+
def f(self):
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
with pytest.raises(TypeError):
|
|
39
|
+
class D(P):
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
D() # type: ignore
|
|
43
|
+
|
|
44
|
+
virtual_check(P)(A)
|
|
45
|
+
with pytest.raises(TypeError):
|
|
46
|
+
virtual_check(P)(B)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_callable():
|
|
50
|
+
with pytest.raises(Exception):
|
|
51
|
+
Callable()
|
|
52
|
+
|
|
53
|
+
with pytest.raises(Exception):
|
|
54
|
+
class C(Callable): # noqa
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
def f():
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
assert isinstance(f, Callable)
|
|
61
|
+
assert not isinstance(5, Callable)
|
|
62
|
+
|
|
63
|
+
class D:
|
|
64
|
+
pass
|
|
65
|
+
|
|
66
|
+
class E:
|
|
67
|
+
def __call__(self):
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
assert isinstance(D, Callable)
|
|
71
|
+
assert isinstance(E, Callable)
|
|
72
|
+
assert isinstance(E(), Callable)
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import types
|
|
3
|
+
import typing as ta
|
|
4
|
+
|
|
5
|
+
from .abstract import make_abstract
|
|
6
|
+
from .restrict import NotInstantiable
|
|
7
|
+
from .restrict import Final
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
T = ta.TypeVar('T')
|
|
11
|
+
Ty = ta.TypeVar('Ty', bound=type)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
##
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _make_not_instantiable():
|
|
18
|
+
def __new__(cls, *args, **kwargs):
|
|
19
|
+
raise TypeError(cls)
|
|
20
|
+
|
|
21
|
+
def __init__(self, *args, **kwargs):
|
|
22
|
+
raise TypeError(self)
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
'__new__': __new__,
|
|
26
|
+
'__initT__': __init__,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class _VirtualMeta(abc.ABCMeta):
|
|
31
|
+
|
|
32
|
+
def __new__(mcls, name, bases, namespace):
|
|
33
|
+
if 'Virtual' not in globals():
|
|
34
|
+
return super().__new__(mcls, name, bases, namespace)
|
|
35
|
+
if Virtual not in bases:
|
|
36
|
+
raise TypeError
|
|
37
|
+
|
|
38
|
+
for k, v in list(namespace.items()):
|
|
39
|
+
absv = make_abstract(v)
|
|
40
|
+
if absv is not v:
|
|
41
|
+
namespace[k] = absv
|
|
42
|
+
|
|
43
|
+
reqs = {k: v for k, v in namespace.items() if getattr(v, '__isabstractmethod__', False)}
|
|
44
|
+
user_subclasshook = namespace.pop('__subclasshook__', None)
|
|
45
|
+
|
|
46
|
+
def get_missing_reqs(cls):
|
|
47
|
+
reqset = set(reqs)
|
|
48
|
+
for mro_cls in cls.__mro__:
|
|
49
|
+
reqset -= set(mro_cls.__dict__)
|
|
50
|
+
return reqset
|
|
51
|
+
|
|
52
|
+
def __subclasshook__(cls, subclass):
|
|
53
|
+
if cls is not kls:
|
|
54
|
+
return super(kls, cls).__subclasshook__(subclass) # type: ignore
|
|
55
|
+
if get_missing_reqs(subclass):
|
|
56
|
+
return False
|
|
57
|
+
if user_subclasshook is not None:
|
|
58
|
+
ret = user_subclasshook(cls, subclass)
|
|
59
|
+
else:
|
|
60
|
+
ret = super(kls, cls).__subclasshook__(subclass) # type: ignore
|
|
61
|
+
return True if ret is NotImplemented else ret
|
|
62
|
+
|
|
63
|
+
namespace['__subclasshook__'] = classmethod(__subclasshook__)
|
|
64
|
+
|
|
65
|
+
namespace.update(_make_not_instantiable())
|
|
66
|
+
|
|
67
|
+
kls = ta.cast(
|
|
68
|
+
type,
|
|
69
|
+
super().__new__(
|
|
70
|
+
abc.ABCMeta,
|
|
71
|
+
name,
|
|
72
|
+
tuple(b for b in bases if b is not Virtual),
|
|
73
|
+
namespace,
|
|
74
|
+
),
|
|
75
|
+
)
|
|
76
|
+
return kls
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class Virtual(metaclass=_VirtualMeta):
|
|
80
|
+
"""Like Protocol but supports more than just methods."""
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def virtual_check(virtual: type) -> ta.Callable[[Ty], Ty]:
|
|
84
|
+
def inner(cls):
|
|
85
|
+
if not issubclass(cls, virtual):
|
|
86
|
+
raise TypeError(cls)
|
|
87
|
+
return cls
|
|
88
|
+
# if not issubclass(type(virtual), _VirtualMeta):
|
|
89
|
+
# raise TypeError(virtual)
|
|
90
|
+
return inner
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
##
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class Descriptor(Virtual):
|
|
97
|
+
|
|
98
|
+
def __get__(self, instance, owner=None):
|
|
99
|
+
raise NotImplementedError
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class Picklable(Virtual):
|
|
103
|
+
|
|
104
|
+
def __getstate__(self):
|
|
105
|
+
raise NotImplementedError
|
|
106
|
+
|
|
107
|
+
def __setstate__(self, state):
|
|
108
|
+
raise NotImplementedError
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
##
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class Callable(NotInstantiable, Final, ta.Generic[T]):
|
|
115
|
+
|
|
116
|
+
def __call__(self, *args: ta.Any, **kwargs: ta.Any) -> T:
|
|
117
|
+
raise TypeError
|
|
118
|
+
|
|
119
|
+
@classmethod
|
|
120
|
+
def __instancecheck__(cls, instance):
|
|
121
|
+
return callable(instance)
|
|
122
|
+
|
|
123
|
+
@classmethod
|
|
124
|
+
def __subclasscheck__(cls, subclass):
|
|
125
|
+
if not hasattr(subclass, '__call__'):
|
|
126
|
+
return False
|
|
127
|
+
call = subclass.__call__
|
|
128
|
+
if isinstance(call, types.MethodWrapperType) and call.__self__ is subclass:
|
|
129
|
+
return False
|
|
130
|
+
return True
|
omlish/lang/clsdct.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import sys
|
|
3
|
+
import types
|
|
4
|
+
import typing as ta
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
_CLS_DCT_ATTR_SETS = [
|
|
8
|
+
{
|
|
9
|
+
'__module__',
|
|
10
|
+
'__qualname__',
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
'__all__',
|
|
14
|
+
},
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _skip_cls_dct_frames(f: types.FrameType) -> types.FrameType:
|
|
19
|
+
if sys.implementation.name == 'pypy':
|
|
20
|
+
if f.f_code is functools.partial.__call__.__code__: # noqa
|
|
21
|
+
return _skip_cls_dct_frames(f.f_back) # type: ignore
|
|
22
|
+
|
|
23
|
+
return f
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def is_possibly_cls_dct(dct: ta.Mapping[str, ta.Any]) -> bool:
|
|
27
|
+
return any(all(a in dct for a in s) for s in _CLS_DCT_ATTR_SETS)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_caller_cls_dct(offset: int = 0) -> ta.MutableMapping[str, ta.Any]:
|
|
31
|
+
f = sys._getframe(offset + 2) # noqa
|
|
32
|
+
cls_dct = _skip_cls_dct_frames(f).f_locals
|
|
33
|
+
if not is_possibly_cls_dct(cls_dct):
|
|
34
|
+
raise TypeError(cls_dct)
|
|
35
|
+
return cls_dct
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ClassDctFn:
|
|
39
|
+
|
|
40
|
+
def __init__(self, fn: ta.Callable, offset: ta.Optional[int] = None, *, wrap=True) -> None:
|
|
41
|
+
super().__init__()
|
|
42
|
+
|
|
43
|
+
self._fn = fn
|
|
44
|
+
self._offset = offset if offset is not None else 1
|
|
45
|
+
|
|
46
|
+
if wrap:
|
|
47
|
+
functools.update_wrapper(self, fn)
|
|
48
|
+
|
|
49
|
+
def __get__(self, instance, owner=None):
|
|
50
|
+
return type(self)(self._fn.__get__(instance, owner), self._offset) # noqa
|
|
51
|
+
|
|
52
|
+
def __call__(self, *args, **kwargs):
|
|
53
|
+
try:
|
|
54
|
+
cls_dct = kwargs.pop('cls_dct')
|
|
55
|
+
except KeyError:
|
|
56
|
+
f = sys._getframe(self._offset) # noqa
|
|
57
|
+
cls_dct = _skip_cls_dct_frames(f).f_locals
|
|
58
|
+
if not is_possibly_cls_dct(cls_dct):
|
|
59
|
+
raise TypeError(cls_dct)
|
|
60
|
+
return self._fn(cls_dct, *args, **kwargs)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def cls_dct_fn(offset=1, *, wrap=True):
|
|
64
|
+
def outer(fn):
|
|
65
|
+
return ClassDctFn(fn, offset, wrap=wrap)
|
|
66
|
+
|
|
67
|
+
return outer
|
omlish/lang/cmp.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import typing as ta
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def cmp(l: ta.Any, r: ta.Any) -> int:
|
|
5
|
+
return int(l > r) - int(l < r)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class InfinityType:
|
|
9
|
+
def __repr__(self) -> str:
|
|
10
|
+
return 'Infinity'
|
|
11
|
+
|
|
12
|
+
def __hash__(self) -> int:
|
|
13
|
+
return hash(repr(self))
|
|
14
|
+
|
|
15
|
+
def __lt__(self, other: ta.Any) -> bool:
|
|
16
|
+
return False
|
|
17
|
+
|
|
18
|
+
def __le__(self, other: ta.Any) -> bool:
|
|
19
|
+
return False
|
|
20
|
+
|
|
21
|
+
def __eq__(self, other: ta.Any) -> bool:
|
|
22
|
+
return isinstance(other, self.__class__)
|
|
23
|
+
|
|
24
|
+
def __gt__(self, other: ta.Any) -> bool:
|
|
25
|
+
return True
|
|
26
|
+
|
|
27
|
+
def __ge__(self, other: ta.Any) -> bool:
|
|
28
|
+
return True
|
|
29
|
+
|
|
30
|
+
def __neg__(self: ta.Any) -> 'NegativeInfinityType':
|
|
31
|
+
return NegativeInfinity
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
Infinity = InfinityType()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class NegativeInfinityType:
|
|
38
|
+
def __repr__(self) -> str:
|
|
39
|
+
return '-Infinity'
|
|
40
|
+
|
|
41
|
+
def __hash__(self) -> int:
|
|
42
|
+
return hash(repr(self))
|
|
43
|
+
|
|
44
|
+
def __lt__(self, other: ta.Any) -> bool:
|
|
45
|
+
return True
|
|
46
|
+
|
|
47
|
+
def __le__(self, other: ta.Any) -> bool:
|
|
48
|
+
return True
|
|
49
|
+
|
|
50
|
+
def __eq__(self, other: ta.Any) -> bool:
|
|
51
|
+
return isinstance(other, self.__class__)
|
|
52
|
+
|
|
53
|
+
def __gt__(self, other: ta.Any) -> bool:
|
|
54
|
+
return False
|
|
55
|
+
|
|
56
|
+
def __ge__(self, other: ta.Any) -> bool:
|
|
57
|
+
return False
|
|
58
|
+
|
|
59
|
+
def __neg__(self: ta.Any) -> InfinityType:
|
|
60
|
+
return Infinity
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
NegativeInfinity = NegativeInfinityType()
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
import contextvars
|
|
3
|
+
import functools
|
|
4
|
+
import threading
|
|
5
|
+
import types
|
|
6
|
+
import typing as ta
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
T = ta.TypeVar('T')
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
##
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ContextManaged:
|
|
16
|
+
|
|
17
|
+
def __enter__(self: ta.Self) -> ta.Self:
|
|
18
|
+
return self
|
|
19
|
+
|
|
20
|
+
def __exit__(
|
|
21
|
+
self,
|
|
22
|
+
exc_type: ta.Optional[ta.Type[Exception]],
|
|
23
|
+
exc_val: ta.Optional[Exception],
|
|
24
|
+
exc_tb: ta.Optional[types.TracebackType]
|
|
25
|
+
) -> ta.Optional[bool]:
|
|
26
|
+
return None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class NopContextManaged(ContextManaged):
|
|
30
|
+
|
|
31
|
+
def __init_subclass__(cls, **kwargs):
|
|
32
|
+
raise TypeError
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
NOP_CONTEXT_MANAGED = NopContextManaged()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class NopContextManager:
|
|
39
|
+
|
|
40
|
+
def __init_subclass__(cls, **kwargs):
|
|
41
|
+
raise TypeError
|
|
42
|
+
|
|
43
|
+
def __call__(self, *args, **kwargs):
|
|
44
|
+
return NOP_CONTEXT_MANAGED
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
NOP_CONTEXT_MANAGER = NopContextManager()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
##
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@contextlib.contextmanager
|
|
54
|
+
def defer(fn: ta.Callable) -> ta.Iterator[ta.Callable]:
|
|
55
|
+
try:
|
|
56
|
+
yield fn
|
|
57
|
+
finally:
|
|
58
|
+
fn()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@contextlib.asynccontextmanager
|
|
62
|
+
async def a_defer(fn: ta.Awaitable) -> ta.AsyncIterator[ta.Awaitable]:
|
|
63
|
+
try:
|
|
64
|
+
yield fn
|
|
65
|
+
finally:
|
|
66
|
+
await fn
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@contextlib.contextmanager
|
|
70
|
+
def maybe_managing(obj: T) -> ta.Iterator[T]:
|
|
71
|
+
if isinstance(obj, ta.ContextManager):
|
|
72
|
+
with obj:
|
|
73
|
+
yield ta.cast(T, obj)
|
|
74
|
+
else:
|
|
75
|
+
yield obj
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@contextlib.contextmanager
|
|
79
|
+
def disposing(obj: T, attr: str = 'dispose') -> ta.Iterator[T]:
|
|
80
|
+
try:
|
|
81
|
+
yield obj
|
|
82
|
+
finally:
|
|
83
|
+
getattr(obj, attr)()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@contextlib.contextmanager
|
|
87
|
+
def breakpoint_on_exception():
|
|
88
|
+
try:
|
|
89
|
+
yield
|
|
90
|
+
except Exception as e: # noqa
|
|
91
|
+
breakpoint()
|
|
92
|
+
raise
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@contextlib.contextmanager
|
|
96
|
+
def context_var_setting(var: contextvars.ContextVar[T], val: T) -> ta.Iterator[T]:
|
|
97
|
+
token = var.set(val)
|
|
98
|
+
try:
|
|
99
|
+
yield val
|
|
100
|
+
finally:
|
|
101
|
+
var.reset(token)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@contextlib.contextmanager
|
|
105
|
+
def attr_setting(obj, attr, val, *, default=None):
|
|
106
|
+
not_set = object()
|
|
107
|
+
orig = getattr(obj, attr, not_set)
|
|
108
|
+
try:
|
|
109
|
+
setattr(obj, attr, val)
|
|
110
|
+
if orig is not not_set:
|
|
111
|
+
yield orig
|
|
112
|
+
else:
|
|
113
|
+
yield default
|
|
114
|
+
finally:
|
|
115
|
+
if orig is not_set:
|
|
116
|
+
delattr(obj, attr)
|
|
117
|
+
else:
|
|
118
|
+
setattr(obj, attr, orig)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
##
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class ExitStacked:
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def _exit_stack(self) -> contextlib.ExitStack:
|
|
128
|
+
try:
|
|
129
|
+
return self.__exit_stack # type: ignore
|
|
130
|
+
except AttributeError:
|
|
131
|
+
es = self.__exit_stack = contextlib.ExitStack()
|
|
132
|
+
return es
|
|
133
|
+
|
|
134
|
+
def _enter_context(self, context_manager: ta.ContextManager[T]) -> T:
|
|
135
|
+
return self._exit_stack.enter_context(ta.cast(ta.ContextManager, context_manager))
|
|
136
|
+
|
|
137
|
+
def __enter__(self: ta.Self) -> ta.Self:
|
|
138
|
+
try:
|
|
139
|
+
superfn = super().__enter__ # type: ignore
|
|
140
|
+
except AttributeError:
|
|
141
|
+
ret = self
|
|
142
|
+
else:
|
|
143
|
+
ret = superfn()
|
|
144
|
+
self._exit_stack.__enter__()
|
|
145
|
+
return ret
|
|
146
|
+
|
|
147
|
+
def __exit__(
|
|
148
|
+
self,
|
|
149
|
+
exc_type: ta.Optional[ta.Type[Exception]],
|
|
150
|
+
exc_val: ta.Optional[Exception],
|
|
151
|
+
exc_tb: ta.Optional[types.TracebackType]
|
|
152
|
+
) -> ta.Optional[bool]:
|
|
153
|
+
self._exit_stack.__exit__(exc_type, exc_val, exc_tb)
|
|
154
|
+
try:
|
|
155
|
+
superfn = super().__exit__ # type: ignore
|
|
156
|
+
except AttributeError:
|
|
157
|
+
return None
|
|
158
|
+
else:
|
|
159
|
+
return superfn(exc_type, exc_val, exc_tb)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
##
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
ContextWrappable: ta.TypeAlias = ta.Union[ta.ContextManager, str, ta.Callable[..., ta.ContextManager]]
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class ContextWrapped:
|
|
169
|
+
|
|
170
|
+
def __init__(self, fn: ta.Callable, cm: ta.Union[str, ContextWrappable]) -> None:
|
|
171
|
+
super().__init__()
|
|
172
|
+
|
|
173
|
+
self._fn = fn
|
|
174
|
+
self._cm = cm
|
|
175
|
+
self._name: str | None = None
|
|
176
|
+
|
|
177
|
+
functools.update_wrapper(self, fn)
|
|
178
|
+
|
|
179
|
+
def __set_name__(self, owner, name):
|
|
180
|
+
if name is not None:
|
|
181
|
+
if self._name is not None:
|
|
182
|
+
if name != self._name:
|
|
183
|
+
raise NameError(name, self._name)
|
|
184
|
+
else:
|
|
185
|
+
self._name = name
|
|
186
|
+
|
|
187
|
+
def __get__(self, instance, owner=None):
|
|
188
|
+
if instance is None and owner is None:
|
|
189
|
+
return self
|
|
190
|
+
fn = self._fn.__get__(instance, owner) # noqa
|
|
191
|
+
cm: ta.Any = self._cm
|
|
192
|
+
if isinstance(self._cm, str):
|
|
193
|
+
if instance is not None:
|
|
194
|
+
cm = getattr(instance, cm)
|
|
195
|
+
elif owner is not None:
|
|
196
|
+
cm = getattr(owner, cm)
|
|
197
|
+
else:
|
|
198
|
+
raise TypeError(cm)
|
|
199
|
+
elif hasattr(cm, '__enter__'):
|
|
200
|
+
pass
|
|
201
|
+
elif callable(cm):
|
|
202
|
+
cm = cm.__get__(instance, owner) # noqa
|
|
203
|
+
else:
|
|
204
|
+
raise TypeError(cm)
|
|
205
|
+
ret = type(self)(fn, cm)
|
|
206
|
+
if self._name is not None:
|
|
207
|
+
try:
|
|
208
|
+
instance.__dict__[self._name] = ret
|
|
209
|
+
except TypeError:
|
|
210
|
+
pass
|
|
211
|
+
return ret
|
|
212
|
+
|
|
213
|
+
def __call__(self, *args, **kwargs):
|
|
214
|
+
if isinstance(self._cm, str):
|
|
215
|
+
raise TypeError(self._cm)
|
|
216
|
+
cm = self._cm
|
|
217
|
+
if not hasattr(cm, '__enter__') and callable(cm):
|
|
218
|
+
cm = cm(*args, **kwargs)
|
|
219
|
+
with cm: # type: ignore
|
|
220
|
+
return self._fn(*args, **kwargs)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def context_wrapped(cm): # ContextWrappable -> ta.Callable[[CallableT], CallableT]:
|
|
224
|
+
def inner(fn):
|
|
225
|
+
return ContextWrapped(fn, cm)
|
|
226
|
+
return inner
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
##
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
Lockable = ta.Callable[[], ta.ContextManager]
|
|
233
|
+
DefaultLockable = ta.Union[None, bool, Lockable, ta.ContextManager]
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def default_lock(value: DefaultLockable, default: DefaultLockable) -> Lockable:
|
|
237
|
+
if value is None:
|
|
238
|
+
value = default
|
|
239
|
+
if value is True:
|
|
240
|
+
lock = threading.RLock()
|
|
241
|
+
return lambda: lock
|
|
242
|
+
elif value is False or value is None:
|
|
243
|
+
return NOP_CONTEXT_MANAGER
|
|
244
|
+
elif callable(value):
|
|
245
|
+
return value
|
|
246
|
+
elif isinstance(value, ta.ContextManager):
|
|
247
|
+
return lambda: value
|
|
248
|
+
else:
|
|
249
|
+
raise TypeError(value)
|