omlish 0.0.0.dev22__py3-none-any.whl → 0.0.0.dev23__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.
- omlish/__about__.py +10 -3
- omlish/asyncs/bridge.py +3 -0
- omlish/bootstrap/__init__.py +39 -0
- omlish/bootstrap/base.py +3 -1
- omlish/bootstrap/diag.py +48 -1
- omlish/bootstrap/main.py +2 -1
- omlish/bootstrap/marshal.py +18 -0
- omlish/check.py +123 -33
- omlish/diag/asts.py +132 -0
- omlish/diag/pycharm.py +80 -0
- omlish/docker.py +3 -0
- omlish/genmachine.py +58 -0
- omlish/marshal/__init__.py +4 -0
- omlish/marshal/base64.py +4 -0
- omlish/marshal/primitives.py +6 -0
- omlish/marshal/standard.py +4 -0
- omlish/marshal/unions.py +101 -0
- omlish/matchfns.py +3 -3
- omlish/sql/__init__.py +18 -0
- omlish/sql/qualifiedname.py +82 -0
- {omlish-0.0.0.dev22.dist-info → omlish-0.0.0.dev23.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev22.dist-info → omlish-0.0.0.dev23.dist-info}/RECORD +25 -19
- {omlish-0.0.0.dev22.dist-info → omlish-0.0.0.dev23.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev22.dist-info → omlish-0.0.0.dev23.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev22.dist-info → omlish-0.0.0.dev23.dist-info}/top_level.txt +0 -0
omlish/__about__.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
__version__ = '0.0.0.
|
2
|
-
__revision__ = '
|
1
|
+
__version__ = '0.0.0.dev23'
|
2
|
+
__revision__ = '1d019b6879a9552ea31622e40f50d260d6b08824'
|
3
3
|
|
4
4
|
|
5
5
|
#
|
@@ -80,7 +80,11 @@ class Project(ProjectBase):
|
|
80
80
|
'sqlalchemy[asyncio] ~= 2.0',
|
81
81
|
|
82
82
|
'pg8000 ~= 1.31',
|
83
|
+
# 'psycopg2 ~= 2.9',
|
84
|
+
|
83
85
|
'pymysql ~= 1.1',
|
86
|
+
# 'mysql-connector-python ~= 9.0',
|
87
|
+
# 'mysqlclient ~= 2.2',
|
84
88
|
|
85
89
|
'aiomysql ~= 0.2',
|
86
90
|
'aiosqlite ~= 0.20',
|
@@ -110,7 +114,10 @@ class SetuptoolsBase:
|
|
110
114
|
include_package_data = False
|
111
115
|
|
112
116
|
find_packages = {
|
113
|
-
'exclude': [
|
117
|
+
'exclude': [
|
118
|
+
'*.tests',
|
119
|
+
'*.tests.*',
|
120
|
+
],
|
114
121
|
}
|
115
122
|
|
116
123
|
|
omlish/asyncs/bridge.py
CHANGED
omlish/bootstrap/__init__.py
CHANGED
@@ -1,3 +1,42 @@
|
|
1
|
+
from .base import ( # noqa
|
2
|
+
Bootstrap,
|
3
|
+
ContextBootstrap,
|
4
|
+
SimpleBootstrap,
|
5
|
+
)
|
6
|
+
|
7
|
+
from .diag import ( # noqa
|
8
|
+
CheckBootstrap,
|
9
|
+
CprofileBootstrap,
|
10
|
+
PycharmBootstrap,
|
11
|
+
ThreadDumpBootstrap,
|
12
|
+
TimebombBootstrap,
|
13
|
+
)
|
14
|
+
|
1
15
|
from .harness import ( # noqa
|
2
16
|
bootstrap,
|
3
17
|
)
|
18
|
+
|
19
|
+
from .sys import ( # noqa
|
20
|
+
CwdBootstrap,
|
21
|
+
EnvBootstrap,
|
22
|
+
FaulthandlerBootstrap,
|
23
|
+
FdsBootstrap,
|
24
|
+
GcBootstrap,
|
25
|
+
GcDebugFlag,
|
26
|
+
ImportBootstrap,
|
27
|
+
LogBootstrap,
|
28
|
+
NiceBootstrap,
|
29
|
+
PidfileBootstrap,
|
30
|
+
PrctlBootstrap,
|
31
|
+
PrintPidBootstrap,
|
32
|
+
RlimitBootstrap,
|
33
|
+
SetuidBootstrap,
|
34
|
+
)
|
35
|
+
|
36
|
+
|
37
|
+
##
|
38
|
+
|
39
|
+
|
40
|
+
from ..lang.imports import _register_conditional_import # noqa
|
41
|
+
|
42
|
+
_register_conditional_import('..marshal', '.marshal', __package__)
|
omlish/bootstrap/base.py
CHANGED
@@ -2,6 +2,8 @@ import abc
|
|
2
2
|
import dataclasses as dc
|
3
3
|
import typing as ta
|
4
4
|
|
5
|
+
from omlish import lang
|
6
|
+
|
5
7
|
|
6
8
|
BootstrapConfigT = ta.TypeVar('BootstrapConfigT', bound='Bootstrap.Config')
|
7
9
|
|
@@ -9,7 +11,7 @@ BootstrapConfigT = ta.TypeVar('BootstrapConfigT', bound='Bootstrap.Config')
|
|
9
11
|
##
|
10
12
|
|
11
13
|
|
12
|
-
class Bootstrap(abc.ABC, ta.Generic[BootstrapConfigT]):
|
14
|
+
class Bootstrap(abc.ABC, lang.PackageSealed, ta.Generic[BootstrapConfigT]):
|
13
15
|
@dc.dataclass(frozen=True)
|
14
16
|
class Config(abc.ABC): # noqa
|
15
17
|
pass
|
omlish/bootstrap/diag.py
CHANGED
@@ -5,28 +5,56 @@ import signal
|
|
5
5
|
import sys
|
6
6
|
import typing as ta
|
7
7
|
|
8
|
+
from .. import check
|
8
9
|
from .. import lang
|
9
10
|
from .base import Bootstrap
|
10
11
|
from .base import ContextBootstrap
|
12
|
+
from .base import SimpleBootstrap
|
11
13
|
|
12
14
|
|
13
15
|
if ta.TYPE_CHECKING:
|
14
16
|
import cProfile # noqa
|
15
17
|
import pstats
|
16
18
|
|
19
|
+
from ..diag import pycharm as diagpc
|
17
20
|
from ..diag import threads as diagt
|
18
21
|
|
19
22
|
else:
|
20
23
|
cProfile = lang.proxy_import('cProfile') # noqa
|
21
24
|
pstats = lang.proxy_import('pstats')
|
22
25
|
|
26
|
+
diagpc = lang.proxy_import('..diag.pycharm', __package__)
|
23
27
|
diagt = lang.proxy_import('..diag.threads', __package__)
|
24
28
|
|
25
29
|
|
26
30
|
##
|
27
31
|
|
28
32
|
|
29
|
-
class
|
33
|
+
class CheckBootstrap(ContextBootstrap['CheckBootstrap.Config']):
|
34
|
+
@dc.dataclass(frozen=True)
|
35
|
+
class Config(Bootstrap.Config):
|
36
|
+
breakpoint: bool = False
|
37
|
+
|
38
|
+
@staticmethod
|
39
|
+
def _breakpoint(exc: Exception) -> None: # noqa
|
40
|
+
breakpoint() # noqa
|
41
|
+
|
42
|
+
@contextlib.contextmanager
|
43
|
+
def enter(self) -> ta.Iterator[None]:
|
44
|
+
if not self._config.breakpoint:
|
45
|
+
return
|
46
|
+
|
47
|
+
check.register_on_raise(CheckBootstrap._breakpoint)
|
48
|
+
try:
|
49
|
+
yield
|
50
|
+
finally:
|
51
|
+
check.unregister_on_raise(CheckBootstrap._breakpoint)
|
52
|
+
|
53
|
+
|
54
|
+
##
|
55
|
+
|
56
|
+
|
57
|
+
class CprofileBootstrap(ContextBootstrap['CprofileBootstrap.Config']):
|
30
58
|
@dc.dataclass(frozen=True)
|
31
59
|
class Config(Bootstrap.Config):
|
32
60
|
enable: bool = False
|
@@ -128,3 +156,22 @@ class TimebombBootstrap(ContextBootstrap['TimebombBootstrap.Config']):
|
|
128
156
|
yield
|
129
157
|
finally:
|
130
158
|
tbt.stop_nowait()
|
159
|
+
|
160
|
+
|
161
|
+
##
|
162
|
+
|
163
|
+
|
164
|
+
class PycharmBootstrap(SimpleBootstrap['PycharmBootstrap.Config']):
|
165
|
+
@dc.dataclass(frozen=True)
|
166
|
+
class Config(Bootstrap.Config):
|
167
|
+
debug_host: ta.Optional[str] = None
|
168
|
+
debug_port: ta.Optional[int] = None
|
169
|
+
debug_version: ta.Optional[str] = None
|
170
|
+
|
171
|
+
def run(self) -> None:
|
172
|
+
if self._config.debug_port is not None:
|
173
|
+
diagpc.pycharm_remote_debugger_attach(
|
174
|
+
self._config.debug_host,
|
175
|
+
self._config.debug_port,
|
176
|
+
version=self._config.debug_version,
|
177
|
+
)
|
omlish/bootstrap/main.py
CHANGED
@@ -156,8 +156,9 @@ def _main() -> int:
|
|
156
156
|
with bootstrap(*cfgs):
|
157
157
|
tgt = args.target
|
158
158
|
|
159
|
+
sys.argv = [tgt, *(args.args or ())]
|
160
|
+
|
159
161
|
if args.module:
|
160
|
-
sys.argv = [tgt, *(args.args or ())]
|
161
162
|
runpy._run_module_as_main(tgt) # type: ignore # noqa
|
162
163
|
|
163
164
|
else:
|
@@ -0,0 +1,18 @@
|
|
1
|
+
from .. import lang
|
2
|
+
from .. import marshal as msh
|
3
|
+
from .base import Bootstrap
|
4
|
+
from .harness import BOOTSTRAP_TYPES_BY_NAME
|
5
|
+
|
6
|
+
|
7
|
+
@lang.cached_function
|
8
|
+
def _install_standard_marshalling() -> None:
|
9
|
+
cfgs_poly = msh.Polymorphism(
|
10
|
+
Bootstrap.Config,
|
11
|
+
[msh.Impl(b.Config, n) for n, b in BOOTSTRAP_TYPES_BY_NAME.items()],
|
12
|
+
)
|
13
|
+
|
14
|
+
msh.STANDARD_MARSHALER_FACTORIES[0:0] = [msh.PolymorphismMarshalerFactory(cfgs_poly)]
|
15
|
+
msh.STANDARD_UNMARSHALER_FACTORIES[0:0] = [msh.PolymorphismUnmarshalerFactory(cfgs_poly)]
|
16
|
+
|
17
|
+
|
18
|
+
_install_standard_marshalling()
|
omlish/check.py
CHANGED
@@ -3,6 +3,7 @@ TODO:
|
|
3
3
|
- def maybe(v: lang.Maybe[T])
|
4
4
|
"""
|
5
5
|
import collections
|
6
|
+
import threading
|
6
7
|
import typing as ta
|
7
8
|
|
8
9
|
|
@@ -21,6 +22,62 @@ _callable = callable
|
|
21
22
|
##
|
22
23
|
|
23
24
|
|
25
|
+
_CONFIG_LOCK = threading.RLock()
|
26
|
+
|
27
|
+
|
28
|
+
OnRaiseFn: ta.TypeAlias = ta.Callable[[Exception], None]
|
29
|
+
_ON_RAISE: ta.Sequence[OnRaiseFn] = []
|
30
|
+
|
31
|
+
|
32
|
+
def register_on_raise(fn: OnRaiseFn) -> None:
|
33
|
+
global _ON_RAISE
|
34
|
+
with _CONFIG_LOCK:
|
35
|
+
_ON_RAISE = [*_ON_RAISE, fn]
|
36
|
+
|
37
|
+
|
38
|
+
def unregister_on_raise(fn: OnRaiseFn) -> None:
|
39
|
+
global _ON_RAISE
|
40
|
+
with _CONFIG_LOCK:
|
41
|
+
_ON_RAISE = [e for e in _ON_RAISE if e != fn]
|
42
|
+
|
43
|
+
|
44
|
+
#
|
45
|
+
|
46
|
+
|
47
|
+
_render_args: ta.Callable[..., str | None] | None = None
|
48
|
+
|
49
|
+
|
50
|
+
def enable_args_rendering() -> bool:
|
51
|
+
global _render_args
|
52
|
+
if _render_args is not None:
|
53
|
+
return True
|
54
|
+
|
55
|
+
with _CONFIG_LOCK:
|
56
|
+
if _render_args is not None:
|
57
|
+
return True # type: ignore
|
58
|
+
|
59
|
+
try:
|
60
|
+
from .diag.asts import ArgsRenderer
|
61
|
+
|
62
|
+
ArgsRenderer.smoketest()
|
63
|
+
|
64
|
+
except Exception: # noqa
|
65
|
+
return False
|
66
|
+
|
67
|
+
def _real_render_args(fmt: str, *args: ta.Any) -> str | None:
|
68
|
+
ra = ArgsRenderer(back=3).render_args(*args)
|
69
|
+
if ra is None:
|
70
|
+
return None
|
71
|
+
|
72
|
+
return fmt % tuple(str(a) for a in ra)
|
73
|
+
|
74
|
+
_render_args = _real_render_args
|
75
|
+
return True
|
76
|
+
|
77
|
+
|
78
|
+
#
|
79
|
+
|
80
|
+
|
24
81
|
def _default_exception_factory(exc_cls: type[Exception], *args, **kwargs) -> Exception:
|
25
82
|
return exc_cls(*args, **kwargs) # noqa
|
26
83
|
|
@@ -28,20 +85,45 @@ def _default_exception_factory(exc_cls: type[Exception], *args, **kwargs) -> Exc
|
|
28
85
|
_EXCEPTION_FACTORY = _default_exception_factory
|
29
86
|
|
30
87
|
|
88
|
+
class _Args:
|
89
|
+
def __init__(self, *args, **kwargs):
|
90
|
+
self.args = args
|
91
|
+
self.kwargs = kwargs
|
92
|
+
|
93
|
+
|
31
94
|
def _raise(
|
32
95
|
exception_type: type[Exception],
|
33
96
|
default_message: str,
|
34
97
|
message: Message,
|
35
|
-
|
36
|
-
|
98
|
+
ak: _Args = _Args(),
|
99
|
+
*,
|
100
|
+
render_fmt: str | None = None,
|
37
101
|
) -> ta.NoReturn:
|
102
|
+
exc_args = ()
|
38
103
|
if _callable(message):
|
39
|
-
message = ta.cast(ta.Callable, message)(*args, **kwargs)
|
104
|
+
message = ta.cast(ta.Callable, message)(*ak.args, **ak.kwargs)
|
40
105
|
if _isinstance(message, tuple):
|
41
|
-
message, *
|
106
|
+
message, *exc_args = message # type: ignore
|
107
|
+
|
42
108
|
if message is None:
|
43
109
|
message = default_message
|
44
|
-
|
110
|
+
|
111
|
+
if render_fmt is not None and _render_args is not None:
|
112
|
+
rendered_args = _render_args(render_fmt, *ak.args)
|
113
|
+
if rendered_args is not None:
|
114
|
+
message = f'{message} : {rendered_args}'
|
115
|
+
|
116
|
+
exc = _EXCEPTION_FACTORY(
|
117
|
+
exception_type,
|
118
|
+
message,
|
119
|
+
*exc_args,
|
120
|
+
*ak.args,
|
121
|
+
**ak.kwargs,
|
122
|
+
)
|
123
|
+
|
124
|
+
for fn in _ON_RAISE:
|
125
|
+
fn(exc)
|
126
|
+
|
45
127
|
raise exc
|
46
128
|
|
47
129
|
|
@@ -60,7 +142,7 @@ def _unpack_isinstance_spec(spec: ta.Any) -> tuple:
|
|
60
142
|
|
61
143
|
def isinstance(v: ta.Any, spec: type[T] | tuple, msg: Message = None) -> T: # noqa
|
62
144
|
if not _isinstance(v, _unpack_isinstance_spec(spec)):
|
63
|
-
_raise(TypeError, 'Must be instance', msg, v, spec)
|
145
|
+
_raise(TypeError, 'Must be instance', msg, _Args(v, spec))
|
64
146
|
return v
|
65
147
|
|
66
148
|
|
@@ -73,7 +155,7 @@ def of_isinstance(spec: type[T] | tuple, msg: Message = None) -> ta.Callable[[ta
|
|
73
155
|
|
74
156
|
def cast(v: ta.Any, cls: type[T], msg: Message = None) -> T: # noqa
|
75
157
|
if not _isinstance(v, cls):
|
76
|
-
_raise(TypeError, 'Must be instance', msg, v, cls)
|
158
|
+
_raise(TypeError, 'Must be instance', msg, _Args(v, cls))
|
77
159
|
return v
|
78
160
|
|
79
161
|
|
@@ -86,7 +168,7 @@ def of_cast(cls: type[T], msg: Message = None) -> ta.Callable[[T], T]:
|
|
86
168
|
|
87
169
|
def not_isinstance(v: T, spec: ta.Any, msg: Message = None) -> T: # noqa
|
88
170
|
if _isinstance(v, _unpack_isinstance_spec(spec)):
|
89
|
-
_raise(TypeError, 'Must not be instance', msg, v, spec)
|
171
|
+
_raise(TypeError, 'Must not be instance', msg, _Args(v, spec))
|
90
172
|
return v
|
91
173
|
|
92
174
|
|
@@ -102,13 +184,13 @@ def of_not_isinstance(spec: ta.Any, msg: Message = None) -> ta.Callable[[T], T]:
|
|
102
184
|
|
103
185
|
def issubclass(v: type[T], spec: ta.Any, msg: Message = None) -> type[T]: # noqa
|
104
186
|
if not _issubclass(v, spec):
|
105
|
-
_raise(TypeError, 'Must be subclass', msg, v, spec)
|
187
|
+
_raise(TypeError, 'Must be subclass', msg, _Args(v, spec))
|
106
188
|
return v
|
107
189
|
|
108
190
|
|
109
191
|
def not_issubclass(v: type[T], spec: ta.Any, msg: Message = None) -> type[T]: # noqa
|
110
192
|
if _issubclass(v, spec):
|
111
|
-
_raise(TypeError, 'Must not be subclass', msg, v, spec)
|
193
|
+
_raise(TypeError, 'Must not be subclass', msg, _Args(v, spec))
|
112
194
|
return v
|
113
195
|
|
114
196
|
|
@@ -117,32 +199,43 @@ def not_issubclass(v: type[T], spec: ta.Any, msg: Message = None) -> type[T]: #
|
|
117
199
|
|
118
200
|
def in_(v: T, c: ta.Container[T], msg: Message = None) -> T:
|
119
201
|
if v not in c:
|
120
|
-
_raise(ValueError, 'Must be in', msg, v, c)
|
202
|
+
_raise(ValueError, 'Must be in', msg, _Args(v, c))
|
121
203
|
return v
|
122
204
|
|
123
205
|
|
124
206
|
def not_in(v: T, c: ta.Container[T], msg: Message = None) -> T:
|
125
207
|
if v in c:
|
126
|
-
_raise(ValueError, 'Must not be in', msg, v, c)
|
208
|
+
_raise(ValueError, 'Must not be in', msg, _Args(v, c))
|
127
209
|
return v
|
128
210
|
|
129
211
|
|
130
212
|
def empty(v: SizedT, msg: Message = None) -> SizedT:
|
131
213
|
if len(v) != 0:
|
132
|
-
_raise(ValueError, 'Must be empty', msg, v)
|
214
|
+
_raise(ValueError, 'Must be empty', msg, _Args(v))
|
215
|
+
return v
|
216
|
+
|
217
|
+
|
218
|
+
def iterempty(v: ta.Iterable[T], msg: Message = None) -> ta.Iterable[T]:
|
219
|
+
it = iter(v)
|
220
|
+
try:
|
221
|
+
next(it)
|
222
|
+
except StopIteration:
|
223
|
+
pass
|
224
|
+
else:
|
225
|
+
_raise(ValueError, 'Must be empty', msg, _Args(v))
|
133
226
|
return v
|
134
227
|
|
135
228
|
|
136
229
|
def not_empty(v: SizedT, msg: Message = None) -> SizedT:
|
137
230
|
if len(v) == 0:
|
138
|
-
_raise(ValueError, 'Must not be empty', msg, v)
|
231
|
+
_raise(ValueError, 'Must not be empty', msg, _Args(v))
|
139
232
|
return v
|
140
233
|
|
141
234
|
|
142
235
|
def unique(it: ta.Iterable[T], msg: Message = None) -> ta.Iterable[T]:
|
143
236
|
dupes = [e for e, c in collections.Counter(it).items() if c > 1]
|
144
237
|
if dupes:
|
145
|
-
_raise(ValueError, 'Must be unique', msg, it, dupes)
|
238
|
+
_raise(ValueError, 'Must be unique', msg, _Args(it, dupes))
|
146
239
|
return it
|
147
240
|
|
148
241
|
|
@@ -150,7 +243,7 @@ def single(obj: ta.Iterable[T], message: Message = None) -> T:
|
|
150
243
|
try:
|
151
244
|
[value] = obj
|
152
245
|
except ValueError:
|
153
|
-
_raise(ValueError, 'Must be single', message, obj)
|
246
|
+
_raise(ValueError, 'Must be single', message, _Args(obj))
|
154
247
|
else:
|
155
248
|
return value
|
156
249
|
|
@@ -165,7 +258,7 @@ def optional_single(obj: ta.Iterable[T], message: Message = None) -> T | None:
|
|
165
258
|
next(it)
|
166
259
|
except StopIteration:
|
167
260
|
return value # noqa
|
168
|
-
_raise(ValueError, 'Must be empty or single', message, obj)
|
261
|
+
_raise(ValueError, 'Must be empty or single', message, _Args(obj))
|
169
262
|
|
170
263
|
|
171
264
|
##
|
@@ -173,48 +266,45 @@ def optional_single(obj: ta.Iterable[T], message: Message = None) -> T | None:
|
|
173
266
|
|
174
267
|
def none(v: ta.Any, msg: Message = None) -> None:
|
175
268
|
if v is not None:
|
176
|
-
_raise(ValueError, 'Must be None', msg, v)
|
269
|
+
_raise(ValueError, 'Must be None', msg, _Args(v))
|
177
270
|
|
178
271
|
|
179
272
|
def not_none(v: T | None, msg: Message = None) -> T:
|
180
273
|
if v is None:
|
181
|
-
_raise(ValueError, 'Must not be None', msg, v)
|
274
|
+
_raise(ValueError, 'Must not be None', msg, _Args(v))
|
182
275
|
return v
|
183
276
|
|
184
277
|
|
185
278
|
##
|
186
279
|
|
187
280
|
|
188
|
-
def equal(v: T,
|
189
|
-
|
190
|
-
|
191
|
-
_raise(ValueError, 'Must be equal', msg, v, os)
|
281
|
+
def equal(v: T, o: ta.Any, msg: Message = None) -> T:
|
282
|
+
if o != v:
|
283
|
+
_raise(ValueError, 'Must be equal', msg, _Args(v, o), render_fmt='%s != %s')
|
192
284
|
return v
|
193
285
|
|
194
286
|
|
195
|
-
def is_(v: T,
|
196
|
-
|
197
|
-
|
198
|
-
_raise(ValueError, 'Must be the same', msg, v, os)
|
287
|
+
def is_(v: T, o: ta.Any, msg: Message = None) -> T:
|
288
|
+
if o is not v:
|
289
|
+
_raise(ValueError, 'Must be the same', msg, _Args(v, o), render_fmt='%s is not %s')
|
199
290
|
return v
|
200
291
|
|
201
292
|
|
202
|
-
def is_not(v: T,
|
203
|
-
|
204
|
-
|
205
|
-
_raise(ValueError, 'Must not be the same', msg, v, os)
|
293
|
+
def is_not(v: T, o: ta.Any, msg: Message = None) -> T:
|
294
|
+
if o is v:
|
295
|
+
_raise(ValueError, 'Must not be the same', msg, _Args(v, o), render_fmt='%s is %s')
|
206
296
|
return v
|
207
297
|
|
208
298
|
|
209
299
|
def callable(v: T, msg: Message = None) -> T: # noqa
|
210
300
|
if not _callable(v):
|
211
|
-
_raise(TypeError, 'Must be callable', msg, v)
|
301
|
+
_raise(TypeError, 'Must be callable', msg, _Args(v))
|
212
302
|
return v # type: ignore
|
213
303
|
|
214
304
|
|
215
305
|
def non_empty_str(v: str | None, msg: Message = None) -> str:
|
216
306
|
if not _isinstance(v, str) or not v:
|
217
|
-
_raise(ValueError, 'Must be non-empty str', msg, v)
|
307
|
+
_raise(ValueError, 'Must be non-empty str', msg, _Args(v))
|
218
308
|
return v
|
219
309
|
|
220
310
|
|
omlish/diag/asts.py
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
import ast
|
2
|
+
import dataclasses as dc
|
3
|
+
import inspect
|
4
|
+
import pprint
|
5
|
+
import textwrap
|
6
|
+
import types
|
7
|
+
import typing as ta
|
8
|
+
|
9
|
+
from .. import lang
|
10
|
+
|
11
|
+
|
12
|
+
if ta.TYPE_CHECKING:
|
13
|
+
import executing
|
14
|
+
else:
|
15
|
+
executing = lang.proxy_import('executing')
|
16
|
+
|
17
|
+
|
18
|
+
class ArgsRenderer:
|
19
|
+
"""
|
20
|
+
TODO:
|
21
|
+
- kwargs
|
22
|
+
- recursion
|
23
|
+
- whatever pytest looks like
|
24
|
+
- make sure not leaking sensitive data
|
25
|
+
"""
|
26
|
+
|
27
|
+
def __init__(
|
28
|
+
self,
|
29
|
+
origin: types.TracebackType | types.FrameType | None = None,
|
30
|
+
back: int = 1,
|
31
|
+
) -> None:
|
32
|
+
super().__init__()
|
33
|
+
|
34
|
+
self._origin = origin
|
35
|
+
|
36
|
+
self._frame: types.FrameType | None
|
37
|
+
if isinstance(origin, types.TracebackType):
|
38
|
+
self._frame = origin.tb_frame
|
39
|
+
elif isinstance(origin, types.FrameType):
|
40
|
+
self._frame = origin
|
41
|
+
elif origin is None:
|
42
|
+
frame = inspect.currentframe()
|
43
|
+
for _ in range(back + 1):
|
44
|
+
if frame is None:
|
45
|
+
break
|
46
|
+
frame = frame.f_back
|
47
|
+
self._frame = frame
|
48
|
+
else:
|
49
|
+
raise TypeError(origin)
|
50
|
+
|
51
|
+
def _get_indented_text(
|
52
|
+
self,
|
53
|
+
src: executing.Source,
|
54
|
+
node: ast.AST,
|
55
|
+
) -> str:
|
56
|
+
result = src.asttokens().get_text(node)
|
57
|
+
if '\n' in result:
|
58
|
+
result = ' ' * node.first_token.start[1] + result # type: ignore
|
59
|
+
result = textwrap.dedent(result)
|
60
|
+
result = result.strip()
|
61
|
+
return result
|
62
|
+
|
63
|
+
def _val_to_string(self, obj: ta.Any) -> str:
|
64
|
+
s = pprint.pformat(obj)
|
65
|
+
s = s.replace('\\n', '\n')
|
66
|
+
return s
|
67
|
+
|
68
|
+
def _is_literal_expr(self, s: str) -> bool:
|
69
|
+
try:
|
70
|
+
ast.literal_eval(s)
|
71
|
+
except Exception: # noqa
|
72
|
+
return False
|
73
|
+
return True
|
74
|
+
|
75
|
+
@dc.dataclass(frozen=True)
|
76
|
+
class RenderedArg:
|
77
|
+
val: str
|
78
|
+
expr: str
|
79
|
+
is_literal_expr: bool
|
80
|
+
|
81
|
+
def __str__(self) -> str:
|
82
|
+
if self.is_literal_expr:
|
83
|
+
return self.val
|
84
|
+
else:
|
85
|
+
return f'({self.expr} = {self.val})'
|
86
|
+
|
87
|
+
def render_args(
|
88
|
+
self,
|
89
|
+
*vals: ta.Any,
|
90
|
+
) -> ta.Sequence[RenderedArg] | None:
|
91
|
+
if self._frame is None:
|
92
|
+
return None
|
93
|
+
|
94
|
+
call_node = executing.Source.executing(self._frame).node
|
95
|
+
if not isinstance(call_node, ast.Call):
|
96
|
+
return None
|
97
|
+
|
98
|
+
source = executing.Source.for_frame(self._frame)
|
99
|
+
|
100
|
+
exprs = [
|
101
|
+
self._get_indented_text(source, arg) # noqa
|
102
|
+
for arg in call_node.args
|
103
|
+
]
|
104
|
+
if len(exprs) != len(vals):
|
105
|
+
return None
|
106
|
+
|
107
|
+
return [
|
108
|
+
self.RenderedArg(
|
109
|
+
val=self._val_to_string(val),
|
110
|
+
expr=expr,
|
111
|
+
is_literal_expr=self._is_literal_expr(expr),
|
112
|
+
)
|
113
|
+
for val, expr in zip(vals, exprs)
|
114
|
+
]
|
115
|
+
|
116
|
+
@classmethod
|
117
|
+
def smoketest(cls) -> bool:
|
118
|
+
def bar(z):
|
119
|
+
return z + 1
|
120
|
+
|
121
|
+
def foo(x, y):
|
122
|
+
return cls().render_args(x, y)
|
123
|
+
|
124
|
+
r = foo(1, bar(2))
|
125
|
+
if r is None:
|
126
|
+
return False
|
127
|
+
|
128
|
+
x, y = r
|
129
|
+
return (
|
130
|
+
x == cls.RenderedArg(val='1', expr='1', is_literal_expr=True) and
|
131
|
+
y == cls.RenderedArg(val='3', expr='bar(2)', is_literal_expr=False)
|
132
|
+
)
|
omlish/diag/pycharm.py
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
import os.path
|
2
|
+
import plistlib
|
3
|
+
import subprocess
|
4
|
+
import sys
|
5
|
+
import typing as ta
|
6
|
+
|
7
|
+
from .. import check
|
8
|
+
from .. import lang
|
9
|
+
|
10
|
+
|
11
|
+
if ta.TYPE_CHECKING:
|
12
|
+
docker = lang.proxy_import('omlish.docker')
|
13
|
+
else:
|
14
|
+
from omlish import docker
|
15
|
+
|
16
|
+
|
17
|
+
##
|
18
|
+
|
19
|
+
|
20
|
+
PYCHARM_HOME = '/Applications/PyCharm.app'
|
21
|
+
|
22
|
+
|
23
|
+
def read_pycharm_info_plist() -> ta.Mapping[str, ta.Any] | None:
|
24
|
+
plist_file = os.path.join(PYCHARM_HOME, 'Contents', 'Info.plist')
|
25
|
+
if not os.path.isfile(plist_file):
|
26
|
+
return None
|
27
|
+
|
28
|
+
with open(plist_file, 'rb') as f:
|
29
|
+
root = plistlib.load(f)
|
30
|
+
|
31
|
+
return root
|
32
|
+
|
33
|
+
|
34
|
+
@lang.cached_function
|
35
|
+
def get_pycharm_version() -> str | None:
|
36
|
+
plist = read_pycharm_info_plist()
|
37
|
+
if plist is None:
|
38
|
+
return None
|
39
|
+
|
40
|
+
ver = check.non_empty_str(plist['CFBundleVersion'])
|
41
|
+
check.state(ver.startswith('PY-'))
|
42
|
+
return ver[3:]
|
43
|
+
|
44
|
+
|
45
|
+
##
|
46
|
+
|
47
|
+
|
48
|
+
def pycharm_remote_debugger_attach(
|
49
|
+
host: str | None,
|
50
|
+
port: int,
|
51
|
+
*,
|
52
|
+
version: str | None = None,
|
53
|
+
) -> None:
|
54
|
+
# if version is None:
|
55
|
+
# version = get_pycharm_version()
|
56
|
+
# check.non_empty_str(version)
|
57
|
+
|
58
|
+
if host is None:
|
59
|
+
if sys.platform == 'linux' and docker.is_likely_in_docker():
|
60
|
+
host = docker.DOCKER_FOR_MAC_HOSTNAME
|
61
|
+
else:
|
62
|
+
host = 'localhost'
|
63
|
+
|
64
|
+
try:
|
65
|
+
import pydevd_pycharm # noqa
|
66
|
+
except ImportError:
|
67
|
+
subprocess.check_call([
|
68
|
+
sys.executable,
|
69
|
+
'-mpip',
|
70
|
+
'install',
|
71
|
+
'pydevd-pycharm' + (f'~={version}' if version is not None else ''),
|
72
|
+
])
|
73
|
+
|
74
|
+
import pydevd_pycharm # noqa
|
75
|
+
pydevd_pycharm.settrace(
|
76
|
+
host,
|
77
|
+
port=port,
|
78
|
+
stdoutToServer=True,
|
79
|
+
stderrToServer=True,
|
80
|
+
)
|
omlish/docker.py
CHANGED
omlish/genmachine.py
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
"""
|
2
|
+
https://github.com/pytransitions/transitions
|
3
|
+
"""
|
4
|
+
import typing as ta
|
5
|
+
|
6
|
+
|
7
|
+
I = ta.TypeVar('I')
|
8
|
+
O = ta.TypeVar('O')
|
9
|
+
|
10
|
+
# MachineGen: ta.TypeAlias = ta.Generator[ta.Iterable[O] | None, I, ta.Optional[MachineGen[I, O]]]
|
11
|
+
MachineGen: ta.TypeAlias = ta.Generator[ta.Any, ta.Any, ta.Any]
|
12
|
+
|
13
|
+
|
14
|
+
##
|
15
|
+
|
16
|
+
|
17
|
+
class IllegalStateError(Exception):
|
18
|
+
pass
|
19
|
+
|
20
|
+
|
21
|
+
class GenMachine(ta.Generic[I, O]):
|
22
|
+
"""
|
23
|
+
Generator-powered state machine. Generators are sent an `I` object and yield any number of `O` objects in response,
|
24
|
+
until they yield a `None` by accepting new input. Generators may return a new generator to switch states, or return
|
25
|
+
`None` to terminate.
|
26
|
+
"""
|
27
|
+
|
28
|
+
def __init__(self, initial: MachineGen) -> None:
|
29
|
+
super().__init__()
|
30
|
+
self._advance(initial)
|
31
|
+
|
32
|
+
@property
|
33
|
+
def state(self) -> str | None:
|
34
|
+
if self._gen is not None:
|
35
|
+
return self._gen.gi_code.co_qualname
|
36
|
+
return None
|
37
|
+
|
38
|
+
def __repr__(self) -> str:
|
39
|
+
return f'{self.__class__.__name__}@{hex(id(self))[2:]}<{self.state}>'
|
40
|
+
|
41
|
+
_gen: MachineGen | None
|
42
|
+
|
43
|
+
def _advance(self, gen: MachineGen) -> None:
|
44
|
+
self._gen = gen
|
45
|
+
if (n := next(self._gen)) is not None: # noqa
|
46
|
+
raise IllegalStateError
|
47
|
+
|
48
|
+
def __call__(self, i: I) -> ta.Iterable[O]:
|
49
|
+
if self._gen is None:
|
50
|
+
raise IllegalStateError
|
51
|
+
try:
|
52
|
+
while (o := self._gen.send(i)) is not None:
|
53
|
+
yield from o
|
54
|
+
except StopIteration as s:
|
55
|
+
if s.value is None:
|
56
|
+
self._gen = None
|
57
|
+
return None
|
58
|
+
self._advance(s.value)
|
omlish/marshal/__init__.py
CHANGED
omlish/marshal/base64.py
CHANGED
omlish/marshal/primitives.py
CHANGED
@@ -9,6 +9,9 @@ from .base import Unmarshaler
|
|
9
9
|
from .values import Value
|
10
10
|
|
11
11
|
|
12
|
+
##
|
13
|
+
|
14
|
+
|
12
15
|
PRIMITIVE_TYPES: tuple[type, ...] = (
|
13
16
|
bool,
|
14
17
|
int,
|
@@ -19,6 +22,9 @@ PRIMITIVE_TYPES: tuple[type, ...] = (
|
|
19
22
|
)
|
20
23
|
|
21
24
|
|
25
|
+
##
|
26
|
+
|
27
|
+
|
22
28
|
class PrimitiveMarshalerUnmarshaler(Marshaler, Unmarshaler):
|
23
29
|
def marshal(self, ctx: MarshalContext, o: ta.Any) -> Value:
|
24
30
|
if isinstance(o, PRIMITIVE_TYPES):
|
omlish/marshal/standard.py
CHANGED
@@ -27,6 +27,8 @@ from .optionals import OptionalMarshalerFactory
|
|
27
27
|
from .optionals import OptionalUnmarshalerFactory
|
28
28
|
from .primitives import PRIMITIVE_MARSHALER_FACTORY
|
29
29
|
from .primitives import PRIMITIVE_UNMARSHALER_FACTORY
|
30
|
+
from .unions import PrimitiveUnionMarshalerFactory
|
31
|
+
from .unions import PrimitiveUnionUnmarshalerFactory
|
30
32
|
from .uuids import UUID_MARSHALER_FACTORY
|
31
33
|
from .uuids import UUID_UNMARSHALER_FACTORY
|
32
34
|
|
@@ -37,6 +39,7 @@ from .uuids import UUID_UNMARSHALER_FACTORY
|
|
37
39
|
STANDARD_MARSHALER_FACTORIES: list[MarshalerFactory] = [
|
38
40
|
PRIMITIVE_MARSHALER_FACTORY,
|
39
41
|
OptionalMarshalerFactory(),
|
42
|
+
PrimitiveUnionMarshalerFactory(),
|
40
43
|
DataclassMarshalerFactory(),
|
41
44
|
EnumMarshalerFactory(),
|
42
45
|
NUMBERS_MARSHALER_FACTORY,
|
@@ -66,6 +69,7 @@ def new_standard_marshaler_factory() -> MarshalerFactory:
|
|
66
69
|
STANDARD_UNMARSHALER_FACTORIES: list[UnmarshalerFactory] = [
|
67
70
|
PRIMITIVE_UNMARSHALER_FACTORY,
|
68
71
|
OptionalUnmarshalerFactory(),
|
72
|
+
PrimitiveUnionUnmarshalerFactory(),
|
69
73
|
DataclassUnmarshalerFactory(),
|
70
74
|
EnumUnmarshalerFactory(),
|
71
75
|
NUMBERS_UNMARSHALER_FACTORY,
|
omlish/marshal/unions.py
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
import dataclasses as dc
|
2
|
+
import typing as ta
|
3
|
+
|
4
|
+
from .. import check
|
5
|
+
from .. import matchfns as mfs
|
6
|
+
from .. import reflect as rfl
|
7
|
+
from .base import MarshalContext
|
8
|
+
from .base import Marshaler
|
9
|
+
from .base import MarshalerFactory
|
10
|
+
from .base import UnmarshalContext
|
11
|
+
from .base import Unmarshaler
|
12
|
+
from .base import UnmarshalerFactory
|
13
|
+
from .values import Value
|
14
|
+
|
15
|
+
|
16
|
+
##
|
17
|
+
|
18
|
+
|
19
|
+
class MatchUnionMarshaler(Marshaler):
|
20
|
+
mmf: mfs.MultiMatchFn[[UnmarshalContext, Value], ta.Any]
|
21
|
+
|
22
|
+
def marshal(self, ctx: MarshalContext, o: ta.Any) -> Value:
|
23
|
+
try:
|
24
|
+
m = self.mmf.match(ctx, o)
|
25
|
+
except mfs.AmbiguousMatchesError:
|
26
|
+
raise ValueError(o) # noqa
|
27
|
+
return m.fn(ctx, o)
|
28
|
+
|
29
|
+
|
30
|
+
class MatchUnionUnmarshaler(Unmarshaler):
|
31
|
+
mmf: mfs.MultiMatchFn[[UnmarshalContext, Value], ta.Any]
|
32
|
+
|
33
|
+
def unmarshal(self, ctx: UnmarshalContext, v: Value) -> ta.Any:
|
34
|
+
try:
|
35
|
+
m = self.mmf.match(ctx, v)
|
36
|
+
except mfs.AmbiguousMatchesError:
|
37
|
+
raise ValueError(v) # noqa
|
38
|
+
return m.fn(ctx, v)
|
39
|
+
|
40
|
+
|
41
|
+
##
|
42
|
+
|
43
|
+
|
44
|
+
PRIMITIVE_UNION_TYPES: tuple[type, ...] = (
|
45
|
+
float,
|
46
|
+
int,
|
47
|
+
str,
|
48
|
+
)
|
49
|
+
|
50
|
+
|
51
|
+
#
|
52
|
+
|
53
|
+
|
54
|
+
@dc.dataclass(frozen=True)
|
55
|
+
class PrimitiveUnionMarshaler(Marshaler):
|
56
|
+
tys: ta.Sequence[type]
|
57
|
+
|
58
|
+
def marshal(self, ctx: MarshalContext, o: ta.Any) -> Value:
|
59
|
+
raise NotImplementedError
|
60
|
+
|
61
|
+
|
62
|
+
@dc.dataclass(frozen=True)
|
63
|
+
class PrimitiveUnionMarshalerFactory(MarshalerFactory):
|
64
|
+
tys: ta.Sequence[type] = PRIMITIVE_UNION_TYPES
|
65
|
+
|
66
|
+
def guard(self, ctx: MarshalContext, rty: rfl.Type) -> bool:
|
67
|
+
return isinstance(rty, rfl.Union) and all(a in self.tys for a in rty.args)
|
68
|
+
|
69
|
+
def fn(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler:
|
70
|
+
args = check.isinstance(rty, rfl.Union).args
|
71
|
+
return PrimitiveUnionMarshaler([t for t in self.tys if t in args])
|
72
|
+
|
73
|
+
|
74
|
+
#
|
75
|
+
|
76
|
+
|
77
|
+
@dc.dataclass(frozen=True)
|
78
|
+
class PrimitiveUnionUnmarshaler(Unmarshaler):
|
79
|
+
tys: ta.Sequence[type]
|
80
|
+
|
81
|
+
def unmarshal(self, ctx: UnmarshalContext, v: Value) -> ta.Any:
|
82
|
+
raise NotImplementedError
|
83
|
+
|
84
|
+
|
85
|
+
@dc.dataclass(frozen=True)
|
86
|
+
class PrimitiveUnionUnmarshalerFactory(UnmarshalerFactory):
|
87
|
+
tys: ta.Sequence[type] = PRIMITIVE_UNION_TYPES
|
88
|
+
|
89
|
+
def guard(self, ctx: UnmarshalContext, rty: rfl.Type) -> bool:
|
90
|
+
return isinstance(rty, rfl.Union) and all(a in self.tys for a in rty.args)
|
91
|
+
|
92
|
+
def fn(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler:
|
93
|
+
args = check.isinstance(rty, rfl.Union).args
|
94
|
+
return PrimitiveUnionUnmarshaler([t for t in self.tys if t in args])
|
95
|
+
|
96
|
+
|
97
|
+
#
|
98
|
+
|
99
|
+
|
100
|
+
PRIMITIVE_UNION_MARSHALER_FACTORY = PrimitiveUnionMarshalerFactory()
|
101
|
+
PRIMITIVE_UNION_UNMARSHALER_FACTORY = PrimitiveUnionUnmarshalerFactory()
|
omlish/matchfns.py
CHANGED
@@ -93,7 +93,7 @@ class MultiMatchFn(MatchFn[P, T]):
|
|
93
93
|
children: ta.Sequence[MatchFn[P, T]]
|
94
94
|
strict: bool = False
|
95
95
|
|
96
|
-
def
|
96
|
+
def match(self, *args: P.args, **kwargs: P.kwargs) -> MatchFn[P, T] | None:
|
97
97
|
matches = []
|
98
98
|
for cur in self.children:
|
99
99
|
if cur.guard(*args, **kwargs):
|
@@ -109,10 +109,10 @@ class MultiMatchFn(MatchFn[P, T]):
|
|
109
109
|
return matches[0]
|
110
110
|
|
111
111
|
def guard(self, *args: P.args, **kwargs: P.kwargs) -> bool:
|
112
|
-
return self.
|
112
|
+
return self.match(*args, **kwargs) is not None
|
113
113
|
|
114
114
|
def fn(self, *args: P.args, **kwargs: P.kwargs) -> T:
|
115
|
-
if (m := self.
|
115
|
+
if (m := self.match(*args, **kwargs)) is None:
|
116
116
|
raise MatchGuardError(*args, **kwargs)
|
117
117
|
return m.fn(*args, **kwargs)
|
118
118
|
|
omlish/sql/__init__.py
CHANGED
@@ -7,3 +7,21 @@ from .asyncs import ( # noqa
|
|
7
7
|
AsyncTransactionLike,
|
8
8
|
async_adapt,
|
9
9
|
)
|
10
|
+
|
11
|
+
from .dbs import ( # noqa
|
12
|
+
DbLoc,
|
13
|
+
DbSpec,
|
14
|
+
DbType,
|
15
|
+
DbTypes,
|
16
|
+
HostDbLoc,
|
17
|
+
UrlDbLoc,
|
18
|
+
)
|
19
|
+
|
20
|
+
from .exprs import ( # noqa
|
21
|
+
paren,
|
22
|
+
)
|
23
|
+
|
24
|
+
from .qualifiedname import ( # noqa
|
25
|
+
QualifiedName,
|
26
|
+
qn,
|
27
|
+
)
|
@@ -0,0 +1,82 @@
|
|
1
|
+
import collections.abc
|
2
|
+
import dataclasses as dc
|
3
|
+
import typing as ta
|
4
|
+
|
5
|
+
|
6
|
+
@dc.dataclass(frozen=True)
|
7
|
+
class QualifiedName(ta.Sequence[str]):
|
8
|
+
parts: ta.Sequence[str]
|
9
|
+
|
10
|
+
def __post_init__(self) -> None:
|
11
|
+
if not (
|
12
|
+
self.parts and
|
13
|
+
not isinstance(self.parts, str) and
|
14
|
+
all(self.parts) and
|
15
|
+
all(isinstance(p, str) for p in self.parts)
|
16
|
+
):
|
17
|
+
raise ValueError(self)
|
18
|
+
|
19
|
+
def __repr__(self) -> str:
|
20
|
+
return f'{self.__class__.__name__}([{", ".join(map(repr, self.parts))}])'
|
21
|
+
|
22
|
+
@property
|
23
|
+
def dotted(self) -> str:
|
24
|
+
return '.'.join(self.parts)
|
25
|
+
|
26
|
+
def prefixed(self, sz: int) -> tuple[str | None, ...]:
|
27
|
+
if len(self) > sz:
|
28
|
+
raise ValueError(self)
|
29
|
+
return ((None,) * (sz - len(self))) + tuple(self.parts)
|
30
|
+
|
31
|
+
@property
|
32
|
+
def pair(self) -> tuple[str | None, str]:
|
33
|
+
return self.prefixed(2) # type: ignore
|
34
|
+
|
35
|
+
@property
|
36
|
+
def triple(self) -> tuple[str | None, str | None, str]:
|
37
|
+
return self.prefixed(3) # type: ignore
|
38
|
+
|
39
|
+
@property
|
40
|
+
def quad(self) -> tuple[str | None, str | None, str | None, str]:
|
41
|
+
return self.prefixed(4) # type: ignore
|
42
|
+
|
43
|
+
def __iter__(self) -> ta.Iterator[str]:
|
44
|
+
return iter(self.parts)
|
45
|
+
|
46
|
+
def __len__(self) -> int:
|
47
|
+
return len(self.parts)
|
48
|
+
|
49
|
+
def __getitem__(self, idx: int) -> str: # type: ignore
|
50
|
+
return self.parts[idx]
|
51
|
+
|
52
|
+
@classmethod
|
53
|
+
def of_dotted(cls, dotted: str) -> 'QualifiedName':
|
54
|
+
return cls(dotted.split('.'))
|
55
|
+
|
56
|
+
@classmethod
|
57
|
+
def of(
|
58
|
+
cls,
|
59
|
+
obj: ta.Union['QualifiedName', ta.Iterable[str]],
|
60
|
+
) -> 'QualifiedName':
|
61
|
+
if isinstance(obj, QualifiedName):
|
62
|
+
return obj
|
63
|
+
elif isinstance(obj, str):
|
64
|
+
raise TypeError(obj)
|
65
|
+
elif isinstance(obj, collections.abc.Iterable):
|
66
|
+
return cls(list(obj))
|
67
|
+
else:
|
68
|
+
raise TypeError(obj)
|
69
|
+
|
70
|
+
@classmethod
|
71
|
+
def of_optional(
|
72
|
+
cls,
|
73
|
+
obj: ta.Union['QualifiedName', ta.Iterable[str], None],
|
74
|
+
) -> ta.Optional['QualifiedName']:
|
75
|
+
if obj is None:
|
76
|
+
return None
|
77
|
+
else:
|
78
|
+
return cls.of(obj)
|
79
|
+
|
80
|
+
|
81
|
+
def qn(*args: str) -> QualifiedName:
|
82
|
+
return QualifiedName(args)
|
@@ -1,17 +1,18 @@
|
|
1
|
-
omlish/__about__.py,sha256=
|
1
|
+
omlish/__about__.py,sha256=KiGBpOncoW1f9W1y130G59BNQdvfrd0cRxVrJ-_d5JA,2532
|
2
2
|
omlish/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
3
|
omlish/argparse.py,sha256=QRQmX9G0-L_nATkFtGHvpd4qrpYzKATdjuFLbBqzJPM,6224
|
4
4
|
omlish/c3.py,sha256=W5EwYx9Por3rWYLkKUitJ6OoRMLLgVTfLTyroOz41Y0,8047
|
5
5
|
omlish/cached.py,sha256=UAizxlH4eMWHPzQtmItmyE6FEpFEUFzIkxaO2BHWZ5s,196
|
6
|
-
omlish/check.py,sha256=
|
6
|
+
omlish/check.py,sha256=18vZWH9qEVMs62LTfn_XC0U5GPeaOotKLmL41qecyNQ,7560
|
7
7
|
omlish/datetimes.py,sha256=HajeM1kBvwlTa-uR1TTZHmZ3zTPnnUr1uGGQhiO1XQ0,2152
|
8
8
|
omlish/defs.py,sha256=T3bq_7h_tO3nDB5RAFBn7DkdeQgqheXzkFColbOHZko,4890
|
9
|
-
omlish/docker.py,sha256=
|
9
|
+
omlish/docker.py,sha256=Nwyk4jpUmpd4NE0H1JeMQ42WC9NymJDZ8TPLQ9P24MY,4748
|
10
10
|
omlish/dynamic.py,sha256=35C_cCX_Vq2HrHzGk5T-zbrMvmUdiIiwDzDNixczoDo,6541
|
11
11
|
omlish/fnpairs.py,sha256=hVuLqQFdRNNze3FYH2cAQO3GC7nK5yQP_GWPUSbL7nE,10601
|
12
|
+
omlish/genmachine.py,sha256=LDIMOc5OXnJ5IBzRyHX4ZR6FqViVKowuiGVWamexP9k,1595
|
12
13
|
omlish/iterators.py,sha256=GGLC7RIT86uXMjhIIIqnff_Iu5SI_b9rXYywYGFyzmo,7292
|
13
14
|
omlish/libc.py,sha256=u0481imCiTFqP_e-v9g0pD-0WD249j5vYzhtn-fnNkY,15308
|
14
|
-
omlish/matchfns.py,sha256=
|
15
|
+
omlish/matchfns.py,sha256=ygqbqthRxgF9I1PJaw9Xl7FoZnAVMmnKBrYgimKQ0ag,6152
|
15
16
|
omlish/math.py,sha256=AVqp5Y8yxKA-wO0BgrzaxA0Ga3PZiCXnYcwivMneC-0,3804
|
16
17
|
omlish/multiprocessing.py,sha256=QZT4C7I-uThCAjaEY3xgUYb-5GagUlnE4etN01LDyU4,5186
|
17
18
|
omlish/os.py,sha256=cz4nL2ujaxH_-XRq3JUD8af8mSe1JXGPIoXP9XAEd0M,2607
|
@@ -23,16 +24,17 @@ omlish/asyncs/__init__.py,sha256=uUz9ziKh4_QrgmdhKFMgq6j7mFbiZd3LiogguDCQsGI,587
|
|
23
24
|
omlish/asyncs/anyio.py,sha256=Hqdi1iCopKoaAWGx-AYTRLEwnavLWx1esfJISK1IVF0,8024
|
24
25
|
omlish/asyncs/asyncio.py,sha256=JfM59QgB3asgEbrps0zoVbNjWD4kL2XdsEkRMEIoFos,971
|
25
26
|
omlish/asyncs/asyncs.py,sha256=Tf7ZodTGepkM7HAuFcDNh9lLzzrMw6rELWvopGmFkh4,2035
|
26
|
-
omlish/asyncs/bridge.py,sha256=
|
27
|
+
omlish/asyncs/bridge.py,sha256=fkMQWG2TNmPB9BfIxtXrE50W5FJw0ma44GleOXbcSKw,8628
|
27
28
|
omlish/asyncs/flavors.py,sha256=1mNxGNRVmjUHzA13K5ht8vdJv4CLEmzYTQ6BZXr1520,4866
|
28
29
|
omlish/asyncs/trio.py,sha256=GKG3wgelFr7gIKKHZhcflvMyCvxXHNZe862KB0Xw2uA,370
|
29
30
|
omlish/asyncs/trio_asyncio.py,sha256=oqdOHy0slj9PjVxaDf3gJkq9AAgg7wYZbB469jOftVw,1327
|
30
|
-
omlish/bootstrap/__init__.py,sha256
|
31
|
+
omlish/bootstrap/__init__.py,sha256=-Rtsg7uPQNhh1dIT9nqrz96XlqizwoLnWf-FwOEstJI,730
|
31
32
|
omlish/bootstrap/__main__.py,sha256=d23loR_cKfTYZwYiqpt_CmKI7dd5WcYFgIYzqMep75E,68
|
32
|
-
omlish/bootstrap/base.py,sha256=
|
33
|
-
omlish/bootstrap/diag.py,sha256=
|
33
|
+
omlish/bootstrap/base.py,sha256=koELbK6UmsZaRj-6Bng5_zPVmEBqDpFCduEdR5BddOs,1077
|
34
|
+
omlish/bootstrap/diag.py,sha256=8ajcsuY__5TlYzpbO6m4T87jw8uLcQ0wuJKdtQ7350w,4396
|
34
35
|
omlish/bootstrap/harness.py,sha256=vQCIhCQY_N0NHWvDh8rG6Lo57U1qHkQf2egifbXzN-8,2027
|
35
|
-
omlish/bootstrap/main.py,sha256=
|
36
|
+
omlish/bootstrap/main.py,sha256=9ZbgXaRNZwt_hXdg8W4YjpP0v0Si_qqlfu86jfgRz04,5215
|
37
|
+
omlish/bootstrap/marshal.py,sha256=qKewGVs-3i2p5W9nywkXSo1pcbVxmOAlTvfLjyo0xpo,554
|
36
38
|
omlish/bootstrap/sys.py,sha256=U0MFxO9tLFV3cdN5Y-Zrink6_45sFvzPUYQXyBk7-ns,8741
|
37
39
|
omlish/collections/__init__.py,sha256=h7gXQNMI_46hRRlIAI3PTaewMV8H381FV_KlONReg9s,1660
|
38
40
|
omlish/collections/_abc.py,sha256=sP7BpTVhx6s6C59mTFeosBi4rHOWC6tbFBYbxdZmvh0,2365
|
@@ -89,9 +91,11 @@ omlish/dataclasses/impl/simple.py,sha256=EovA-GpmQYtB_svItO2byTAWqbKGF4njz0MdQts
|
|
89
91
|
omlish/dataclasses/impl/slots.py,sha256=_sm-x9v1tPnXEHBHNUMTHZocgVyhZaPdvamIPPBUVyk,2635
|
90
92
|
omlish/dataclasses/impl/utils.py,sha256=aER2iL3UAtgS1BdLuEvTr9Tr2wC28wk1kiOeO-jIymw,6138
|
91
93
|
omlish/diag/__init__.py,sha256=BYQoq12W2qU0O7m2Z-RLCX6YLIYEW9MmfN7_i9--Yk0,132
|
94
|
+
omlish/diag/asts.py,sha256=BveUUNUcaAm4Hg55f4ZxGSI313E4L8cCZ5XjHpEkKVI,3325
|
92
95
|
omlish/diag/procfs.py,sha256=ggIeFoaNZ4j6HvKTiXD6Q3b9apgto7j55pwswCrIHXE,9581
|
93
96
|
omlish/diag/procstats.py,sha256=9uQMvVL7Yzf_1F8dW55pcZeCFtP5yUbK1KuGn9bt2Qg,502
|
94
97
|
omlish/diag/ps.py,sha256=1JWxZen3fVG-20R6ZZ8BtO_gpzw_5bhHZiKdoHkgxoU,1004
|
98
|
+
omlish/diag/pycharm.py,sha256=HgPvABfgzNU3rb77B5W8hZ4_5sP6l3W5hg8R6vM1Yr4,1686
|
95
99
|
omlish/diag/pydevd.py,sha256=Fsx9rfCOnwLD6RLBqH0uAdtq75rbNeBAQfiDvIBd3e0,7295
|
96
100
|
omlish/diag/threads.py,sha256=1-x02VCDZ407gfbtXm1pWK-ubqhqfePm9PMqkHCVoqk,3642
|
97
101
|
omlish/diag/replserver/__init__.py,sha256=uLo6V2aQ29v9z3IMELlPDSlG3_2iOT4-_X8VniF-EgE,235
|
@@ -206,10 +210,10 @@ omlish/logs/formatters.py,sha256=AFs9C6-qrFkgXZ0nL39wih_LGck1Tc79alvGyibBdQo,720
|
|
206
210
|
omlish/logs/handlers.py,sha256=nyuFgmO05By_Xwq7es58ClzS51-F53lJL7gD0x5IqAg,228
|
207
211
|
omlish/logs/noisy.py,sha256=8JORjI1dH38yU2MddM54OB6qt32Xozfocdb88vY4wro,335
|
208
212
|
omlish/logs/utils.py,sha256=MgGovbP0zUrZ3FGD3qYNQWn-l0jy0Y0bStcQvv5BOmQ,391
|
209
|
-
omlish/marshal/__init__.py,sha256=
|
213
|
+
omlish/marshal/__init__.py,sha256=Oqp0e27zj3YsI5Ek0CPvyH8Rfm3hWU0wzXJcjeu5HdQ,1573
|
210
214
|
omlish/marshal/any.py,sha256=e82OyYK3Emm1P1ClnsnxP7fIWC2iNVyW0H5nK4mLmWM,779
|
211
215
|
omlish/marshal/base.py,sha256=EIgrqsQ1OQ4mVUMuDH5zRBCwJpn8ijVS98Nmoka_Mrs,6025
|
212
|
-
omlish/marshal/base64.py,sha256=
|
216
|
+
omlish/marshal/base64.py,sha256=F-3ogJdcFCtWINRgJgWT0rErqgx6f4qahhcg8OrkqhE,1089
|
213
217
|
omlish/marshal/dataclasses.py,sha256=cqHfzkQe7T-A_eAx-xZm6AIOZLnqE-alMUXMtRTYxuI,3910
|
214
218
|
omlish/marshal/datetimes.py,sha256=0ffg8cEvx9SMKIXZGD9b7MqpLfmgw0uKKdn6YTfoqok,3714
|
215
219
|
omlish/marshal/enums.py,sha256=-0fKutBbyz8ygEaA0_P_8IOJrI9jMGigmnPbutV9Bg4,1464
|
@@ -226,9 +230,10 @@ omlish/marshal/numbers.py,sha256=oY_yMNJEnJhjfLh89gpPXvKqeUyhQcaTcQB6ecyHiG8,170
|
|
226
230
|
omlish/marshal/objects.py,sha256=T3prlm0HESpOLXHWcdsYekfmythJRZQOvaoL0is8J_o,3097
|
227
231
|
omlish/marshal/optionals.py,sha256=r0XB5rqfasvgZJNrKYd6Unq2U4nHt3JURi26j0dYHlw,1499
|
228
232
|
omlish/marshal/polymorphism.py,sha256=gyvNYUAkmQVhWrcXBLzXINxqx6RHyulf9n16Iv38PFI,5597
|
229
|
-
omlish/marshal/primitives.py,sha256
|
233
|
+
omlish/marshal/primitives.py,sha256=wcvcs5GH_TWVmzAszh3dvyKibJgBxnXke-AlAXiwrrI,1107
|
230
234
|
omlish/marshal/registries.py,sha256=GI2KogcxawMkk02Ky7-TsnijChoe1I7YTOPIbUNwSAI,1665
|
231
|
-
omlish/marshal/standard.py,sha256=
|
235
|
+
omlish/marshal/standard.py,sha256=uQZIGiCwihmhB1tmhpKnZWZly0DDkdGjCnN0d41WHho,2985
|
236
|
+
omlish/marshal/unions.py,sha256=m9uVp2HrkZKY9lDyGoWQGXFgan8mRuBaKimtWNksl64,2635
|
232
237
|
omlish/marshal/utils.py,sha256=puKJpwPpuDlMOIrKMcLTRLJyMiL6n_Xs-p59AuDEymA,543
|
233
238
|
omlish/marshal/uuids.py,sha256=H4B7UX_EPNmP2tC8bubcKrPLTS4aQu98huvbXQ3Zv2g,910
|
234
239
|
omlish/marshal/values.py,sha256=ssHiWdg_L6M17kAn8GiGdPW7UeQOm3RDikWkvwblf5I,263
|
@@ -257,12 +262,13 @@ omlish/specs/jsonschema/keywords/validation.py,sha256=ghewVUSG5qPYnmexy1Ylc-Xoui
|
|
257
262
|
omlish/specs/jsonschema/schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
258
263
|
omlish/specs/jsonschema/schemas/draft202012/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
259
264
|
omlish/specs/jsonschema/schemas/draft202012/vocabularies/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
260
|
-
omlish/sql/__init__.py,sha256=
|
265
|
+
omlish/sql/__init__.py,sha256=QHKkiM0rvSDWKUYrvTSynOni5fB46QQ2gOw_TcMBQ2o,398
|
261
266
|
omlish/sql/_abc.py,sha256=HLhnnLZ7l0r_N99I-RCqJe6VHth-9iluU7cR-7-5jfs,1519
|
262
267
|
omlish/sql/asyncs.py,sha256=Wye3dwh7oZEGYz2Y4DZQSHtW4xjI2AH5qjW-BSS2IfU,3688
|
263
268
|
omlish/sql/dbs.py,sha256=lpdFmm2vTwLoBiVYGj9yPsVcTEYYNCxlYZZpjfChzkY,1870
|
264
269
|
omlish/sql/duckdb.py,sha256=Z3wiZEn_21Lu1ElFRX0ATzoBMCw0KJxINjTRuTexYGM,3748
|
265
270
|
omlish/sql/exprs.py,sha256=gO4Fj4xEY-PuDgV-N8hBMy55glZz7O-4H7v1LWabfZY,323
|
271
|
+
omlish/sql/qualifiedname.py,sha256=rlW3gVmyucJbqwcxj_7BfK4X2HoXrMroZT2H45zPgJQ,2264
|
266
272
|
omlish/sql/secrets.py,sha256=mDUunIACxHBsPD_ONbHQJVndeMMzJR4vMC2WWX7tGfY,177
|
267
273
|
omlish/sql/sqlean.py,sha256=RbkuOuFIfM4fowwKk8-sQ6Dxk-tTUwxS94nY5Kxt52s,403
|
268
274
|
omlish/testing/__init__.py,sha256=kfiF10ykrjWXniedIl3g8j3pNAFyuSbp1D3ZXug3-Eo,168
|
@@ -289,8 +295,8 @@ omlish/text/delimit.py,sha256=ubPXcXQmtbOVrUsNh5gH1mDq5H-n1y2R4cPL5_DQf68,4928
|
|
289
295
|
omlish/text/glyphsplit.py,sha256=Ug-dPRO7x-OrNNr8g1y6DotSZ2KH0S-VcOmUobwa4B0,3296
|
290
296
|
omlish/text/indent.py,sha256=6Jj6TFY9unaPa4xPzrnZemJ-fHsV53IamP93XGjSUHs,1274
|
291
297
|
omlish/text/parts.py,sha256=KGgo0wHOIMVMZtDso-rhSWKAcAkYAH2IGpg9tULabu8,6505
|
292
|
-
omlish-0.0.0.
|
293
|
-
omlish-0.0.0.
|
294
|
-
omlish-0.0.0.
|
295
|
-
omlish-0.0.0.
|
296
|
-
omlish-0.0.0.
|
298
|
+
omlish-0.0.0.dev23.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
|
299
|
+
omlish-0.0.0.dev23.dist-info/METADATA,sha256=vhWl21vTSXsQivm5hv_6iQ-60K2Iiaa2UigiH6Hbkww,3666
|
300
|
+
omlish-0.0.0.dev23.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
|
301
|
+
omlish-0.0.0.dev23.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
|
302
|
+
omlish-0.0.0.dev23.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|