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 CHANGED
@@ -1,5 +1,5 @@
1
- __version__ = '0.0.0.dev22'
2
- __revision__ = '3d902899000237758f62e46319a8da3bccd2d447'
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': ['*.tests', '*.tests.*'],
117
+ 'exclude': [
118
+ '*.tests',
119
+ '*.tests.*',
120
+ ],
114
121
  }
115
122
 
116
123
 
omlish/asyncs/bridge.py CHANGED
@@ -8,6 +8,9 @@ KEEP THE SPACE SHUTTLE FLYING.
8
8
 
9
9
  TODO:
10
10
  - reuse greenlet if nested somehow?
11
+
12
+ See:
13
+ - https://greenback.readthedocs.io/en/latest/
11
14
  """
12
15
  import itertools
13
16
  import sys
@@ -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 ProfilingBootstrap(ContextBootstrap['ProfilingBootstrap.Config']):
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
- *args: ta.Any,
36
- **kwargs: ta.Any,
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, *args = message # type: ignore
106
+ message, *exc_args = message # type: ignore
107
+
42
108
  if message is None:
43
109
  message = default_message
44
- exc = _EXCEPTION_FACTORY(exception_type, message, *args, **kwargs)
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, *os: ta.Any, msg: Message = None) -> T:
189
- for o in os:
190
- if o != v:
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, *os: ta.Any, msg: Message = None) -> T:
196
- for o in os:
197
- if o is not v:
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, *os: ta.Any, msg: Message = None) -> T:
203
- for o in os:
204
- if o is v:
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
@@ -174,6 +174,9 @@ def timebomb_payload(delay_s: float, name: str = 'omlish-docker-timebomb') -> st
174
174
  ##
175
175
 
176
176
 
177
+ DOCKER_FOR_MAC_HOSTNAME = 'docker.for.mac.localhost'
178
+
179
+
177
180
  _LIKELY_IN_DOCKER_PATTERN = re.compile(r'^overlay / .*/docker/')
178
181
 
179
182
 
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)
@@ -65,6 +65,10 @@ from .polymorphism import ( # noqa
65
65
  polymorphism_from_subclasses,
66
66
  )
67
67
 
68
+ from .primitives import ( # noqa
69
+ PRIMITIVE_TYPES,
70
+ )
71
+
68
72
  from .registries import ( # noqa
69
73
  Registry,
70
74
  )
omlish/marshal/base64.py CHANGED
@@ -1,3 +1,7 @@
1
+ """
2
+ FIXME:
3
+ - don't base64 by default, only for json-esque targets
4
+ """
1
5
  import base64
2
6
  import typing as ta
3
7
 
@@ -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):
@@ -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,
@@ -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 _match(self, *args: P.args, **kwargs: P.kwargs) -> MatchFn[P, T] | None:
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._match(*args, **kwargs) is not None
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._match(*args, **kwargs)) is None:
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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omlish
3
- Version: 0.0.0.dev22
3
+ Version: 0.0.0.dev23
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -1,17 +1,18 @@
1
- omlish/__about__.py,sha256=V2bnCKc-3aJwdTP1xtp2ipVCaAqOuhGj6P8aqD-yHvs,2380
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=o3UJnIEmmRsv9ggIIDtz8fDSudW1CatxbwxP42M4dno,5543
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=5WyXJyFwqIJJ11QWwPIjHjDHnsaOVZszZAjyTvg3xew,4693
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=o2evI7q0CAMHR8RQ_Jks6L0UoNpEDltnLjOiamJDtmU,6155
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=dTVJrubiPlP0PlgJQT1Pj0jNDXFuPADtWymwSsgbl4k,8575
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=Nii56mGsr1MsrQPQTX45kS6NsU0APK5t13Wlb1n8jlo,48
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=nyf2PYCoYzNEX_78Rm8szqpdTdhhSbSJEt3hf7OncvA,1032
33
- omlish/bootstrap/diag.py,sha256=BQf1MkhT-cY2vJGoSauOMZZagTc73tPV-NqmP5-AGWQ,3107
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=ZNzbT1t_dI4MhCB7so0glINw5XONTehIO5cv3hyXN44,5218
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=Co5UUlCyd3eDToQTOvTURmCICKzuULnR5B6ylgb2nFM,1515
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=Q3ibujdhgFgDaeHahSe7WdcqvOyalWigwUlV-U-2ckQ,1018
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=-gLR_RTPTM8YP6t6PL5sNM1lAtwcZDHc9lxkimoQ9jw,1097
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=059gc16ycgqFbBz1F3rCkbi5lgxH9sQt9We-ghiLpBs,2803
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=pLv2EQmAwcGXN0Y3AE8K5JjyN_4GaHRMHyExaDFm5yU,181
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.dev22.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
293
- omlish-0.0.0.dev22.dist-info/METADATA,sha256=VLYWv2M6zOnFd6tjfwCF6zxknpq4PAREgFChwcGrM-4,3666
294
- omlish-0.0.0.dev22.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
295
- omlish-0.0.0.dev22.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
296
- omlish-0.0.0.dev22.dist-info/RECORD,,
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,,