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.

Files changed (187) hide show
  1. omlish/__about__.py +7 -0
  2. omlish/__init__.py +0 -0
  3. omlish/argparse.py +223 -0
  4. omlish/asyncs/__init__.py +17 -0
  5. omlish/asyncs/anyio.py +23 -0
  6. omlish/asyncs/asyncio.py +19 -0
  7. omlish/asyncs/asyncs.py +76 -0
  8. omlish/asyncs/futures.py +179 -0
  9. omlish/asyncs/trio.py +11 -0
  10. omlish/c3.py +173 -0
  11. omlish/cached.py +9 -0
  12. omlish/check.py +231 -0
  13. omlish/collections/__init__.py +63 -0
  14. omlish/collections/_abc.py +156 -0
  15. omlish/collections/_io_abc.py +78 -0
  16. omlish/collections/cache/__init__.py +11 -0
  17. omlish/collections/cache/descriptor.py +188 -0
  18. omlish/collections/cache/impl.py +485 -0
  19. omlish/collections/cache/types.py +37 -0
  20. omlish/collections/coerce.py +337 -0
  21. omlish/collections/frozen.py +148 -0
  22. omlish/collections/identity.py +106 -0
  23. omlish/collections/indexed.py +75 -0
  24. omlish/collections/mappings.py +127 -0
  25. omlish/collections/ordered.py +81 -0
  26. omlish/collections/persistent.py +36 -0
  27. omlish/collections/skiplist.py +193 -0
  28. omlish/collections/sorted.py +126 -0
  29. omlish/collections/treap.py +228 -0
  30. omlish/collections/treapmap.py +144 -0
  31. omlish/collections/unmodifiable.py +174 -0
  32. omlish/collections/utils.py +110 -0
  33. omlish/configs/__init__.py +0 -0
  34. omlish/configs/flattening.py +147 -0
  35. omlish/configs/props.py +64 -0
  36. omlish/dataclasses/__init__.py +83 -0
  37. omlish/dataclasses/impl/__init__.py +6 -0
  38. omlish/dataclasses/impl/api.py +260 -0
  39. omlish/dataclasses/impl/as_.py +76 -0
  40. omlish/dataclasses/impl/exceptions.py +2 -0
  41. omlish/dataclasses/impl/fields.py +148 -0
  42. omlish/dataclasses/impl/frozen.py +55 -0
  43. omlish/dataclasses/impl/hashing.py +85 -0
  44. omlish/dataclasses/impl/init.py +173 -0
  45. omlish/dataclasses/impl/internals.py +118 -0
  46. omlish/dataclasses/impl/main.py +150 -0
  47. omlish/dataclasses/impl/metaclass.py +126 -0
  48. omlish/dataclasses/impl/metadata.py +74 -0
  49. omlish/dataclasses/impl/order.py +47 -0
  50. omlish/dataclasses/impl/params.py +150 -0
  51. omlish/dataclasses/impl/processing.py +16 -0
  52. omlish/dataclasses/impl/reflect.py +173 -0
  53. omlish/dataclasses/impl/replace.py +40 -0
  54. omlish/dataclasses/impl/repr.py +34 -0
  55. omlish/dataclasses/impl/simple.py +92 -0
  56. omlish/dataclasses/impl/slots.py +80 -0
  57. omlish/dataclasses/impl/utils.py +167 -0
  58. omlish/defs.py +193 -0
  59. omlish/dispatch/__init__.py +3 -0
  60. omlish/dispatch/dispatch.py +137 -0
  61. omlish/dispatch/functions.py +52 -0
  62. omlish/dispatch/methods.py +162 -0
  63. omlish/docker.py +149 -0
  64. omlish/dynamic.py +220 -0
  65. omlish/graphs/__init__.py +0 -0
  66. omlish/graphs/dot/__init__.py +19 -0
  67. omlish/graphs/dot/items.py +162 -0
  68. omlish/graphs/dot/rendering.py +147 -0
  69. omlish/graphs/dot/utils.py +30 -0
  70. omlish/graphs/trees.py +249 -0
  71. omlish/http/__init__.py +0 -0
  72. omlish/http/consts.py +20 -0
  73. omlish/http/wsgi.py +34 -0
  74. omlish/inject/__init__.py +85 -0
  75. omlish/inject/binder.py +12 -0
  76. omlish/inject/bindings.py +49 -0
  77. omlish/inject/eagers.py +21 -0
  78. omlish/inject/elements.py +43 -0
  79. omlish/inject/exceptions.py +49 -0
  80. omlish/inject/impl/__init__.py +0 -0
  81. omlish/inject/impl/bindings.py +19 -0
  82. omlish/inject/impl/elements.py +154 -0
  83. omlish/inject/impl/injector.py +182 -0
  84. omlish/inject/impl/inspect.py +98 -0
  85. omlish/inject/impl/private.py +109 -0
  86. omlish/inject/impl/providers.py +132 -0
  87. omlish/inject/impl/scopes.py +198 -0
  88. omlish/inject/injector.py +40 -0
  89. omlish/inject/inspect.py +14 -0
  90. omlish/inject/keys.py +43 -0
  91. omlish/inject/managed.py +24 -0
  92. omlish/inject/overrides.py +18 -0
  93. omlish/inject/private.py +29 -0
  94. omlish/inject/providers.py +111 -0
  95. omlish/inject/proxy.py +48 -0
  96. omlish/inject/scopes.py +84 -0
  97. omlish/inject/types.py +21 -0
  98. omlish/iterators.py +184 -0
  99. omlish/json.py +194 -0
  100. omlish/lang/__init__.py +112 -0
  101. omlish/lang/cached.py +267 -0
  102. omlish/lang/classes/__init__.py +24 -0
  103. omlish/lang/classes/abstract.py +74 -0
  104. omlish/lang/classes/restrict.py +137 -0
  105. omlish/lang/classes/simple.py +120 -0
  106. omlish/lang/classes/test/__init__.py +0 -0
  107. omlish/lang/classes/test/test_abstract.py +89 -0
  108. omlish/lang/classes/test/test_restrict.py +71 -0
  109. omlish/lang/classes/test/test_simple.py +58 -0
  110. omlish/lang/classes/test/test_virtual.py +72 -0
  111. omlish/lang/classes/virtual.py +130 -0
  112. omlish/lang/clsdct.py +67 -0
  113. omlish/lang/cmp.py +63 -0
  114. omlish/lang/contextmanagers.py +249 -0
  115. omlish/lang/datetimes.py +67 -0
  116. omlish/lang/descriptors.py +52 -0
  117. omlish/lang/functions.py +126 -0
  118. omlish/lang/imports.py +153 -0
  119. omlish/lang/iterables.py +54 -0
  120. omlish/lang/maybes.py +136 -0
  121. omlish/lang/objects.py +103 -0
  122. omlish/lang/resolving.py +50 -0
  123. omlish/lang/strings.py +128 -0
  124. omlish/lang/typing.py +92 -0
  125. omlish/libc.py +532 -0
  126. omlish/logs/__init__.py +9 -0
  127. omlish/logs/_abc.py +247 -0
  128. omlish/logs/configs.py +62 -0
  129. omlish/logs/filters.py +9 -0
  130. omlish/logs/formatters.py +67 -0
  131. omlish/logs/utils.py +20 -0
  132. omlish/marshal/__init__.py +52 -0
  133. omlish/marshal/any.py +25 -0
  134. omlish/marshal/base.py +201 -0
  135. omlish/marshal/base64.py +25 -0
  136. omlish/marshal/dataclasses.py +115 -0
  137. omlish/marshal/datetimes.py +90 -0
  138. omlish/marshal/enums.py +43 -0
  139. omlish/marshal/exceptions.py +7 -0
  140. omlish/marshal/factories.py +129 -0
  141. omlish/marshal/global_.py +33 -0
  142. omlish/marshal/iterables.py +57 -0
  143. omlish/marshal/mappings.py +66 -0
  144. omlish/marshal/naming.py +17 -0
  145. omlish/marshal/objects.py +106 -0
  146. omlish/marshal/optionals.py +49 -0
  147. omlish/marshal/polymorphism.py +147 -0
  148. omlish/marshal/primitives.py +43 -0
  149. omlish/marshal/registries.py +57 -0
  150. omlish/marshal/standard.py +80 -0
  151. omlish/marshal/utils.py +23 -0
  152. omlish/marshal/uuids.py +29 -0
  153. omlish/marshal/values.py +30 -0
  154. omlish/math.py +184 -0
  155. omlish/os.py +32 -0
  156. omlish/reflect.py +359 -0
  157. omlish/replserver/__init__.py +5 -0
  158. omlish/replserver/__main__.py +4 -0
  159. omlish/replserver/console.py +247 -0
  160. omlish/replserver/server.py +146 -0
  161. omlish/runmodule.py +28 -0
  162. omlish/stats.py +342 -0
  163. omlish/term.py +222 -0
  164. omlish/testing/__init__.py +7 -0
  165. omlish/testing/pydevd.py +225 -0
  166. omlish/testing/pytest/__init__.py +8 -0
  167. omlish/testing/pytest/helpers.py +35 -0
  168. omlish/testing/pytest/inject/__init__.py +1 -0
  169. omlish/testing/pytest/inject/harness.py +159 -0
  170. omlish/testing/pytest/plugins/__init__.py +20 -0
  171. omlish/testing/pytest/plugins/_registry.py +6 -0
  172. omlish/testing/pytest/plugins/logging.py +13 -0
  173. omlish/testing/pytest/plugins/pycharm.py +54 -0
  174. omlish/testing/pytest/plugins/repeat.py +19 -0
  175. omlish/testing/pytest/plugins/skips.py +32 -0
  176. omlish/testing/pytest/plugins/spacing.py +19 -0
  177. omlish/testing/pytest/plugins/switches.py +70 -0
  178. omlish/testing/testing.py +102 -0
  179. omlish/text/__init__.py +0 -0
  180. omlish/text/delimit.py +171 -0
  181. omlish/text/indent.py +50 -0
  182. omlish/text/parts.py +265 -0
  183. omlish-0.0.0.dev1.dist-info/LICENSE +21 -0
  184. omlish-0.0.0.dev1.dist-info/METADATA +17 -0
  185. omlish-0.0.0.dev1.dist-info/RECORD +187 -0
  186. omlish-0.0.0.dev1.dist-info/WHEEL +5 -0
  187. 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)