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
omlish/lang/cached.py ADDED
@@ -0,0 +1,267 @@
1
+ """
2
+ TODO:
3
+ - integrate / expose with collections.cache
4
+ - weakrefs (selectable by arg)
5
+ - locks
6
+ """
7
+ import dataclasses as dc
8
+ import functools
9
+ import inspect
10
+ import typing as ta
11
+
12
+ from .contextmanagers import DefaultLockable
13
+ from .contextmanagers import default_lock
14
+ from .functions import unwrap_func
15
+ from .functions import unwrap_func_with_partials
16
+
17
+
18
+ P = ta.ParamSpec('P')
19
+ T = ta.TypeVar('T')
20
+
21
+ _IGNORE = object()
22
+
23
+
24
+ def _nullary_cache_keyer():
25
+ return ()
26
+
27
+
28
+ def _simple_cache_keyer(*args, **kwargs):
29
+ return (args, tuple(sorted(kwargs.items())))
30
+
31
+
32
+ def _make_cache_keyer(fn, *, simple=False, bound=False):
33
+ if simple:
34
+ return _simple_cache_keyer
35
+
36
+ fn, partials = unwrap_func_with_partials(fn)
37
+
38
+ if inspect.isgeneratorfunction(fn) or inspect.iscoroutinefunction(fn):
39
+ raise TypeError(fn)
40
+
41
+ sig = inspect.signature(fn)
42
+ sig_params = list(sig.parameters.values())[1 if bound else 0:]
43
+ if not sig_params:
44
+ return _nullary_cache_keyer
45
+
46
+ ns = {}
47
+ src_params = []
48
+ src_vals = []
49
+ kwargs_name = None
50
+ render_pos_only_separator = False
51
+ render_kw_only_separator = True
52
+ for p in sig_params:
53
+ formatted = p.name
54
+ if p.default is not inspect.Parameter.empty:
55
+ ns[p.name] = p.default
56
+ formatted = f'{formatted}={formatted}'
57
+ kind = p.kind
58
+ if kind == inspect.Parameter.VAR_POSITIONAL:
59
+ formatted = '*' + formatted
60
+ elif kind == inspect.Parameter.VAR_KEYWORD:
61
+ formatted = '**' + formatted
62
+ if kind == inspect.Parameter.POSITIONAL_ONLY:
63
+ render_pos_only_separator = True
64
+ elif render_pos_only_separator:
65
+ src_params.append('/')
66
+ render_pos_only_separator = False
67
+ if kind == inspect.Parameter.VAR_POSITIONAL:
68
+ render_kw_only_separator = False
69
+ elif kind == inspect.Parameter.KEYWORD_ONLY and render_kw_only_separator:
70
+ src_params.append('*')
71
+ render_kw_only_separator = False
72
+ src_params.append(formatted)
73
+ if kind == inspect.Parameter.VAR_KEYWORD:
74
+ kwargs_name = p.name
75
+ else:
76
+ src_vals.append(p.name)
77
+ if render_pos_only_separator:
78
+ src_params.append('/')
79
+
80
+ kwa = f', __builtins__.tuple(__builtins__.sorted({kwargs_name}.items()))' if kwargs_name else ''
81
+ rendered = (
82
+ f'def __func__({", ".join(src_params)}):\n'
83
+ f' return ({", ".join(src_vals)}{kwa})\n'
84
+ )
85
+ exec(rendered, ns)
86
+
87
+ kfn = ns['__func__']
88
+ for part in partials[::-1]:
89
+ kfn = functools.partial(kfn, *part.args, **part.keywords)
90
+
91
+ return kfn
92
+
93
+
94
+ class _CachedFunction(ta.Generic[T]):
95
+ @dc.dataclass(frozen=True)
96
+ class Opts:
97
+ map_maker: ta.Callable[[], ta.MutableMapping] = dict
98
+ simple_key: bool = False
99
+ lock: DefaultLockable = None
100
+
101
+ def __init__(
102
+ self,
103
+ fn: ta.Callable[P, T],
104
+ *,
105
+ opts: Opts = Opts(),
106
+ keyer: ta.Callable[..., tuple] | None = None,
107
+ values: ta.MutableMapping | None = None,
108
+ value_fn: ta.Optional[ta.Callable[P, T]] = None,
109
+ ) -> None:
110
+ super().__init__()
111
+
112
+ self._fn = fn
113
+ self._opts = opts
114
+ self._keyer = keyer if keyer is not None else _make_cache_keyer(fn, simple=opts.simple_key)
115
+
116
+ self._lock = default_lock(opts.lock, False)() if opts.lock is not None else None
117
+ self._values = values if values is not None else opts.map_maker()
118
+ self._value_fn = value_fn if value_fn is not None else fn
119
+ functools.update_wrapper(self, fn)
120
+
121
+ def reset(self) -> None:
122
+ self._values = {}
123
+
124
+ def __bool__(self) -> bool:
125
+ raise TypeError
126
+
127
+ def __call__(self, *args, **kwargs) -> T:
128
+ k = self._keyer(*args, **kwargs)
129
+
130
+ try:
131
+ return self._values[k]
132
+ except KeyError:
133
+ pass
134
+
135
+ if self._lock is not None:
136
+ with self._lock:
137
+ try:
138
+ return self._values[k]
139
+ except KeyError:
140
+ pass
141
+
142
+ value = self._value_fn(*args, **kwargs)
143
+
144
+ else:
145
+ value = self._value_fn(*args, **kwargs)
146
+
147
+ self._values[k] = value
148
+ return value
149
+
150
+
151
+ class _CachedFunctionDescriptor(_CachedFunction[T]):
152
+
153
+ def __init__(
154
+ self,
155
+ fn: ta.Callable[P, T],
156
+ scope: ta.Any,
157
+ *,
158
+ instance: ta.Any = None,
159
+ owner: ta.Any = None,
160
+ name: ta.Optional[str] = None,
161
+ **kwargs
162
+ ) -> None:
163
+ super().__init__(fn, **kwargs)
164
+
165
+ self._scope = scope
166
+ self._instance = instance
167
+ self._owner = owner
168
+ self._name = name if name is not None else unwrap_func(fn).__name__
169
+ self._bound_keyer = None
170
+
171
+ def __get__(self, instance, owner=None):
172
+ scope = self._scope
173
+ if owner is self._owner and (instance is self._instance or scope is classmethod):
174
+ return self
175
+
176
+ fn = self._fn
177
+ name = self._name
178
+ bound_fn = fn.__get__(instance, owner)
179
+ if self._bound_keyer is None:
180
+ self._bound_keyer = _make_cache_keyer(fn, simple=self._opts.simple_key, bound=True)
181
+
182
+ bound = self.__class__(
183
+ fn,
184
+ scope,
185
+ opts=self._opts,
186
+ instance=instance,
187
+ owner=owner,
188
+ name=name,
189
+ keyer=self._bound_keyer,
190
+ # values=None if scope is classmethod else self._values,
191
+ value_fn=bound_fn,
192
+ )
193
+
194
+ if scope is classmethod and owner is not None:
195
+ setattr(owner, name, bound)
196
+ elif instance is not None:
197
+ instance.__dict__[name] = bound
198
+
199
+ return bound
200
+
201
+
202
+ def cached_function(fn=None, **kwargs):
203
+ if fn is None:
204
+ return functools.partial(cached_function, **kwargs)
205
+ opts = _CachedFunction.Opts(**kwargs)
206
+ if isinstance(fn, staticmethod):
207
+ return _CachedFunction(fn, opts=opts, value_fn=unwrap_func(fn))
208
+ scope = classmethod if isinstance(fn, classmethod) else None
209
+ return _CachedFunctionDescriptor(fn, scope, opts=opts)
210
+
211
+
212
+ cached_function = cached_function
213
+
214
+
215
+ ##
216
+
217
+
218
+ class _CachedProperty:
219
+ def __init__(
220
+ self,
221
+ fn,
222
+ *,
223
+ name=None,
224
+ ignore_if=lambda _: False,
225
+ clear_on_init=False,
226
+ ):
227
+ super().__init__()
228
+ if isinstance(fn, property):
229
+ fn = fn.fget
230
+ self._fn = fn
231
+ self._ignore_if = ignore_if
232
+ self._name = name
233
+ self._clear_on_init = clear_on_init
234
+
235
+ def __set_name__(self, owner, name):
236
+ if self._name is None:
237
+ self._name = name
238
+
239
+ def __get__(self, instance, owner=None):
240
+ if instance is None:
241
+ return self
242
+ if self._name is None:
243
+ raise TypeError(self)
244
+
245
+ try:
246
+ return instance.__dict__[self._name]
247
+ except KeyError:
248
+ pass
249
+
250
+ value = self._fn.__get__(instance, owner)()
251
+ if value is _IGNORE:
252
+ return None
253
+ instance.__dict__[self._name] = value
254
+ return value
255
+
256
+ def __set__(self, instance, value):
257
+ if self._ignore_if(value):
258
+ return
259
+ if instance.__dict__[self._name] == value:
260
+ return
261
+ raise TypeError(self._name)
262
+
263
+
264
+ def cached_property(fn=None, **kwargs):
265
+ if fn is None:
266
+ return functools.partial(cached_property, **kwargs)
267
+ return _CachedProperty(fn, **kwargs)
@@ -0,0 +1,24 @@
1
+ from .abstract import Abstract # noqa
2
+ from .abstract import is_abstract # noqa
3
+ from .abstract import is_abstract_class # noqa
4
+ from .abstract import is_abstract_method # noqa
5
+ from .abstract import make_abstract # noqa
6
+ from .restrict import Final # noqa
7
+ from .restrict import FinalException # noqa
8
+ from .restrict import NoBool # noqa
9
+ from .restrict import NotInstantiable # noqa
10
+ from .restrict import NotPicklable # noqa
11
+ from .restrict import PackageSealed # noqa
12
+ from .restrict import Sealed # noqa
13
+ from .restrict import SealedException # noqa
14
+ from .restrict import no_bool # noqa
15
+ from .simple import LazySingleton # noqa
16
+ from .simple import Marker # noqa
17
+ from .simple import Namespace # noqa
18
+ from .simple import SimpleMetaDict # noqa
19
+ from .simple import Singleton # noqa
20
+ from .virtual import Callable # noqa
21
+ from .virtual import Descriptor # noqa
22
+ from .virtual import Picklable # noqa
23
+ from .virtual import Virtual # noqa
24
+ from .virtual import virtual_check # noqa
@@ -0,0 +1,74 @@
1
+ import abc
2
+ import typing as ta
3
+
4
+
5
+ T = ta.TypeVar('T')
6
+
7
+
8
+ _DISABLE_CHECKS = False
9
+
10
+
11
+ def make_abstract(obj: T) -> T:
12
+ if callable(obj):
13
+ return ta.cast(T, abc.abstractmethod(obj))
14
+ elif isinstance(obj, property):
15
+ return ta.cast(T, property(
16
+ abc.abstractmethod(obj.fget) if obj.fget is not None else None,
17
+ abc.abstractmethod(obj.fset) if obj.fset is not None else None,
18
+ abc.abstractmethod(obj.fdel) if obj.fdel is not None else None,
19
+ ))
20
+ elif isinstance(obj, (classmethod, staticmethod)):
21
+ return ta.cast(T, type(obj)(abc.abstractmethod(obj.__func__)))
22
+ else:
23
+ return obj
24
+
25
+
26
+ class Abstract(abc.ABC):
27
+ __slots__ = ()
28
+
29
+ def __forceabstract__(self):
30
+ raise TypeError
31
+
32
+ setattr(__forceabstract__, '__isabstractmethod__', True)
33
+
34
+ def __init_subclass__(cls, **kwargs) -> None:
35
+ if Abstract in cls.__bases__:
36
+ cls.__forceabstract__ = Abstract.__forceabstract__ # type: ignore
37
+ else:
38
+ cls.__forceabstract__ = False # type: ignore
39
+
40
+ super().__init_subclass__(**kwargs)
41
+
42
+ if not _DISABLE_CHECKS and Abstract not in cls.__bases__:
43
+ ams = {a for a, o in cls.__dict__.items() if is_abstract_method(o)}
44
+ seen = set(cls.__dict__)
45
+ for b in cls.__bases__:
46
+ ams.update(set(getattr(b, '__abstractmethods__', [])) - seen)
47
+ seen.update(dir(b))
48
+ if ams:
49
+ raise TypeError(
50
+ f'Cannot subclass abstract class {cls.__name__} with abstract methods'
51
+ f'{", ".join(map(str, sorted(ams)))}'
52
+ )
53
+
54
+
55
+ def is_abstract_method(obj: ta.Any) -> bool:
56
+ return bool(getattr(obj, '__isabstractmethod__', False))
57
+
58
+
59
+ def is_abstract_class(obj: ta.Any) -> bool:
60
+ if bool(getattr(obj, '__abstractmethods__', [])):
61
+ return True
62
+ if isinstance(obj, type):
63
+ if Abstract in obj.__bases__:
64
+ return True
65
+ if (
66
+ Abstract in obj.__mro__
67
+ and getattr(obj.__dict__.get('__forceabstract__', None), '__isabstractmethod__', False)
68
+ ):
69
+ return True
70
+ return False
71
+
72
+
73
+ def is_abstract(obj: ta.Any) -> bool:
74
+ return is_abstract_method(obj) or is_abstract_class(obj)
@@ -0,0 +1,137 @@
1
+ import functools
2
+ import typing as ta
3
+
4
+ from .abstract import Abstract
5
+ from .abstract import is_abstract
6
+
7
+
8
+ ##
9
+
10
+
11
+ class FinalException(TypeError):
12
+
13
+ def __init__(self, _type: ta.Type) -> None:
14
+ super().__init__()
15
+
16
+ self._type = _type
17
+
18
+ def __repr__(self) -> str:
19
+ return f'{type(self).__name__}({self._type})'
20
+
21
+
22
+ class Final(Abstract):
23
+ __slots__ = ()
24
+
25
+ def __init_subclass__(cls, **kwargs) -> None:
26
+ super().__init_subclass__(**kwargs)
27
+
28
+ abstracts: set[ta.Any] = set()
29
+ for base in cls.__bases__:
30
+ if base is Abstract:
31
+ raise FinalException(base)
32
+ elif base is Final:
33
+ continue
34
+ elif Final in base.__mro__:
35
+ raise FinalException(base)
36
+ else:
37
+ abstracts.update(getattr(base, '__abstractmethods__', []))
38
+
39
+ for a in abstracts:
40
+ try:
41
+ v = cls.__dict__[a]
42
+ except KeyError:
43
+ raise FinalException(a)
44
+ if is_abstract(v):
45
+ raise FinalException(a)
46
+
47
+
48
+ ##
49
+
50
+
51
+ class SealedException(TypeError):
52
+
53
+ def __init__(self, _type) -> None:
54
+ super().__init__()
55
+
56
+ self._type = _type
57
+
58
+ def __repr__(self) -> str:
59
+ return f'{type(self).__name__}({self._type})'
60
+
61
+
62
+ class Sealed:
63
+ __slots__ = ()
64
+
65
+ def __init_subclass__(cls, **kwargs) -> None:
66
+ for base in cls.__bases__:
67
+ if base is not Abstract:
68
+ if Sealed in base.__bases__:
69
+ if cls.__module__ != base.__module__:
70
+ raise SealedException(base)
71
+ super().__init_subclass__(**kwargs)
72
+
73
+
74
+ class PackageSealed:
75
+ __slots__ = ()
76
+
77
+ def __init_subclass__(cls, **kwargs) -> None:
78
+ for base in cls.__bases__:
79
+ if base is not Abstract:
80
+ if PackageSealed in base.__bases__:
81
+ if cls.__module__.split('.')[:-1] != base.__module__.split('.')[:-1]:
82
+ raise SealedException(base)
83
+ super().__init_subclass__(**kwargs)
84
+
85
+
86
+ ##
87
+
88
+
89
+ class NotInstantiable(Abstract):
90
+ __slots__ = ()
91
+
92
+ def __new__(cls, *args, **kwargs) -> ta.NoReturn:
93
+ raise TypeError
94
+
95
+
96
+ class NotPicklable:
97
+ __slots__ = ()
98
+
99
+ def __getstate__(self) -> ta.NoReturn:
100
+ raise TypeError
101
+
102
+ def __setstate__(self, state) -> ta.NoReturn:
103
+ raise TypeError
104
+
105
+
106
+ ##
107
+
108
+
109
+ class NoBool:
110
+
111
+ def __bool__(self) -> bool:
112
+ raise TypeError
113
+
114
+
115
+ class _NoBoolDescriptor:
116
+
117
+ def __init__(self, fn, instance=None, owner=None) -> None:
118
+ super().__init__()
119
+ self._fn = fn
120
+ self._instance = instance
121
+ self._owner = owner
122
+ functools.update_wrapper(self, fn)
123
+
124
+ def __bool__(self) -> bool:
125
+ raise TypeError
126
+
127
+ def __get__(self, instance, owner=None):
128
+ if instance is self._instance and owner is self._owner:
129
+ return self
130
+ return _NoBoolDescriptor(self._fn.__get__(instance, owner), instance, owner)
131
+
132
+ def __call__(self, *args, **kwargs):
133
+ return self._fn(*args, **kwargs)
134
+
135
+
136
+ def no_bool(fn):
137
+ return _NoBoolDescriptor(fn)
@@ -0,0 +1,120 @@
1
+ import abc
2
+ import functools
3
+ import threading
4
+ import typing as ta
5
+
6
+ from .restrict import Final
7
+ from .restrict import NotInstantiable
8
+
9
+
10
+ K = ta.TypeVar('K')
11
+ V = ta.TypeVar('V')
12
+
13
+
14
+ ##
15
+
16
+
17
+ class _Namespace(Final):
18
+
19
+ def __mro_entries__(self, bases, **kwargs):
20
+ return (Final, NotInstantiable)
21
+
22
+
23
+ Namespace: type = _Namespace() # type: ignore
24
+
25
+
26
+ ##
27
+
28
+
29
+ _MARKER_NAMESPACE_KEYS: ta.Optional[set[str]] = None
30
+
31
+
32
+ class _MarkerMeta(abc.ABCMeta):
33
+
34
+ def __new__(mcls, name, bases, namespace):
35
+ global _MARKER_NAMESPACE_KEYS
36
+ if _MARKER_NAMESPACE_KEYS is None:
37
+ if not (namespace.get('__module__') == __name__ and name == 'Marker'):
38
+ raise RuntimeError
39
+ _MARKER_NAMESPACE_KEYS = set(namespace)
40
+ else:
41
+ if set(namespace) - _MARKER_NAMESPACE_KEYS:
42
+ raise TypeError('Markers must not include contents. Did you mean to use Namespace?')
43
+ if Final not in bases:
44
+ bases += (Final,)
45
+ return super().__new__(mcls, name, bases, namespace)
46
+
47
+ def __instancecheck__(self, instance):
48
+ return instance is self
49
+
50
+ def __repr__(cls) -> str:
51
+ return f'<{cls.__name__}>'
52
+
53
+
54
+ class Marker(NotInstantiable, metaclass=_MarkerMeta):
55
+ """A marker."""
56
+
57
+ __slots__ = ()
58
+
59
+
60
+ ##
61
+
62
+
63
+ class SimpleMetaDict(dict):
64
+
65
+ def update(self, m: ta.Mapping[K, V], **kwargs: V) -> None: # type: ignore
66
+ for k, v in m.items():
67
+ self[k] = v
68
+ for k, v in kwargs.items(): # type: ignore
69
+ self[k] = v
70
+
71
+
72
+ ##
73
+
74
+
75
+ _SINGLETON_INSTANCE_ATTR = '__singleton_instance__'
76
+ _SINGLETON_LOCK = threading.RLock()
77
+
78
+
79
+ def _set_singleton_instance(inst):
80
+ cls = type(inst)
81
+ if _SINGLETON_INSTANCE_ATTR in cls.__dict__:
82
+ raise TypeError(cls)
83
+
84
+ inst.__init__()
85
+ old_init = cls.__init__
86
+
87
+ @functools.wraps(old_init)
88
+ def new_init(self):
89
+ if type(self) is not cls:
90
+ old_init(self) # noqa
91
+
92
+ setattr(cls, '__init__', new_init)
93
+ setattr(cls, _SINGLETON_INSTANCE_ATTR, inst)
94
+
95
+ return inst
96
+
97
+
98
+ class Singleton:
99
+
100
+ def __new__(cls):
101
+ return cls.__dict__[_SINGLETON_INSTANCE_ATTR]
102
+
103
+ def __init_subclass__(cls, **kwargs):
104
+ super().__init_subclass__(**kwargs)
105
+ _set_singleton_instance(super().__new__(cls)) # noqa
106
+
107
+
108
+ class LazySingleton:
109
+
110
+ def __new__(cls):
111
+ try:
112
+ return cls.__dict__[_SINGLETON_INSTANCE_ATTR]
113
+ except KeyError:
114
+ pass
115
+ with _SINGLETON_LOCK:
116
+ try:
117
+ return cls.__dict__[_SINGLETON_INSTANCE_ATTR]
118
+ except KeyError:
119
+ pass
120
+ return _set_singleton_instance(super().__new__(cls))
File without changes
@@ -0,0 +1,89 @@
1
+ import abc
2
+
3
+ import pytest
4
+
5
+ from ..abstract import Abstract
6
+ from ..abstract import is_abstract
7
+
8
+
9
+ def test_abstract():
10
+ class C(Abstract):
11
+ pass
12
+
13
+ class D(C):
14
+ pass
15
+
16
+ class E(D, Abstract):
17
+ pass
18
+
19
+ class F(E):
20
+ pass
21
+
22
+ with pytest.raises(TypeError):
23
+ C()
24
+ D()
25
+ with pytest.raises(TypeError):
26
+ E()
27
+ F()
28
+
29
+
30
+ def test_abstract2():
31
+ class O(Abstract):
32
+ pass
33
+
34
+ assert is_abstract(O)
35
+
36
+ with pytest.raises(TypeError):
37
+ O()
38
+
39
+ class A(Abstract):
40
+ @abc.abstractmethod
41
+ def f(self):
42
+ pass
43
+
44
+ assert is_abstract(A)
45
+
46
+ with pytest.raises(TypeError):
47
+ A() # type: ignore
48
+
49
+ class B(A, Abstract):
50
+ pass
51
+
52
+ with pytest.raises(TypeError):
53
+ B() # type: ignore
54
+
55
+ class C(B):
56
+ f = 0
57
+
58
+ assert C()
59
+
60
+ with pytest.raises(TypeError):
61
+ class D(A):
62
+ pass
63
+
64
+
65
+ def test_is_abstract():
66
+ class A(Abstract):
67
+ pass
68
+
69
+ assert is_abstract(A)
70
+
71
+ class B(A):
72
+ pass
73
+
74
+ assert not is_abstract(B)
75
+
76
+ class C(Abstract):
77
+
78
+ def __init_subclass__(cls, **kwargs):
79
+ super().__init_subclass__(**kwargs)
80
+ if cls.__name__ == 'D':
81
+ assert is_abstract(cls)
82
+ else:
83
+ assert not is_abstract(cls)
84
+
85
+ class D(C, Abstract):
86
+ pass
87
+
88
+ class E(D):
89
+ pass