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,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()
@@ -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
@@ -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)