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
omlish/lang/datetimes.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import re
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def to_seconds(value: datetime.timedelta) -> float:
|
|
6
|
+
return 86400 * value.days + value.seconds + 0.000001 * value.microseconds
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def months_ago(date: datetime.date, num: int) -> datetime.date:
|
|
10
|
+
ago_year = date.year
|
|
11
|
+
ago_month = date.month - num
|
|
12
|
+
while ago_month < 1:
|
|
13
|
+
ago_year -= 1
|
|
14
|
+
ago_month += 12
|
|
15
|
+
while ago_month > 12:
|
|
16
|
+
ago_year += 1
|
|
17
|
+
ago_month -= 12
|
|
18
|
+
return datetime.date(ago_year, ago_month, 1)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def parse_date(s: str) -> datetime.date:
|
|
22
|
+
if s.lower() in ['today', 'now']:
|
|
23
|
+
return datetime.date.today()
|
|
24
|
+
elif s.lower() == 'yesterday':
|
|
25
|
+
return datetime.date.today() - datetime.timedelta(days=1)
|
|
26
|
+
elif s.lower().endswith(' days ago'):
|
|
27
|
+
num = int(s.split(' ', 1)[0])
|
|
28
|
+
return datetime.date.today() - datetime.timedelta(days=num)
|
|
29
|
+
elif s.lower().endswith(' months ago'):
|
|
30
|
+
months = int(s.split(' ', 1)[0])
|
|
31
|
+
return months_ago(datetime.date.today(), months)
|
|
32
|
+
else:
|
|
33
|
+
return datetime.date(*map(int, s.split('-', 3)))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
_TIMEDELTA_STR_RE = re.compile(
|
|
37
|
+
r'^\s*'
|
|
38
|
+
r'((?P<days>-?\d+)\s*days?,\s*)?'
|
|
39
|
+
r'(?P<hours>\d?\d):(?P<minutes>\d\d)'
|
|
40
|
+
r':(?P<seconds>\d\d+(\.\d+)?)'
|
|
41
|
+
r'\s*$')
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
_TIMEDELTA_DHMS_RE = re.compile(
|
|
45
|
+
r'^\s*'
|
|
46
|
+
r'(?P<negative>-)?'
|
|
47
|
+
r'((?P<days>\d+(\.\d+)?)\s*(d|days?))?'
|
|
48
|
+
r',?\s*((?P<hours>\d+(\.\d+)?)\s*(h|hours?))?'
|
|
49
|
+
r',?\s*((?P<minutes>\d+(\.\d+)?)\s*(m|minutes?))?'
|
|
50
|
+
r',?\s*((?P<seconds>\d+(\.\d+)?)\s*(s|secs?|seconds?))?'
|
|
51
|
+
r'\s*$')
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def parse_timedelta(s: str) -> datetime.timedelta:
|
|
55
|
+
match = _TIMEDELTA_DHMS_RE.match(s)
|
|
56
|
+
if not match:
|
|
57
|
+
match = _TIMEDELTA_STR_RE.match(s)
|
|
58
|
+
if not match:
|
|
59
|
+
raise ValueError
|
|
60
|
+
timedelta_kwargs = {
|
|
61
|
+
k: float(v)
|
|
62
|
+
for k, v in match.groupdict().items()
|
|
63
|
+
if k != 'negative' and v is not None}
|
|
64
|
+
if not timedelta_kwargs:
|
|
65
|
+
raise ValueError()
|
|
66
|
+
sign = -1 if match.groupdict().get('negative') else 1
|
|
67
|
+
return sign * datetime.timedelta(**timedelta_kwargs)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import typing as ta
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
##
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
BUILTIN_METHOD_DESCRIPTORS = (classmethod, staticmethod)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class _MethodDescriptor:
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def is_method_descriptor(obj: ta.Any) -> bool:
|
|
15
|
+
return isinstance(obj, (*BUILTIN_METHOD_DESCRIPTORS, _MethodDescriptor))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def unwrap_method_descriptors(fn: ta.Callable) -> ta.Callable:
|
|
19
|
+
while is_method_descriptor(fn):
|
|
20
|
+
fn = fn.__func__ # type: ignore # noqa
|
|
21
|
+
return fn
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
##
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class AccessForbiddenException(Exception):
|
|
28
|
+
|
|
29
|
+
def __init__(self, name: ta.Optional[str] = None, *args: ta.Any, **kwargs: ta.Any) -> None:
|
|
30
|
+
super().__init__(*((name,) if name is not None else ()), *args, **kwargs) # noqa
|
|
31
|
+
self.name = name
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class AccessForbiddenDescriptor:
|
|
35
|
+
|
|
36
|
+
def __init__(self, name: ta.Optional[str] = None) -> None:
|
|
37
|
+
super().__init__()
|
|
38
|
+
|
|
39
|
+
self._name = name
|
|
40
|
+
|
|
41
|
+
def __set_name__(self, owner: ta.Type, name: str) -> None:
|
|
42
|
+
if self._name is None:
|
|
43
|
+
self._name = name
|
|
44
|
+
elif name != self._name:
|
|
45
|
+
raise NameError(name)
|
|
46
|
+
|
|
47
|
+
def __get__(self, instance, owner=None):
|
|
48
|
+
raise AccessForbiddenException(self._name)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def access_forbidden():
|
|
52
|
+
return AccessForbiddenDescriptor()
|
omlish/lang/functions.py
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import time
|
|
3
|
+
import typing as ta
|
|
4
|
+
|
|
5
|
+
from .descriptors import is_method_descriptor
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
T = ta.TypeVar('T')
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def is_lambda(f: ta.Any) -> bool:
|
|
12
|
+
l = lambda: 0
|
|
13
|
+
return isinstance(f, type(l)) and f.__name__ == l.__name__
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def maybe_call(obj: ta.Any, att: str, *args, default: ta.Any = None, **kwargs) -> ta.Any:
|
|
17
|
+
try:
|
|
18
|
+
fn = getattr(obj, att)
|
|
19
|
+
except AttributeError:
|
|
20
|
+
return default
|
|
21
|
+
else:
|
|
22
|
+
return fn(*args, **kwargs)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def unwrap_func(fn: ta.Callable) -> ta.Callable:
|
|
26
|
+
fn, _ = unwrap_func_with_partials(fn)
|
|
27
|
+
return fn
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def unwrap_func_with_partials(fn: ta.Callable) -> tuple[ta.Callable, list[functools.partial]]:
|
|
31
|
+
ps = []
|
|
32
|
+
while True:
|
|
33
|
+
if is_method_descriptor(fn):
|
|
34
|
+
fn = fn.__func__ # type: ignore
|
|
35
|
+
elif isinstance(fn, functools.partial):
|
|
36
|
+
ps.append(fn)
|
|
37
|
+
fn = fn.func
|
|
38
|
+
else:
|
|
39
|
+
nxt = getattr(fn, '__wrapped__', None)
|
|
40
|
+
if not callable(nxt):
|
|
41
|
+
break
|
|
42
|
+
elif nxt is fn:
|
|
43
|
+
raise TypeError(fn)
|
|
44
|
+
fn = nxt
|
|
45
|
+
return fn, ps
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def raise_(o: BaseException) -> ta.NoReturn:
|
|
49
|
+
raise o
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def try_(
|
|
53
|
+
exc: ta.Union[type[Exception], ta.Iterable[type[Exception]]] = Exception,
|
|
54
|
+
default: ta.Optional[T] = None,
|
|
55
|
+
) -> ta.Callable[..., T]:
|
|
56
|
+
def outer(fn):
|
|
57
|
+
def inner(*args, **kwargs):
|
|
58
|
+
try:
|
|
59
|
+
return fn(*args, **kwargs)
|
|
60
|
+
except exct:
|
|
61
|
+
return default
|
|
62
|
+
|
|
63
|
+
return inner
|
|
64
|
+
|
|
65
|
+
exct = (exc,) if isinstance(exc, type) else tuple(exc)
|
|
66
|
+
return outer
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def recurse(fn: ta.Callable[..., T], *args, **kwargs) -> T:
|
|
70
|
+
def rec(*args, **kwargs) -> T:
|
|
71
|
+
return fn(rec, *args, **kwargs)
|
|
72
|
+
|
|
73
|
+
return rec(*args, **kwargs)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def identity(obj: T) -> T:
|
|
77
|
+
return obj
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class constant(ta.Generic[T]): # noqa
|
|
81
|
+
|
|
82
|
+
def __init__(self, obj: T) -> None:
|
|
83
|
+
super().__init__()
|
|
84
|
+
|
|
85
|
+
self._obj = obj
|
|
86
|
+
|
|
87
|
+
def __call__(self) -> T:
|
|
88
|
+
return self._obj
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def is_none(o: ta.Any) -> bool:
|
|
92
|
+
return o is None
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def is_not_none(o: ta.Any) -> bool:
|
|
96
|
+
return o is not None
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class VoidException(Exception):
|
|
100
|
+
pass
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class Void:
|
|
104
|
+
|
|
105
|
+
def __new__(cls, *args, **kwargs):
|
|
106
|
+
raise VoidException
|
|
107
|
+
|
|
108
|
+
def __init_subclass__(cls, **kwargs):
|
|
109
|
+
raise VoidException
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def void(*args, **kwargs) -> ta.NoReturn:
|
|
113
|
+
raise VoidException
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def ticking_timeout(
|
|
117
|
+
s: int | float | None,
|
|
118
|
+
ex: type[BaseException] | BaseException = TimeoutError,
|
|
119
|
+
) -> ta.Callable[[], None]:
|
|
120
|
+
if s is None:
|
|
121
|
+
return lambda: None
|
|
122
|
+
def tick(): # noqa
|
|
123
|
+
if time.time() >= deadline:
|
|
124
|
+
raise ex
|
|
125
|
+
deadline = time.time() + s
|
|
126
|
+
return tick
|
omlish/lang/imports.py
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import importlib
|
|
3
|
+
import sys
|
|
4
|
+
import types
|
|
5
|
+
import typing as ta
|
|
6
|
+
|
|
7
|
+
from .cached import cached_function
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def lazy_import(name: str, package: ta.Optional[str] = None) -> ta.Callable[[], ta.Any]:
|
|
14
|
+
return cached_function(functools.partial(importlib.import_module, name, package=package))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def proxy_import(name: str, package: ta.Optional[str] = None) -> types.ModuleType:
|
|
18
|
+
omod = None
|
|
19
|
+
|
|
20
|
+
def __getattr__(att):
|
|
21
|
+
nonlocal omod
|
|
22
|
+
if omod is None:
|
|
23
|
+
omod = importlib.import_module(name, package=package)
|
|
24
|
+
return getattr(omod, att)
|
|
25
|
+
|
|
26
|
+
lmod = types.ModuleType(name)
|
|
27
|
+
lmod.__getattr__ = __getattr__ # type: ignore
|
|
28
|
+
return lmod
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
##
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
_pkg_resources = lazy_import('pkg_resources')
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def import_module(dotted_path: str) -> types.ModuleType:
|
|
38
|
+
if not dotted_path:
|
|
39
|
+
raise ImportError(dotted_path)
|
|
40
|
+
mod = __import__(dotted_path, globals(), locals(), [])
|
|
41
|
+
for name in dotted_path.split('.')[1:]:
|
|
42
|
+
try:
|
|
43
|
+
mod = getattr(mod, name)
|
|
44
|
+
except AttributeError:
|
|
45
|
+
raise AttributeError('Module %r has no attribute %r' % (mod, name))
|
|
46
|
+
return mod
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def import_module_attr(dotted_path: str) -> ta.Any:
|
|
50
|
+
module_name, _, class_name = dotted_path.rpartition('.')
|
|
51
|
+
mod = import_module(module_name)
|
|
52
|
+
try:
|
|
53
|
+
return getattr(mod, class_name)
|
|
54
|
+
except AttributeError:
|
|
55
|
+
raise AttributeError('Module %r has no attr %r' % (module_name, class_name))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
SPECIAL_IMPORTABLE: ta.AbstractSet[str] = frozenset([
|
|
59
|
+
'__init__.py',
|
|
60
|
+
'__main__.py',
|
|
61
|
+
])
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def yield_importable(
|
|
65
|
+
package_root: str,
|
|
66
|
+
*,
|
|
67
|
+
recursive: bool = False,
|
|
68
|
+
filter: ta.Optional[ta.Callable[[str], bool]] = None,
|
|
69
|
+
include_special: bool = False,
|
|
70
|
+
) -> ta.Iterator[str]:
|
|
71
|
+
def rec(dir):
|
|
72
|
+
if dir.split('.')[-1] == '__pycache__':
|
|
73
|
+
return
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
module = sys.modules[dir]
|
|
77
|
+
except KeyError:
|
|
78
|
+
try:
|
|
79
|
+
__import__(dir)
|
|
80
|
+
except ImportError:
|
|
81
|
+
return
|
|
82
|
+
module = sys.modules[dir]
|
|
83
|
+
|
|
84
|
+
# FIXME: pyox
|
|
85
|
+
if getattr(module, '__file__', None) is None:
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
for file in _pkg_resources().resource_listdir(dir, '.'):
|
|
89
|
+
if file.endswith('.py'):
|
|
90
|
+
if not (include_special or file not in SPECIAL_IMPORTABLE):
|
|
91
|
+
continue
|
|
92
|
+
|
|
93
|
+
name = dir + '.' + file[:-3]
|
|
94
|
+
if filter is not None and not filter(name):
|
|
95
|
+
continue
|
|
96
|
+
|
|
97
|
+
yield name
|
|
98
|
+
|
|
99
|
+
elif recursive and '.' not in file:
|
|
100
|
+
name = dir + '.' + file
|
|
101
|
+
if filter is not None and not filter(name):
|
|
102
|
+
continue
|
|
103
|
+
|
|
104
|
+
try:
|
|
105
|
+
yield from rec(name)
|
|
106
|
+
except (ImportError, NotImplementedError):
|
|
107
|
+
pass
|
|
108
|
+
|
|
109
|
+
yield from rec(package_root)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def yield_import_all(
|
|
113
|
+
package_root: str,
|
|
114
|
+
*,
|
|
115
|
+
globals: ta.Optional[dict[str, ta.Any]] = None,
|
|
116
|
+
locals: ta.Optional[dict[str, ta.Any]] = None,
|
|
117
|
+
recursive: bool = False,
|
|
118
|
+
filter: ta.Optional[ta.Callable[[str], bool]] = None,
|
|
119
|
+
include_special: bool = False,
|
|
120
|
+
) -> ta.Iterator[str]:
|
|
121
|
+
for import_path in yield_importable(
|
|
122
|
+
package_root,
|
|
123
|
+
recursive=recursive,
|
|
124
|
+
filter=filter,
|
|
125
|
+
include_special=include_special,
|
|
126
|
+
):
|
|
127
|
+
__import__(import_path, globals=globals, locals=locals)
|
|
128
|
+
yield import_path
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def import_all(
|
|
132
|
+
package_root: str,
|
|
133
|
+
*,
|
|
134
|
+
recursive: bool = False,
|
|
135
|
+
filter: ta.Optional[ta.Callable[[str], bool]] = None,
|
|
136
|
+
include_special: bool = False,
|
|
137
|
+
) -> None:
|
|
138
|
+
for _ in yield_import_all(
|
|
139
|
+
package_root,
|
|
140
|
+
recursive=recursive,
|
|
141
|
+
filter=filter,
|
|
142
|
+
include_special=include_special,
|
|
143
|
+
):
|
|
144
|
+
pass
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def try_import(spec: str) -> ta.Optional[types.ModuleType]:
|
|
148
|
+
s = spec.lstrip('.')
|
|
149
|
+
l = len(spec) - len(s)
|
|
150
|
+
try:
|
|
151
|
+
return __import__(s, globals(), level=l)
|
|
152
|
+
except ImportError:
|
|
153
|
+
return None
|
omlish/lang/iterables.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import itertools
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
T = ta.TypeVar('T')
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
BUILTIN_SCALAR_ITERABLE_TYPES: tuple[type, ...] = (
|
|
9
|
+
bytearray,
|
|
10
|
+
bytes,
|
|
11
|
+
str,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def ilen(it: ta.Iterable) -> int:
|
|
16
|
+
c = 0
|
|
17
|
+
for _ in it:
|
|
18
|
+
c += 1
|
|
19
|
+
return c
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def take(n: int, it: ta.Iterable[T]) -> list[T]:
|
|
23
|
+
return list(itertools.islice(it, n))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def exhaust(it: ta.Iterable[ta.Any]) -> None:
|
|
27
|
+
for _ in it:
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def peek(vs: ta.Iterable[T]) -> ta.Tuple[T, ta.Iterator[T]]:
|
|
32
|
+
it = iter(vs)
|
|
33
|
+
v = next(it)
|
|
34
|
+
return v, itertools.chain(iter((v,)), it)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
Rangeable: ta.TypeAlias = ta.Union[int, ta.Tuple[int], ta.Tuple[int, int], ta.Iterable[int]]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def asrange(i: Rangeable) -> ta.Iterable[int]:
|
|
41
|
+
if isinstance(i, int):
|
|
42
|
+
return range(i)
|
|
43
|
+
elif isinstance(i, tuple):
|
|
44
|
+
return range(*i)
|
|
45
|
+
elif isinstance(i, ta.Iterable):
|
|
46
|
+
return i
|
|
47
|
+
else:
|
|
48
|
+
raise TypeError(i)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def prodrange(*dims: Rangeable) -> ta.Iterable[ta.Sequence[int]]:
|
|
52
|
+
if not dims:
|
|
53
|
+
return []
|
|
54
|
+
return itertools.product(*map(asrange, dims))
|
omlish/lang/maybes.py
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
T = ta.TypeVar('T')
|
|
6
|
+
U = ta.TypeVar('U')
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ValueNotPresentException(BaseException):
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Maybe(abc.ABC, ta.Generic[T]):
|
|
14
|
+
|
|
15
|
+
@property
|
|
16
|
+
@abc.abstractmethod
|
|
17
|
+
def present(self) -> bool:
|
|
18
|
+
raise NotImplementedError
|
|
19
|
+
|
|
20
|
+
@abc.abstractmethod
|
|
21
|
+
def must(self) -> T:
|
|
22
|
+
raise NotImplementedError
|
|
23
|
+
|
|
24
|
+
@abc.abstractmethod
|
|
25
|
+
def __call__(self) -> T:
|
|
26
|
+
raise NotImplementedError
|
|
27
|
+
|
|
28
|
+
@abc.abstractmethod
|
|
29
|
+
def __iter__(self) -> ta.Iterator[T]:
|
|
30
|
+
raise NotImplementedError
|
|
31
|
+
|
|
32
|
+
@abc.abstractmethod
|
|
33
|
+
def __getitem__(self, item: int) -> T:
|
|
34
|
+
raise NotImplementedError
|
|
35
|
+
|
|
36
|
+
@abc.abstractmethod
|
|
37
|
+
def if_present(self, consumer: ta.Callable[[T], None]) -> None:
|
|
38
|
+
raise NotImplementedError
|
|
39
|
+
|
|
40
|
+
@abc.abstractmethod
|
|
41
|
+
def filter(self, predicate: ta.Callable[[T], bool]) -> 'Maybe[T]':
|
|
42
|
+
raise NotImplementedError
|
|
43
|
+
|
|
44
|
+
@abc.abstractmethod
|
|
45
|
+
def map(self, mapper: ta.Callable[[T], U]) -> 'Maybe[U]':
|
|
46
|
+
raise NotImplementedError
|
|
47
|
+
|
|
48
|
+
@abc.abstractmethod
|
|
49
|
+
def flat_map(self, mapper: ta.Callable[[T], 'Maybe[U]']) -> 'Maybe[U]':
|
|
50
|
+
raise NotImplementedError
|
|
51
|
+
|
|
52
|
+
@abc.abstractmethod
|
|
53
|
+
def or_else(self, other: T) -> 'Maybe[T]':
|
|
54
|
+
raise NotImplementedError
|
|
55
|
+
|
|
56
|
+
@abc.abstractmethod
|
|
57
|
+
def or_else_get(self, supplier: ta.Callable[[], T]) -> 'Maybe[T]':
|
|
58
|
+
raise NotImplementedError
|
|
59
|
+
|
|
60
|
+
@abc.abstractmethod
|
|
61
|
+
def or_else_raise(self, exception_supplier: ta.Callable[[], Exception]) -> 'Maybe[T]':
|
|
62
|
+
raise NotImplementedError
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class _Maybe(Maybe[T], tuple):
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def present(self) -> bool:
|
|
69
|
+
return bool(self)
|
|
70
|
+
|
|
71
|
+
def must(self) -> T:
|
|
72
|
+
if not self:
|
|
73
|
+
raise ValueNotPresentException
|
|
74
|
+
return self[0]
|
|
75
|
+
|
|
76
|
+
__call__ = must
|
|
77
|
+
|
|
78
|
+
def __iter__(self) -> ta.Iterator[T]:
|
|
79
|
+
raise TypeError
|
|
80
|
+
|
|
81
|
+
locals()['__iter__'] = tuple.__iter__
|
|
82
|
+
|
|
83
|
+
def __getitem__(self, item: int) -> T: # type: ignore
|
|
84
|
+
raise TypeError
|
|
85
|
+
|
|
86
|
+
locals()['__getitem__'] = tuple.__getitem__
|
|
87
|
+
|
|
88
|
+
def if_present(self, consumer: ta.Callable[[T], None]) -> None:
|
|
89
|
+
if self:
|
|
90
|
+
consumer(self[0])
|
|
91
|
+
|
|
92
|
+
def filter(self, predicate: ta.Callable[[T], bool]) -> Maybe[T]:
|
|
93
|
+
return self if self and predicate(self[0]) else _empty
|
|
94
|
+
|
|
95
|
+
def map(self, mapper: ta.Callable[[T], U]) -> Maybe[U]:
|
|
96
|
+
if self:
|
|
97
|
+
value = mapper(self[0])
|
|
98
|
+
if value is not None:
|
|
99
|
+
return just(value)
|
|
100
|
+
return _empty # noqa
|
|
101
|
+
|
|
102
|
+
def flat_map(self, mapper: ta.Callable[[T], Maybe[U]]) -> Maybe[U]:
|
|
103
|
+
if self:
|
|
104
|
+
value = mapper(self[0])
|
|
105
|
+
if not isinstance(value, Maybe):
|
|
106
|
+
raise TypeError(value)
|
|
107
|
+
return value
|
|
108
|
+
return _empty # noqa
|
|
109
|
+
|
|
110
|
+
def or_else(self, other: T) -> Maybe[T]:
|
|
111
|
+
return self if self else just(other)
|
|
112
|
+
|
|
113
|
+
def or_else_get(self, supplier: ta.Callable[[], T]) -> Maybe[T]:
|
|
114
|
+
return self if self else just(supplier())
|
|
115
|
+
|
|
116
|
+
def or_else_raise(self, exception_supplier: ta.Callable[[], Exception]) -> Maybe[T]:
|
|
117
|
+
if self:
|
|
118
|
+
return self
|
|
119
|
+
raise exception_supplier()
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def just(v: T) -> Maybe[T]:
|
|
123
|
+
return tuple.__new__(_Maybe, (v,)) # noqa
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
_empty = tuple.__new__(_Maybe, ())
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def empty() -> Maybe[T]:
|
|
130
|
+
return _empty # noqa
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def maybe(o: ta.Optional[T]) -> Maybe[T]:
|
|
134
|
+
if o is None:
|
|
135
|
+
return _empty # noqa
|
|
136
|
+
return just(o)
|
omlish/lang/objects.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import types
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
T = ta.TypeVar('T')
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
##
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def attr_repr(obj: ta.Any, *attrs: str) -> str:
|
|
12
|
+
return '%s(%s)' % (
|
|
13
|
+
type(obj).__name__,
|
|
14
|
+
', '.join('%s=%r' % (attr, getattr(obj, attr)) for attr in attrs))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def arg_repr(*args, **kwargs) -> str:
|
|
18
|
+
return ', '.join(*(
|
|
19
|
+
list(map(repr, args)) +
|
|
20
|
+
[f'{k}={repr(v)}' for k, v in kwargs.items()]
|
|
21
|
+
))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
##
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def new_type(
|
|
28
|
+
name: str,
|
|
29
|
+
bases: ta.Sequence[ta.Any],
|
|
30
|
+
namespace: ta.Mapping[str, ta.Any],
|
|
31
|
+
**kwargs
|
|
32
|
+
) -> ta.Type:
|
|
33
|
+
return types.new_class(
|
|
34
|
+
name,
|
|
35
|
+
tuple(bases),
|
|
36
|
+
kwds=kwargs,
|
|
37
|
+
exec_body=lambda ns: ns.update(namespace),
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def super_meta(
|
|
42
|
+
super_meta: ta.Any,
|
|
43
|
+
meta: ta.Type,
|
|
44
|
+
name: str,
|
|
45
|
+
bases: ta.Sequence[ta.Any],
|
|
46
|
+
namespace: ta.MutableMapping[str, ta.Any],
|
|
47
|
+
**kwargs
|
|
48
|
+
) -> ta.Type:
|
|
49
|
+
"""Per types.new_class"""
|
|
50
|
+
resolved_bases = types.resolve_bases(bases)
|
|
51
|
+
if resolved_bases is not bases:
|
|
52
|
+
if '__orig_bases__' in namespace:
|
|
53
|
+
raise TypeError((bases, resolved_bases))
|
|
54
|
+
namespace['__orig_bases__'] = bases
|
|
55
|
+
return super_meta.__new__(meta, name, resolved_bases, dict(namespace), **kwargs)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
##
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class SimpleProxy(ta.Generic[T]):
|
|
62
|
+
|
|
63
|
+
class Descriptor:
|
|
64
|
+
|
|
65
|
+
def __init__(self, attr: str) -> None:
|
|
66
|
+
super().__init__()
|
|
67
|
+
self._attr = attr
|
|
68
|
+
|
|
69
|
+
def __get__(self, instance, owner=None):
|
|
70
|
+
if instance is None:
|
|
71
|
+
return self
|
|
72
|
+
return getattr(object.__getattribute__(instance, '__wrapped__'), self._attr)
|
|
73
|
+
|
|
74
|
+
def __set__(self, instance, value):
|
|
75
|
+
if instance is None:
|
|
76
|
+
return self
|
|
77
|
+
setattr(object.__getattribute__(instance, '__wrapped__'), self._attr, value)
|
|
78
|
+
|
|
79
|
+
def __delete__(self, instance):
|
|
80
|
+
if instance is None:
|
|
81
|
+
return self
|
|
82
|
+
delattr(object.__getattribute__(instance, '__wrapped__'), self._attr)
|
|
83
|
+
|
|
84
|
+
__wrapped_attrs__: ta.Iterable[str] = set()
|
|
85
|
+
|
|
86
|
+
def __init__(self, wrapped: T) -> None:
|
|
87
|
+
super().__init__()
|
|
88
|
+
object.__setattr__(self, '__wrapped__', wrapped)
|
|
89
|
+
|
|
90
|
+
def __init_subclass__(cls, **kwargs):
|
|
91
|
+
super().__init_subclass__(**kwargs)
|
|
92
|
+
|
|
93
|
+
for attr in cls.__wrapped_attrs__:
|
|
94
|
+
setattr(cls, attr, SimpleProxy.Descriptor(attr))
|
|
95
|
+
|
|
96
|
+
def __getattr__(self, item):
|
|
97
|
+
return getattr(object.__getattribute__(self, '__wrapped__'), item)
|
|
98
|
+
|
|
99
|
+
def __setattr__(self, name, value):
|
|
100
|
+
setattr(object.__getattribute__(self, '__wrapped__'), name, value)
|
|
101
|
+
|
|
102
|
+
def __delattr__(self, item):
|
|
103
|
+
delattr(object.__getattribute__(self, '__wrapped__'), item)
|