omlish 0.0.0.dev20__py3-none-any.whl → 0.0.0.dev22__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.dev20'
2
- __revision__ = '56a3421b375112818e306f2c17d467c7aac1d26b'
1
+ __version__ = '0.0.0.dev22'
2
+ __revision__ = '3d902899000237758f62e46319a8da3bccd2d447'
3
3
 
4
4
 
5
5
  #
@@ -33,10 +33,10 @@ class Project(ProjectBase):
33
33
  'anyio ~= 4.4',
34
34
  'sniffio ~= 1.3',
35
35
 
36
- 'greenlet ~= 3.0; python_version < "3.13"',
36
+ 'greenlet ~= 3.1',
37
37
 
38
38
  'trio ~= 0.26',
39
- 'trio-asyncio ~= 0.15; python_version < "3.13"',
39
+ 'trio-asyncio ~= 0.15',
40
40
  ],
41
41
 
42
42
  'compression': [
@@ -46,12 +46,15 @@ class Project(ProjectBase):
46
46
  ],
47
47
 
48
48
  'diag': [
49
+ 'asttokens ~= 2.4',
50
+ 'executing ~= 2.1',
51
+
49
52
  'psutil ~= 6.0',
50
53
  ],
51
54
 
52
55
  'formats': [
53
56
  'orjson ~= 3.10',
54
- # 'ujson ~= 5.10',
57
+ 'ujson ~= 5.10',
55
58
 
56
59
  'json5 ~= 0.9',
57
60
 
@@ -74,8 +77,7 @@ class Project(ProjectBase):
74
77
  ],
75
78
 
76
79
  'sql': [
77
- 'sqlalchemy ~= 2.0; python_version ~= "3.13"',
78
- 'sqlalchemy[asyncio] ~= 2.0; python_version < "3.13"',
80
+ 'sqlalchemy[asyncio] ~= 2.0',
79
81
 
80
82
  'pg8000 ~= 1.31',
81
83
  'pymysql ~= 1.1',
omlish/asyncs/bridge.py CHANGED
@@ -15,22 +15,21 @@ import types
15
15
  import typing as ta
16
16
  import weakref
17
17
 
18
+ from .. import check
18
19
  from .. import lang
20
+ from .. import sync
21
+ from ..concurrent import threadlets
19
22
  from .asyncs import sync_await
20
23
 
21
24
 
22
25
  if ta.TYPE_CHECKING:
23
26
  import asyncio
24
27
 
25
- import greenlet
26
-
27
28
  from . import anyio as aiu
28
29
 
29
30
  else:
30
31
  asyncio = lang.proxy_import('asyncio')
31
32
 
32
- greenlet = lang.proxy_import('greenlet')
33
-
34
33
  aiu = lang.proxy_import('.anyio', __package__)
35
34
 
36
35
 
@@ -56,6 +55,20 @@ def trivial_a_to_s(fn):
56
55
  # https://gist.github.com/snaury/202bf4f22c41ca34e56297bae5f33fef
57
56
 
58
57
 
58
+ _THREADLETS_IMPL = threadlets.GreenletThreadlets
59
+ # from ..concurrent.tests.real import RealThreadlets
60
+ # _THREADLETS_IMPL = RealThreadlets
61
+
62
+ _THREADLETS = sync.LazyFn(lambda: _THREADLETS_IMPL())
63
+
64
+
65
+ def _threadlets() -> threadlets.Threadlets:
66
+ return _THREADLETS.get()
67
+
68
+
69
+ #
70
+
71
+
59
72
  class BridgeAwaitRequiredError(Exception):
60
73
  pass
61
74
 
@@ -101,7 +114,7 @@ def _make_transition(seq: int, a_to_s: bool, obj: ta.Any) -> _BridgeTransition:
101
114
 
102
115
  _BRIDGED_TASKS: ta.MutableMapping[ta.Any, list[_BridgeTransition]] = weakref.WeakKeyDictionary()
103
116
 
104
- _BRIDGE_GREENLET_ATTR = f'__{__package__.replace(".", "__")}__bridge_greenlet__'
117
+ _BRIDGE_THREADLET_ATTR = f'__{__package__.replace(".", "__")}__bridge_threadlet__'
105
118
 
106
119
 
107
120
  def _push_transition(a_to_s: bool, l: list[_BridgeTransition], t: _BridgeTransition) -> _BridgeTransition:
@@ -129,9 +142,9 @@ def _get_transitions() -> list[_BridgeTransition]:
129
142
  else:
130
143
  l.extend(tl)
131
144
 
132
- g = greenlet.getcurrent()
145
+ g = _threadlets().get_current()
133
146
  try:
134
- gl = getattr(g, _BRIDGE_GREENLET_ATTR)
147
+ gl = getattr(g.underlying, _BRIDGE_THREADLET_ATTR)
135
148
  except AttributeError:
136
149
  pass
137
150
  else:
@@ -158,9 +171,9 @@ def is_in_bridge() -> bool:
158
171
  else:
159
172
  last_t = None
160
173
 
161
- g = greenlet.getcurrent()
174
+ g = _threadlets().get_current()
162
175
  try:
163
- gl = getattr(g, _BRIDGE_GREENLET_ATTR)
176
+ gl = getattr(g.underlying, _BRIDGE_THREADLET_ATTR)
164
177
  except AttributeError:
165
178
  last_g = None
166
179
  else:
@@ -194,13 +207,13 @@ def _safe_cancel_awaitable(awaitable: ta.Awaitable[ta.Any]) -> None:
194
207
 
195
208
 
196
209
  def s_to_a_await(awaitable: ta.Awaitable[T]) -> T:
197
- g = greenlet.getcurrent()
210
+ g = _threadlets().get_current()
198
211
 
199
- if not getattr(g, _BRIDGE_GREENLET_ATTR, False):
212
+ if not getattr(g.underlying, _BRIDGE_THREADLET_ATTR, False):
200
213
  _safe_cancel_awaitable(awaitable)
201
214
  raise MissingBridgeGreenletError
202
215
 
203
- return g.parent.switch(awaitable)
216
+ return check.not_none(g.parent).switch(awaitable)
204
217
 
205
218
 
206
219
  def s_to_a(fn, *, require_await=False):
@@ -210,7 +223,7 @@ def s_to_a(fn, *, require_await=False):
210
223
  try:
211
224
  return fn(*args, **kwargs)
212
225
  finally:
213
- if (gl2 := getattr(g, _BRIDGE_GREENLET_ATTR)) is not gl: # noqa
226
+ if (gl2 := getattr(g.underlying, _BRIDGE_THREADLET_ATTR)) is not gl: # noqa
214
227
  raise UnexpectedBridgeNestingError
215
228
  if (cur_g := _pop_transition(False, gl)) is not added_g: # noqa
216
229
  raise UnexpectedBridgeNestingError
@@ -219,8 +232,8 @@ def s_to_a(fn, *, require_await=False):
219
232
 
220
233
  seq = next(_BRIDGE_TRANSITIONS_SEQ)
221
234
 
222
- g = greenlet.greenlet(inner)
223
- setattr(g, _BRIDGE_GREENLET_ATTR, gl := []) # type: ignore
235
+ g = _threadlets().spawn(inner)
236
+ setattr(g.underlying, _BRIDGE_THREADLET_ATTR, gl := []) # type: ignore
224
237
  added_g = _push_transition(False, gl, _make_transition(seq, False, g))
225
238
 
226
239
  if (t := aiu.get_current_backend_task()) is not None:
@@ -270,11 +283,11 @@ def a_to_s(fn):
270
283
  else:
271
284
  added_t = None
272
285
 
273
- g = greenlet.getcurrent()
286
+ g = _threadlets().get_current()
274
287
  try:
275
- gl = getattr(g, _BRIDGE_GREENLET_ATTR)
288
+ gl = getattr(g.underlying, _BRIDGE_THREADLET_ATTR)
276
289
  except AttributeError:
277
- setattr(g, _BRIDGE_GREENLET_ATTR, gl := [])
290
+ setattr(g.underlying, _BRIDGE_THREADLET_ATTR, gl := [])
278
291
  added_g = _push_transition(True, gl, _make_transition(seq, True, g))
279
292
 
280
293
  try:
@@ -306,7 +319,7 @@ def a_to_s(fn):
306
319
  if (cur_t := _pop_transition(True, tl)) is not added_t: # noqa
307
320
  raise UnexpectedBridgeNestingError
308
321
 
309
- if (gl2 := getattr(g, _BRIDGE_GREENLET_ATTR)) is not gl: # noqa
322
+ if (gl2 := getattr(g.underlying, _BRIDGE_THREADLET_ATTR)) is not gl: # noqa
310
323
  raise UnexpectedBridgeNestingError
311
324
  if (cur_g := _pop_transition(True, gl)) is not added_g: # noqa
312
325
  raise UnexpectedBridgeNestingError
omlish/bootstrap/diag.py CHANGED
@@ -70,6 +70,7 @@ class ThreadDumpBootstrap(ContextBootstrap['ThreadDumpBootstrap.Config']):
70
70
  @dc.dataclass(frozen=True)
71
71
  class Config(Bootstrap.Config):
72
72
  interval_s: ta.Optional[float] = None
73
+ nodaemon: bool = False
73
74
 
74
75
  on_sigquit: bool = False
75
76
 
@@ -79,6 +80,7 @@ class ThreadDumpBootstrap(ContextBootstrap['ThreadDumpBootstrap.Config']):
79
80
  tdt = diagt.create_thread_dump_thread(
80
81
  interval_s=self._config.interval_s,
81
82
  start=True,
83
+ nodaemon=self._config.nodaemon,
82
84
  )
83
85
  else:
84
86
  tdt = None
@@ -97,7 +99,7 @@ class ThreadDumpBootstrap(ContextBootstrap['ThreadDumpBootstrap.Config']):
97
99
  yield
98
100
 
99
101
  finally:
100
- if tdt is not None:
102
+ if tdt is not None and not self._config.nodaemon:
101
103
  tdt.stop_nowait()
102
104
 
103
105
  if prev_sq.present:
@@ -0,0 +1,11 @@
1
+ from .executors import ( # noqa
2
+ ImmediateExecutor,
3
+ new_executor,
4
+ )
5
+
6
+ from .futures import ( # noqa
7
+ FutureError,
8
+ FutureTimeoutError,
9
+ wait_futures,
10
+ wait_dependent_futures,
11
+ )
@@ -0,0 +1,52 @@
1
+ import concurrent.futures as cf
2
+ import contextlib
3
+ import typing as ta
4
+
5
+
6
+ T = ta.TypeVar('T')
7
+ P = ta.ParamSpec('P')
8
+
9
+
10
+ class ImmediateExecutor(cf.Executor):
11
+
12
+ def __init__(self, *, immediate_exceptions: bool = False) -> None:
13
+ super().__init__()
14
+ self._immediate_exceptions = immediate_exceptions
15
+
16
+ def submit(
17
+ self,
18
+ fn: ta.Callable[P, T],
19
+ /,
20
+ *args: P.args,
21
+ **kwargs: P.kwargs,
22
+ ) -> cf.Future[T]:
23
+ future: ta.Any = cf.Future()
24
+ try:
25
+ result = fn(*args, **kwargs)
26
+ future.set_result(result)
27
+ except Exception as e:
28
+ if self._immediate_exceptions:
29
+ raise
30
+ future.set_exception(e)
31
+ return future
32
+
33
+
34
+ @contextlib.contextmanager
35
+ def new_executor(
36
+ max_workers: int | None = None,
37
+ cls: type[cf.Executor] = cf.ThreadPoolExecutor,
38
+ *,
39
+ immediate_exceptions: bool = False,
40
+ **kwargs: ta.Any,
41
+ ) -> ta.Generator[cf.Executor, None, None]:
42
+ if max_workers == 0:
43
+ yield ImmediateExecutor(
44
+ immediate_exceptions=immediate_exceptions,
45
+ )
46
+
47
+ else:
48
+ with cls( # type: ignore
49
+ max_workers,
50
+ **kwargs,
51
+ ) as exe:
52
+ yield exe
@@ -1,11 +1,9 @@
1
1
  import concurrent.futures as cf
2
- import contextlib
3
2
  import time
4
3
  import typing as ta
5
4
 
6
5
 
7
6
  T = ta.TypeVar('T')
8
- P = ta.ParamSpec('P')
9
7
 
10
8
 
11
9
  ##
@@ -137,45 +135,3 @@ def wait_dependent_futures(
137
135
 
138
136
  futs_by_fn = {fn: fut for fut, fn in fns_by_fut.items()}
139
137
  return futs_by_fn
140
-
141
-
142
- ##
143
-
144
-
145
- class ImmediateExecutor(cf.Executor):
146
-
147
- def __init__(self, *, immediate_exceptions: bool = False) -> None:
148
- super().__init__()
149
- self._immediate_exceptions = immediate_exceptions
150
-
151
- def submit(self, fn: ta.Callable[P, T], /, *args: P.args, **kwargs: P.kwargs) -> cf.Future[T]:
152
- future: ta.Any = cf.Future()
153
- try:
154
- result = fn(*args, **kwargs)
155
- future.set_result(result)
156
- except Exception as e:
157
- if self._immediate_exceptions:
158
- raise
159
- future.set_exception(e)
160
- return future
161
-
162
-
163
- @contextlib.contextmanager
164
- def new_executor(
165
- max_workers: int | None = None,
166
- cls: type[cf.Executor] = cf.ThreadPoolExecutor,
167
- *,
168
- immediate_exceptions: bool = False,
169
- **kwargs: ta.Any,
170
- ) -> ta.Generator[cf.Executor, None, None]:
171
- if max_workers == 0:
172
- yield ImmediateExecutor(
173
- immediate_exceptions=immediate_exceptions,
174
- )
175
-
176
- else:
177
- with cls( # type: ignore
178
- max_workers,
179
- **kwargs,
180
- ) as exe:
181
- yield exe
@@ -0,0 +1,91 @@
1
+ import abc
2
+ import dataclasses as dc
3
+ import typing as ta
4
+
5
+ from omlish import lang
6
+
7
+
8
+ if ta.TYPE_CHECKING:
9
+ import greenlet
10
+ else:
11
+ greenlet = lang.proxy_import('greenlet')
12
+
13
+
14
+ ##
15
+
16
+
17
+ class Threadlet(abc.ABC):
18
+ """Not safe to identity-key - use `underlying`."""
19
+
20
+ def __hash__(self):
21
+ raise TypeError('use `underlying`')
22
+
23
+ def __eq__(self, other):
24
+ raise TypeError('use `underlying`')
25
+
26
+ @property
27
+ @abc.abstractmethod
28
+ def underlying(self) -> ta.Any:
29
+ raise NotImplementedError
30
+
31
+ @property
32
+ @abc.abstractmethod
33
+ def parent(self) -> ta.Optional['Threadlet']:
34
+ raise NotImplementedError
35
+
36
+ @property
37
+ @abc.abstractmethod
38
+ def dead(self) -> bool:
39
+ raise NotImplementedError
40
+
41
+ @abc.abstractmethod
42
+ def switch(self, *args: ta.Any, **kwargs: ta.Any) -> ta.Any:
43
+ raise NotImplementedError
44
+
45
+ @abc.abstractmethod
46
+ def throw(self, ex: Exception) -> ta.Any:
47
+ raise NotImplementedError
48
+
49
+
50
+ class Threadlets(abc.ABC):
51
+ @abc.abstractmethod
52
+ def spawn(self, fn: ta.Callable[[], None]) -> Threadlet:
53
+ raise NotImplementedError
54
+
55
+ @abc.abstractmethod
56
+ def get_current(self) -> Threadlet:
57
+ raise NotImplementedError
58
+
59
+
60
+ ##
61
+
62
+
63
+ @dc.dataclass(frozen=True)
64
+ class GreenletThreadlet(Threadlet):
65
+ g: 'greenlet.greenlet'
66
+
67
+ @property
68
+ def underlying(self) -> 'greenlet.greenlet':
69
+ return self.g
70
+
71
+ @property
72
+ def parent(self) -> ta.Optional['GreenletThreadlet']:
73
+ return GreenletThreadlet(self.g.parent)
74
+
75
+ @property
76
+ def dead(self) -> bool:
77
+ return self.g.dead
78
+
79
+ def switch(self, *args: ta.Any, **kwargs: ta.Any) -> ta.Any:
80
+ return self.g.switch(*args, **kwargs)
81
+
82
+ def throw(self, ex: Exception) -> ta.Any:
83
+ return self.g.throw(ex)
84
+
85
+
86
+ class GreenletThreadlets(Threadlets):
87
+ def spawn(self, fn: ta.Callable[[], None]) -> Threadlet:
88
+ return GreenletThreadlet(greenlet.greenlet(fn))
89
+
90
+ def get_current(self) -> Threadlet:
91
+ return GreenletThreadlet(greenlet.getcurrent())
@@ -4,10 +4,13 @@ import typing as ta
4
4
 
5
5
  from ... import check as check_
6
6
  from ... import lang
7
+ from .internals import FIELDS_ATTR
7
8
  from .internals import FieldType
8
9
  from .internals import is_classvar
9
10
  from .internals import is_initvar
11
+ from .internals import is_kw_only
10
12
  from .params import get_field_extras
13
+ from .processing import Processor
11
14
 
12
15
 
13
16
  if ta.TYPE_CHECKING:
@@ -19,6 +22,9 @@ else:
19
22
  MISSING = dc.MISSING
20
23
 
21
24
 
25
+ ##
26
+
27
+
22
28
  def field_type(f: dc.Field) -> FieldType:
23
29
  if (ft := getattr(f, '_field_type')) is not None:
24
30
  return FieldType(ft)
@@ -30,6 +36,51 @@ def has_default(f: dc.Field) -> bool:
30
36
  return not (f.default is MISSING and f.default_factory is MISSING)
31
37
 
32
38
 
39
+ ##
40
+
41
+
42
+ class FieldsProcessor(Processor):
43
+ def _process(self) -> None:
44
+ cls = self._info.cls
45
+ fields: dict[str, dc.Field] = {}
46
+
47
+ for b in cls.__mro__[-1:0:-1]:
48
+ base_fields = getattr(b, FIELDS_ATTR, None)
49
+ if base_fields is not None:
50
+ for f in base_fields.values():
51
+ fields[f.name] = f
52
+
53
+ cls_fields: list[dc.Field] = []
54
+
55
+ kw_only = self._info.params.kw_only
56
+ kw_only_seen = False
57
+ for name, ann in self._info.cls_annotations.items():
58
+ if is_kw_only(cls, ann):
59
+ if kw_only_seen:
60
+ raise TypeError(f'{name!r} is KW_ONLY, but KW_ONLY has already been specified')
61
+ kw_only_seen = True
62
+ kw_only = True
63
+ else:
64
+ cls_fields.append(preprocess_field(cls, name, ann, kw_only))
65
+
66
+ for f in cls_fields:
67
+ fields[f.name] = f
68
+ if isinstance(getattr(cls, f.name, None), dc.Field):
69
+ if f.default is MISSING:
70
+ delattr(cls, f.name)
71
+ else:
72
+ setattr(cls, f.name, f.default)
73
+
74
+ for name, value in cls.__dict__.items():
75
+ if isinstance(value, dc.Field) and name not in self._info.cls_annotations:
76
+ raise TypeError(f'{name!r} is a field but has no type annotation')
77
+
78
+ setattr(cls, FIELDS_ATTR, fields)
79
+
80
+
81
+ ##
82
+
83
+
33
84
  def preprocess_field(
34
85
  cls: type,
35
86
  a_name: str,
@@ -1,12 +1,47 @@
1
1
  import dataclasses as dc
2
2
  import typing as ta
3
3
 
4
+ from ... import lang
5
+ from .internals import FIELDS_ATTR
6
+ from .internals import PARAMS_ATTR
4
7
  from .processing import Processor
8
+ from .reflect import ClassInfo
5
9
  from .utils import Namespace
6
10
  from .utils import create_fn
7
11
  from .utils import set_new_attribute
8
12
 
9
13
 
14
+ if ta.TYPE_CHECKING:
15
+ from . import metaclass
16
+ else:
17
+ metaclass = lang.proxy_import('.metaclass', __package__)
18
+
19
+
20
+ def check_frozen_bases(info: ClassInfo) -> None:
21
+ mc_base = getattr(metaclass, 'Data', None)
22
+ all_frozen_bases = None
23
+ any_frozen_base = False
24
+ has_dataclass_bases = False
25
+ for b in info.cls.__mro__[-1:0:-1]:
26
+ if b is mc_base:
27
+ continue
28
+ base_fields = getattr(b, FIELDS_ATTR, None)
29
+ if base_fields is not None:
30
+ has_dataclass_bases = True
31
+ if all_frozen_bases is None:
32
+ all_frozen_bases = True
33
+ current_frozen = getattr(b, PARAMS_ATTR).frozen
34
+ all_frozen_bases = all_frozen_bases and current_frozen
35
+ any_frozen_base = any_frozen_base or current_frozen
36
+
37
+ if has_dataclass_bases:
38
+ if any_frozen_base and not info.params.frozen:
39
+ raise TypeError('cannot inherit non-frozen dataclass from a frozen one')
40
+
41
+ if all_frozen_bases is False and info.params.frozen:
42
+ raise TypeError('cannot inherit frozen dataclass from a non-frozen one')
43
+
44
+
10
45
  def frozen_get_del_attr(
11
46
  cls: type,
12
47
  fields: ta.Sequence[dc.Field],
@@ -46,6 +81,9 @@ def frozen_get_del_attr(
46
81
 
47
82
 
48
83
  class FrozenProcessor(Processor):
84
+ def check(self) -> None:
85
+ check_frozen_bases(self._info)
86
+
49
87
  def _process(self) -> None:
50
88
  if not self._info.params.frozen:
51
89
  return
@@ -5,14 +5,13 @@ import typing as ta
5
5
  from ... import check
6
6
  from ... import lang
7
7
  from .copy import CopyProcessor
8
- from .fields import preprocess_field
8
+ from .fields import FieldsProcessor
9
9
  from .frozen import FrozenProcessor
10
10
  from .hashing import HashProcessor
11
11
  from .init import InitProcessor
12
12
  from .internals import FIELDS_ATTR
13
13
  from .internals import PARAMS_ATTR
14
14
  from .internals import Params
15
- from .internals import is_kw_only
16
15
  from .order import OrderProcessor
17
16
  from .params import ParamsExtras
18
17
  from .processing import Processor
@@ -26,12 +25,6 @@ from .simple import OverridesProcessor
26
25
  from .slots import add_slots
27
26
 
28
27
 
29
- if ta.TYPE_CHECKING:
30
- from . import metaclass
31
- else:
32
- metaclass = lang.proxy_import('.metaclass', __package__)
33
-
34
-
35
28
  MISSING = dc.MISSING
36
29
 
37
30
 
@@ -50,67 +43,6 @@ class MainProcessor:
50
43
  if self._info.params.order and not self._info.params.eq:
51
44
  raise ValueError('eq must be true if order is true')
52
45
 
53
- def _check_frozen_bases(self) -> None:
54
- mc_base = getattr(metaclass, 'Data', None)
55
- all_frozen_bases = None
56
- any_frozen_base = False
57
- has_dataclass_bases = False
58
- for b in self._cls.__mro__[-1:0:-1]:
59
- if b is mc_base:
60
- continue
61
- base_fields = getattr(b, FIELDS_ATTR, None)
62
- if base_fields is not None:
63
- has_dataclass_bases = True
64
- if all_frozen_bases is None:
65
- all_frozen_bases = True
66
- current_frozen = getattr(b, PARAMS_ATTR).frozen
67
- all_frozen_bases = all_frozen_bases and current_frozen
68
- any_frozen_base = any_frozen_base or current_frozen
69
-
70
- if has_dataclass_bases:
71
- if any_frozen_base and not self._info.params.frozen:
72
- raise TypeError('cannot inherit non-frozen dataclass from a frozen one')
73
-
74
- if all_frozen_bases is False and self._info.params.frozen:
75
- raise TypeError('cannot inherit frozen dataclass from a non-frozen one')
76
-
77
- @lang.cached_function
78
- def _process_fields(self) -> None:
79
- fields: dict[str, dc.Field] = {}
80
-
81
- for b in self._cls.__mro__[-1:0:-1]:
82
- base_fields = getattr(b, FIELDS_ATTR, None)
83
- if base_fields is not None:
84
- for f in base_fields.values():
85
- fields[f.name] = f
86
-
87
- cls_fields: list[dc.Field] = []
88
-
89
- kw_only = self._info.params.kw_only
90
- kw_only_seen = False
91
- for name, ann in self._info.cls_annotations.items():
92
- if is_kw_only(self._cls, ann):
93
- if kw_only_seen:
94
- raise TypeError(f'{name!r} is KW_ONLY, but KW_ONLY has already been specified')
95
- kw_only_seen = True
96
- kw_only = True
97
- else:
98
- cls_fields.append(preprocess_field(self._cls, name, ann, kw_only))
99
-
100
- for f in cls_fields:
101
- fields[f.name] = f
102
- if isinstance(getattr(self._cls, f.name, None), dc.Field):
103
- if f.default is MISSING:
104
- delattr(self._cls, f.name)
105
- else:
106
- setattr(self._cls, f.name, f.default)
107
-
108
- for name, value in self._cls.__dict__.items():
109
- if isinstance(value, dc.Field) and name not in self._info.cls_annotations:
110
- raise TypeError(f'{name!r} is a field but has no type annotation')
111
-
112
- setattr(self._cls, FIELDS_ATTR, fields)
113
-
114
46
  @lang.cached_function
115
47
  def _transform_slots(self) -> None:
116
48
  if self._info.params.weakref_slot and not self._info.params.slots:
@@ -119,28 +51,32 @@ class MainProcessor:
119
51
  return
120
52
  self._cls = add_slots(self._cls, self._info.params.frozen, self._info.params.weakref_slot)
121
53
 
54
+ PROCESSOR_TYPES: ta.ClassVar[ta.Sequence[type[Processor]]] = [
55
+ FieldsProcessor,
56
+ InitProcessor,
57
+ OverridesProcessor,
58
+ ReprProcessor,
59
+ EqProcessor,
60
+ OrderProcessor,
61
+ FrozenProcessor,
62
+ HashProcessor,
63
+ DocProcessor,
64
+ MatchArgsProcessor,
65
+ ReplaceProcessor,
66
+ CopyProcessor,
67
+ ]
68
+
122
69
  @lang.cached_function
123
70
  def process(self) -> type:
124
71
  self._check_params()
125
- self._check_frozen_bases()
126
-
127
- self._process_fields()
128
-
129
- pcls: type[Processor]
130
- for pcls in [
131
- InitProcessor,
132
- OverridesProcessor,
133
- ReprProcessor,
134
- EqProcessor,
135
- OrderProcessor,
136
- FrozenProcessor,
137
- HashProcessor,
138
- DocProcessor,
139
- MatchArgsProcessor,
140
- ReplaceProcessor,
141
- CopyProcessor,
142
- ]:
143
- pcls(self._info).process()
72
+
73
+ ps = [pcls(self._info) for pcls in self.PROCESSOR_TYPES]
74
+
75
+ for p in ps:
76
+ p.check()
77
+
78
+ for p in ps:
79
+ p.process()
144
80
 
145
81
  self._transform_slots()
146
82
 
@@ -1,13 +1,21 @@
1
+ import typing as ta
2
+
1
3
  from ... import lang
2
- from .reflect import ClassInfo
4
+
5
+
6
+ if ta.TYPE_CHECKING:
7
+ from .reflect import ClassInfo
3
8
 
4
9
 
5
10
  class Processor(lang.Abstract):
6
- def __init__(self, info: ClassInfo) -> None:
11
+ def __init__(self, info: 'ClassInfo') -> None:
7
12
  super().__init__()
8
13
  self._cls = info.cls
9
14
  self._info = info
10
15
 
16
+ def check(self) -> None:
17
+ pass
18
+
11
19
  @lang.cached_function
12
20
  def process(self) -> None:
13
21
  self._process()
@@ -45,15 +45,18 @@ class _MarkerMeta(abc.ABCMeta):
45
45
 
46
46
  def __new__(mcls, name, bases, namespace):
47
47
  global _MARKER_NAMESPACE_KEYS
48
+
48
49
  if _MARKER_NAMESPACE_KEYS is None:
49
50
  if not (namespace.get('__module__') == __name__ and name == 'Marker'):
50
51
  raise RuntimeError
51
52
  _MARKER_NAMESPACE_KEYS = set(namespace)
53
+
52
54
  else:
53
55
  if set(namespace) - _MARKER_NAMESPACE_KEYS:
54
56
  raise TypeError('Markers must not include contents. Did you mean to use Namespace?')
55
57
  if Final not in bases:
56
58
  bases += (Final,)
59
+
57
60
  return super().__new__(mcls, name, bases, namespace)
58
61
 
59
62
  def __instancecheck__(self, instance):
omlish/lang/clsdct.py CHANGED
@@ -55,8 +55,10 @@ class ClassDctFn:
55
55
  except KeyError:
56
56
  f = sys._getframe(self._offset) # noqa
57
57
  cls_dct = _skip_cls_dct_frames(f).f_locals
58
+
58
59
  if not is_possibly_cls_dct(cls_dct):
59
60
  raise TypeError(cls_dct)
61
+
60
62
  return self._fn(cls_dct, *args, **kwargs)
61
63
 
62
64
 
@@ -227,6 +227,47 @@ class ExitStacked:
227
227
  return superfn(exc_type, exc_val, exc_tb)
228
228
 
229
229
 
230
+ class AsyncExitStacked:
231
+
232
+ @property
233
+ def _exit_stack(self) -> contextlib.AsyncExitStack:
234
+ try:
235
+ return self.__exit_stack # type: ignore
236
+ except AttributeError:
237
+ es = self.__exit_stack = contextlib.AsyncExitStack()
238
+ return es
239
+
240
+ async def _enter_async_context(self, context_manager: ta.AsyncContextManager[T]) -> T:
241
+ return await self._exit_stack.enter_async_context(ta.cast(ta.AsyncContextManager, context_manager))
242
+
243
+ def _enter_context(self, context_manager: ta.ContextManager[T]) -> T:
244
+ return self._exit_stack.enter_context(ta.cast(ta.ContextManager, context_manager))
245
+
246
+ async def __aenter__(self) -> ta.Self:
247
+ try:
248
+ superfn = super().__aenter__ # type: ignore
249
+ except AttributeError:
250
+ ret = self
251
+ else:
252
+ ret = await superfn()
253
+ await self._exit_stack.__aenter__()
254
+ return ret
255
+
256
+ async def __aexit__(
257
+ self,
258
+ exc_type: type[BaseException] | None,
259
+ exc_val: BaseException | None,
260
+ exc_tb: types.TracebackType | None,
261
+ ) -> bool | None:
262
+ await self._exit_stack.__aexit__(exc_type, exc_val, exc_tb)
263
+ try:
264
+ superfn = super().__aexit__ # type: ignore
265
+ except AttributeError:
266
+ return None
267
+ else:
268
+ return await superfn(exc_type, exc_val, exc_tb)
269
+
270
+
230
271
  ##
231
272
 
232
273
 
@@ -37,8 +37,10 @@ def _has_method_descriptor(obj: ta.Any) -> bool:
37
37
  while True:
38
38
  if is_method_descriptor(obj):
39
39
  return True
40
+
40
41
  elif isinstance(obj, functools.partial):
41
42
  obj = obj.func
43
+
42
44
  else:
43
45
  try:
44
46
  obj = getattr(obj, '__wrapped__')
@@ -65,6 +67,7 @@ def unwrap_func_with_partials(fn: ta.Callable) -> tuple[ta.Callable, list[functo
65
67
  while True:
66
68
  if is_method_descriptor(fn) or isinstance(fn, types.MethodType):
67
69
  fn = fn.__func__ # type: ignore
70
+
68
71
  elif hasattr(fn, '__wrapped__'):
69
72
  nxt = fn.__wrapped__
70
73
  if not callable(nxt):
@@ -72,13 +75,16 @@ def unwrap_func_with_partials(fn: ta.Callable) -> tuple[ta.Callable, list[functo
72
75
  if nxt is fn:
73
76
  raise TypeError(fn)
74
77
  fn = nxt
78
+
75
79
  # NOTE: __wrapped__ takes precedence - a partial might point to a bound Method when the important information is
76
80
  # still the unbound func. see _decorator_descriptor for an example of this.
77
81
  elif isinstance(fn, functools.partial):
78
82
  ps.append(fn)
79
83
  fn = fn.func
84
+
80
85
  else:
81
86
  break
87
+
82
88
  return fn, ps
83
89
 
84
90
 
omlish/lang/functions.py CHANGED
@@ -108,6 +108,9 @@ def issubclass_of(class_or_tuple: ta.Any) -> ta.Callable[[ta.Any], bool]:
108
108
  return lambda o: issubclass(o, class_or_tuple)
109
109
 
110
110
 
111
+ ##
112
+
113
+
111
114
  class VoidError(Exception):
112
115
  pass
113
116
 
omlish/lang/objects.py CHANGED
@@ -105,20 +105,24 @@ def build_mro_dict(
105
105
  ) -> ta.Mapping[str, ta.Any]:
106
106
  if owner_cls is None:
107
107
  owner_cls = instance_cls
108
+
108
109
  mro = instance_cls.__mro__[-2::-1]
109
110
  try:
110
111
  pos = mro.index(owner_cls)
111
112
  except ValueError:
112
113
  raise TypeError(f'Owner class {owner_cls} not in mro of instance class {instance_cls}') from None
114
+
113
115
  dct: dict[str, ta.Any] = {}
114
116
  if not bottom_up_key_order:
115
117
  for cur_cls in mro[:pos + 1][::-1]:
116
118
  for k, v in cur_cls.__dict__.items():
117
119
  if k not in dct:
118
120
  dct[k] = v
121
+
119
122
  else:
120
123
  for cur_cls in mro[:pos + 1]:
121
124
  dct.update(cur_cls.__dict__)
125
+
122
126
  return dct
123
127
 
124
128
 
omlish/lang/resolving.py CHANGED
@@ -10,37 +10,46 @@ class ResolvableClassNameError(NameError):
10
10
  def get_cls_fqcn(cls: type, *, nocheck: bool = False) -> str:
11
11
  if not isinstance(cls, type):
12
12
  raise TypeError(cls)
13
+
13
14
  mn = cls.__module__
14
15
  if set(mn) - set(string.ascii_lowercase + string.digits + '_.'):
15
16
  raise ResolvableClassNameError(cls)
17
+
16
18
  qn = cls.__qualname__
17
19
  if not all(qp[0].isupper() for qp in qn.split('.')) or (set(qn) - set(string.ascii_letters + string.digits + '.')):
18
20
  raise ResolvableClassNameError(cls)
21
+
19
22
  fqcn = '.'.join([cls.__module__, cls.__qualname__])
20
23
  if not nocheck:
21
24
  if get_fqcn_cls(fqcn, nocheck=True) is not cls:
22
25
  raise ResolvableClassNameError(cls, fqcn)
26
+
23
27
  return fqcn
24
28
 
25
29
 
26
30
  def get_fqcn_cls(fqcn: str, *, nocheck: bool = False) -> type:
27
31
  if not isinstance(fqcn, str) or not fqcn:
28
32
  raise TypeError(fqcn)
33
+
29
34
  parts = fqcn.split('.')
30
35
  pos = next(i for i, p in enumerate(parts) if p[0].isupper())
31
36
  mps, qps = parts[:pos], parts[pos:]
32
37
  mod = importlib.import_module('.'.join(mps))
38
+
33
39
  o: ta.Any = mod
34
40
  for qp in qps:
35
41
  o = getattr(o, qp)
36
42
  if not isinstance(o, type):
37
43
  raise TypeError(o)
44
+
38
45
  cls = o
39
46
  if not isinstance(cls, type):
40
47
  raise TypeError(cls)
48
+
41
49
  if not nocheck:
42
50
  if not get_cls_fqcn(cls, nocheck=True) == fqcn:
43
51
  raise ResolvableClassNameError(cls, fqcn)
52
+
44
53
  return o
45
54
 
46
55
 
omlish/lite/marshal.py CHANGED
@@ -232,8 +232,10 @@ _OBJ_MARSHALER_GENERIC_MAPPING_TYPES: ta.Dict[ta.Any, type] = {
232
232
 
233
233
  _OBJ_MARSHALER_GENERIC_ITERABLE_TYPES: ta.Dict[ta.Any, type] = {
234
234
  **{t: t for t in (list, tuple, set, frozenset)},
235
- **{t: frozenset for t in (collections.abc.Set, collections.abc.MutableSet)},
236
- **{t: tuple for t in (collections.abc.Sequence, collections.abc.MutableSequence)},
235
+ collections.abc.Set: frozenset,
236
+ collections.abc.MutableSet: set,
237
+ collections.abc.Sequence: tuple,
238
+ collections.abc.MutableSequence: list,
237
239
  }
238
240
 
239
241
 
omlish/logs/__init__.py CHANGED
@@ -8,6 +8,10 @@ from .formatters import ( # noqa
8
8
  StandardLogFormatter,
9
9
  )
10
10
 
11
+ from .handlers import ( # noqa
12
+ ListHandler,
13
+ )
14
+
11
15
  from .utils import ( # noqa
12
16
  error_logging,
13
17
  )
@@ -0,0 +1,10 @@
1
+ import logging
2
+
3
+
4
+ class ListHandler(logging.Handler):
5
+ def __init__(self) -> None:
6
+ super().__init__()
7
+ self.records: list[logging.LogRecord] = []
8
+
9
+ def emit(self, record):
10
+ self.records.append(record)
omlish/multiprocessing.py CHANGED
@@ -47,19 +47,19 @@ class DummyValueProxy(ValueProxy[T]):
47
47
 
48
48
  @dc.dataclass(frozen=True, kw_only=True)
49
49
  class SpawnExtras:
50
- fds: ta.AbstractSet[int] | None = None
50
+ pass_fds: ta.AbstractSet[int] | None = None
51
51
  deathsig: int | None = None
52
52
 
53
53
 
54
54
  class ExtrasSpawnPosixPopen(mp.popen_spawn_posix.Popen):
55
55
  def __init__(self, process_obj: 'ExtrasSpawnProcess', *, extras: SpawnExtras) -> None:
56
56
  self.__extras = extras
57
- self.__extra_fds = extras.fds
57
+ self.__pass_fds = extras.pass_fds
58
58
  super().__init__(process_obj)
59
59
 
60
60
  def _launch(self, process_obj: 'ExtrasSpawnProcess') -> None:
61
- if self.__extra_fds:
62
- for fd in self.__extra_fds:
61
+ if self.__pass_fds:
62
+ for fd in self.__pass_fds:
63
63
  self.duplicate_for_child(fd)
64
64
  self._extra_fds = None
65
65
 
omlish/secrets/openssl.py CHANGED
@@ -42,7 +42,7 @@ def generate_key(self, sz: int = DEFAULT_KEY_SIZE) -> bytes:
42
42
  ##
43
43
 
44
44
 
45
- class OpensslAes265CbcCrypto(Crypto):
45
+ class OpensslAescbcCrypto(Crypto):
46
46
  """
47
47
  !!! https://docs.openssl.org/3.0/man7/passphrase-encoding/
48
48
  https://cryptography.io/en/latest/hazmat/primitives/symmetric-encryption/#cryptography.hazmat.primitives.ciphers.Cipher
@@ -122,7 +122,7 @@ class OpensslAes265CbcCrypto(Crypto):
122
122
  ##
123
123
 
124
124
 
125
- class OpensslSubprocessAes256CbcCrypto(Crypto):
125
+ class OpensslSubprocessAescbcCrypto(Crypto):
126
126
  def __init__(
127
127
  self,
128
128
  *,
@@ -1,4 +1,5 @@
1
1
  import enum
2
+ import typing as ta
2
3
 
3
4
 
4
5
  class JsonType(enum.Enum):
@@ -8,3 +9,13 @@ class JsonType(enum.Enum):
8
9
  ARRAY = enum.auto()
9
10
  NUMBER = enum.auto()
10
11
  STRING = enum.auto()
12
+
13
+
14
+ TYPE_SETS_BY_JSON_TYPE: ta.Mapping[JsonType, ta.AbstractSet[type]] = {
15
+ JsonType.NULL: {type(None)},
16
+ JsonType.BOOLEAN: {bool},
17
+ JsonType.OBJECT: {dict},
18
+ JsonType.ARRAY: {list},
19
+ JsonType.NUMBER: {int, float},
20
+ JsonType.STRING: {str},
21
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omlish
3
- Version: 0.0.0.dev20
3
+ Version: 0.0.0.dev22
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -15,11 +15,16 @@ License-File: LICENSE
15
15
  Provides-Extra: all
16
16
  Requires-Dist: anyio ~=4.4 ; extra == 'all'
17
17
  Requires-Dist: sniffio ~=1.3 ; extra == 'all'
18
+ Requires-Dist: greenlet ~=3.1 ; extra == 'all'
18
19
  Requires-Dist: trio ~=0.26 ; extra == 'all'
20
+ Requires-Dist: trio-asyncio ~=0.15 ; extra == 'all'
19
21
  Requires-Dist: lz4 ~=4.0 ; extra == 'all'
20
22
  Requires-Dist: zstd ~=1.5 ; extra == 'all'
23
+ Requires-Dist: asttokens ~=2.4 ; extra == 'all'
24
+ Requires-Dist: executing ~=2.1 ; extra == 'all'
21
25
  Requires-Dist: psutil ~=6.0 ; extra == 'all'
22
26
  Requires-Dist: orjson ~=3.10 ; extra == 'all'
27
+ Requires-Dist: ujson ~=5.10 ; extra == 'all'
23
28
  Requires-Dist: json5 ~=0.9 ; extra == 'all'
24
29
  Requires-Dist: pyyaml ~=5.0 ; extra == 'all'
25
30
  Requires-Dist: cloudpickle ~=3.0 ; extra == 'all'
@@ -27,33 +32,33 @@ Requires-Dist: httpx[http2] ~=0.27 ; extra == 'all'
27
32
  Requires-Dist: jinja2 ~=3.1 ; extra == 'all'
28
33
  Requires-Dist: wrapt ~=1.14 ; extra == 'all'
29
34
  Requires-Dist: cryptography ~=43.0 ; extra == 'all'
35
+ Requires-Dist: sqlalchemy[asyncio] ~=2.0 ; extra == 'all'
30
36
  Requires-Dist: pg8000 ~=1.31 ; extra == 'all'
31
37
  Requires-Dist: pymysql ~=1.1 ; extra == 'all'
32
38
  Requires-Dist: aiomysql ~=0.2 ; extra == 'all'
33
39
  Requires-Dist: aiosqlite ~=0.20 ; extra == 'all'
34
40
  Requires-Dist: duckdb ~=1.1 ; extra == 'all'
35
41
  Requires-Dist: pytest ~=8.0 ; extra == 'all'
36
- Requires-Dist: greenlet ~=3.0 ; (python_version < "3.13") and extra == 'all'
37
- Requires-Dist: trio-asyncio ~=0.15 ; (python_version < "3.13") and extra == 'all'
38
42
  Requires-Dist: python-snappy ~=0.7 ; (python_version < "3.13") and extra == 'all'
39
- Requires-Dist: sqlalchemy[asyncio] ~=2.0 ; (python_version < "3.13") and extra == 'all'
40
43
  Requires-Dist: asyncpg ~=0.29 ; (python_version < "3.13") and extra == 'all'
41
44
  Requires-Dist: sqlean.py ~=3.45 ; (python_version < "3.13") and extra == 'all'
42
- Requires-Dist: sqlalchemy ~=2.0 ; (python_version ~= "3.13") and extra == 'all'
43
45
  Provides-Extra: async
44
46
  Requires-Dist: anyio ~=4.4 ; extra == 'async'
45
47
  Requires-Dist: sniffio ~=1.3 ; extra == 'async'
48
+ Requires-Dist: greenlet ~=3.1 ; extra == 'async'
46
49
  Requires-Dist: trio ~=0.26 ; extra == 'async'
47
- Requires-Dist: greenlet ~=3.0 ; (python_version < "3.13") and extra == 'async'
48
- Requires-Dist: trio-asyncio ~=0.15 ; (python_version < "3.13") and extra == 'async'
50
+ Requires-Dist: trio-asyncio ~=0.15 ; extra == 'async'
49
51
  Provides-Extra: compression
50
52
  Requires-Dist: lz4 ~=4.0 ; extra == 'compression'
51
53
  Requires-Dist: zstd ~=1.5 ; extra == 'compression'
52
54
  Requires-Dist: python-snappy ~=0.7 ; (python_version < "3.13") and extra == 'compression'
53
55
  Provides-Extra: diag
56
+ Requires-Dist: asttokens ~=2.4 ; extra == 'diag'
57
+ Requires-Dist: executing ~=2.1 ; extra == 'diag'
54
58
  Requires-Dist: psutil ~=6.0 ; extra == 'diag'
55
59
  Provides-Extra: formats
56
60
  Requires-Dist: orjson ~=3.10 ; extra == 'formats'
61
+ Requires-Dist: ujson ~=5.10 ; extra == 'formats'
57
62
  Requires-Dist: json5 ~=0.9 ; extra == 'formats'
58
63
  Requires-Dist: pyyaml ~=5.0 ; extra == 'formats'
59
64
  Requires-Dist: cloudpickle ~=3.0 ; extra == 'formats'
@@ -65,13 +70,12 @@ Requires-Dist: wrapt ~=1.14 ; extra == 'misc'
65
70
  Provides-Extra: secrets
66
71
  Requires-Dist: cryptography ~=43.0 ; extra == 'secrets'
67
72
  Provides-Extra: sql
73
+ Requires-Dist: sqlalchemy[asyncio] ~=2.0 ; extra == 'sql'
68
74
  Requires-Dist: pg8000 ~=1.31 ; extra == 'sql'
69
75
  Requires-Dist: pymysql ~=1.1 ; extra == 'sql'
70
76
  Requires-Dist: aiomysql ~=0.2 ; extra == 'sql'
71
77
  Requires-Dist: aiosqlite ~=0.20 ; extra == 'sql'
72
- Requires-Dist: sqlalchemy[asyncio] ~=2.0 ; (python_version < "3.13") and extra == 'sql'
73
78
  Requires-Dist: asyncpg ~=0.29 ; (python_version < "3.13") and extra == 'sql'
74
- Requires-Dist: sqlalchemy ~=2.0 ; (python_version ~= "3.13") and extra == 'sql'
75
79
  Provides-Extra: sqlx
76
80
  Requires-Dist: duckdb ~=1.1 ; extra == 'sqlx'
77
81
  Requires-Dist: sqlean.py ~=3.45 ; (python_version < "3.13") and extra == 'sqlx'
@@ -1,10 +1,9 @@
1
- omlish/__about__.py,sha256=aaPwsrPbNAl3zQVkQ9n1ZeBGXbeIaqkXe-qJK-YhMn4,2451
1
+ omlish/__about__.py,sha256=V2bnCKc-3aJwdTP1xtp2ipVCaAqOuhGj6P8aqD-yHvs,2380
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
6
  omlish/check.py,sha256=o3UJnIEmmRsv9ggIIDtz8fDSudW1CatxbwxP42M4dno,5543
7
- omlish/concurrent.py,sha256=jCGG76Ca_8761g1uY3nCJONg_Nxcf4JQQZYvvrmsKE4,5395
8
7
  omlish/datetimes.py,sha256=HajeM1kBvwlTa-uR1TTZHmZ3zTPnnUr1uGGQhiO1XQ0,2152
9
8
  omlish/defs.py,sha256=T3bq_7h_tO3nDB5RAFBn7DkdeQgqheXzkFColbOHZko,4890
10
9
  omlish/docker.py,sha256=5WyXJyFwqIJJ11QWwPIjHjDHnsaOVZszZAjyTvg3xew,4693
@@ -14,7 +13,7 @@ omlish/iterators.py,sha256=GGLC7RIT86uXMjhIIIqnff_Iu5SI_b9rXYywYGFyzmo,7292
14
13
  omlish/libc.py,sha256=u0481imCiTFqP_e-v9g0pD-0WD249j5vYzhtn-fnNkY,15308
15
14
  omlish/matchfns.py,sha256=o2evI7q0CAMHR8RQ_Jks6L0UoNpEDltnLjOiamJDtmU,6155
16
15
  omlish/math.py,sha256=AVqp5Y8yxKA-wO0BgrzaxA0Ga3PZiCXnYcwivMneC-0,3804
17
- omlish/multiprocessing.py,sha256=Jbcr71p_HlsM5rIFl9obzSv9TCusPhxciwpBXLTYpmc,5179
16
+ omlish/multiprocessing.py,sha256=QZT4C7I-uThCAjaEY3xgUYb-5GagUlnE4etN01LDyU4,5186
18
17
  omlish/os.py,sha256=cz4nL2ujaxH_-XRq3JUD8af8mSe1JXGPIoXP9XAEd0M,2607
19
18
  omlish/runmodule.py,sha256=PWvuAaJ9wQQn6bx9ftEL3_d04DyotNn8dR_twm2pgw0,700
20
19
  omlish/stats.py,sha256=uqjN-focDVssFZMagj22HqmyJ1TBO4Wt-XnHp8-EtVw,9927
@@ -24,14 +23,14 @@ omlish/asyncs/__init__.py,sha256=uUz9ziKh4_QrgmdhKFMgq6j7mFbiZd3LiogguDCQsGI,587
24
23
  omlish/asyncs/anyio.py,sha256=Hqdi1iCopKoaAWGx-AYTRLEwnavLWx1esfJISK1IVF0,8024
25
24
  omlish/asyncs/asyncio.py,sha256=JfM59QgB3asgEbrps0zoVbNjWD4kL2XdsEkRMEIoFos,971
26
25
  omlish/asyncs/asyncs.py,sha256=Tf7ZodTGepkM7HAuFcDNh9lLzzrMw6rELWvopGmFkh4,2035
27
- omlish/asyncs/bridge.py,sha256=AabrRVz5k75dTB59M70DWkl6JrLusjhpvsaj5jld9io,8151
26
+ omlish/asyncs/bridge.py,sha256=dTVJrubiPlP0PlgJQT1Pj0jNDXFuPADtWymwSsgbl4k,8575
28
27
  omlish/asyncs/flavors.py,sha256=1mNxGNRVmjUHzA13K5ht8vdJv4CLEmzYTQ6BZXr1520,4866
29
28
  omlish/asyncs/trio.py,sha256=GKG3wgelFr7gIKKHZhcflvMyCvxXHNZe862KB0Xw2uA,370
30
29
  omlish/asyncs/trio_asyncio.py,sha256=oqdOHy0slj9PjVxaDf3gJkq9AAgg7wYZbB469jOftVw,1327
31
30
  omlish/bootstrap/__init__.py,sha256=Nii56mGsr1MsrQPQTX45kS6NsU0APK5t13Wlb1n8jlo,48
32
31
  omlish/bootstrap/__main__.py,sha256=d23loR_cKfTYZwYiqpt_CmKI7dd5WcYFgIYzqMep75E,68
33
32
  omlish/bootstrap/base.py,sha256=nyf2PYCoYzNEX_78Rm8szqpdTdhhSbSJEt3hf7OncvA,1032
34
- omlish/bootstrap/diag.py,sha256=TV4s3KcSvrDD0AK5Ggz7_rYMWbEK9_NfP3I0zf91m0o,2998
33
+ omlish/bootstrap/diag.py,sha256=BQf1MkhT-cY2vJGoSauOMZZagTc73tPV-NqmP5-AGWQ,3107
35
34
  omlish/bootstrap/harness.py,sha256=vQCIhCQY_N0NHWvDh8rG6Lo57U1qHkQf2egifbXzN-8,2027
36
35
  omlish/bootstrap/main.py,sha256=ZNzbT1t_dI4MhCB7so0glINw5XONTehIO5cv3hyXN44,5218
37
36
  omlish/bootstrap/sys.py,sha256=U0MFxO9tLFV3cdN5Y-Zrink6_45sFvzPUYQXyBk7-ns,8741
@@ -56,6 +55,10 @@ omlish/collections/cache/__init__.py,sha256=Cv8RX-Ehit3um0QLDq7uRDqJUCcdqTKoAB9T
56
55
  omlish/collections/cache/descriptor.py,sha256=t-1Gh4DTABDuNmeDJlpoW4LV3gi_uSlBd9ZfBINfYCM,5023
57
56
  omlish/collections/cache/impl.py,sha256=nQox5kChhns9h2a5gnX-ayQGBQJ5-B1aZkLQ2Aej19g,15137
58
57
  omlish/collections/cache/types.py,sha256=yNjwd6CGyTJQdxN2CQxFqqBAlcs1Z7vvNV-aU1K7p8E,685
58
+ omlish/concurrent/__init__.py,sha256=9p-s8MvBEYDqHIoYU3OYoe-Nni22QdkW7nhZGEukJTM,197
59
+ omlish/concurrent/executors.py,sha256=FYKCDYYuj-OgMa8quLsA47SfFNX3KDJvRENVk8NDsrA,1292
60
+ omlish/concurrent/futures.py,sha256=J2s9wYURUskqRJiBbAR0PNEAp1pXbIMYldOVBTQduQY,4239
61
+ omlish/concurrent/threadlets.py,sha256=2fzXEp_KPqIY7uoa5Jk2DJ3bLMY73ZsnzgN5myGanwc,2034
59
62
  omlish/configs/__init__.py,sha256=3uh09ezodTwkMI0nRmAMP0eEuJ_0VdF-LYyNmPjHiCE,77
60
63
  omlish/configs/classes.py,sha256=GLbB8xKjHjjoUQRCUQm3nEjM8z1qNTx9gPV7ODSt5dg,1317
61
64
  omlish/configs/flattening.py,sha256=AOlRpBHm449MxwMp3CiIRGunStOC1DUNs1f3CLou0wc,4731
@@ -68,17 +71,17 @@ omlish/dataclasses/impl/as_.py,sha256=CD-t7hkC1EP2F_jvZKIA_cVoDuwZ-Ln_xC4fJumPYX
68
71
  omlish/dataclasses/impl/copy.py,sha256=Tn8_n6Vohs-w4otbGdubBEvhd3TsSTaM3EfNGdS2LYo,591
69
72
  omlish/dataclasses/impl/descriptors.py,sha256=rEYE1Len99agTQCC25hSPMnM19BgPr0ZChABGi58Fdk,2476
70
73
  omlish/dataclasses/impl/exceptions.py,sha256=WD0Tr1TnjUN4OR3f3rs8CgqtP2KBA1tKjPIbZO8Tzm0,178
71
- omlish/dataclasses/impl/fields.py,sha256=zBa-XjJJQ7dOP_se-A8Q4Vv3ol2xjpTQXTUBCnr1UVs,4377
72
- omlish/dataclasses/impl/frozen.py,sha256=5wKtt8WvPYCkHPrwR024mpvbFzqcbPQfgiFvy3orRJo,1692
74
+ omlish/dataclasses/impl/fields.py,sha256=6Ry-fTntJEPigSHBtaegssxeOi0L4hffLSL4SMtqjSE,5908
75
+ omlish/dataclasses/impl/frozen.py,sha256=x87DSM8FIMZ3c_BIUE8NooCkExFjPsabeqIueEP5qKs,2988
73
76
  omlish/dataclasses/impl/hashing.py,sha256=FKnHuXCg9ylrzK2TLGqO5yfRN4HX3F415CSLlVYXtYE,3190
74
77
  omlish/dataclasses/impl/init.py,sha256=yw9iInFHaR_TFWRzsryr8vgStHMQwqubL-s7pY5k1sA,5652
75
78
  omlish/dataclasses/impl/internals.py,sha256=LTCqGT8AhyGTWwioGrBpTJzDzPvAtizQKb0NBNKcNs0,2989
76
- omlish/dataclasses/impl/main.py,sha256=vAVdqb4cgvyssA7rCFl9i2D0MAffX45YtQbGQ2te5_c,5189
79
+ omlish/dataclasses/impl/main.py,sha256=Ti0PKbFKraKvfmoPuR-G7nLVNzRC8mvEuXhCuC-M2kc,2574
77
80
  omlish/dataclasses/impl/metaclass.py,sha256=dlQEIN9MHBirll7Nx3StpzxYxXjrqxJ-QsorMcCNt7w,2828
78
81
  omlish/dataclasses/impl/metadata.py,sha256=binwdG0cyKdGEypRKYTbcDyH_be4aTaSnVnK2V5rwB8,1402
79
82
  omlish/dataclasses/impl/order.py,sha256=zWvWDkSTym8cc7vO1cLHqcBhhjOlucHOCUVJcdh4jt0,1369
80
83
  omlish/dataclasses/impl/params.py,sha256=M-xg9IeFftVy795oqlp7Yw8jE-7wG2K4vaFhXHKmL1k,2538
81
- omlish/dataclasses/impl/processing.py,sha256=iUvaNwRAt8rQsLIIkvRye5rfk6xhgR35EbcdwZCZec8,366
84
+ omlish/dataclasses/impl/processing.py,sha256=DFxyFjL_h3awRyF_5eyTnB8QkuApx7Zc4QFnVoltlao,459
82
85
  omlish/dataclasses/impl/reflect.py,sha256=13af257_sjjM-4wfH84nl7CoHHMV_eFZpkIgUawZ3TE,5307
83
86
  omlish/dataclasses/impl/replace.py,sha256=wS9GHX4fIwaPv1JBJzIewdBfXyK3X3V7_t55Da87dYo,1217
84
87
  omlish/dataclasses/impl/repr.py,sha256=oLXBTxqp88NKmz82HrJeGiTEiwK4l5LlXQT9Q0-tX3c,1605
@@ -156,17 +159,17 @@ omlish/inject/impl/proxy.py,sha256=1ko0VaKqzu9UG8bIldp9xtUrAVUOFTKWKTjOCqIGr4s,1
156
159
  omlish/inject/impl/scopes.py,sha256=M_RO_pGUr5mX84YyYmkr6CsMhkkV189_gOUsaYmYes4,5768
157
160
  omlish/lang/__init__.py,sha256=sjeN4p46itXbYfYJNqgin8olSU62xD700Af_vlgWiwI,3165
158
161
  omlish/lang/cached.py,sha256=0gjdxVELu69oRQ3kqSV3cGIHg6Nf4pcCIIRTEU52tCc,7607
159
- omlish/lang/clsdct.py,sha256=LXwLCULeI_8Np-7-pZkyNAHpUZLcQiBEQiHwKYQ0WRo,1742
162
+ omlish/lang/clsdct.py,sha256=AjtIWLlx2E6D5rC97zQ3Lwq2SOMkbg08pdO_AxpzEHI,1744
160
163
  omlish/lang/cmp.py,sha256=5vbzWWbqdzDmNKAGL19z6ZfUKe5Ci49e-Oegf9f4BsE,1346
161
- omlish/lang/contextmanagers.py,sha256=C6FU_1ftMvp_Zbz9ixf_HsCSYQot_TZ7mpZBJEBc3Xc,8333
162
- omlish/lang/descriptors.py,sha256=J0oRWys9smHp9UYeearBATA7u-6t5pDnQgPfWiXN3B4,5902
164
+ omlish/lang/contextmanagers.py,sha256=rzMSwJU7ObFXl46r6pGDbD45Zi_qZ9NHxDPnLNuux9o,9732
165
+ omlish/lang/descriptors.py,sha256=VGbebrhWR0WLe3SASN444lBBvAreVMcwdam1FjcRxkQ,5908
163
166
  omlish/lang/exceptions.py,sha256=qJBo3NU1mOWWm-NhQUHCY5feYXR3arZVyEHinLsmRH4,47
164
- omlish/lang/functions.py,sha256=T4nPl46EHHGjMkz3FTRjsVhS9Y8HKcwM0jROU6_-Rv0,3619
167
+ omlish/lang/functions.py,sha256=yJxWwqlXEAT2gied4uTwiz5x1qXeuVubOSXyn9zy5aI,3624
165
168
  omlish/lang/imports.py,sha256=04ugFC8NI5sbL7NH4V0r0q_nFsP_AMkHLz697CVkMtQ,6274
166
169
  omlish/lang/iterables.py,sha256=_q6rHbdFfW3VBqez0IV3rUABoNxsA_oBv_sykm5zsbQ,2243
167
170
  omlish/lang/maybes.py,sha256=NYHZDjqDtwPMheDrj2VtUVujxRPf8Qpgk4ZlZCTvBZc,3492
168
- omlish/lang/objects.py,sha256=ewGWcbtjNQtoRba0uAEekLbn1e884ZJh09xPHzZkfy4,4306
169
- omlish/lang/resolving.py,sha256=UgrX-vxXtCGGEmnAMUYP4bUZ6-Ok0EcHVEKAZYbAS-o,1597
171
+ omlish/lang/objects.py,sha256=1dY8dX5voIZf5FBYUiN0BRsWg2JCdsgRbDl9fLG7OtY,4310
172
+ omlish/lang/resolving.py,sha256=OuN2mDTPNyBUbcrswtvFKtj4xgH4H4WglgqSKv3MTy0,1606
170
173
  omlish/lang/strings.py,sha256=ykeoou4JK7CEZXzrUJfqVOalEDvE--j0uhHt_SrsrUs,2834
171
174
  omlish/lang/sys.py,sha256=UoZz_PJYVKLQAKqYxxn-LHz1okK_38I__maZgnXMcxU,406
172
175
  omlish/lang/timeouts.py,sha256=vECdWYhc_IZgcal1Ng1Y42wf2FV3KAx-i8As-MgGHIQ,1186
@@ -174,7 +177,7 @@ omlish/lang/typing.py,sha256=pKDBHTYzoQtFCUxtlDDTpnKixZBxeE5Pblvv1nJbRYE,3236
174
177
  omlish/lang/classes/__init__.py,sha256=j1p0_uuMznKrY2EhMoj20uv6vx4LXljMzp7AaKe0mmU,530
175
178
  omlish/lang/classes/abstract.py,sha256=goIV14oY24EOs88eVe6E6NyrSPOOLMOcWTXTMuYKiqc,2304
176
179
  omlish/lang/classes/restrict.py,sha256=n_B-XqafVyO1caZzfbsv7tPxDJOBLl1BzQkoDSse0A4,3353
177
- omlish/lang/classes/simple.py,sha256=U9WB3S9svudIWF7bitptXzVjLHnlG_Xh72K-jIAekaU,3068
180
+ omlish/lang/classes/simple.py,sha256=_OE6-eph3-pllGck83FcGZE8fDMUqPdWP9OeSDCSgww,3071
178
181
  omlish/lang/classes/virtual.py,sha256=54D4qOpDWHpP28Oc6bbBjdxquLvdvqMqLXj2XOnAeaQ,3323
179
182
  omlish/lifecycles/__init__.py,sha256=1FjYceXs-4fc-S-C9zFYmc2axHs4znnQHcJVHdY7a6E,578
180
183
  omlish/lifecycles/abstract.py,sha256=70CQyZy-c9a2o0ZJxPeUT7eYjWZTBrp2HpUBnrHdAOM,1109
@@ -190,16 +193,17 @@ omlish/lite/check.py,sha256=DR3Zj-7o4Y7pNheln68nN_BdX9zaotGQ2y8v97GDiWQ,535
190
193
  omlish/lite/contextmanagers.py,sha256=HnQJiyrOmSvTL22XRJrFl5CLpCyHD9fsntEUAr9G-60,427
191
194
  omlish/lite/json.py,sha256=7-02Ny4fq-6YAu5ynvqoijhuYXWpLmfCI19GUeZnb1c,740
192
195
  omlish/lite/logs.py,sha256=qgP0pNjkoG5gjQ6xuXM7qpsLwZX3WbS7WaZlDqQKry4,2641
193
- omlish/lite/marshal.py,sha256=5uwri-KzPiktnbYORkGXcJ4kulZvp_nS4MxPsU1Y-G0,8608
196
+ omlish/lite/marshal.py,sha256=u6jYUN_AndvI6__HJBvSw5ElHWC0CfHqgiDS28Vpqjg,8593
194
197
  omlish/lite/reflect.py,sha256=9QYJwdINraq1JNMEgvoqeSlVvRRgOXpxAkpgX8EgRXc,1307
195
198
  omlish/lite/runtime.py,sha256=VUhmNQvwf8QzkWSKj4Q0ReieJA_PzHaJNRBivfTseow,452
196
199
  omlish/lite/secrets.py,sha256=FEc47dcU9M1CyMTnrihAAVwKf0ySZLahf83djOEDWXw,488
197
200
  omlish/lite/strings.py,sha256=9dO_A6EkhcTZ2xmOUGSOMT-mx9BnoOzYu1-ocSrDJaA,670
198
201
  omlish/lite/subprocesses.py,sha256=KuGV3ImehMjCUK0JoV3pUtG_7o5wei1lRDn9HxzByAg,3063
199
- omlish/logs/__init__.py,sha256=UPMdG3mbUm4PUJw6muXs4dk-uNE0aMDj_XebKCa-Wpk,224
202
+ omlish/logs/__init__.py,sha256=E4m-RTIaTh0rT9ADY9iB1Yc3pYBcnmn-tfkWXsjRTW4,276
200
203
  omlish/logs/_abc.py,sha256=UgrCUQVUi_PvT3p1CEkb3P74CFrFcZq2AFby3GEUv9M,5974
201
204
  omlish/logs/configs.py,sha256=VfZjhyA4CeMYNhlsv5oD2IZ6Iv4d_lbUgZzcnLAkxNA,1052
202
205
  omlish/logs/formatters.py,sha256=AFs9C6-qrFkgXZ0nL39wih_LGck1Tc79alvGyibBdQo,720
206
+ omlish/logs/handlers.py,sha256=nyuFgmO05By_Xwq7es58ClzS51-F53lJL7gD0x5IqAg,228
203
207
  omlish/logs/noisy.py,sha256=8JORjI1dH38yU2MddM54OB6qt32Xozfocdb88vY4wro,335
204
208
  omlish/logs/utils.py,sha256=MgGovbP0zUrZ3FGD3qYNQWn-l0jy0Y0bStcQvv5BOmQ,391
205
209
  omlish/marshal/__init__.py,sha256=Co5UUlCyd3eDToQTOvTURmCICKzuULnR5B6ylgb2nFM,1515
@@ -236,13 +240,13 @@ omlish/reflect/types.py,sha256=R9AH5YnOvdZs6QhzJ6VmjvcvGibQEQi6YqK25f5VUxw,6862
236
240
  omlish/secrets/__init__.py,sha256=VKB2IF9vz4h4RXcZxgXj36KXOLcGBzfqVnxPgPDWpmg,408
237
241
  omlish/secrets/crypto.py,sha256=6CsLy0UEqCrBK8Xx_3-iFF6SKtu2GlEqUQ8-MliY3tk,3709
238
242
  omlish/secrets/marshal.py,sha256=nVzsvQH5w3T2oMP7DCc1SLKxyR5e66psM57VOQoL0QA,2086
239
- omlish/secrets/openssl.py,sha256=nAA_wxk86G92B7027AoAlAiFliMjxLVs7xlaOAFGayE,6225
243
+ omlish/secrets/openssl.py,sha256=wxA_wIlxtuOUy71ABxAJgavh-UI_taOfm-A0dVlmSwM,6219
240
244
  omlish/secrets/passwords.py,sha256=3r-vEK6Gp6aq4L5Csnd06QnrjO9xfzHJP-g_7I9W_ao,4101
241
245
  omlish/secrets/secrets.py,sha256=hFN82uYiBVx8YSE86leWNxb4IRp3qdwZPOi4w04h8u0,6855
242
246
  omlish/secrets/subprocesses.py,sha256=EcnKlHHtnUMHGrBWXDfu8tv28wlgZx4P4GOiuPW9Vo8,1105
243
247
  omlish/specs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
244
248
  omlish/specs/jsonschema/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
245
- omlish/specs/jsonschema/types.py,sha256=aJrZq2oqjmbGa32YnWyGFPbEyNfm0YO9ixNy7qU-snE,189
249
+ omlish/specs/jsonschema/types.py,sha256=qoxExgKfrI-UZXdk3qcVZIEyp1WckFbb85_eGInEoAY,467
246
250
  omlish/specs/jsonschema/keywords/__init__.py,sha256=Zt2g1BXd654uU2AQ5P7_-x2Wrtf6cNbP9mxI1wGN4wo,596
247
251
  omlish/specs/jsonschema/keywords/base.py,sha256=HA4OvsdvAl4o5JgJI_tuaHBGURjxT5QeFN6_ja5Z5xQ,1957
248
252
  omlish/specs/jsonschema/keywords/core.py,sha256=irJ5Rreg9hTeXu3FovSdzhFb3vUly-hYQVYflEgST3g,368
@@ -285,8 +289,8 @@ omlish/text/delimit.py,sha256=ubPXcXQmtbOVrUsNh5gH1mDq5H-n1y2R4cPL5_DQf68,4928
285
289
  omlish/text/glyphsplit.py,sha256=Ug-dPRO7x-OrNNr8g1y6DotSZ2KH0S-VcOmUobwa4B0,3296
286
290
  omlish/text/indent.py,sha256=6Jj6TFY9unaPa4xPzrnZemJ-fHsV53IamP93XGjSUHs,1274
287
291
  omlish/text/parts.py,sha256=KGgo0wHOIMVMZtDso-rhSWKAcAkYAH2IGpg9tULabu8,6505
288
- omlish-0.0.0.dev20.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
289
- omlish-0.0.0.dev20.dist-info/METADATA,sha256=BBHZ7D3ieK9P1cgSW23jGabeqRXDnzccCc8Tsby0J5Y,3718
290
- omlish-0.0.0.dev20.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
291
- omlish-0.0.0.dev20.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
292
- omlish-0.0.0.dev20.dist-info/RECORD,,
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,,