omlish 0.0.0.dev24__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 (54) hide show
  1. omlish/__about__.py +20 -4
  2. omlish/_manifests.json +1 -0
  3. omlish/bootstrap/diag.py +25 -0
  4. omlish/bootstrap/harness.py +3 -2
  5. omlish/check.py +1 -1
  6. omlish/collections/__init__.py +1 -0
  7. omlish/collections/utils.py +7 -2
  8. omlish/dataclasses/__init__.py +4 -4
  9. omlish/dataclasses/impl/LICENSE +279 -0
  10. omlish/dataclasses/impl/api.py +5 -3
  11. omlish/dataclasses/impl/exceptions.py +2 -2
  12. omlish/dataclasses/impl/fields.py +7 -4
  13. omlish/dataclasses/impl/init.py +8 -8
  14. omlish/dataclasses/impl/metadata.py +3 -3
  15. omlish/dataclasses/impl/params.py +4 -3
  16. omlish/diag/replserver/server.py +17 -2
  17. omlish/docker.py +74 -6
  18. omlish/formats/yaml.py +10 -0
  19. omlish/graphs/dot/__init__.py +31 -19
  20. omlish/graphs/dot/make.py +16 -0
  21. omlish/graphs/dot/rendering.py +9 -8
  22. omlish/http/cookies.py +2 -1
  23. omlish/inject/impl/scopes.py +2 -0
  24. omlish/inject/keys.py +1 -1
  25. omlish/inject/multis.py +4 -4
  26. omlish/inject/providers.py +1 -1
  27. omlish/lang/__init__.py +9 -0
  28. omlish/lang/classes/__init__.py +3 -0
  29. omlish/lang/classes/restrict.py +25 -4
  30. omlish/lang/classes/simple.py +0 -4
  31. omlish/lang/classes/virtual.py +6 -4
  32. omlish/lang/datetimes.py +9 -0
  33. omlish/lang/typing.py +32 -0
  34. omlish/lite/logs.py +30 -25
  35. omlish/lite/secrets.py +3 -1
  36. omlish/logs/configs.py +2 -2
  37. omlish/marshal/dataclasses.py +1 -1
  38. omlish/secrets/secrets.py +1 -1
  39. omlish/specs/jsonschema/schemas/draft202012/metaschema.json +58 -0
  40. omlish/specs/jsonschema/schemas/draft202012/vocabularies/applicator.json +48 -0
  41. omlish/specs/jsonschema/schemas/draft202012/vocabularies/content.json +17 -0
  42. omlish/specs/jsonschema/schemas/draft202012/vocabularies/core.json +51 -0
  43. omlish/specs/jsonschema/schemas/draft202012/vocabularies/format-annotation.json +14 -0
  44. omlish/specs/jsonschema/schemas/draft202012/vocabularies/format-assertion.json +14 -0
  45. omlish/specs/jsonschema/schemas/draft202012/vocabularies/format.json +14 -0
  46. omlish/specs/jsonschema/schemas/draft202012/vocabularies/meta-data.json +37 -0
  47. omlish/specs/jsonschema/schemas/draft202012/vocabularies/unevaluated.json +15 -0
  48. omlish/specs/jsonschema/schemas/draft202012/vocabularies/validation.json +98 -0
  49. omlish/stats.py +1 -1
  50. {omlish-0.0.0.dev24.dist-info → omlish-0.0.0.dev26.dist-info}/METADATA +1 -1
  51. {omlish-0.0.0.dev24.dist-info → omlish-0.0.0.dev26.dist-info}/RECORD +54 -40
  52. {omlish-0.0.0.dev24.dist-info → omlish-0.0.0.dev26.dist-info}/WHEEL +1 -1
  53. {omlish-0.0.0.dev24.dist-info → omlish-0.0.0.dev26.dist-info}/LICENSE +0 -0
  54. {omlish-0.0.0.dev24.dist-info → omlish-0.0.0.dev26.dist-info}/top_level.txt +0 -0
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,
@@ -202,6 +210,7 @@ from .typing import ( # noqa
202
210
  Func1,
203
211
  Func2,
204
212
  Func3,
213
+ SequenceNotStr,
205
214
  protocol_check,
206
215
  typed_lambda,
207
216
  typed_partial,
@@ -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/lang/typing.py CHANGED
@@ -14,6 +14,9 @@ import typing as ta
14
14
  Ty = ta.TypeVar('Ty', bound=type)
15
15
 
16
16
  T = ta.TypeVar('T')
17
+ T_co = ta.TypeVar('T_co', covariant=True)
18
+ T_contra = ta.TypeVar('T_contra', contravariant=True)
19
+
17
20
  A0 = ta.TypeVar('A0')
18
21
  A1 = ta.TypeVar('A1')
19
22
  A2 = ta.TypeVar('A2')
@@ -133,3 +136,32 @@ class Func3(ta.Generic[A0, A1, A2, T]):
133
136
 
134
137
  def __call__(self, a0: A0, a1: A1, a2: A2) -> T:
135
138
  return self.fn(a0, a1, a2)
139
+
140
+
141
+ ##
142
+
143
+
144
+ class SequenceNotStr(ta.Protocol[T_co]):
145
+ """
146
+ https://github.com/python/mypy/issues/11001
147
+ https://github.com/python/typing/issues/256#issuecomment-1442633430
148
+ https://github.com/hauntsaninja/useful_types/blob/735ef9dd0b55b35b118ef630d5a0f3618ecedbff/useful_types/__init__.py#L285
149
+ """
150
+
151
+ @ta.overload
152
+ def __getitem__(self, index: ta.SupportsIndex, /) -> T_co: ...
153
+
154
+ @ta.overload
155
+ def __getitem__(self, index: slice, /) -> ta.Sequence[T_co]: ... # noqa
156
+
157
+ def __contains__(self, value: object, /) -> bool: ...
158
+
159
+ def __len__(self) -> int: ...
160
+
161
+ def __iter__(self) -> ta.Iterator[T_co]: ...
162
+
163
+ def index(self, value: ta.Any, start: int = 0, stop: int = ..., /) -> int: ...
164
+
165
+ def count(self, value: ta.Any, /) -> int: ...
166
+
167
+ def __reversed__(self) -> ta.Iterator[T_co]: ...
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: