omlish 0.0.0.dev25__py3-none-any.whl → 0.0.0.dev26__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.
Files changed (41) hide show
  1. omlish/__about__.py +6 -2
  2. omlish/bootstrap/diag.py +25 -0
  3. omlish/bootstrap/harness.py +3 -2
  4. omlish/check.py +1 -1
  5. omlish/collections/__init__.py +1 -0
  6. omlish/collections/utils.py +7 -2
  7. omlish/dataclasses/__init__.py +4 -4
  8. omlish/dataclasses/impl/api.py +5 -3
  9. omlish/dataclasses/impl/exceptions.py +2 -2
  10. omlish/dataclasses/impl/fields.py +7 -4
  11. omlish/dataclasses/impl/init.py +8 -8
  12. omlish/dataclasses/impl/metadata.py +3 -3
  13. omlish/dataclasses/impl/params.py +4 -3
  14. omlish/diag/replserver/server.py +17 -2
  15. omlish/docker.py +74 -6
  16. omlish/formats/yaml.py +10 -0
  17. omlish/graphs/dot/__init__.py +31 -19
  18. omlish/graphs/dot/make.py +16 -0
  19. omlish/graphs/dot/rendering.py +9 -8
  20. omlish/http/cookies.py +2 -1
  21. omlish/inject/impl/scopes.py +2 -0
  22. omlish/inject/keys.py +1 -1
  23. omlish/inject/multis.py +4 -4
  24. omlish/inject/providers.py +1 -1
  25. omlish/lang/__init__.py +8 -0
  26. omlish/lang/classes/__init__.py +3 -0
  27. omlish/lang/classes/restrict.py +25 -4
  28. omlish/lang/classes/simple.py +0 -4
  29. omlish/lang/classes/virtual.py +6 -4
  30. omlish/lang/datetimes.py +9 -0
  31. omlish/lite/logs.py +30 -25
  32. omlish/lite/secrets.py +3 -1
  33. omlish/logs/configs.py +2 -2
  34. omlish/marshal/dataclasses.py +1 -1
  35. omlish/secrets/secrets.py +1 -1
  36. omlish/stats.py +1 -1
  37. {omlish-0.0.0.dev25.dist-info → omlish-0.0.0.dev26.dist-info}/METADATA +1 -1
  38. {omlish-0.0.0.dev25.dist-info → omlish-0.0.0.dev26.dist-info}/RECORD +41 -39
  39. {omlish-0.0.0.dev25.dist-info → omlish-0.0.0.dev26.dist-info}/WHEEL +1 -1
  40. {omlish-0.0.0.dev25.dist-info → omlish-0.0.0.dev26.dist-info}/LICENSE +0 -0
  41. {omlish-0.0.0.dev25.dist-info → omlish-0.0.0.dev26.dist-info}/top_level.txt +0 -0
omlish/__about__.py CHANGED
@@ -1,5 +1,5 @@
1
- __version__ = '0.0.0.dev25'
2
- __revision__ = '123812c0156cbb06af42b6e2ee3b7912d5bf5fc8'
1
+ __version__ = '0.0.0.dev26'
2
+ __revision__ = 'd3cb2e43eac2aa63dc4606aa371faa78ec0daf1a'
3
3
 
4
4
 
5
5
  #
@@ -41,7 +41,9 @@ class Project(ProjectBase):
41
41
 
42
42
  'compression': [
43
43
  'lz4 ~= 4.0',
44
+
44
45
  'python-snappy ~= 0.7; python_version < "3.13"',
46
+
45
47
  'zstd ~= 1.5',
46
48
  ],
47
49
 
@@ -69,6 +71,7 @@ class Project(ProjectBase):
69
71
 
70
72
  'misc': [
71
73
  'jinja2 ~= 3.1',
74
+
72
75
  'wrapt ~= 1.14',
73
76
  ],
74
77
 
@@ -81,6 +84,7 @@ class Project(ProjectBase):
81
84
 
82
85
  'pg8000 ~= 1.31',
83
86
  # 'psycopg2 ~= 2.9',
87
+ # 'psycopg ~= 3.2',
84
88
 
85
89
  'pymysql ~= 1.1',
86
90
  # 'mysql-connector-python ~= 9.0',
omlish/bootstrap/diag.py CHANGED
@@ -3,6 +3,7 @@ import contextlib
3
3
  import dataclasses as dc
4
4
  import signal
5
5
  import sys
6
+ import threading
6
7
  import typing as ta
7
8
 
8
9
  from .. import check
@@ -17,6 +18,7 @@ if ta.TYPE_CHECKING:
17
18
  import pstats
18
19
 
19
20
  from ..diag import pycharm as diagpc
21
+ from ..diag import replserver as diagrs
20
22
  from ..diag import threads as diagt
21
23
 
22
24
  else:
@@ -24,6 +26,7 @@ else:
24
26
  pstats = lang.proxy_import('pstats')
25
27
 
26
28
  diagpc = lang.proxy_import('..diag.pycharm', __package__)
29
+ diagrs = lang.proxy_import('..diag.replserver', __package__)
27
30
  diagt = lang.proxy_import('..diag.threads', __package__)
28
31
 
29
32
 
@@ -175,3 +178,25 @@ class PycharmBootstrap(SimpleBootstrap['PycharmBootstrap.Config']):
175
178
  self._config.debug_port,
176
179
  version=self._config.version,
177
180
  )
181
+
182
+
183
+ ##
184
+
185
+
186
+ class ReplServerBootstrap(ContextBootstrap['ReplServerBootstrap.Config']):
187
+ @dc.dataclass(frozen=True)
188
+ class Config(Bootstrap.Config):
189
+ path: str | None = None
190
+
191
+ @contextlib.contextmanager
192
+ def enter(self) -> ta.Iterator[None]:
193
+ if self._config.path is None:
194
+ return
195
+
196
+ with diagrs.ReplServer(diagrs.ReplServer.Config(
197
+ path=self._config.path,
198
+ )) as rs:
199
+ thread = threading.Thread(target=rs.run, name='replserver')
200
+ thread.start()
201
+
202
+ yield
@@ -6,6 +6,7 @@ TODO:
6
6
  - multiprocess profiling - afterfork, suffix with pid
7
7
 
8
8
  TODO diag:
9
+ - tracemalloc
9
10
  - yappi
10
11
  - stackscope
11
12
  - https://github.com/pythonspeed/filprofiler
@@ -13,10 +14,10 @@ TODO diag:
13
14
  - https://pypi.org/project/memory-profiler/
14
15
  - https://pypi.org/project/Pympler/
15
16
 
17
+
16
18
  TODO new items:
17
- - pydevd connect-back
18
19
  - debugging / pdb
19
- - repl server
20
+ - https://github.com/inducer/pudb
20
21
  - packaging fixups
21
22
  - daemonize ( https://github.com/thesharp/daemonize/blob/master/daemonize.py )
22
23
  """
omlish/check.py CHANGED
@@ -332,7 +332,7 @@ def single(obj: ta.Iterable[T], message: Message = None) -> T:
332
332
  return value
333
333
 
334
334
 
335
- def optional_single(obj: ta.Iterable[T], message: Message = None) -> T | None:
335
+ def opt_single(obj: ta.Iterable[T], message: Message = None) -> T | None:
336
336
  it = iter(obj)
337
337
  try:
338
338
  value = next(it)
@@ -91,6 +91,7 @@ from .unmodifiable import ( # noqa
91
91
  )
92
92
 
93
93
  from .utils import ( # noqa
94
+ PartitionResult,
94
95
  all_equal,
95
96
  all_not_equal,
96
97
  indexes,
@@ -39,7 +39,12 @@ def toposort(data: ta.Mapping[T, ta.AbstractSet[T]]) -> ta.Iterator[set[T]]:
39
39
  ##
40
40
 
41
41
 
42
- def partition(items: ta.Iterable[T], pred: ta.Callable[[T], bool]) -> tuple[list[T], list[T]]:
42
+ class PartitionResult(ta.NamedTuple, ta.Generic[T]):
43
+ t: list[T]
44
+ f: list[T]
45
+
46
+
47
+ def partition(items: ta.Iterable[T], pred: ta.Callable[[T], bool]) -> PartitionResult[T]:
43
48
  t: list[T] = []
44
49
  f: list[T] = []
45
50
  for e in items:
@@ -47,7 +52,7 @@ def partition(items: ta.Iterable[T], pred: ta.Callable[[T], bool]) -> tuple[list
47
52
  t.append(e)
48
53
  else:
49
54
  f.append(e)
50
- return t, f
55
+ return PartitionResult(t, f)
51
56
 
52
57
 
53
58
  def unique(
@@ -59,8 +59,8 @@ globals()['make_dataclass'] = xmake_dataclass
59
59
 
60
60
 
61
61
  from .impl.exceptions import ( # noqa
62
- CheckError,
63
- FieldCheckError,
62
+ FieldValidationError,
63
+ ValidationError,
64
64
  )
65
65
 
66
66
  from .impl.metaclass import ( # noqa
@@ -76,8 +76,8 @@ from .impl.metadata import ( # noqa
76
76
  UserMetadata,
77
77
  metadata,
78
78
 
79
- Check,
80
- check,
79
+ Validate,
80
+ validate,
81
81
 
82
82
  Init,
83
83
  init,
@@ -30,8 +30,9 @@ def field( # noqa
30
30
  metadata=None,
31
31
  kw_only=MISSING,
32
32
 
33
- coerce: ta.Callable[[ta.Any], ta.Any] | None = None,
34
- check: ta.Callable[[ta.Any], bool] | None = None,
33
+ derive: ta.Callable[..., ta.Any] | None = None,
34
+ coerce: bool | ta.Callable[[ta.Any], ta.Any] | None = None,
35
+ validate: ta.Callable[[ta.Any], bool] | None = None,
35
36
  check_type: bool | None = None,
36
37
  override: bool = False,
37
38
  repr_fn: ta.Callable[[ta.Any], str | None] | None = None,
@@ -40,8 +41,9 @@ def field( # noqa
40
41
  raise ValueError('cannot specify both default and default_factory')
41
42
 
42
43
  fx = FieldExtras(
44
+ derive=derive,
43
45
  coerce=coerce,
44
- check=check,
46
+ validate=validate,
45
47
  check_type=check_type,
46
48
  override=override,
47
49
  repr_fn=repr_fn,
@@ -1,8 +1,8 @@
1
- class CheckError(Exception):
1
+ class ValidationError(Exception):
2
2
  pass
3
3
 
4
4
 
5
- class FieldCheckError(CheckError):
5
+ class FieldValidationError(ValidationError):
6
6
  def __init__(self, field: str) -> None:
7
7
  super().__init__(field)
8
8
  self.field = field
@@ -175,15 +175,18 @@ def field_init(
175
175
  else:
176
176
  pass
177
177
 
178
+ if fx.derive is not None:
179
+ raise NotImplementedError
180
+
178
181
  if fx.coerce is not None:
179
182
  cn = f'__dataclass_coerce__{f.name}__'
180
183
  locals[cn] = fx.coerce
181
184
  lines.append(f'{value} = {cn}({value})')
182
185
 
183
- if fx.check is not None:
184
- cn = f'__dataclass_check__{f.name}__'
185
- locals[cn] = fx.check
186
- lines.append(f'if not {cn}({value}): raise __dataclass_FieldCheckError__({f.name})')
186
+ if fx.validate is not None:
187
+ cn = f'__dataclass_validate__{f.name}__'
188
+ locals[cn] = fx.validate
189
+ lines.append(f'if not {cn}({value}): raise __dataclass_FieldValidationError__({f.name})')
187
190
 
188
191
  if fx.check_type:
189
192
  cn = f'__dataclass_check_type__{f.name}__'
@@ -3,16 +3,16 @@ import inspect
3
3
  import typing as ta
4
4
 
5
5
  from ... import lang
6
- from .exceptions import CheckError
7
- from .exceptions import FieldCheckError
6
+ from .exceptions import FieldValidationError
7
+ from .exceptions import ValidationError
8
8
  from .fields import field_init
9
9
  from .fields import field_type
10
10
  from .fields import has_default
11
11
  from .internals import HAS_DEFAULT_FACTORY
12
12
  from .internals import POST_INIT_NAME
13
13
  from .internals import FieldType
14
- from .metadata import Check
15
14
  from .metadata import Init
15
+ from .metadata import Validate
16
16
  from .processing import Processor
17
17
  from .reflect import ClassInfo
18
18
  from .utils import Namespace
@@ -100,8 +100,8 @@ class InitBuilder:
100
100
  '__dataclass_builtins_object__': object,
101
101
  '__dataclass_builtins_isinstance__': isinstance,
102
102
  '__dataclass_builtins_TypeError__': TypeError,
103
- '__dataclass_CheckError__': CheckError,
104
- '__dataclass_FieldCheckError__': FieldCheckError,
103
+ '__dataclass_ValidationError__': ValidationError,
104
+ '__dataclass_FieldValidationError__': FieldValidationError,
105
105
  })
106
106
 
107
107
  body_lines: list[str] = []
@@ -121,14 +121,14 @@ class InitBuilder:
121
121
  params_str = ','.join(f.name for f in ifs.all if field_type(f) is FieldType.INIT)
122
122
  body_lines.append(f'{self._self_name}.{POST_INIT_NAME}({params_str})')
123
123
 
124
- for i, fn in enumerate(self._info.merged_metadata.get(Check, [])):
124
+ for i, fn in enumerate(self._info.merged_metadata.get(Validate, [])):
125
125
  if isinstance(fn, staticmethod):
126
126
  fn = fn.__func__
127
- cn = f'__dataclass_check_{i}__'
127
+ cn = f'__dataclass_validate_{i}__'
128
128
  locals[cn] = fn
129
129
  csig = inspect.signature(fn)
130
130
  cas = ', '.join(p.name for p in csig.parameters.values())
131
- body_lines.append(f'if not {cn}({cas}): raise __dataclass_CheckError__')
131
+ body_lines.append(f'if not {cn}({cas}): raise __dataclass_ValidationError__')
132
132
 
133
133
  inits = self._info.merged_metadata.get(Init, [])
134
134
  mro_dct = lang.build_mro_dict(self._info.cls)
@@ -54,12 +54,12 @@ def metadata(cls_dct, *args) -> None:
54
54
 
55
55
 
56
56
  @_class_merged
57
- class Check(lang.Marker):
57
+ class Validate(lang.Marker):
58
58
  pass
59
59
 
60
60
 
61
- def check(fn: ta.Callable[..., bool] | staticmethod) -> None:
62
- _append_cls_md(Check, fn)
61
+ def validate(fn: ta.Callable[..., bool] | staticmethod) -> None:
62
+ _append_cls_md(Validate, fn)
63
63
 
64
64
 
65
65
  ##
@@ -40,10 +40,11 @@ from .metadata import METADATA_ATTR
40
40
  ##
41
41
 
42
42
 
43
- @dc.dataclass(frozen=True)
43
+ @dc.dataclass(frozen=True, kw_only=True)
44
44
  class FieldExtras(lang.Final):
45
+ derive: ta.Callable[..., ta.Any] | None = None
45
46
  coerce: bool | ta.Callable[[ta.Any], ta.Any] | None = None
46
- check: ta.Callable[[ta.Any], bool] | None = None
47
+ validate: ta.Callable[[ta.Any], bool] | None = None
47
48
  check_type: bool | None = None
48
49
  override: bool = False
49
50
  repr_fn: ta.Callable[[ta.Any], str | None] | None = None
@@ -79,7 +80,7 @@ def get_params(obj: ta.Any) -> Params:
79
80
  ##
80
81
 
81
82
 
82
- @dc.dataclass(frozen=True)
83
+ @dc.dataclass(frozen=True, kw_only=True)
83
84
  class ParamsExtras(lang.Final):
84
85
  reorder: bool = False
85
86
  cache_hash: bool = False
@@ -1,8 +1,13 @@
1
1
  """
2
+ FIXME:
3
+ - lol shutdown deadlocks
4
+ - whole thing is just gross is this its own thread or what
5
+
2
6
  TODO:
3
7
  - !!! ANYIO !!!
4
8
  - optional paramiko ssh-server
5
9
  - optional ipython embed
10
+ - https://github.com/python/cpython/tree/56470004e58911b146c016fc9fec4461b8f69454/Lib/_pyrepl
6
11
 
7
12
  lookit:
8
13
  - https://github.com/vxgmichel/aioconsole/blob/e55f4b0601da3b3a40a88c965526d35ab38b5841/aioconsole/server.py
@@ -38,7 +43,7 @@ class ReplServer:
38
43
  @dc.dataclass(frozen=True)
39
44
  class Config:
40
45
  path: str
41
- file_mode: int | None = None
46
+ file_mode: int | None = 0o660
42
47
  poll_interval: float = 0.5
43
48
  exit_timeout: float = 10.0
44
49
 
@@ -80,7 +85,17 @@ class ReplServer:
80
85
 
81
86
  self._socket = sock.socket(sock.AF_UNIX, sock.SOCK_STREAM)
82
87
  self._socket.settimeout(self._config.poll_interval)
83
- self._socket.bind(self._config.path)
88
+
89
+ if self._config.file_mode is not None:
90
+ prev_umask = os.umask(~self._config.file_mode)
91
+ else:
92
+ prev_umask = None
93
+ try:
94
+ self._socket.bind(self._config.path)
95
+ finally:
96
+ if prev_umask is not None:
97
+ os.umask(prev_umask)
98
+
84
99
  with contextlib.closing(self._socket):
85
100
  self._socket.listen(1)
86
101
 
omlish/docker.py CHANGED
@@ -7,12 +7,6 @@ TODO:
7
7
  - https://stackoverflow.com/questions/55386202/how-can-i-use-the-docker-registry-api-to-pull-information-about-a-container-get
8
8
  - https://ops.tips/blog/inspecting-docker-image-without-pull/
9
9
 
10
- repo=library/nginx
11
- token=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:${repo}:pull" | jq -r '.token')
12
- curl -H "Authorization: Bearer $token" -s "https://registry-1.docker.io/v2/${repo}/tags/list" | jq
13
- api="application/vnd.docker.distribution.manifest.v2+json"
14
- apil="application/vnd.docker.distribution.manifest.list.v2+json"
15
- curl -H "Accept: ${api}" -H "Accept: ${apil}" -H "Authorization: Bearer $token" -s "https://registry-1.docker.io/v2/${repo}/manifests/latest" | jq .
16
10
  """ # noqa
17
11
  import datetime
18
12
  import os
@@ -21,6 +15,7 @@ import shlex
21
15
  import subprocess
22
16
  import sys
23
17
  import typing as ta
18
+ import urllib.request
24
19
 
25
20
  from . import check
26
21
  from . import dataclasses as dc
@@ -191,3 +186,76 @@ DOCKER_HOST_PLATFORM_KEY = 'DOCKER_HOST_PLATFORM'
191
186
 
192
187
  def get_docker_host_platform() -> str | None:
193
188
  return os.environ.get(DOCKER_HOST_PLATFORM_KEY)
189
+
190
+
191
+ ##
192
+
193
+
194
+ @dc.dataclass(frozen=True)
195
+ class HubRepoInfo:
196
+ repo: str
197
+ tags: ta.Mapping[str, ta.Any]
198
+ latest_manifests: ta.Mapping[str, ta.Any]
199
+
200
+
201
+ def get_hub_repo_info(
202
+ repo: str,
203
+ *,
204
+ auth_url: str = 'https://auth.docker.io/',
205
+ api_url: str = 'https://registry-1.docker.io/v2/',
206
+ ) -> HubRepoInfo:
207
+ """
208
+ https://stackoverflow.com/a/39376254
209
+
210
+ ==
211
+
212
+ repo=library/nginx
213
+ token=$(
214
+ curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:${repo}:pull" \
215
+ | jq -r '.token' \
216
+ )
217
+ curl -H "Authorization: Bearer $token" -s "https://registry-1.docker.io/v2/${repo}/tags/list" | jq
218
+ curl \
219
+ -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
220
+ -H "Accept: application/vnd.docker.distribution.manifest.list.v2+json" \
221
+ -H "Authorization: Bearer $token" \
222
+ -s "https://registry-1.docker.io/v2/${repo}/manifests/latest" \
223
+ | jq .
224
+ """
225
+
226
+ auth_url = auth_url.rstrip('/')
227
+ api_url = api_url.rstrip('/')
228
+
229
+ #
230
+
231
+ def req_json(url: str, **kwargs: ta.Any) -> ta.Any:
232
+ with urllib.request.urlopen(urllib.request.Request(url, **kwargs)) as resp: # noqa
233
+ return json.loads(resp.read().decode('utf-8'))
234
+
235
+ #
236
+
237
+ token_dct = req_json(f'{auth_url}/token?service=registry.docker.io&scope=repository:{repo}:pull')
238
+ token = token_dct['token']
239
+
240
+ req_hdrs = {'Authorization': f'Bearer {token}'}
241
+
242
+ #
243
+
244
+ tags_dct = req_json(
245
+ f'{api_url}/{repo}/tags/list',
246
+ headers=req_hdrs,
247
+ )
248
+
249
+ latest_mani_dct = req_json(
250
+ f'{api_url}/{repo}/manifests/latest',
251
+ headers={
252
+ **req_hdrs,
253
+ 'Accept': 'application/vnd.docker.distribution.manifest.v2+json',
254
+ },
255
+ )
256
+
257
+ return HubRepoInfo(
258
+ repo,
259
+ tags_dct,
260
+ latest_mani_dct,
261
+ )
omlish/formats/yaml.py CHANGED
@@ -5,6 +5,7 @@ TODO:
5
5
  - goal: perfect rewrites (comments, whitespace)
6
6
  - or at least comments
7
7
  - rename 'objects'? codecs/serde interplay still unresolved
8
+ - look ma, a monad
8
9
  """
9
10
  import datetime
10
11
  import types
@@ -26,6 +27,9 @@ else:
26
27
  T = ta.TypeVar('T')
27
28
 
28
29
 
30
+ ##
31
+
32
+
29
33
  @dc.dataclass(frozen=True)
30
34
  class NodeWrapped(lang.Final, ta.Generic[T]):
31
35
  value: T
@@ -124,6 +128,9 @@ class NodeWrappingConstructorMixin:
124
128
  return self.__construct_yaml_pairs(node, super().construct_yaml_pairs) # type: ignore # noqa
125
129
 
126
130
 
131
+ ##
132
+
133
+
127
134
  class _cached_class_property: # noqa
128
135
  def __init__(self, fn):
129
136
  super().__init__()
@@ -204,6 +211,9 @@ class WrappedLoaders(lang.Namespace):
204
211
  return cls.CUnsafe(*args, **kwargs)
205
212
 
206
213
 
214
+ ##
215
+
216
+
207
217
  def load(stream, Loader): # noqa
208
218
  with lang.disposing(Loader(stream)) as loader:
209
219
  return loader.get_single_data()
@@ -1,19 +1,31 @@
1
- from .items import Attrs # noqa
2
- from .items import Cell # noqa
3
- from .items import Edge # noqa
4
- from .items import Graph # noqa
5
- from .items import Id # noqa
6
- from .items import Item # noqa
7
- from .items import Node # noqa
8
- from .items import Raw # noqa
9
- from .items import RawStmt # noqa
10
- from .items import Row # noqa
11
- from .items import Stmt # noqa
12
- from .items import Table # noqa
13
- from .items import Text # noqa
14
- from .rendering import Renderer # noqa
15
- from .rendering import open_dot # noqa
16
- from .rendering import render # noqa
17
- from .utils import Color # noqa
18
- from .utils import escape # noqa
19
- from .utils import gen_rainbow # noqa
1
+ from .items import ( # noqa
2
+ Attrs,
3
+ Cell,
4
+ Edge,
5
+ Graph,
6
+ Id,
7
+ Item,
8
+ Node,
9
+ Raw,
10
+ RawStmt,
11
+ Row,
12
+ Stmt,
13
+ Table,
14
+ Text,
15
+ )
16
+
17
+ from .make import ( # noqa
18
+ make_simple,
19
+ )
20
+
21
+ from .rendering import ( # noqa
22
+ Renderer,
23
+ open_dot,
24
+ render,
25
+ )
26
+
27
+ from .utils import ( # noqa
28
+ Color,
29
+ escape,
30
+ gen_rainbow,
31
+ )
@@ -0,0 +1,16 @@
1
+ import typing as ta
2
+
3
+ from ... import lang
4
+ from .items import Edge
5
+ from .items import Graph
6
+ from .items import Node
7
+
8
+
9
+ T = ta.TypeVar('T')
10
+
11
+
12
+ def make_simple(graph: ta.Mapping[T, ta.Iterable[T]]) -> Graph:
13
+ return Graph([
14
+ *[Node(n) for n in {*graph, *lang.flatten(graph.values())}],
15
+ *[Edge(k, v) for k, vs in graph.items() for v in vs],
16
+ ])
@@ -120,6 +120,7 @@ def open_dot(
120
120
  *,
121
121
  timeout_s: float = 1.,
122
122
  sleep_s: float = 0.,
123
+ delete: bool = False,
123
124
  ) -> None:
124
125
  stdout, _ = subprocess.Popen(
125
126
  ['dot', '-Tpdf'],
@@ -132,16 +133,16 @@ def open_dot(
132
133
 
133
134
  with tempfile.NamedTemporaryFile(
134
135
  suffix='.pdf',
135
- delete=True,
136
+ delete=delete,
136
137
  ) as pdf:
137
138
  pdf.file.write(stdout)
138
139
  pdf.file.flush()
139
140
 
140
- _, _ = subprocess.Popen(
141
- ['open', pdf.name],
142
- ).communicate(
143
- timeout=timeout_s,
144
- )
141
+ _, _ = subprocess.Popen(
142
+ ['open', pdf.name],
143
+ ).communicate(
144
+ timeout=timeout_s,
145
+ )
145
146
 
146
- if sleep_s > 0.:
147
- time.sleep(sleep_s)
147
+ if sleep_s > 0.:
148
+ time.sleep(sleep_s)
omlish/http/cookies.py CHANGED
@@ -29,6 +29,7 @@ import typing as ta
29
29
  import urllib.parse
30
30
 
31
31
  from .. import collections as col
32
+ from .. import lang
32
33
  from .dates import http_date
33
34
 
34
35
 
@@ -140,7 +141,7 @@ def dump_cookie(
140
141
  if not isinstance(expires, str):
141
142
  expires = http_date(expires)
142
143
  elif max_age is not None and sync_expires:
143
- expires = http_date(datetime.datetime.now(tz=datetime.UTC).timestamp() + max_age)
144
+ expires = http_date(lang.utcnow().timestamp() + max_age)
144
145
 
145
146
  if samesite is not None:
146
147
  samesite = samesite.title()
@@ -1,6 +1,8 @@
1
1
  """
2
2
  TODO:
3
3
  - ContextVar ('context')
4
+ - greenlet?
5
+ - dynamic? https://github.com/wrmsr/iceworm/blob/2f6b4d5e9d237ef9665f7d57cfa6ce328efa0757/iceworm/utils/inject.py#L44
4
6
  """
5
7
  import abc
6
8
  import contextlib
omlish/inject/keys.py CHANGED
@@ -18,7 +18,7 @@ class Key(lang.Final, ta.Generic[T]):
18
18
  tag: ta.Any = dc.xfield(
19
19
  default=None,
20
20
  kw_only=True,
21
- check=lambda o: not isinstance(o, Tag),
21
+ validate=lambda o: not isinstance(o, Tag),
22
22
  repr_fn=dc.opt_repr,
23
23
  )
24
24
 
omlish/inject/multis.py CHANGED
@@ -32,14 +32,14 @@ def _check_set_multi_key(mk: Key) -> bool:
32
32
  @dc.dataclass(frozen=True)
33
33
  @dc.extra_params(cache_hash=True)
34
34
  class SetBinding(Element, lang.Final):
35
- multi_key: Key = dc.xfield(check=_check_set_multi_key)
35
+ multi_key: Key = dc.xfield(validate=_check_set_multi_key)
36
36
  dst: Key = dc.xfield(coerce=check.of_isinstance(Key))
37
37
 
38
38
 
39
39
  @dc.dataclass(frozen=True)
40
40
  @dc.extra_params(cache_hash=True)
41
41
  class SetProvider(Provider):
42
- multi_key: Key = dc.xfield(check=_check_set_multi_key)
42
+ multi_key: Key = dc.xfield(validate=_check_set_multi_key)
43
43
 
44
44
 
45
45
  ##
@@ -52,7 +52,7 @@ def _check_map_multi_key(mk: Key) -> bool:
52
52
  @dc.dataclass(frozen=True)
53
53
  @dc.extra_params(cache_hash=True)
54
54
  class MapBinding(Element, lang.Final):
55
- multi_key: Key = dc.xfield(check=_check_map_multi_key)
55
+ multi_key: Key = dc.xfield(validate=_check_map_multi_key)
56
56
  map_key: ta.Any = dc.xfield()
57
57
  dst: Key = dc.xfield(coerce=check.of_isinstance(Key))
58
58
 
@@ -60,7 +60,7 @@ class MapBinding(Element, lang.Final):
60
60
  @dc.dataclass(frozen=True)
61
61
  @dc.extra_params(cache_hash=True)
62
62
  class MapProvider(Provider):
63
- multi_key: Key = dc.xfield(check=_check_map_multi_key)
63
+ multi_key: Key = dc.xfield(validate=_check_map_multi_key)
64
64
 
65
65
 
66
66
  ##
@@ -17,7 +17,7 @@ class Provider(lang.Abstract):
17
17
  @dc.dataclass(frozen=True)
18
18
  @dc.extra_params(cache_hash=True)
19
19
  class FnProvider(Provider):
20
- fn: ta.Any = dc.xfield(check=callable)
20
+ fn: ta.Any = dc.xfield(validate=callable)
21
21
 
22
22
 
23
23
  @dc.dataclass(frozen=True)
omlish/lang/__init__.py CHANGED
@@ -5,6 +5,7 @@ from .cached import ( # noqa
5
5
 
6
6
  from .classes import ( # noqa
7
7
  Abstract,
8
+ AnySensitive,
8
9
  Callable,
9
10
  Descriptor,
10
11
  Final,
@@ -17,8 +18,10 @@ from .classes import ( # noqa
17
18
  NotPicklable,
18
19
  PackageSealed,
19
20
  Picklable,
21
+ SENSITIVE_ATTR,
20
22
  Sealed,
21
23
  SealedError,
24
+ Sensitive,
22
25
  SimpleMetaDict,
23
26
  Singleton,
24
27
  Virtual,
@@ -68,6 +71,11 @@ from .contextmanagers import ( # noqa
68
71
  maybe_managing,
69
72
  )
70
73
 
74
+ from .datetimes import ( # noqa
75
+ utcnow,
76
+ utcfromtimestamp,
77
+ )
78
+
71
79
  from .descriptors import ( # noqa
72
80
  AccessForbiddenError,
73
81
  access_forbidden,
@@ -7,14 +7,17 @@ from .abstract import ( # noqa
7
7
  )
8
8
 
9
9
  from .restrict import ( # noqa
10
+ AnySensitive,
10
11
  Final,
11
12
  FinalError,
12
13
  NoBool,
13
14
  NotInstantiable,
14
15
  NotPicklable,
15
16
  PackageSealed,
17
+ SENSITIVE_ATTR,
16
18
  Sealed,
17
19
  SealedError,
20
+ Sensitive,
18
21
  no_bool,
19
22
  )
20
23
 
@@ -1,3 +1,4 @@
1
+ import abc
1
2
  import functools
2
3
  import typing as ta
3
4
 
@@ -9,7 +10,6 @@ from .abstract import is_abstract
9
10
 
10
11
 
11
12
  class FinalError(TypeError):
12
-
13
13
  def __init__(self, _type: type) -> None:
14
14
  super().__init__()
15
15
 
@@ -49,7 +49,6 @@ class Final(Abstract):
49
49
 
50
50
 
51
51
  class SealedError(TypeError):
52
-
53
52
  def __init__(self, _type) -> None:
54
53
  super().__init__()
55
54
 
@@ -118,13 +117,11 @@ class NotPicklable:
118
117
 
119
118
 
120
119
  class NoBool:
121
-
122
120
  def __bool__(self) -> bool:
123
121
  raise TypeError
124
122
 
125
123
 
126
124
  class _NoBoolDescriptor:
127
-
128
125
  def __init__(self, fn, instance=None, owner=None) -> None:
129
126
  super().__init__()
130
127
  self._fn = fn
@@ -146,3 +143,27 @@ class _NoBoolDescriptor:
146
143
 
147
144
  def no_bool(fn): # noqa
148
145
  return _NoBoolDescriptor(fn)
146
+
147
+
148
+ ##
149
+
150
+
151
+ SENSITIVE_ATTR = '__sensitive__'
152
+
153
+
154
+ class Sensitive:
155
+ __sensitive__ = True
156
+
157
+
158
+ class _AnySensitiveMeta(abc.ABCMeta):
159
+ @classmethod
160
+ def __instancecheck__(cls, instance: object) -> bool:
161
+ return hasattr(instance, SENSITIVE_ATTR) or super().__instancecheck__(AnySensitive, instance)
162
+
163
+ @classmethod
164
+ def __subclasscheck__(cls, subclass: type) -> bool:
165
+ return hasattr(subclass, SENSITIVE_ATTR) or super().__subclasscheck__(AnySensitive, subclass)
166
+
167
+
168
+ class AnySensitive(NotInstantiable, Final, metaclass=_AnySensitiveMeta):
169
+ pass
@@ -42,7 +42,6 @@ _MARKER_NAMESPACE_KEYS: set[str] | None = None
42
42
 
43
43
 
44
44
  class _MarkerMeta(abc.ABCMeta):
45
-
46
45
  def __new__(mcls, name, bases, namespace):
47
46
  global _MARKER_NAMESPACE_KEYS
48
47
 
@@ -74,7 +73,6 @@ class Marker(NotInstantiable, metaclass=_MarkerMeta):
74
73
 
75
74
 
76
75
  class SimpleMetaDict(dict):
77
-
78
76
  def update(self, m: ta.Mapping[K, V], **kwargs: V) -> None: # type: ignore
79
77
  for k, v in m.items():
80
78
  self[k] = v
@@ -109,7 +107,6 @@ def _set_singleton_instance(inst):
109
107
 
110
108
 
111
109
  class Singleton:
112
-
113
110
  def __new__(cls):
114
111
  return cls.__dict__[_SINGLETON_INSTANCE_ATTR]
115
112
 
@@ -119,7 +116,6 @@ class Singleton:
119
116
 
120
117
 
121
118
  class LazySingleton:
122
-
123
119
  def __new__(cls):
124
120
  try:
125
121
  return cls.__dict__[_SINGLETON_INSTANCE_ATTR]
@@ -40,7 +40,12 @@ class _VirtualMeta(abc.ABCMeta):
40
40
  if absv is not v:
41
41
  namespace[k] = absv
42
42
 
43
- reqs = {k: v for k, v in namespace.items() if getattr(v, '__isabstractmethod__', False)}
43
+ reqs = {
44
+ k: v
45
+ for k, v in namespace.items()
46
+ if getattr(v, '__isabstractmethod__', False)
47
+ }
48
+
44
49
  user_subclasshook = namespace.pop('__subclasshook__', None)
45
50
 
46
51
  def get_missing_reqs(cls):
@@ -94,13 +99,11 @@ def virtual_check(virtual: type) -> ta.Callable[[Ty], Ty]:
94
99
 
95
100
 
96
101
  class Descriptor(Virtual):
97
-
98
102
  def __get__(self, instance, owner=None):
99
103
  raise NotImplementedError
100
104
 
101
105
 
102
106
  class Picklable(Virtual):
103
-
104
107
  def __getstate__(self):
105
108
  raise NotImplementedError
106
109
 
@@ -112,7 +115,6 @@ class Picklable(Virtual):
112
115
 
113
116
 
114
117
  class Callable(NotInstantiable, Final, ta.Generic[T]):
115
-
116
118
  def __call__(self, *args: ta.Any, **kwargs: ta.Any) -> T:
117
119
  raise TypeError
118
120
 
@@ -0,0 +1,9 @@
1
+ import datetime
2
+
3
+
4
+ def utcnow() -> datetime.datetime:
5
+ return datetime.datetime.now(tz=datetime.UTC)
6
+
7
+
8
+ def utcfromtimestamp(ts: float) -> datetime.datetime:
9
+ return datetime.datetime.fromtimestamp(ts, tz=datetime.UTC)
omlish/lite/logs.py CHANGED
@@ -208,43 +208,48 @@ def configure_standard_logging(
208
208
  *,
209
209
  json: bool = False,
210
210
  target: ta.Optional[logging.Logger] = None,
211
- no_check: bool = False,
211
+ force: bool = False,
212
212
  ) -> ta.Optional[StandardLogHandler]:
213
- if target is None:
214
- target = logging.root
213
+ logging._acquireLock() # type: ignore # noqa
214
+ try:
215
+ if target is None:
216
+ target = logging.root
215
217
 
216
- #
218
+ #
217
219
 
218
- if not no_check:
219
- if any(isinstance(h, StandardLogHandler) for h in list(target.handlers)):
220
- return None
220
+ if not force:
221
+ if any(isinstance(h, StandardLogHandler) for h in list(target.handlers)):
222
+ return None
221
223
 
222
- #
224
+ #
223
225
 
224
- handler = logging.StreamHandler()
226
+ handler = logging.StreamHandler()
225
227
 
226
- #
228
+ #
227
229
 
228
- formatter: logging.Formatter
229
- if json:
230
- formatter = JsonLogFormatter()
231
- else:
232
- formatter = StandardLogFormatter(StandardLogFormatter.build_log_format(STANDARD_LOG_FORMAT_PARTS))
233
- handler.setFormatter(formatter)
230
+ formatter: logging.Formatter
231
+ if json:
232
+ formatter = JsonLogFormatter()
233
+ else:
234
+ formatter = StandardLogFormatter(StandardLogFormatter.build_log_format(STANDARD_LOG_FORMAT_PARTS))
235
+ handler.setFormatter(formatter)
236
+
237
+ #
234
238
 
235
- #
239
+ handler.addFilter(TidLogFilter())
236
240
 
237
- handler.addFilter(TidLogFilter())
241
+ #
238
242
 
239
- #
243
+ target.addHandler(handler)
240
244
 
241
- target.addHandler(handler)
245
+ #
242
246
 
243
- #
247
+ if level is not None:
248
+ target.setLevel(level)
244
249
 
245
- if level is not None:
246
- target.setLevel(level)
250
+ #
247
251
 
248
- #
252
+ return StandardLogHandler(handler)
249
253
 
250
- return StandardLogHandler(handler)
254
+ finally:
255
+ logging._releaseLock() # type: ignore # noqa
omlish/lite/secrets.py CHANGED
@@ -3,9 +3,11 @@ import typing as ta
3
3
 
4
4
 
5
5
  class Secret:
6
+ __sensitive__ = True
7
+
6
8
  _VALUE_ATTR = '__secret_value__'
7
9
 
8
- def __init__(self, *, key: ta.Optional[str], value: str) -> None:
10
+ def __init__(self, *, key: ta.Optional[str] = None, value: str) -> None:
9
11
  super().__init__()
10
12
  self._key = key
11
13
  setattr(self, self._VALUE_ATTR, lambda: value)
omlish/logs/configs.py CHANGED
@@ -36,13 +36,13 @@ def configure_standard_logging(
36
36
  *,
37
37
  json: bool = False,
38
38
  target: logging.Logger | None = None,
39
- no_check: bool = False,
39
+ force: bool = False,
40
40
  ) -> StandardLogHandler | None:
41
41
  handler = configure_lite_standard_logging(
42
42
  level,
43
43
  json=json,
44
44
  target=target,
45
- no_check=no_check,
45
+ force=force,
46
46
  )
47
47
 
48
48
  if handler is None:
@@ -24,7 +24,7 @@ from .objects import ObjectUnmarshaler
24
24
 
25
25
 
26
26
  def get_dataclass_metadata(ty: type) -> ObjectMetadata:
27
- return check.optional_single(
27
+ return check.opt_single(
28
28
  e
29
29
  for e in dc.get_merged_metadata(ty).get(dc.UserMetadata, [])
30
30
  if isinstance(e, ObjectMetadata)
omlish/secrets/secrets.py CHANGED
@@ -28,7 +28,7 @@ log = logging.getLogger(__name__)
28
28
  ##
29
29
 
30
30
 
31
- class Secret(lang.NotPicklable, lang.Final):
31
+ class Secret(lang.NotPicklable, lang.Sensitive, lang.Final):
32
32
  _VALUE_ATTR = '__secret_value__'
33
33
 
34
34
  def __init__(self, *, key: str | None, value: str) -> None:
omlish/stats.py CHANGED
@@ -2,7 +2,7 @@
2
2
  TODO:
3
3
  - reservoir
4
4
  - dep tdigest?
5
- - struct-of-arrays backed SamplingHistogram
5
+ - struct-of-arrays - array.array('f', ...) - backed SamplingHistogram
6
6
  """
7
7
  import bisect
8
8
  import collections
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omlish
3
- Version: 0.0.0.dev25
3
+ Version: 0.0.0.dev26
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -1,13 +1,13 @@
1
- omlish/__about__.py,sha256=feVoI3TrA0qaUB6uUzLiE8nrfmqvV3sTmOCAx7e7810,2679
1
+ omlish/__about__.py,sha256=ItiHbRodt34cICvvQxV6BpYmCzBfKRGd46tuo5JLO-o,2714
2
2
  omlish/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  omlish/_manifests.json,sha256=N1F-Xz3GaBn2H1p7uKzhkhKCQV8QVR0t76XD6wmFtXA,3
4
4
  omlish/argparse.py,sha256=QRQmX9G0-L_nATkFtGHvpd4qrpYzKATdjuFLbBqzJPM,6224
5
5
  omlish/c3.py,sha256=W5EwYx9Por3rWYLkKUitJ6OoRMLLgVTfLTyroOz41Y0,8047
6
6
  omlish/cached.py,sha256=UAizxlH4eMWHPzQtmItmyE6FEpFEUFzIkxaO2BHWZ5s,196
7
- omlish/check.py,sha256=-SY9SvZEobAXShAQA7mv3vrt4YYPWHBW5brlg8ZOO5A,10083
7
+ omlish/check.py,sha256=3qp1_W8uRp23I26nWvG_c7YFxdTwJAZEFxmY8Bfw50Y,10078
8
8
  omlish/datetimes.py,sha256=HajeM1kBvwlTa-uR1TTZHmZ3zTPnnUr1uGGQhiO1XQ0,2152
9
9
  omlish/defs.py,sha256=T3bq_7h_tO3nDB5RAFBn7DkdeQgqheXzkFColbOHZko,4890
10
- omlish/docker.py,sha256=R1YrjFSNrZ7Ls9iaUu6nsTrCiKmjUmh3wKPxriGsc0I,4865
10
+ omlish/docker.py,sha256=GyzrHUTdD3rHeDBPGOI_mBzysVDtv5YAo1XN-HnMeo8,6231
11
11
  omlish/dynamic.py,sha256=35C_cCX_Vq2HrHzGk5T-zbrMvmUdiIiwDzDNixczoDo,6541
12
12
  omlish/fnpairs.py,sha256=hVuLqQFdRNNze3FYH2cAQO3GC7nK5yQP_GWPUSbL7nE,10601
13
13
  omlish/genmachine.py,sha256=LCMiqvK32dAWtrlB6lKw9tXdQFiXC8rRdk4TMQYIroU,1603
@@ -18,7 +18,7 @@ omlish/math.py,sha256=AVqp5Y8yxKA-wO0BgrzaxA0Ga3PZiCXnYcwivMneC-0,3804
18
18
  omlish/multiprocessing.py,sha256=QZT4C7I-uThCAjaEY3xgUYb-5GagUlnE4etN01LDyU4,5186
19
19
  omlish/os.py,sha256=cz4nL2ujaxH_-XRq3JUD8af8mSe1JXGPIoXP9XAEd0M,2607
20
20
  omlish/runmodule.py,sha256=PWvuAaJ9wQQn6bx9ftEL3_d04DyotNn8dR_twm2pgw0,700
21
- omlish/stats.py,sha256=AbrdepKPLAzPCgI2MTFSgJhzuxjzOHfG6I87EIuKOvg,9972
21
+ omlish/stats.py,sha256=vJOJgYIW6DGDEgNm5AGWIp08GiLk61Cjld71fnC29fY,9998
22
22
  omlish/sync.py,sha256=AqwIfIuCMVHLwlJUa7dmaSjfA4sM5AYPCD5-nsz3XVQ,1516
23
23
  omlish/term.py,sha256=NEmxqAhicyInGtmFamZAizI2xdu819MzFYPEp0Fx97M,6111
24
24
  omlish/asyncs/__init__.py,sha256=uUz9ziKh4_QrgmdhKFMgq6j7mFbiZd3LiogguDCQsGI,587
@@ -32,12 +32,12 @@ omlish/asyncs/trio_asyncio.py,sha256=oqdOHy0slj9PjVxaDf3gJkq9AAgg7wYZbB469jOftVw
32
32
  omlish/bootstrap/__init__.py,sha256=-Rtsg7uPQNhh1dIT9nqrz96XlqizwoLnWf-FwOEstJI,730
33
33
  omlish/bootstrap/__main__.py,sha256=d23loR_cKfTYZwYiqpt_CmKI7dd5WcYFgIYzqMep75E,68
34
34
  omlish/bootstrap/base.py,sha256=koELbK6UmsZaRj-6Bng5_zPVmEBqDpFCduEdR5BddOs,1077
35
- omlish/bootstrap/diag.py,sha256=KrrRtDy92HNJpFBVyiGfv6vf5idr2yNI26ByKcFO11Y,4384
36
- omlish/bootstrap/harness.py,sha256=vQCIhCQY_N0NHWvDh8rG6Lo57U1qHkQf2egifbXzN-8,2027
35
+ omlish/bootstrap/diag.py,sha256=x_BKS_uhfW8QFk1NeH_VIocHif-A6FVTZ37262qCgZ0,5052
36
+ omlish/bootstrap/harness.py,sha256=pIeSXKfMsF7-3ZkU0gGpde-PtLAKKcVrWaxcin7Xzy0,2041
37
37
  omlish/bootstrap/main.py,sha256=9ZbgXaRNZwt_hXdg8W4YjpP0v0Si_qqlfu86jfgRz04,5215
38
38
  omlish/bootstrap/marshal.py,sha256=qKewGVs-3i2p5W9nywkXSo1pcbVxmOAlTvfLjyo0xpo,554
39
39
  omlish/bootstrap/sys.py,sha256=U0MFxO9tLFV3cdN5Y-Zrink6_45sFvzPUYQXyBk7-ns,8741
40
- omlish/collections/__init__.py,sha256=Y4F43NeFdRHkkUUvFUsqqY6pln0MaRxHKjyRpwFtiDk,1656
40
+ omlish/collections/__init__.py,sha256=tGUzvS_ZjiqALsLRy7JX3h4KZRQX2CmtdAfTRD7UwMk,1677
41
41
  omlish/collections/_abc.py,sha256=sP7BpTVhx6s6C59mTFeosBi4rHOWC6tbFBYbxdZmvh0,2365
42
42
  omlish/collections/_io_abc.py,sha256=Cxs8KB1B_69rxpUYxI-MTsilAmNooJJn3w07DKqYKkE,1255
43
43
  omlish/collections/coerce.py,sha256=o11AMrUiyoadd8WkdqeKPIpXf2xd0LyylzNCyJivCLU,7036
@@ -53,7 +53,7 @@ omlish/collections/sorted.py,sha256=E5ZOdNn7Jju1EcQ7CX2Ltk9StIXsBOzqvh7EsT3ZA2U,
53
53
  omlish/collections/treap.py,sha256=wq9L5hvxq4QgPvIpHmueZMF8t7UrvX5vmlNN4BOqY4g,7720
54
54
  omlish/collections/treapmap.py,sha256=TxOM-ZRF5PK2xe5wRIhESNt7DGh9b_MeZfE7HLkCOs4,5804
55
55
  omlish/collections/unmodifiable.py,sha256=QmUEi9IBXqiM_KGgH2rqg15VmkHJo1MZ6kwq2twEMho,4750
56
- omlish/collections/utils.py,sha256=oNdMkJYLmqq4Fm08vA99UWQ2RMO7FRu9zQruj-squ-c,4239
56
+ omlish/collections/utils.py,sha256=9o9STwzAn5YjZRZ9ns1kuo7NgLXLaoVPFu6AJd-3JT8,4336
57
57
  omlish/collections/cache/__init__.py,sha256=Cv8RX-Ehit3um0QLDq7uRDqJUCcdqTKoAB9T0pM_5hg,392
58
58
  omlish/collections/cache/descriptor.py,sha256=t-1Gh4DTABDuNmeDJlpoW4LV3gi_uSlBd9ZfBINfYCM,5023
59
59
  omlish/collections/cache/impl.py,sha256=nQox5kChhns9h2a5gnX-ayQGBQJ5-B1aZkLQ2Aej19g,15137
@@ -66,25 +66,25 @@ omlish/configs/__init__.py,sha256=3uh09ezodTwkMI0nRmAMP0eEuJ_0VdF-LYyNmPjHiCE,77
66
66
  omlish/configs/classes.py,sha256=GLbB8xKjHjjoUQRCUQm3nEjM8z1qNTx9gPV7ODSt5dg,1317
67
67
  omlish/configs/flattening.py,sha256=AOlRpBHm449MxwMp3CiIRGunStOC1DUNs1f3CLou0wc,4731
68
68
  omlish/configs/strings.py,sha256=0brx1duL85r1GpfbNvbHcSvH4jWzutwuvMFXda9NeI0,2651
69
- omlish/dataclasses/__init__.py,sha256=RYS0xUvtPzREV2Y8IhsenU5v3F91P7E9oObdxOCKGUg,1321
69
+ omlish/dataclasses/__init__.py,sha256=L2kRMvsWgsennXVw7VgZdczCtdLsQzyPcMFit2rBpbA,1337
70
70
  omlish/dataclasses/utils.py,sha256=qBKHyW2LSjnJYfPSAExEsVZ5EdmDBfp4napAhfWkZFs,3221
71
71
  omlish/dataclasses/impl/LICENSE,sha256=Oy-B_iHRgcSZxZolbI4ZaEVdZonSaaqFNzv7avQdo78,13936
72
72
  omlish/dataclasses/impl/__init__.py,sha256=rQJRcE9fVsGEvRUOZ18r4DE0xiLbSRjspyJRXgbLS3I,348
73
- omlish/dataclasses/impl/api.py,sha256=NOuw5dlwztG5AA_gwdF2PJAqvouMdbUf1Si80G26qAY,6244
73
+ omlish/dataclasses/impl/api.py,sha256=PyBMuoamkp77r3OS7TIWKv27cy7laqN2iviq5JoihuE,6339
74
74
  omlish/dataclasses/impl/as_.py,sha256=CD-t7hkC1EP2F_jvZKIA_cVoDuwZ-Ln_xC4fJumPYX0,2598
75
75
  omlish/dataclasses/impl/copy.py,sha256=Tn8_n6Vohs-w4otbGdubBEvhd3TsSTaM3EfNGdS2LYo,591
76
76
  omlish/dataclasses/impl/descriptors.py,sha256=rEYE1Len99agTQCC25hSPMnM19BgPr0ZChABGi58Fdk,2476
77
- omlish/dataclasses/impl/exceptions.py,sha256=WD0Tr1TnjUN4OR3f3rs8CgqtP2KBA1tKjPIbZO8Tzm0,178
78
- omlish/dataclasses/impl/fields.py,sha256=6Ry-fTntJEPigSHBtaegssxeOi0L4hffLSL4SMtqjSE,5908
77
+ omlish/dataclasses/impl/exceptions.py,sha256=DeiM6rcjgncudn-XVuph9TDbVDEwBtyYb1bcbO3FFcA,193
78
+ omlish/dataclasses/impl/fields.py,sha256=yh2mwXVZW-eOhPesY-BG9sVMWSdn0VqzXNHkbs4Jmok,5987
79
79
  omlish/dataclasses/impl/frozen.py,sha256=x87DSM8FIMZ3c_BIUE8NooCkExFjPsabeqIueEP5qKs,2988
80
80
  omlish/dataclasses/impl/hashing.py,sha256=FKnHuXCg9ylrzK2TLGqO5yfRN4HX3F415CSLlVYXtYE,3190
81
- omlish/dataclasses/impl/init.py,sha256=Znn2ggfA5Wkk84OhDykjI2b7UDxEiQvnphUAmlwySRs,6117
81
+ omlish/dataclasses/impl/init.py,sha256=IgxO9nwHaHF8jGrUAk-Y5xke9uV2OwzfEe-88McE1Wg,6161
82
82
  omlish/dataclasses/impl/internals.py,sha256=LTCqGT8AhyGTWwioGrBpTJzDzPvAtizQKb0NBNKcNs0,2989
83
83
  omlish/dataclasses/impl/main.py,sha256=Ti0PKbFKraKvfmoPuR-G7nLVNzRC8mvEuXhCuC-M2kc,2574
84
84
  omlish/dataclasses/impl/metaclass.py,sha256=dlQEIN9MHBirll7Nx3StpzxYxXjrqxJ-QsorMcCNt7w,2828
85
- omlish/dataclasses/impl/metadata.py,sha256=UNI452ljUO87cZFXiPwdcC25vFZ1wnVH_i1o81tmTf4,1392
85
+ omlish/dataclasses/impl/metadata.py,sha256=4veWwTr-aA0KP-Y1cPEeOcXHup9EKJTYNJ0ozIxtzD4,1401
86
86
  omlish/dataclasses/impl/order.py,sha256=zWvWDkSTym8cc7vO1cLHqcBhhjOlucHOCUVJcdh4jt0,1369
87
- omlish/dataclasses/impl/params.py,sha256=M-xg9IeFftVy795oqlp7Yw8jE-7wG2K4vaFhXHKmL1k,2538
87
+ omlish/dataclasses/impl/params.py,sha256=1kvL75eptGll9NXqBv66HN94ACwv_tucBRb1cDAvglM,2620
88
88
  omlish/dataclasses/impl/processing.py,sha256=DFxyFjL_h3awRyF_5eyTnB8QkuApx7Zc4QFnVoltlao,459
89
89
  omlish/dataclasses/impl/reflect.py,sha256=a19BbNxrmjNTbXzWuAl_794RCIQSMYyVqQ2Bf-DnNnM,5305
90
90
  omlish/dataclasses/impl/replace.py,sha256=wS9GHX4fIwaPv1JBJzIewdBfXyK3X3V7_t55Da87dYo,1217
@@ -103,7 +103,7 @@ omlish/diag/threads.py,sha256=1-x02VCDZ407gfbtXm1pWK-ubqhqfePm9PMqkHCVoqk,3642
103
103
  omlish/diag/replserver/__init__.py,sha256=uLo6V2aQ29v9z3IMELlPDSlG3_2iOT4-_X8VniF-EgE,235
104
104
  omlish/diag/replserver/__main__.py,sha256=LmU41lQ58bm1h4Mx7S8zhE_uEBSC6kPcp9mn5JRpulA,32
105
105
  omlish/diag/replserver/console.py,sha256=XzBDVhYlr8FY6ym4OwoaIHuFOHnGK3dTYlMDIOMUUlA,7410
106
- omlish/diag/replserver/server.py,sha256=MBLMouRuJ7eMXBHVsMQS8KGHwZuDThPoNkiJb32UVI0,4896
106
+ omlish/diag/replserver/server.py,sha256=To2rLS-FHc0Ny8iFJaeEsU8ZZdkcWpk9bi1FYS7YNPA,5348
107
107
  omlish/dispatch/__init__.py,sha256=GsiGJ91NKiQptSROtnCSkrZExBkvfDwYvdoTu5dBqF0,117
108
108
  omlish/dispatch/_dispatch2.py,sha256=v3tCNyxGpOwY8qTwdp54TlM8mG6OVwtQoUZfYJ_griU,1756
109
109
  omlish/dispatch/_dispatch3.py,sha256=Vnu5DfoPWFJLodudBqoZBXGTi2wYk-Az56MXJgdQvwc,2608
@@ -114,20 +114,21 @@ omlish/formats/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
114
114
  omlish/formats/dotenv.py,sha256=UjZl3gac-0U24sDjCCGMcCqO1UCWG2Zs8PZ4JdAg2YE,17348
115
115
  omlish/formats/json.py,sha256=61XG6rveb3SSXmYrKvUmRdaVDyMD6C-7yVqXBBMu8t8,1017
116
116
  omlish/formats/props.py,sha256=diYjZDsG1s50ImJhkpeinMwjr8952nIVI-0gYhBIvCY,18897
117
- omlish/formats/yaml.py,sha256=5gvH-e9fVn6C-xpcagnrtM0bSLOsuk3sAQT-aaE4PD4,6653
117
+ omlish/formats/yaml.py,sha256=R3NTkjomsIfjsUNmSf_bOaCUIID3JTyHJHsliQDSYQo,6688
118
118
  omlish/graphs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
119
119
  omlish/graphs/dags.py,sha256=JpTGxt5rsK7hy5EUy9rNUlIeDStT9ri86m8xEKiHQLE,3063
120
120
  omlish/graphs/domination.py,sha256=45iTyn7mZWPJ1ANrqD96aPXqzEeyFpybMvvcVxo9XvQ,7592
121
121
  omlish/graphs/trees.py,sha256=t9kzLy33ynYV0TowVkyDvkkRBQV5x--1vtNBSB4Auus,8156
122
- omlish/graphs/dot/__init__.py,sha256=6-LAx7ytHFQvfLrnxVQG1s8xmApgblERJJCTB2knOHk,642
122
+ omlish/graphs/dot/__init__.py,sha256=Y1MZRQBZkcYyG1Tn7K2FhL8aYbm4v4tk6f5g9AqEkUw,359
123
123
  omlish/graphs/dot/items.py,sha256=OWPf0-hjBgS1uyy2QgAEn4IgFHJcEg7sHVWeTx1ghZc,4083
124
- omlish/graphs/dot/rendering.py,sha256=hUSJ5HvSvxnTIC9ynSxxP8IJnVH6quEeDbLzNlUnmuk,3626
124
+ omlish/graphs/dot/make.py,sha256=RN30gHfJPiXx5Q51kbDdhVJYf59Fr84Lz9J-mXRt9sI,360
125
+ omlish/graphs/dot/rendering.py,sha256=2UgXvMRN4Z9cfIqLlC7Iu_8bWbwUDEL4opHHkFfSqTw,3630
125
126
  omlish/graphs/dot/utils.py,sha256=_FMwn77WfiiAfLsRTOKWm4IYbNv5kQN22YJ5psw6CWg,801
126
127
  omlish/http/__init__.py,sha256=v2f9Hh6bTgcz0MkG9T2JQarzeIgHlDxDT2cbINbpyw8,367
127
128
  omlish/http/asgi.py,sha256=wXhBZ21bEl32Kv9yBrRwUR_7pHEgVtHP8ZZwbasQ6-4,3307
128
129
  omlish/http/collections.py,sha256=s8w5s4Gewgxxhe2Ai0R45PgJYYifrLgTbU3VXVflHj4,260
129
130
  omlish/http/consts.py,sha256=-O0F6qiVWGGT18j8TMP7UNfHCECg1MmByx05oc7Ae9Q,1985
130
- omlish/http/cookies.py,sha256=x56AvYtwNyQKFzVD1zSGIsfg9oiArd6ICTMEdugP008,6356
131
+ omlish/http/cookies.py,sha256=uuOYlHR6e2SC3GM41V0aozK10nef9tYg83Scqpn5-HM,6351
131
132
  omlish/http/dates.py,sha256=Otgp8wRxPgNGyzx8LFowu1vC4EKJYARCiAwLFncpfHM,2875
132
133
  omlish/http/encodings.py,sha256=w2WoKajpaZnQH8j-IBvk5ZFL2O2pAU_iBvZnkocaTlw,164
133
134
  omlish/http/json.py,sha256=9XwAsl4966Mxrv-1ytyCqhcE6lbBJw-0_tFZzGszgHE,7440
@@ -141,14 +142,14 @@ omlish/inject/elements.py,sha256=BzTnkNS-3iAMI47LMC2543u6A8Tfk3aJXn3CO191ez4,154
141
142
  omlish/inject/exceptions.py,sha256=_wkN2tF55gQzmMOMKJC_9jYHBZzaBiCDcyqI9Sf2UZs,626
142
143
  omlish/inject/injector.py,sha256=CoCUeMm1Oot4sG4Ti1sKCWrhlvtJ5QAeAI22AFWu2RQ,1066
143
144
  omlish/inject/inspect.py,sha256=tw49r1RJVHrEHmT8WWA3_Bl9Z0L3lEGRqlLhbM5OmAM,592
144
- omlish/inject/keys.py,sha256=05KePew5_Cn_zWgBpIcXsa5orxm6xn4YL_IWXEvrSr8,651
145
+ omlish/inject/keys.py,sha256=BczsGcNvoziQYggGKX3_XpRZQ3b0F_pYWSDLBg5lRvE,654
145
146
  omlish/inject/listeners.py,sha256=AIWUs4OTUPeURU1tS2CXeEbJwC1bZiuya9K6I2ElMns,581
146
147
  omlish/inject/managed.py,sha256=Ls54Ah5KDrLHYWmso64xipvCLWPcgl1sUOn2n1tuWag,2107
147
- omlish/inject/multis.py,sha256=PQcIZbKpTJEOca_4pCp__3jLWLb9n90jZLbDe-xx8dQ,3351
148
+ omlish/inject/multis.py,sha256=a3DparPZjLdkAYFuEFTI6kM3BhNpJbtjeAMRdbeJx0c,3363
148
149
  omlish/inject/origins.py,sha256=OVQkiuRxx6ZtE8ZliufdndtFexcfpj-wZSDkUeGUCYM,534
149
150
  omlish/inject/overrides.py,sha256=hrm243slCw_DDRbn3dK5QK1jfHezVokG-WYO2JaQOV8,535
150
151
  omlish/inject/privates.py,sha256=hZOa_keY3KlXAzyiZ-sfN697UKXpkfXXNUIEmGT5TAA,641
151
- omlish/inject/providers.py,sha256=MAla7uDQR63zj9oB9XqN8rK3DGGLSzUKZ4rUU_5dM40,751
152
+ omlish/inject/providers.py,sha256=Z6UzNCwRhKHHR0L5CyBMo4F-1M_xElLkPA6EKQWcqlw,754
152
153
  omlish/inject/scopes.py,sha256=0b8Y0VidkpEk7tyxKSbv_1fE-2LomKTYlhFeoEemkwg,1979
153
154
  omlish/inject/types.py,sha256=11WVEPkZ-_8cv1BeTDRU-soIYxB_6x7dyWtsa2Iej9U,251
154
155
  omlish/inject/utils.py,sha256=_UOZqA8IcLWPqf4Mcg9iIusQ5yxP_6Txg6PWtUYl23o,408
@@ -162,12 +163,13 @@ omlish/inject/impl/origins.py,sha256=-cdcwz3BWb5LuC9Yn5ynYOwyPsKH06-kCc-3U0PxZ5w
162
163
  omlish/inject/impl/privates.py,sha256=alpCYyk5VJ9lJknbRH2nLVNFYVvFhkj-VC1Vco3zCFQ,2613
163
164
  omlish/inject/impl/providers.py,sha256=QnwhsujJFIHC0JTgd2Wlo1kP53i3CWTrj1nKU2DNxwg,2375
164
165
  omlish/inject/impl/proxy.py,sha256=1ko0VaKqzu9UG8bIldp9xtUrAVUOFTKWKTjOCqIGr4s,1636
165
- omlish/inject/impl/scopes.py,sha256=M_RO_pGUr5mX84YyYmkr6CsMhkkV189_gOUsaYmYes4,5768
166
- omlish/lang/__init__.py,sha256=Fs2OS5sU4OVNTgTDBI85nHnd3ZfgVf88pOwpY6EcM4Q,3377
166
+ omlish/inject/impl/scopes.py,sha256=ASfULXgP_ETlsAqFJfrZmyEaZt64Zr8tNn5ScA-EoXk,5900
167
+ omlish/lang/__init__.py,sha256=WF4vYG4awiOAXezL4gwCjzYu4JpzqU57U-w3c6rzoLw,3500
167
168
  omlish/lang/cached.py,sha256=LwsgWQjQ5op618rBvI8vbASOEGWDTt_SKq6Tc1vlgZM,7680
168
169
  omlish/lang/clsdct.py,sha256=AjtIWLlx2E6D5rC97zQ3Lwq2SOMkbg08pdO_AxpzEHI,1744
169
170
  omlish/lang/cmp.py,sha256=5vbzWWbqdzDmNKAGL19z6ZfUKe5Ci49e-Oegf9f4BsE,1346
170
171
  omlish/lang/contextmanagers.py,sha256=rzMSwJU7ObFXl46r6pGDbD45Zi_qZ9NHxDPnLNuux9o,9732
172
+ omlish/lang/datetimes.py,sha256=ehI_DhQRM-bDxAavnp470XcekbbXc4Gdw9y1KpHDJT0,223
171
173
  omlish/lang/descriptors.py,sha256=tZzDyXSp3wMNzTytt9BScFFriBwMRpt0EeYP3CRKPX8,6602
172
174
  omlish/lang/exceptions.py,sha256=qJBo3NU1mOWWm-NhQUHCY5feYXR3arZVyEHinLsmRH4,47
173
175
  omlish/lang/functions.py,sha256=yJxWwqlXEAT2gied4uTwiz5x1qXeuVubOSXyn9zy5aI,3624
@@ -181,11 +183,11 @@ omlish/lang/strings.py,sha256=ykeoou4JK7CEZXzrUJfqVOalEDvE--j0uhHt_SrsrUs,2834
181
183
  omlish/lang/sys.py,sha256=UoZz_PJYVKLQAKqYxxn-LHz1okK_38I__maZgnXMcxU,406
182
184
  omlish/lang/timeouts.py,sha256=vECdWYhc_IZgcal1Ng1Y42wf2FV3KAx-i8As-MgGHIQ,1186
183
185
  omlish/lang/typing.py,sha256=lJ2NGe4Pmb61I0Tx4A_rOqXNFTws1XHOzafg2knRUio,4155
184
- omlish/lang/classes/__init__.py,sha256=j1p0_uuMznKrY2EhMoj20uv6vx4LXljMzp7AaKe0mmU,530
186
+ omlish/lang/classes/__init__.py,sha256=h9QXrvAKD17_pIog0uF-7BCqZbSpJZYxL7kzVzvljp0,583
185
187
  omlish/lang/classes/abstract.py,sha256=goIV14oY24EOs88eVe6E6NyrSPOOLMOcWTXTMuYKiqc,2304
186
- omlish/lang/classes/restrict.py,sha256=n_B-XqafVyO1caZzfbsv7tPxDJOBLl1BzQkoDSse0A4,3353
187
- omlish/lang/classes/simple.py,sha256=_OE6-eph3-pllGck83FcGZE8fDMUqPdWP9OeSDCSgww,3071
188
- omlish/lang/classes/virtual.py,sha256=54D4qOpDWHpP28Oc6bbBjdxquLvdvqMqLXj2XOnAeaQ,3323
188
+ omlish/lang/classes/restrict.py,sha256=pSK7ZT_kpwqS6lWRrxwuEe-tt07F0-uZVazgGh-HDco,3921
189
+ omlish/lang/classes/simple.py,sha256=JkWYrRWnSKil6kVMgXgRMJeCxkFHXQIKIzYtG1jK2Pk,3067
190
+ omlish/lang/classes/virtual.py,sha256=W-QJuKsDehOcrydwg6eMN0bFPTYbk3Tz84TSH3blb44,3367
189
191
  omlish/lifecycles/__init__.py,sha256=1FjYceXs-4fc-S-C9zFYmc2axHs4znnQHcJVHdY7a6E,578
190
192
  omlish/lifecycles/abstract.py,sha256=70CQyZy-c9a2o0ZJxPeUT7eYjWZTBrp2HpUBnrHdAOM,1109
191
193
  omlish/lifecycles/base.py,sha256=ceXrNSzuv7iiTlX96UI1fvsQ70OgOmZl-UisDPyA3NA,1394
@@ -199,16 +201,16 @@ omlish/lite/cached.py,sha256=dUm647FbIsoxWT23XUFM51F7i-C2Buxr5b5zzgbCtQI,546
199
201
  omlish/lite/check.py,sha256=DR3Zj-7o4Y7pNheln68nN_BdX9zaotGQ2y8v97GDiWQ,535
200
202
  omlish/lite/contextmanagers.py,sha256=HnQJiyrOmSvTL22XRJrFl5CLpCyHD9fsntEUAr9G-60,427
201
203
  omlish/lite/json.py,sha256=7-02Ny4fq-6YAu5ynvqoijhuYXWpLmfCI19GUeZnb1c,740
202
- omlish/lite/logs.py,sha256=Vxux6l6ZlnM8tIutwCQftwZD-ZegXHEMyKI-K0Fksx8,5330
204
+ omlish/lite/logs.py,sha256=PeJZUJWd1K-UFErVs0wtD0zRMFW5-olUNFVVZAj_ScY,5549
203
205
  omlish/lite/marshal.py,sha256=u6jYUN_AndvI6__HJBvSw5ElHWC0CfHqgiDS28Vpqjg,8593
204
206
  omlish/lite/reflect.py,sha256=9QYJwdINraq1JNMEgvoqeSlVvRRgOXpxAkpgX8EgRXc,1307
205
207
  omlish/lite/runtime.py,sha256=VUhmNQvwf8QzkWSKj4Q0ReieJA_PzHaJNRBivfTseow,452
206
- omlish/lite/secrets.py,sha256=FEc47dcU9M1CyMTnrihAAVwKf0ySZLahf83djOEDWXw,488
208
+ omlish/lite/secrets.py,sha256=KZvGG7bFrjzIeLwohxsJ0Ctzpi42C-3A1PEMCIszJJs,521
207
209
  omlish/lite/strings.py,sha256=9dO_A6EkhcTZ2xmOUGSOMT-mx9BnoOzYu1-ocSrDJaA,670
208
210
  omlish/lite/subprocesses.py,sha256=KuGV3ImehMjCUK0JoV3pUtG_7o5wei1lRDn9HxzByAg,3063
209
211
  omlish/logs/__init__.py,sha256=FbOyAW-lGH8gyBlSVArwljdYAU6RnwZLI5LwAfuNnrk,438
210
212
  omlish/logs/_abc.py,sha256=UgrCUQVUi_PvT3p1CEkb3P74CFrFcZq2AFby3GEUv9M,5974
211
- omlish/logs/configs.py,sha256=uSf1DGHSzCz1I6Sy7o0LAaVcRd_jAqYmPSd9trYh-Aw,1292
213
+ omlish/logs/configs.py,sha256=EE0jlNaXJbGnM7V-y4xS5VwyTBSTzFzc0BYaVjg0JmA,1283
212
214
  omlish/logs/formatters.py,sha256=q79nMnR2mRIStPyGrydQHpYTXgC5HHptt8lH3W2Wwbs,671
213
215
  omlish/logs/handlers.py,sha256=nyuFgmO05By_Xwq7es58ClzS51-F53lJL7gD0x5IqAg,228
214
216
  omlish/logs/noisy.py,sha256=8JORjI1dH38yU2MddM54OB6qt32Xozfocdb88vY4wro,335
@@ -217,7 +219,7 @@ omlish/marshal/__init__.py,sha256=8r5nY0DMVT_kT4hARuTDsa8GOdshko40VEzOq0qNdS8,16
217
219
  omlish/marshal/any.py,sha256=e82OyYK3Emm1P1ClnsnxP7fIWC2iNVyW0H5nK4mLmWM,779
218
220
  omlish/marshal/base.py,sha256=EIgrqsQ1OQ4mVUMuDH5zRBCwJpn8ijVS98Nmoka_Mrs,6025
219
221
  omlish/marshal/base64.py,sha256=F-3ogJdcFCtWINRgJgWT0rErqgx6f4qahhcg8OrkqhE,1089
220
- omlish/marshal/dataclasses.py,sha256=cqHfzkQe7T-A_eAx-xZm6AIOZLnqE-alMUXMtRTYxuI,3910
222
+ omlish/marshal/dataclasses.py,sha256=g1y5b1UO86oPTbL18l9T_cPnsMJnirmCIbJYh_i7CB0,3905
221
223
  omlish/marshal/datetimes.py,sha256=0ffg8cEvx9SMKIXZGD9b7MqpLfmgw0uKKdn6YTfoqok,3714
222
224
  omlish/marshal/enums.py,sha256=-0fKutBbyz8ygEaA0_P_8IOJrI9jMGigmnPbutV9Bg4,1464
223
225
  omlish/marshal/exceptions.py,sha256=jwQWn4LcPnadT2KRI_1JJCOSkwWh0yHnYK9BmSkNN4U,302
@@ -250,7 +252,7 @@ omlish/secrets/crypto.py,sha256=6CsLy0UEqCrBK8Xx_3-iFF6SKtu2GlEqUQ8-MliY3tk,3709
250
252
  omlish/secrets/marshal.py,sha256=nVzsvQH5w3T2oMP7DCc1SLKxyR5e66psM57VOQoL0QA,2086
251
253
  omlish/secrets/openssl.py,sha256=wxA_wIlxtuOUy71ABxAJgavh-UI_taOfm-A0dVlmSwM,6219
252
254
  omlish/secrets/passwords.py,sha256=3r-vEK6Gp6aq4L5Csnd06QnrjO9xfzHJP-g_7I9W_ao,4101
253
- omlish/secrets/secrets.py,sha256=hFN82uYiBVx8YSE86leWNxb4IRp3qdwZPOi4w04h8u0,6855
255
+ omlish/secrets/secrets.py,sha256=ClD7t_mkmWkseVk4ahLzYLuLXeTxiwwPiidYm42vLh4,6871
254
256
  omlish/secrets/subprocesses.py,sha256=EcnKlHHtnUMHGrBWXDfu8tv28wlgZx4P4GOiuPW9Vo8,1105
255
257
  omlish/specs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
256
258
  omlish/specs/jsonschema/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -308,8 +310,8 @@ omlish/text/delimit.py,sha256=ubPXcXQmtbOVrUsNh5gH1mDq5H-n1y2R4cPL5_DQf68,4928
308
310
  omlish/text/glyphsplit.py,sha256=Ug-dPRO7x-OrNNr8g1y6DotSZ2KH0S-VcOmUobwa4B0,3296
309
311
  omlish/text/indent.py,sha256=6Jj6TFY9unaPa4xPzrnZemJ-fHsV53IamP93XGjSUHs,1274
310
312
  omlish/text/parts.py,sha256=KGgo0wHOIMVMZtDso-rhSWKAcAkYAH2IGpg9tULabu8,6505
311
- omlish-0.0.0.dev25.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
312
- omlish-0.0.0.dev25.dist-info/METADATA,sha256=49XZvgzY6Zz76FgkG9QR08FbmhrKIZxnn34vmIQF90M,3666
313
- omlish-0.0.0.dev25.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
314
- omlish-0.0.0.dev25.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
315
- omlish-0.0.0.dev25.dist-info/RECORD,,
313
+ omlish-0.0.0.dev26.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
314
+ omlish-0.0.0.dev26.dist-info/METADATA,sha256=0sGW6ZHnoftJe1kQm2MsivDjv2_mfWT81njFwRslZmA,3666
315
+ omlish-0.0.0.dev26.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
316
+ omlish-0.0.0.dev26.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
317
+ omlish-0.0.0.dev26.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (74.1.2)
2
+ Generator: setuptools (75.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5