omlish 0.0.0.dev18__py3-none-any.whl → 0.0.0.dev20__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 (46) hide show
  1. omlish/__about__.py +3 -3
  2. omlish/asyncs/anyio.py +13 -6
  3. omlish/bootstrap/__init__.py +3 -0
  4. omlish/bootstrap/__main__.py +4 -0
  5. omlish/bootstrap/base.py +38 -0
  6. omlish/bootstrap/diag.py +128 -0
  7. omlish/bootstrap/harness.py +81 -0
  8. omlish/bootstrap/main.py +183 -0
  9. omlish/bootstrap/sys.py +361 -0
  10. omlish/dataclasses/__init__.py +2 -0
  11. omlish/dataclasses/impl/metadata.py +2 -1
  12. omlish/dataclasses/utils.py +45 -0
  13. omlish/defs.py +29 -5
  14. omlish/fnpairs.py +1 -1
  15. omlish/formats/json.py +1 -0
  16. omlish/lang/descriptors.py +2 -0
  17. omlish/lite/logs.py +2 -2
  18. omlish/logs/configs.py +11 -23
  19. omlish/logs/noisy.py +15 -0
  20. omlish/marshal/__init__.py +4 -0
  21. omlish/marshal/dataclasses.py +16 -3
  22. omlish/marshal/helpers.py +22 -0
  23. omlish/marshal/objects.py +33 -14
  24. omlish/multiprocessing.py +32 -0
  25. omlish/specs/__init__.py +0 -0
  26. omlish/specs/jsonschema/__init__.py +0 -0
  27. omlish/specs/jsonschema/keywords/__init__.py +42 -0
  28. omlish/specs/jsonschema/keywords/base.py +86 -0
  29. omlish/specs/jsonschema/keywords/core.py +26 -0
  30. omlish/specs/jsonschema/keywords/metadata.py +22 -0
  31. omlish/specs/jsonschema/keywords/parse.py +69 -0
  32. omlish/specs/jsonschema/keywords/render.py +47 -0
  33. omlish/specs/jsonschema/keywords/validation.py +68 -0
  34. omlish/specs/jsonschema/schemas/__init__.py +0 -0
  35. omlish/specs/jsonschema/schemas/draft202012/__init__.py +0 -0
  36. omlish/specs/jsonschema/schemas/draft202012/vocabularies/__init__.py +0 -0
  37. omlish/specs/jsonschema/types.py +10 -0
  38. omlish/testing/pytest/plugins/__init__.py +8 -3
  39. omlish/testing/pytest/plugins/depskip.py +81 -0
  40. omlish/testing/pytest/plugins/utils.py +14 -0
  41. {omlish-0.0.0.dev18.dist-info → omlish-0.0.0.dev20.dist-info}/METADATA +3 -3
  42. {omlish-0.0.0.dev18.dist-info → omlish-0.0.0.dev20.dist-info}/RECORD +45 -22
  43. omlish/bootstrap.py +0 -746
  44. {omlish-0.0.0.dev18.dist-info → omlish-0.0.0.dev20.dist-info}/LICENSE +0 -0
  45. {omlish-0.0.0.dev18.dist-info → omlish-0.0.0.dev20.dist-info}/WHEEL +0 -0
  46. {omlish-0.0.0.dev18.dist-info → omlish-0.0.0.dev20.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,361 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import contextlib
3
+ import dataclasses as dc
4
+ import enum
5
+ import faulthandler
6
+ import gc
7
+ import importlib
8
+ import logging
9
+ import os
10
+ import pwd
11
+ import resource
12
+ import signal
13
+ import sys
14
+ import typing as ta
15
+
16
+ from .. import lang
17
+ from .base import Bootstrap
18
+ from .base import ContextBootstrap
19
+ from .base import SimpleBootstrap
20
+
21
+
22
+ if ta.TYPE_CHECKING:
23
+ from .. import libc
24
+ from .. import logs
25
+ from .. import os as osu
26
+ from ..formats import dotenv
27
+
28
+ else:
29
+ libc = lang.proxy_import('..libc', __package__)
30
+ logs = lang.proxy_import('..logs', __package__)
31
+ osu = lang.proxy_import('..os', __package__)
32
+ dotenv = lang.proxy_import('.formats.dotenv', __package__)
33
+
34
+
35
+ ##
36
+
37
+
38
+ class CwdBootstrap(ContextBootstrap['CwdBootstrap.Config']):
39
+ @dc.dataclass(frozen=True)
40
+ class Config(Bootstrap.Config):
41
+ path: ta.Optional[str] = None
42
+
43
+ @contextlib.contextmanager
44
+ def enter(self) -> ta.Iterator[None]:
45
+ if self._config.path is not None:
46
+ prev = os.getcwd()
47
+ os.chdir(self._config.path)
48
+ else:
49
+ prev = None
50
+
51
+ try:
52
+ yield
53
+
54
+ finally:
55
+ if prev is not None:
56
+ os.chdir(prev)
57
+
58
+
59
+ ##
60
+
61
+
62
+ class SetuidBootstrap(SimpleBootstrap['SetuidBootstrap.Config']):
63
+ @dc.dataclass(frozen=True)
64
+ class Config(Bootstrap.Config):
65
+ user: ta.Optional[str] = None
66
+
67
+ def run(self) -> None:
68
+ if self._config.user is not None:
69
+ user = pwd.getpwnam(self._config.user)
70
+ os.setuid(user.pw_uid)
71
+
72
+
73
+ ##
74
+
75
+
76
+ class GcDebugFlag(enum.Enum):
77
+ STATS = gc.DEBUG_STATS
78
+ COLLECTABLE = gc.DEBUG_COLLECTABLE
79
+ UNCOLLECTABLE = gc.DEBUG_UNCOLLECTABLE
80
+ SAVEALL = gc.DEBUG_SAVEALL
81
+ LEAK = gc.DEBUG_LEAK
82
+
83
+
84
+ class GcBootstrap(ContextBootstrap['GcBootstrap.Config']):
85
+ @dc.dataclass(frozen=True)
86
+ class Config(Bootstrap.Config):
87
+ disable: bool = False
88
+ debug: ta.Optional[int] = None
89
+
90
+ @contextlib.contextmanager
91
+ def enter(self) -> ta.Iterator[None]:
92
+ prev_enabled = gc.isenabled()
93
+ if self._config.disable:
94
+ gc.disable()
95
+
96
+ if self._config.debug is not None:
97
+ prev_debug = gc.get_debug()
98
+ gc.set_debug(self._config.debug)
99
+ else:
100
+ prev_debug = None
101
+
102
+ try:
103
+ yield
104
+
105
+ finally:
106
+ if prev_enabled:
107
+ gc.enable()
108
+
109
+ if prev_debug is not None:
110
+ gc.set_debug(prev_debug)
111
+
112
+
113
+ ##
114
+
115
+
116
+ class NiceBootstrap(SimpleBootstrap['NiceBootstrap.Config']):
117
+ @dc.dataclass(frozen=True)
118
+ class Config(Bootstrap.Config):
119
+ nice: ta.Optional[int] = None
120
+
121
+ def run(self) -> None:
122
+ if self._config.nice is not None:
123
+ os.nice(self._config.nice)
124
+
125
+
126
+ ##
127
+
128
+
129
+ class LogBootstrap(ContextBootstrap['LogBootstrap.Config']):
130
+ @dc.dataclass(frozen=True)
131
+ class Config(Bootstrap.Config):
132
+ level: ta.Union[str, int, None] = None
133
+ json: bool = False
134
+
135
+ @contextlib.contextmanager
136
+ def enter(self) -> ta.Iterator[None]:
137
+ if self._config.level is None:
138
+ yield
139
+ return
140
+
141
+ handler = logs.configure_standard_logging(
142
+ self._config.level,
143
+ json=self._config.json,
144
+ )
145
+
146
+ try:
147
+ yield
148
+
149
+ finally:
150
+ if handler is not None:
151
+ logging.root.removeHandler(handler)
152
+
153
+
154
+ ##
155
+
156
+
157
+ class FaulthandlerBootstrap(ContextBootstrap['FaulthandlerBootstrap.Config']):
158
+ @dc.dataclass(frozen=True)
159
+ class Config(Bootstrap.Config):
160
+ enabled: ta.Optional[bool] = None
161
+
162
+ @contextlib.contextmanager
163
+ def enter(self) -> ta.Iterator[None]:
164
+ if self._config.enabled is None:
165
+ yield
166
+ return
167
+
168
+ prev = faulthandler.is_enabled()
169
+ if self._config.enabled:
170
+ faulthandler.enable()
171
+ else:
172
+ faulthandler.disable()
173
+
174
+ try:
175
+ yield
176
+
177
+ finally:
178
+ if prev:
179
+ faulthandler.enable()
180
+ else:
181
+ faulthandler.disable()
182
+
183
+
184
+ ##
185
+
186
+
187
+ SIGNALS_BY_NAME = {
188
+ a[len('SIG'):]: v # noqa
189
+ for a in dir(signal)
190
+ if a.startswith('SIG')
191
+ and not a.startswith('SIG_')
192
+ and a == a.upper()
193
+ and isinstance((v := getattr(signal, a)), int)
194
+ }
195
+
196
+
197
+ class PrctlBootstrap(SimpleBootstrap['PrctlBootstrap.Config']):
198
+ @dc.dataclass(frozen=True)
199
+ class Config(Bootstrap.Config):
200
+ dumpable: bool = False
201
+ deathsig: ta.Union[int, str, None] = None
202
+
203
+ def run(self) -> None:
204
+ if self._config.dumpable:
205
+ libc.prctl(libc.PR_SET_DUMPABLE, 1, 0, 0, 0, 0)
206
+
207
+ if self._config.deathsig is not None:
208
+ if isinstance(self._config.deathsig, int):
209
+ sig = self._config.deathsig
210
+ else:
211
+ sig = SIGNALS_BY_NAME[self._config.deathsig.upper()]
212
+ libc.prctl(libc.PR_SET_PDEATHSIG, sig, 0, 0, 0, 0)
213
+
214
+
215
+ ##
216
+
217
+
218
+ RLIMITS_BY_NAME = {
219
+ a[len('RLIMIT_'):]: v # noqa
220
+ for a in dir(resource)
221
+ if a.startswith('RLIMIT_')
222
+ and a == a.upper()
223
+ and isinstance((v := getattr(resource, a)), int)
224
+ }
225
+
226
+
227
+ class RlimitBootstrap(ContextBootstrap['RlimitBootstrap.Config']):
228
+ @dc.dataclass(frozen=True)
229
+ class Config(Bootstrap.Config):
230
+ limits: ta.Optional[ta.Mapping[str, ta.Tuple[ta.Optional[int], ta.Optional[int]]]] = None
231
+
232
+ @contextlib.contextmanager
233
+ def enter(self) -> ta.Iterator[None]:
234
+ if not self._config.limits:
235
+ yield
236
+ return
237
+
238
+ def or_infin(l: ta.Optional[int]) -> int:
239
+ return l if l is not None else resource.RLIM_INFINITY
240
+
241
+ prev = {}
242
+ for k, (s, h) in self._config.limits.items():
243
+ i = RLIMITS_BY_NAME[k.upper()]
244
+ prev[i] = resource.getrlimit(i)
245
+ resource.setrlimit(i, (or_infin(s), or_infin(h)))
246
+
247
+ try:
248
+ yield
249
+
250
+ finally:
251
+ for i, (s, h) in prev.items():
252
+ resource.setrlimit(i, (s, h))
253
+
254
+
255
+ ##
256
+
257
+
258
+ class ImportBootstrap(SimpleBootstrap['ImportBootstrap.Config']):
259
+ @dc.dataclass(frozen=True)
260
+ class Config(Bootstrap.Config):
261
+ modules: ta.Optional[ta.Sequence[str]] = None
262
+
263
+ def run(self) -> None:
264
+ for m in self._config.modules or ():
265
+ importlib.import_module(m)
266
+
267
+
268
+ ##
269
+
270
+
271
+ class EnvBootstrap(ContextBootstrap['EnvBootstrap.Config']):
272
+ @dc.dataclass(frozen=True)
273
+ class Config(Bootstrap.Config):
274
+ vars: ta.Optional[ta.Mapping[str, ta.Optional[str]]] = None
275
+ files: ta.Optional[ta.Sequence[str]] = None
276
+
277
+ @contextlib.contextmanager
278
+ def enter(self) -> ta.Iterator[None]:
279
+ if not (self._config.vars or self._config.files):
280
+ yield
281
+ return
282
+
283
+ new = dict(self._config.vars or {})
284
+ for f in self._config.files or ():
285
+ new.update(dotenv.dotenv_values(f, env=os.environ))
286
+
287
+ prev: ta.Dict[str, ta.Optional[str]] = {k: os.environ.get(k) for k in new}
288
+
289
+ def do(k: str, v: ta.Optional[str]) -> None:
290
+ if v is not None:
291
+ os.environ[k] = v
292
+ else:
293
+ del os.environ[k]
294
+
295
+ for k, v in new.items():
296
+ do(k, v)
297
+
298
+ try:
299
+ yield
300
+
301
+ finally:
302
+ for k, v in prev.items():
303
+ do(k, v)
304
+
305
+
306
+ ##
307
+
308
+
309
+ class PidfileBootstrap(ContextBootstrap['PidfileBootstrap.Config']):
310
+ @dc.dataclass(frozen=True)
311
+ class Config(Bootstrap.Config):
312
+ path: ta.Optional[str] = None
313
+
314
+ @contextlib.contextmanager
315
+ def enter(self) -> ta.Iterator[None]:
316
+ if self._config.path is None:
317
+ yield
318
+ return
319
+
320
+ with osu.Pidfile(self._config.path) as pf:
321
+ pf.write()
322
+ yield
323
+
324
+
325
+ ##
326
+
327
+
328
+ class FdsBootstrap(SimpleBootstrap['FdsBootstrap.Config']):
329
+ @dc.dataclass(frozen=True)
330
+ class Config(Bootstrap.Config):
331
+ redirects: ta.Optional[ta.Mapping[int, ta.Union[int, str, None]]] = None
332
+
333
+ def run(self) -> None:
334
+ for dst, src in (self._config.redirects or {}).items():
335
+ if src is None:
336
+ src = '/dev/null'
337
+ if isinstance(src, int):
338
+ os.dup2(src, dst)
339
+ elif isinstance(src, str):
340
+ sfd = os.open(src, os.O_RDWR)
341
+ os.dup2(sfd, dst)
342
+ os.close(sfd)
343
+ else:
344
+ raise TypeError(src)
345
+
346
+
347
+ ##
348
+
349
+
350
+ class PrintPidBootstrap(SimpleBootstrap['PrintPidBootstrap.Config']):
351
+ @dc.dataclass(frozen=True)
352
+ class Config(Bootstrap.Config):
353
+ enable: bool = False
354
+ pause: bool = False
355
+
356
+ def run(self) -> None:
357
+ if not (self._config.enable or self._config.pause):
358
+ return
359
+ print(str(os.getpid()), file=sys.stderr)
360
+ if self._config.pause:
361
+ input()
@@ -96,4 +96,6 @@ from .utils import ( # noqa
96
96
  opt_repr,
97
97
  update_field_extras,
98
98
  update_field_metadata,
99
+ update_fields,
100
+ update_fields_metadata,
99
101
  )
@@ -70,5 +70,6 @@ class Init(lang.Marker):
70
70
  pass
71
71
 
72
72
 
73
- def init(fn: ta.Callable[..., None]) -> None:
73
+ def init(fn: ta.Callable):
74
74
  _append_cls_md(Init, fn)
75
+ return fn
@@ -12,6 +12,9 @@ from .impl.params import get_field_extras
12
12
  T = ta.TypeVar('T')
13
13
 
14
14
 
15
+ #
16
+
17
+
15
18
  def maybe_post_init(sup: ta.Any) -> bool:
16
19
  try:
17
20
  fn = sup.__post_init__
@@ -21,10 +24,16 @@ def maybe_post_init(sup: ta.Any) -> bool:
21
24
  return True
22
25
 
23
26
 
27
+ #
28
+
29
+
24
30
  def opt_repr(o: ta.Any) -> str | None:
25
31
  return repr(o) if o is not None else None
26
32
 
27
33
 
34
+ #
35
+
36
+
28
37
  class field_modifier: # noqa
29
38
  def __init__(self, fn: ta.Callable[[dc.Field], dc.Field]) -> None:
30
39
  super().__init__()
@@ -58,6 +67,42 @@ def update_field_extras(f: dc.Field, *, unless_non_default: bool = False, **kwar
58
67
  })
59
68
 
60
69
 
70
+ def update_fields(
71
+ fn: ta.Callable[[str, dc.Field], dc.Field],
72
+ fields: ta.Iterable[str] | None = None,
73
+ ) -> ta.Callable[[type[T]], type[T]]:
74
+ def inner(cls):
75
+ if fields is None:
76
+ for a, v in list(cls.__dict__.items()):
77
+ if isinstance(v, dc.Field):
78
+ setattr(cls, a, fn(a, v))
79
+
80
+ else:
81
+ for a in fields:
82
+ v = cls.__dict__[a]
83
+ if not isinstance(v, dc.Field):
84
+ v = dc.field(default=v)
85
+ setattr(cls, a, fn(a, v))
86
+
87
+ return cls
88
+
89
+ check.not_isinstance(fields, str)
90
+ return inner
91
+
92
+
93
+ def update_fields_metadata(
94
+ nmd: ta.Mapping,
95
+ fields: ta.Iterable[str] | None = None,
96
+ ) -> ta.Callable[[type[T]], type[T]]:
97
+ def inner(a: str, f: dc.Field) -> dc.Field:
98
+ return update_field_metadata(f, nmd)
99
+
100
+ return update_fields(inner, fields)
101
+
102
+
103
+ #
104
+
105
+
61
106
  def deep_replace(o: T, *args: str | ta.Callable[[ta.Any], ta.Mapping[str, ta.Any]]) -> T:
62
107
  if not args:
63
108
  return o
omlish/defs.py CHANGED
@@ -5,7 +5,6 @@ remain @property's for type annotation, tool assistance, debugging, and otherwis
5
5
  certain circumstances (the real-world alternative usually being simply not adding them).
6
6
  """
7
7
  # ruff: noqa: ANN201
8
-
9
8
  import abc
10
9
  import functools
11
10
  import operator
@@ -22,6 +21,7 @@ BASICS = {}
22
21
  def _basic(fn):
23
22
  if fn.__name__ in BASICS:
24
23
  raise NameError(fn.__name__)
24
+
25
25
  BASICS[fn.__name__] = fn
26
26
  return fn
27
27
 
@@ -30,6 +30,7 @@ def _basic(fn):
30
30
  def basic(cls_dct, *attrs, basics=None):
31
31
  if basics is None:
32
32
  basics = BASICS.keys()
33
+
33
34
  for k in basics:
34
35
  fn = BASICS[k]
35
36
  fn(*attrs, cls_dct=cls_dct)
@@ -43,6 +44,7 @@ def _repr_guard(fn):
43
44
  def inner(obj, *args, **kwargs):
44
45
  try:
45
46
  ids = _REPR_SEEN.ids
47
+
46
48
  except AttributeError:
47
49
  ids = _REPR_SEEN.ids = set()
48
50
  try:
@@ -50,6 +52,7 @@ def _repr_guard(fn):
50
52
  return fn(obj, *args, **kwargs)
51
53
  finally:
52
54
  del _REPR_SEEN.ids
55
+
53
56
  else:
54
57
  if id(obj) in ids:
55
58
  return f'<seen:{type(obj).__name__}@{hex(id(obj))[2:]}>'
@@ -64,17 +67,27 @@ def build_attr_repr(obj, *, mro=False):
64
67
  if mro:
65
68
  attrs = [
66
69
  attr
67
- for ty in sorted(reversed(type(obj).__mro__), key=lambda _ty: _ty.__dict__.get('__repr_priority__', 0)) # noqa
68
- for attr in ty.__dict__.get('__repr_attrs__', [])]
70
+ for ty in sorted( # noqa
71
+ reversed(type(obj).__mro__),
72
+ key=lambda _ty: _ty.__dict__.get('__repr_priority__', 0),
73
+ )
74
+ for attr in ty.__dict__.get('__repr_attrs__', [])
75
+ ]
76
+
69
77
  else:
70
78
  attrs = obj.__repr_attrs__
79
+
71
80
  s = ', '.join(f'{a}={"<self>" if v is obj else _repr(v)}' for a in attrs for v in [getattr(obj, a)])
72
81
  return f'{type(obj).__name__}@{hex(id(obj))[2:]}({s})'
73
82
 
74
83
 
75
84
  @_repr_guard
76
85
  def build_repr(obj, *attrs):
77
- return f'{type(obj).__name__}@{hex(id(obj))[2:]}({", ".join(f"{attr}={getattr(obj, attr)!r}" for attr in attrs)})'
86
+ return (
87
+ f'{type(obj).__name__}'
88
+ f'@{hex(id(obj))[2:]}'
89
+ f'({", ".join(f"{attr}={getattr(obj, attr)!r}" for attr in attrs)})'
90
+ )
78
91
 
79
92
 
80
93
  @_basic
@@ -86,6 +99,7 @@ def repr(cls_dct, *attrs, mro=False, priority=None): # noqa
86
99
  cls_dct['__repr_attrs__'] = attrs
87
100
  if priority is not None:
88
101
  cls_dct['__repr_priority__'] = priority
102
+
89
103
  cls_dct['__repr__'] = __repr__
90
104
 
91
105
 
@@ -141,9 +155,11 @@ def hash_eq(cls_dct, *attrs):
141
155
  def __eq__(self, other): # noqa
142
156
  if type(other) is not type(self):
143
157
  return False
158
+
144
159
  for attr in attrs: # noqa
145
160
  if getattr(self, attr) != getattr(other, attr):
146
161
  return False
162
+
147
163
  return True
148
164
 
149
165
  cls_dct['__eq__'] = __eq__
@@ -162,6 +178,7 @@ def not_implemented(cls_dct, *names, **kwargs):
162
178
  wrapper = kwargs.pop('wrapper', lambda _: _)
163
179
  if kwargs:
164
180
  raise TypeError(kwargs)
181
+
165
182
  ret = []
166
183
  for name in names:
167
184
  @wrapper
@@ -171,6 +188,7 @@ def not_implemented(cls_dct, *names, **kwargs):
171
188
  not_implemented.__name__ = name
172
189
  cls_dct[name] = not_implemented
173
190
  ret.append(not_implemented)
191
+
174
192
  return tuple(ret)
175
193
 
176
194
 
@@ -186,4 +204,10 @@ def abstract_property(cls_dct, *names):
186
204
 
187
205
  @lang.cls_dct_fn()
188
206
  def abstract_hash_eq(cls_dct):
189
- return not_implemented(cls_dct, '__hash__', '__eq__', '__ne__', wrapper=abc.abstractmethod)
207
+ return not_implemented(
208
+ cls_dct,
209
+ '__hash__',
210
+ '__eq__',
211
+ '__ne__',
212
+ wrapper=abc.abstractmethod,
213
+ )
omlish/fnpairs.py CHANGED
@@ -449,7 +449,7 @@ class Toml(ObjectStr_):
449
449
  #
450
450
 
451
451
 
452
- @_register_extension('cpkl')
452
+ @_register_extension('clpkl')
453
453
  @dc.dataclass(frozen=True)
454
454
  class Cloudpickle(ObjectBytes_):
455
455
  protocol: int | None = None
omlish/formats/json.py CHANGED
@@ -13,6 +13,7 @@ from .. import lang
13
13
  if ta.TYPE_CHECKING:
14
14
  import orjson as _orjson
15
15
  import ujson as _ujson
16
+
16
17
  else:
17
18
  _orjson = lang.proxy_import('orjson')
18
19
  _ujson = lang.proxy_import('ujson')
@@ -125,11 +125,13 @@ class _decorator_descriptor: # noqa
125
125
 
126
126
  def __get__(self, instance, owner=None):
127
127
  fn = self._fn.__get__(instance, owner)
128
+
128
129
  if self._md or instance is not None:
129
130
  @functools.wraps(fn)
130
131
  def inner(*args, **kwargs):
131
132
  return self._wrapper(fn, *args, **kwargs)
132
133
  return inner
134
+
133
135
  else:
134
136
  @functools.wraps(fn)
135
137
  def outer(this, *args, **kwargs):
omlish/lite/logs.py CHANGED
@@ -69,8 +69,8 @@ class JsonLogFormatter(logging.Formatter):
69
69
  STANDARD_LOG_FORMAT_PARTS = [
70
70
  ('asctime', '%(asctime)-15s'),
71
71
  ('process', 'pid=%(process)-6s'),
72
- ('thread', 'tid=%(thread)-16s'),
73
- ('levelname', '%(levelname)-8s'),
72
+ ('thread', 'tid=%(thread)x'),
73
+ ('levelname', '%(levelname)s'),
74
74
  ('name', '%(name)s'),
75
75
  ('separator', '::'),
76
76
  ('message', '%(message)s'),
omlish/logs/configs.py CHANGED
@@ -3,21 +3,16 @@ import logging
3
3
  import typing as ta
4
4
 
5
5
  from ..lite.logs import configure_standard_logging as configure_lite_standard_logging
6
+ from .noisy import silence_noisy_loggers
6
7
 
7
8
 
8
9
  ##
9
10
 
10
11
 
11
- NOISY_LOGGERS: set[str] = {
12
- 'boto3.resources.action',
13
- 'datadog.dogstatsd',
14
- 'elasticsearch',
15
- 'kazoo.client',
16
- 'requests.packages.urllib3.connectionpool',
17
- }
18
-
19
-
20
- ##
12
+ FilterConfig = dict[str, ta.Any]
13
+ FormatterConfig = dict[str, ta.Any]
14
+ HandlerConfig = dict[str, ta.Any]
15
+ LoggerConfig = dict[str, ta.Any]
21
16
 
22
17
 
23
18
  @dc.dataclass()
@@ -25,17 +20,11 @@ class DictConfig:
25
20
  version: int = 1
26
21
  incremental: bool = False
27
22
  disable_existing_loggers: bool = False
28
- filters: dict[str, 'FilterConfig'] = dc.field(default_factory=dict)
29
- formatters: dict[str, 'FormatterConfig'] = dc.field(default_factory=dict)
30
- handlers: dict[str, 'HandlerConfig'] = dc.field(default_factory=dict)
31
- loggers: dict[str, 'LoggerConfig'] = dc.field(default_factory=dict)
32
- root: ta.Optional['LoggerConfig'] = None
33
-
34
-
35
- FilterConfig = dict[str, ta.Any]
36
- FormatterConfig = dict[str, ta.Any]
37
- HandlerConfig = dict[str, ta.Any]
38
- LoggerConfig = dict[str, ta.Any]
23
+ filters: dict[str, FilterConfig] = dc.field(default_factory=dict)
24
+ formatters: dict[str, FormatterConfig] = dc.field(default_factory=dict)
25
+ handlers: dict[str, HandlerConfig] = dc.field(default_factory=dict)
26
+ loggers: dict[str, LoggerConfig] = dc.field(default_factory=dict)
27
+ root: LoggerConfig | None = None
39
28
 
40
29
 
41
30
  ##
@@ -51,7 +40,6 @@ def configure_standard_logging(
51
40
  json=json,
52
41
  )
53
42
 
54
- for noisy_logger in NOISY_LOGGERS:
55
- logging.getLogger(noisy_logger).setLevel(logging.WARNING)
43
+ silence_noisy_loggers()
56
44
 
57
45
  return handler
omlish/logs/noisy.py ADDED
@@ -0,0 +1,15 @@
1
+ import logging
2
+
3
+
4
+ NOISY_LOGGERS: set[str] = {
5
+ 'boto3.resources.action',
6
+ 'datadog.dogstatsd',
7
+ 'elasticsearch',
8
+ 'kazoo.client',
9
+ 'requests.packages.urllib3.connectionpool',
10
+ }
11
+
12
+
13
+ def silence_noisy_loggers() -> None:
14
+ for noisy_logger in NOISY_LOGGERS:
15
+ logging.getLogger(noisy_logger).setLevel(logging.WARNING)
@@ -48,6 +48,10 @@ from .global_ import ( # noqa
48
48
  unmarshal,
49
49
  )
50
50
 
51
+ from .helpers import ( # noqa
52
+ update_fields_metadata,
53
+ )
54
+
51
55
  from .objects import ( # noqa
52
56
  FieldMetadata,
53
57
  ObjectMetadata,