omlish 0.0.0.dev3__py3-none-any.whl → 0.0.0.dev5__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 +1 -1
- omlish/__init__.py +8 -0
- omlish/asyncs/__init__.py +18 -0
- omlish/asyncs/anyio.py +66 -0
- omlish/asyncs/flavors.py +227 -0
- omlish/asyncs/trio_asyncio.py +47 -0
- omlish/c3.py +1 -1
- omlish/cached.py +1 -2
- omlish/collections/__init__.py +4 -1
- omlish/collections/cache/impl.py +1 -1
- omlish/collections/indexed.py +1 -1
- omlish/collections/utils.py +38 -6
- omlish/configs/__init__.py +5 -0
- omlish/configs/classes.py +53 -0
- omlish/configs/dotenv.py +586 -0
- omlish/configs/props.py +589 -49
- omlish/dataclasses/impl/api.py +1 -1
- omlish/dataclasses/impl/as_.py +1 -1
- omlish/dataclasses/impl/fields.py +1 -0
- omlish/dataclasses/impl/init.py +1 -1
- omlish/dataclasses/impl/main.py +1 -0
- omlish/dataclasses/impl/metaclass.py +6 -1
- omlish/dataclasses/impl/order.py +1 -1
- omlish/dataclasses/impl/reflect.py +15 -2
- omlish/defs.py +1 -1
- omlish/diag/procfs.py +29 -1
- omlish/diag/procstats.py +32 -0
- omlish/diag/replserver/console.py +3 -3
- omlish/diag/replserver/server.py +6 -5
- omlish/diag/threads.py +86 -0
- omlish/docker.py +19 -0
- omlish/dynamic.py +2 -2
- omlish/fnpairs.py +121 -24
- omlish/graphs/dags.py +113 -0
- omlish/graphs/domination.py +268 -0
- omlish/graphs/trees.py +2 -2
- omlish/http/__init__.py +25 -0
- omlish/http/asgi.py +131 -0
- omlish/http/consts.py +31 -4
- omlish/http/cookies.py +194 -0
- omlish/http/dates.py +70 -0
- omlish/http/encodings.py +6 -0
- omlish/http/json.py +273 -0
- omlish/http/sessions.py +197 -0
- omlish/inject/__init__.py +8 -2
- omlish/inject/bindings.py +3 -3
- omlish/inject/exceptions.py +3 -3
- omlish/inject/impl/elements.py +46 -25
- omlish/inject/impl/injector.py +8 -5
- omlish/inject/impl/multis.py +74 -0
- omlish/inject/impl/providers.py +19 -39
- omlish/inject/{proxy.py → impl/proxy.py} +2 -2
- omlish/inject/impl/scopes.py +4 -2
- omlish/inject/injector.py +1 -0
- omlish/inject/keys.py +3 -9
- omlish/inject/multis.py +70 -0
- omlish/inject/providers.py +23 -23
- omlish/inject/scopes.py +7 -3
- omlish/inject/types.py +0 -8
- omlish/iterators.py +13 -0
- omlish/json.py +138 -1
- omlish/lang/__init__.py +8 -0
- omlish/lang/classes/restrict.py +1 -1
- omlish/lang/classes/virtual.py +2 -2
- omlish/lang/contextmanagers.py +64 -0
- omlish/lang/datetimes.py +6 -5
- omlish/lang/functions.py +10 -0
- omlish/lang/imports.py +11 -2
- omlish/lang/sys.py +7 -0
- omlish/lang/typing.py +1 -0
- omlish/logs/utils.py +1 -1
- omlish/marshal/datetimes.py +1 -1
- omlish/reflect.py +8 -2
- omlish/sql/__init__.py +9 -0
- omlish/sql/asyncs.py +148 -0
- omlish/sync.py +70 -0
- omlish/term.py +6 -1
- omlish/testing/pydevd.py +2 -0
- omlish/testing/pytest/__init__.py +5 -0
- omlish/testing/pytest/helpers.py +0 -24
- omlish/testing/pytest/inject/harness.py +1 -1
- omlish/testing/pytest/marks.py +48 -0
- omlish/testing/pytest/plugins/__init__.py +2 -0
- omlish/testing/pytest/plugins/managermarks.py +60 -0
- omlish/testing/testing.py +10 -0
- omlish/text/delimit.py +4 -0
- {omlish-0.0.0.dev3.dist-info → omlish-0.0.0.dev5.dist-info}/METADATA +4 -1
- {omlish-0.0.0.dev3.dist-info → omlish-0.0.0.dev5.dist-info}/RECORD +91 -70
- {omlish-0.0.0.dev3.dist-info → omlish-0.0.0.dev5.dist-info}/WHEEL +1 -1
- {omlish-0.0.0.dev3.dist-info → omlish-0.0.0.dev5.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev3.dist-info → omlish-0.0.0.dev5.dist-info}/top_level.txt +0 -0
omlish/inject/multis.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TODO:
|
|
3
|
+
- scopes
|
|
4
|
+
"""
|
|
5
|
+
import collections.abc
|
|
6
|
+
import typing as ta
|
|
7
|
+
|
|
8
|
+
from .. import dataclasses as dc
|
|
9
|
+
from .. import lang
|
|
10
|
+
from .. import reflect as rfl
|
|
11
|
+
from .bindings import Binding
|
|
12
|
+
from .elements import Element
|
|
13
|
+
from .keys import Key
|
|
14
|
+
from .keys import as_key
|
|
15
|
+
from .providers import Provider
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
##
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _check_set_multi_key(mk: Key) -> bool:
|
|
22
|
+
return rfl.get_concrete_type(mk.ty) is collections.abc.Set
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dc.dataclass(frozen=True)
|
|
26
|
+
@dc.extra_params(cache_hash=True)
|
|
27
|
+
class SetBinding(Element, lang.Final):
|
|
28
|
+
multi_key: Key = dc.xfield(check=_check_set_multi_key)
|
|
29
|
+
dst: Key = dc.xfield()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dc.dataclass(frozen=True, eq=False)
|
|
33
|
+
class SetProvider(Provider):
|
|
34
|
+
multi_key: Key = dc.xfield(check=_check_set_multi_key)
|
|
35
|
+
|
|
36
|
+
def provided_ty(self) -> rfl.Type | None:
|
|
37
|
+
return self.multi_key.ty
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def bind_set_provider(multi_key: ta.Any) -> Element:
|
|
41
|
+
multi_key = as_key(multi_key)
|
|
42
|
+
return Binding(multi_key, SetProvider(multi_key))
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
##
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _check_map_multi_key(mk: Key) -> bool:
|
|
49
|
+
return rfl.get_concrete_type(mk.ty) is collections.abc.Mapping
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dc.dataclass(frozen=True)
|
|
53
|
+
@dc.extra_params(cache_hash=True)
|
|
54
|
+
class MapBinding(Element, lang.Final):
|
|
55
|
+
multi_key: Key = dc.xfield(check=_check_map_multi_key)
|
|
56
|
+
map_key: ta.Any = dc.xfield(())
|
|
57
|
+
dst: Key = dc.xfield(())
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dc.dataclass(frozen=True, eq=False)
|
|
61
|
+
class MapProvider(Provider):
|
|
62
|
+
multi_key: Key = dc.xfield(check=_check_map_multi_key)
|
|
63
|
+
|
|
64
|
+
def provided_ty(self) -> rfl.Type | None:
|
|
65
|
+
return self.multi_key.ty
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def bind_map_provider(multi_key: ta.Any) -> Element:
|
|
69
|
+
multi_key = as_key(multi_key)
|
|
70
|
+
return Binding(multi_key, MapProvider(multi_key))
|
omlish/inject/providers.py
CHANGED
|
@@ -4,12 +4,12 @@ import typing as ta
|
|
|
4
4
|
from .. import check
|
|
5
5
|
from .. import dataclasses as dc
|
|
6
6
|
from .. import lang
|
|
7
|
+
from .. import reflect as rfl
|
|
7
8
|
from .elements import Element
|
|
8
9
|
from .elements import Elements
|
|
9
10
|
from .impl.inspect import signature
|
|
10
11
|
from .keys import Key
|
|
11
12
|
from .keys import as_key
|
|
12
|
-
from .types import Cls
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class _Missing(lang.NotInstantiable):
|
|
@@ -21,7 +21,7 @@ class _Missing(lang.NotInstantiable):
|
|
|
21
21
|
|
|
22
22
|
class Provider(lang.Abstract):
|
|
23
23
|
@abc.abstractmethod
|
|
24
|
-
def
|
|
24
|
+
def provided_ty(self) -> rfl.Type | None:
|
|
25
25
|
raise NotImplementedError
|
|
26
26
|
|
|
27
27
|
|
|
@@ -47,19 +47,19 @@ def as_provider(o: ta.Any) -> Provider:
|
|
|
47
47
|
@dc.dataclass(frozen=True, eq=False)
|
|
48
48
|
class FnProvider(Provider):
|
|
49
49
|
fn: ta.Any
|
|
50
|
-
|
|
50
|
+
ty: rfl.Type | None = None
|
|
51
51
|
|
|
52
|
-
def
|
|
53
|
-
return self.
|
|
52
|
+
def provided_ty(self) -> rfl.Type | None:
|
|
53
|
+
return self.ty
|
|
54
54
|
|
|
55
55
|
|
|
56
|
-
def fn(fn: ta.Any,
|
|
56
|
+
def fn(fn: ta.Any, ty: rfl.Type | None = _Missing) -> Provider:
|
|
57
57
|
check.not_isinstance(fn, type)
|
|
58
58
|
check.callable(fn)
|
|
59
|
-
if
|
|
59
|
+
if ty is _Missing:
|
|
60
60
|
sig = signature(fn)
|
|
61
|
-
|
|
62
|
-
return FnProvider(fn,
|
|
61
|
+
ty = check.isinstance(sig.return_annotation, type)
|
|
62
|
+
return FnProvider(fn, ty)
|
|
63
63
|
|
|
64
64
|
|
|
65
65
|
##
|
|
@@ -67,15 +67,15 @@ def fn(fn: ta.Any, cls: Cls | None = _Missing) -> Provider:
|
|
|
67
67
|
|
|
68
68
|
@dc.dataclass(frozen=True, eq=False)
|
|
69
69
|
class CtorProvider(Provider):
|
|
70
|
-
|
|
70
|
+
ty: type
|
|
71
71
|
|
|
72
|
-
def
|
|
73
|
-
return self.
|
|
72
|
+
def provided_ty(self) -> type:
|
|
73
|
+
return self.ty
|
|
74
74
|
|
|
75
75
|
|
|
76
|
-
def ctor(
|
|
77
|
-
check.isinstance(
|
|
78
|
-
return CtorProvider(
|
|
76
|
+
def ctor(ty: type) -> Provider:
|
|
77
|
+
check.isinstance(ty, type)
|
|
78
|
+
return CtorProvider(ty)
|
|
79
79
|
|
|
80
80
|
|
|
81
81
|
##
|
|
@@ -84,16 +84,16 @@ def ctor(cls: type) -> Provider:
|
|
|
84
84
|
@dc.dataclass(frozen=True, eq=False)
|
|
85
85
|
class ConstProvider(Provider):
|
|
86
86
|
v: ta.Any
|
|
87
|
-
|
|
87
|
+
ty: rfl.Type | None = None
|
|
88
88
|
|
|
89
|
-
def
|
|
90
|
-
return self.
|
|
89
|
+
def provided_ty(self) -> rfl.Type | None:
|
|
90
|
+
return self.ty
|
|
91
91
|
|
|
92
92
|
|
|
93
|
-
def const(v: ta.Any,
|
|
94
|
-
if
|
|
95
|
-
|
|
96
|
-
return ConstProvider(v,
|
|
93
|
+
def const(v: ta.Any, ty: rfl.Type | None = _Missing) -> Provider:
|
|
94
|
+
if ty is _Missing:
|
|
95
|
+
ty = type(v)
|
|
96
|
+
return ConstProvider(v, ty)
|
|
97
97
|
|
|
98
98
|
|
|
99
99
|
##
|
|
@@ -103,7 +103,7 @@ def const(v: ta.Any, cls: Cls | None = _Missing) -> Provider:
|
|
|
103
103
|
class LinkProvider(Provider):
|
|
104
104
|
k: Key
|
|
105
105
|
|
|
106
|
-
def
|
|
106
|
+
def provided_ty(self) -> rfl.Type | None:
|
|
107
107
|
return None
|
|
108
108
|
|
|
109
109
|
|
omlish/inject/scopes.py
CHANGED
|
@@ -5,21 +5,25 @@ import typing as ta
|
|
|
5
5
|
from .. import check
|
|
6
6
|
from .. import dataclasses as dc
|
|
7
7
|
from .. import lang
|
|
8
|
+
from .. import reflect as rfl
|
|
8
9
|
from .bindings import Binding
|
|
9
10
|
from .bindings import as_binding
|
|
10
11
|
from .elements import Element
|
|
11
12
|
from .keys import Key
|
|
12
13
|
from .keys import as_key
|
|
13
14
|
from .providers import Provider
|
|
14
|
-
from .types import Cls
|
|
15
15
|
from .types import Scope
|
|
16
16
|
|
|
17
|
+
|
|
17
18
|
if ta.TYPE_CHECKING:
|
|
18
19
|
from . import injector as injector_
|
|
19
20
|
else:
|
|
20
21
|
injector_ = lang.proxy_import('.injector', __package__)
|
|
21
22
|
|
|
22
23
|
|
|
24
|
+
##
|
|
25
|
+
|
|
26
|
+
|
|
23
27
|
@dc.dataclass(frozen=True)
|
|
24
28
|
@dc.extra_params(cache_hash=True)
|
|
25
29
|
class ScopeBinding(Element, lang.Final):
|
|
@@ -65,8 +69,8 @@ class ScopeSeededProvider(Provider):
|
|
|
65
69
|
ss: SeededScope = dc.xfield(coerce=check.of_isinstance(SeededScope))
|
|
66
70
|
key: Key = dc.xfield(coerce=check.of_isinstance(Key))
|
|
67
71
|
|
|
68
|
-
def
|
|
69
|
-
return self.key.
|
|
72
|
+
def provided_ty(self) -> rfl.Type | None:
|
|
73
|
+
return self.key.ty
|
|
70
74
|
|
|
71
75
|
|
|
72
76
|
def bind_scope_seed(ss: SeededScope, k: ta.Any) -> Element:
|
omlish/inject/types.py
CHANGED
omlish/iterators.py
CHANGED
|
@@ -218,3 +218,16 @@ def expand_indexed_pairs(
|
|
|
218
218
|
if idx < width_:
|
|
219
219
|
result[idx] = value
|
|
220
220
|
return result
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
##
|
|
224
|
+
# https://docs.python.org/3/library/itertools.html#itertools-recipes
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def sliding_window(it: ta.Iterable[T], n: int) -> ta.Iterator[tuple[T, ...]]:
|
|
228
|
+
# sliding_window('ABCDEFG', 4) -> ABCD BCDE CDEF DEFG
|
|
229
|
+
iterator = iter(it)
|
|
230
|
+
window = collections.deque(itertools.islice(iterator, n - 1), maxlen=n)
|
|
231
|
+
for x in iterator:
|
|
232
|
+
window.append(x)
|
|
233
|
+
yield tuple(window)
|
omlish/json.py
CHANGED
|
@@ -1,7 +1,143 @@
|
|
|
1
|
+
"""
|
|
2
|
+
json
|
|
3
|
+
dump
|
|
4
|
+
skipkeys=False
|
|
5
|
+
ensure_ascii=True
|
|
6
|
+
check_circular=True
|
|
7
|
+
allow_nan=True
|
|
8
|
+
cls=None
|
|
9
|
+
indent=None
|
|
10
|
+
separators=None
|
|
11
|
+
default=None
|
|
12
|
+
sort_keys=False
|
|
13
|
+
dumps
|
|
14
|
+
^
|
|
15
|
+
load
|
|
16
|
+
cls=None
|
|
17
|
+
object_hook=None
|
|
18
|
+
parse_float=None
|
|
19
|
+
parse_int=None
|
|
20
|
+
parse_constant=None
|
|
21
|
+
object_pairs_hook=None
|
|
22
|
+
loads
|
|
23
|
+
^
|
|
24
|
+
|
|
25
|
+
ujson
|
|
26
|
+
dump
|
|
27
|
+
ensure_ascii
|
|
28
|
+
encode_html_chars
|
|
29
|
+
escape_forward_slashes
|
|
30
|
+
sort_keys
|
|
31
|
+
indent
|
|
32
|
+
allow_nan
|
|
33
|
+
reject_bytes
|
|
34
|
+
default
|
|
35
|
+
separators
|
|
36
|
+
dumps
|
|
37
|
+
^
|
|
38
|
+
load
|
|
39
|
+
loads
|
|
40
|
+
|
|
41
|
+
orjson
|
|
42
|
+
dumps
|
|
43
|
+
default
|
|
44
|
+
option
|
|
45
|
+
OPT_INDENT_2
|
|
46
|
+
OPT_NAIVE_UTC
|
|
47
|
+
OPT_NON_STR_KEYS
|
|
48
|
+
OPT_OMIT_MICROSECONDS
|
|
49
|
+
OPT_PASSTHROUGH_DATACLASS
|
|
50
|
+
OPT_PASSTHROUGH_DATETIME
|
|
51
|
+
OPT_PASSTHROUGH_SUBCLASS
|
|
52
|
+
OPT_SERIALIZE_DATACLASS
|
|
53
|
+
OPT_SERIALIZE_NUMPY
|
|
54
|
+
OPT_SERIALIZE_UUID
|
|
55
|
+
OPT_SORT_KEYS
|
|
56
|
+
OPT_STRICT_INTEGER
|
|
57
|
+
OPT_UTC_Z
|
|
58
|
+
loads
|
|
59
|
+
|
|
60
|
+
rapidjson
|
|
61
|
+
dump
|
|
62
|
+
skipkeys=False,
|
|
63
|
+
ensure_ascii=True,
|
|
64
|
+
write_mode=WM_COMPACT,
|
|
65
|
+
WM_COMPACT
|
|
66
|
+
WM_PRETTY
|
|
67
|
+
WM_SINGLE_LINE_ARRAY
|
|
68
|
+
indent=4,
|
|
69
|
+
default=None,
|
|
70
|
+
sort_keys=False,
|
|
71
|
+
number_mode=None,
|
|
72
|
+
NM_NONE
|
|
73
|
+
NM_DECIMAL
|
|
74
|
+
NM_NAN
|
|
75
|
+
NM_NATIVE
|
|
76
|
+
datetime_mode=None,
|
|
77
|
+
DM_NONE
|
|
78
|
+
DM_ISO8601
|
|
79
|
+
DM_UNIX_TIME
|
|
80
|
+
DM_ONLY_SECONDS
|
|
81
|
+
DM_IGNORE_TZ
|
|
82
|
+
DM_NAIVE_IS_UTC
|
|
83
|
+
DM_SHIFT_TO_UTC
|
|
84
|
+
uuid_mode=None,
|
|
85
|
+
UM_NONE
|
|
86
|
+
UM_CANONICAL
|
|
87
|
+
UM_HEX
|
|
88
|
+
bytes_mode=BM_UTF8,
|
|
89
|
+
BM_NONE
|
|
90
|
+
BM_UTF8
|
|
91
|
+
iterable_mode=IM_ANY_ITERABLE,
|
|
92
|
+
IM_ANY_ITERABLE
|
|
93
|
+
IM_ONLY_LISTS
|
|
94
|
+
mapping_mode=MM_ANY_MAPPING,
|
|
95
|
+
MM_ANY_MAPPING
|
|
96
|
+
MM_ONLY_DICTS
|
|
97
|
+
MM_COERCE_KEYS_TO_STRINGS
|
|
98
|
+
MM_SKIP_NON_STRING_KEYS
|
|
99
|
+
MM_SORT_KEYS
|
|
100
|
+
chunk_size
|
|
101
|
+
allow_nan=True
|
|
102
|
+
dumps
|
|
103
|
+
^
|
|
104
|
+
-chunk_size
|
|
105
|
+
load
|
|
106
|
+
object_hook=None,
|
|
107
|
+
number_mode=None,
|
|
108
|
+
^
|
|
109
|
+
datetime_mode=None,
|
|
110
|
+
^
|
|
111
|
+
uuid_mode=None,
|
|
112
|
+
^
|
|
113
|
+
parse_mode=None,
|
|
114
|
+
PM_NONE
|
|
115
|
+
PM_COMMENTS
|
|
116
|
+
PM_TRAILING_COMMAS
|
|
117
|
+
chunk_size=65536,
|
|
118
|
+
allow_nan=True
|
|
119
|
+
loads
|
|
120
|
+
^
|
|
121
|
+
-chunk_size
|
|
122
|
+
|
|
123
|
+
"""
|
|
1
124
|
import functools
|
|
2
125
|
import json as _json
|
|
3
126
|
import typing as ta
|
|
4
127
|
|
|
128
|
+
from . import lang
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
if ta.TYPE_CHECKING:
|
|
132
|
+
import orjson as _orjson
|
|
133
|
+
import ujson as _ujson
|
|
134
|
+
else:
|
|
135
|
+
_orjson = lang.proxy_import('orjson')
|
|
136
|
+
_ujson = lang.proxy_import('ujson')
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
##
|
|
140
|
+
|
|
5
141
|
|
|
6
142
|
dump = _json.dump
|
|
7
143
|
dumps = _json.dumps
|
|
@@ -11,6 +147,7 @@ detect_encoding = _json.detect_encoding
|
|
|
11
147
|
load = _json.load
|
|
12
148
|
loads = _json.loads
|
|
13
149
|
|
|
150
|
+
|
|
14
151
|
##
|
|
15
152
|
|
|
16
153
|
|
|
@@ -29,7 +166,7 @@ dumps_pretty: ta.Callable[..., str] = functools.partial(dumps, **PRETTY_KWARGS)
|
|
|
29
166
|
COMPACT_SEPARATORS = (',', ':')
|
|
30
167
|
|
|
31
168
|
COMPACT_KWARGS: ta.Mapping[str, ta.Any] = dict(
|
|
32
|
-
indent=
|
|
169
|
+
indent=None,
|
|
33
170
|
separators=COMPACT_SEPARATORS,
|
|
34
171
|
)
|
|
35
172
|
|
omlish/lang/__init__.py
CHANGED
|
@@ -46,7 +46,9 @@ from .cmp import ( # noqa
|
|
|
46
46
|
)
|
|
47
47
|
|
|
48
48
|
from .contextmanagers import ( # noqa
|
|
49
|
+
AsyncContextManager,
|
|
49
50
|
ContextManaged,
|
|
51
|
+
ContextManager,
|
|
50
52
|
ContextWrapped,
|
|
51
53
|
DefaultLockable,
|
|
52
54
|
ExitStacked,
|
|
@@ -90,6 +92,7 @@ from .exceptions import ( # noqa
|
|
|
90
92
|
from .functions import ( # noqa
|
|
91
93
|
Args,
|
|
92
94
|
VoidError,
|
|
95
|
+
as_async,
|
|
93
96
|
constant,
|
|
94
97
|
finally_,
|
|
95
98
|
identity,
|
|
@@ -108,6 +111,7 @@ from .functions import ( # noqa
|
|
|
108
111
|
)
|
|
109
112
|
|
|
110
113
|
from .imports import ( # noqa
|
|
114
|
+
can_import,
|
|
111
115
|
import_all,
|
|
112
116
|
import_module,
|
|
113
117
|
import_module_attr,
|
|
@@ -156,6 +160,10 @@ from .strings import ( # noqa
|
|
|
156
160
|
snake_case,
|
|
157
161
|
)
|
|
158
162
|
|
|
163
|
+
from .sys import ( # noqa
|
|
164
|
+
is_gil_enabled,
|
|
165
|
+
)
|
|
166
|
+
|
|
159
167
|
from .timeouts import ( # noqa
|
|
160
168
|
DeadlineTimeout,
|
|
161
169
|
InfiniteTimeout,
|
omlish/lang/classes/restrict.py
CHANGED
omlish/lang/classes/virtual.py
CHANGED
|
@@ -3,8 +3,8 @@ import types
|
|
|
3
3
|
import typing as ta
|
|
4
4
|
|
|
5
5
|
from .abstract import make_abstract
|
|
6
|
-
from .restrict import NotInstantiable
|
|
7
6
|
from .restrict import Final
|
|
7
|
+
from .restrict import NotInstantiable
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
T = ta.TypeVar('T')
|
|
@@ -122,7 +122,7 @@ class Callable(NotInstantiable, Final, ta.Generic[T]):
|
|
|
122
122
|
|
|
123
123
|
@classmethod
|
|
124
124
|
def __subclasscheck__(cls, subclass: type) -> bool:
|
|
125
|
-
if not hasattr(subclass, '__call__'):
|
|
125
|
+
if not hasattr(subclass, '__call__'): # noqa
|
|
126
126
|
return False
|
|
127
127
|
call = subclass.__call__
|
|
128
128
|
if isinstance(call, types.MethodWrapperType) and call.__self__ is subclass:
|
omlish/lang/contextmanagers.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import abc
|
|
1
2
|
import contextlib
|
|
2
3
|
import contextvars
|
|
3
4
|
import functools
|
|
@@ -50,6 +51,69 @@ NOP_CONTEXT_MANAGER = NopContextManager()
|
|
|
50
51
|
##
|
|
51
52
|
|
|
52
53
|
|
|
54
|
+
class ContextManager(abc.ABC, ta.Generic[T]):
|
|
55
|
+
|
|
56
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
|
57
|
+
super().__init_subclass__(**kwargs)
|
|
58
|
+
|
|
59
|
+
if not hasattr(cls.__contextmanager__, '_is_contextmanager'):
|
|
60
|
+
cls.__contextmanager__ = contextlib.contextmanager(cls.__contextmanager__) # type: ignore # noqa
|
|
61
|
+
cls.__contextmanager__._is_contextmanager = True # type: ignore # noqa
|
|
62
|
+
|
|
63
|
+
@abc.abstractmethod
|
|
64
|
+
def __contextmanager__(self) -> ta.Iterable[T]:
|
|
65
|
+
raise NotImplementedError
|
|
66
|
+
|
|
67
|
+
__contextmanager__._is_contextmanager = True # type: ignore # noqa
|
|
68
|
+
|
|
69
|
+
_contextmanager: ta.Any
|
|
70
|
+
|
|
71
|
+
def __enter__(self) -> T:
|
|
72
|
+
self._contextmanager = self.__contextmanager__()
|
|
73
|
+
return self._contextmanager.__enter__() # type: ignore
|
|
74
|
+
|
|
75
|
+
def __exit__(
|
|
76
|
+
self,
|
|
77
|
+
exc_type: type[BaseException] | None,
|
|
78
|
+
exc_val: BaseException | None,
|
|
79
|
+
exc_tb: types.TracebackType | None,
|
|
80
|
+
) -> None:
|
|
81
|
+
return self._contextmanager.__exit__(exc_type, exc_val, exc_tb)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class AsyncContextManager(abc.ABC, ta.Generic[T]):
|
|
85
|
+
|
|
86
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
|
87
|
+
super().__init_subclass__(**kwargs)
|
|
88
|
+
|
|
89
|
+
if not hasattr(cls.__asynccontextmanager__, '_is_asynccontextmanager'):
|
|
90
|
+
cls.__asynccontextmanager__ = contextlib.asynccontextmanager(cls.__asynccontextmanager__) # type: ignore # noqa
|
|
91
|
+
cls.__asynccontextmanager__._is_asynccontextmanager = True # type: ignore # noqa
|
|
92
|
+
|
|
93
|
+
@abc.abstractmethod
|
|
94
|
+
def __asynccontextmanager__(self) -> ta.AsyncIterator[T]:
|
|
95
|
+
raise NotImplementedError
|
|
96
|
+
|
|
97
|
+
__asynccontextmanager__._is_asynccontextmanager = True # type: ignore # noqa
|
|
98
|
+
|
|
99
|
+
_asynccontextmanager: ta.Any
|
|
100
|
+
|
|
101
|
+
async def __aenter__(self) -> T:
|
|
102
|
+
self._asynccontextmanager = self.__asynccontextmanager__()
|
|
103
|
+
return await self._asynccontextmanager.__aenter__() # type: ignore
|
|
104
|
+
|
|
105
|
+
async def __aexit__(
|
|
106
|
+
self,
|
|
107
|
+
exc_type: type[BaseException] | None,
|
|
108
|
+
exc_val: BaseException | None,
|
|
109
|
+
exc_tb: types.TracebackType | None,
|
|
110
|
+
) -> None:
|
|
111
|
+
return await self._asynccontextmanager.__aexit__(exc_type, exc_val, exc_tb)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
##
|
|
115
|
+
|
|
116
|
+
|
|
53
117
|
@contextlib.contextmanager
|
|
54
118
|
def defer(fn: ta.Callable) -> ta.Generator[ta.Callable, None, None]:
|
|
55
119
|
try:
|
omlish/lang/datetimes.py
CHANGED
|
@@ -18,17 +18,18 @@ def months_ago(date: datetime.date, num: int) -> datetime.date:
|
|
|
18
18
|
return datetime.date(ago_year, ago_month, 1)
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
def parse_date(s: str) -> datetime.date:
|
|
21
|
+
def parse_date(s: str, tz: datetime.timezone | None = None) -> datetime.date:
|
|
22
|
+
|
|
22
23
|
if s.lower() in ['today', 'now']:
|
|
23
|
-
return datetime.
|
|
24
|
+
return datetime.datetime.now(tz=tz)
|
|
24
25
|
elif s.lower() == 'yesterday':
|
|
25
|
-
return datetime.
|
|
26
|
+
return datetime.datetime.now(tz=tz) - datetime.timedelta(days=1)
|
|
26
27
|
elif s.lower().endswith(' days ago'):
|
|
27
28
|
num = int(s.split(' ', 1)[0])
|
|
28
|
-
return datetime.
|
|
29
|
+
return datetime.datetime.now(tz=tz) - datetime.timedelta(days=num)
|
|
29
30
|
elif s.lower().endswith(' months ago'):
|
|
30
31
|
months = int(s.split(' ', 1)[0])
|
|
31
|
-
return months_ago(datetime.
|
|
32
|
+
return months_ago(datetime.datetime.now(tz=tz), months)
|
|
32
33
|
else:
|
|
33
34
|
return datetime.date(*map(int, s.split('-', 3)))
|
|
34
35
|
|
omlish/lang/functions.py
CHANGED
|
@@ -148,6 +148,16 @@ def void(*args: ta.Any, **kwargs: ta.Any) -> ta.NoReturn:
|
|
|
148
148
|
##
|
|
149
149
|
|
|
150
150
|
|
|
151
|
+
def as_async(fn: ta.Callable[P, T]) -> ta.Callable[P, ta.Awaitable[T]]:
|
|
152
|
+
@functools.wraps(fn)
|
|
153
|
+
async def inner(*args, **kwargs):
|
|
154
|
+
return fn(*args, **kwargs)
|
|
155
|
+
return inner
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
##
|
|
159
|
+
|
|
160
|
+
|
|
151
161
|
_MISSING = object()
|
|
152
162
|
|
|
153
163
|
|
omlish/lang/imports.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import contextlib
|
|
2
2
|
import functools
|
|
3
|
-
import importlib.
|
|
3
|
+
import importlib.util
|
|
4
4
|
import sys
|
|
5
5
|
import types
|
|
6
6
|
import typing as ta
|
|
@@ -11,6 +11,13 @@ from .cached import cached_function
|
|
|
11
11
|
##
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
def can_import(name: str, package: str | None = None) -> bool:
|
|
15
|
+
return importlib.util.find_spec(name, package) is not None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
##
|
|
19
|
+
|
|
20
|
+
|
|
14
21
|
def lazy_import(name: str, package: str | None = None) -> ta.Callable[[], ta.Any]:
|
|
15
22
|
return cached_function(functools.partial(importlib.import_module, name, package=package))
|
|
16
23
|
|
|
@@ -66,6 +73,8 @@ def yield_importable(
|
|
|
66
73
|
filter: ta.Callable[[str], bool] | None = None, # noqa
|
|
67
74
|
include_special: bool = False,
|
|
68
75
|
) -> ta.Iterator[str]:
|
|
76
|
+
from importlib import resources
|
|
77
|
+
|
|
69
78
|
def rec(cur):
|
|
70
79
|
if cur.split('.')[-1] == '__pycache__':
|
|
71
80
|
return
|
|
@@ -83,7 +92,7 @@ def yield_importable(
|
|
|
83
92
|
if getattr(module, '__file__', None) is None:
|
|
84
93
|
return
|
|
85
94
|
|
|
86
|
-
for file in
|
|
95
|
+
for file in resources.files(cur).iterdir():
|
|
87
96
|
if file.is_file() and file.name.endswith('.py'):
|
|
88
97
|
if not (include_special or file.name not in SPECIAL_IMPORTABLE):
|
|
89
98
|
continue
|
omlish/lang/sys.py
ADDED
omlish/lang/typing.py
CHANGED
omlish/logs/utils.py
CHANGED