omlish 0.0.0.dev5__py3-none-any.whl → 0.0.0.dev7__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.

Potentially problematic release.


This version of omlish might be problematic. Click here for more details.

Files changed (163) hide show
  1. omlish/__about__.py +109 -5
  2. omlish/__init__.py +0 -8
  3. omlish/asyncs/__init__.py +9 -9
  4. omlish/asyncs/anyio.py +123 -19
  5. omlish/asyncs/asyncio.py +23 -0
  6. omlish/asyncs/asyncs.py +9 -6
  7. omlish/asyncs/bridge.py +316 -0
  8. omlish/asyncs/trio_asyncio.py +7 -3
  9. omlish/bootstrap.py +737 -0
  10. omlish/check.py +1 -1
  11. omlish/collections/__init__.py +5 -0
  12. omlish/collections/exceptions.py +2 -0
  13. omlish/collections/identity.py +7 -0
  14. omlish/collections/utils.py +38 -9
  15. omlish/configs/strings.py +96 -0
  16. omlish/dataclasses/__init__.py +16 -0
  17. omlish/dataclasses/impl/copy.py +30 -0
  18. omlish/dataclasses/impl/descriptors.py +95 -0
  19. omlish/dataclasses/impl/exceptions.py +6 -0
  20. omlish/dataclasses/impl/fields.py +24 -25
  21. omlish/dataclasses/impl/init.py +4 -2
  22. omlish/dataclasses/impl/main.py +2 -0
  23. omlish/dataclasses/impl/reflect.py +1 -1
  24. omlish/dataclasses/utils.py +67 -0
  25. omlish/{lang/datetimes.py → datetimes.py} +8 -4
  26. omlish/diag/__init__.py +4 -0
  27. omlish/diag/procfs.py +2 -2
  28. omlish/{testing → diag}/pydevd.py +35 -0
  29. omlish/diag/threads.py +131 -48
  30. omlish/dispatch/_dispatch2.py +65 -0
  31. omlish/dispatch/_dispatch3.py +104 -0
  32. omlish/docker.py +16 -1
  33. omlish/fnpairs.py +11 -4
  34. omlish/formats/__init__.py +0 -0
  35. omlish/{configs → formats}/dotenv.py +15 -24
  36. omlish/{json.py → formats/json.py} +2 -1
  37. omlish/formats/yaml.py +223 -0
  38. omlish/graphs/trees.py +1 -1
  39. omlish/http/asgi.py +2 -1
  40. omlish/http/collections.py +15 -0
  41. omlish/http/consts.py +22 -1
  42. omlish/http/sessions.py +10 -3
  43. omlish/inject/__init__.py +49 -17
  44. omlish/inject/binder.py +185 -5
  45. omlish/inject/bindings.py +3 -36
  46. omlish/inject/eagers.py +2 -8
  47. omlish/inject/elements.py +31 -10
  48. omlish/inject/exceptions.py +1 -1
  49. omlish/inject/impl/elements.py +37 -12
  50. omlish/inject/impl/injector.py +72 -25
  51. omlish/inject/impl/inspect.py +33 -5
  52. omlish/inject/impl/origins.py +77 -0
  53. omlish/inject/impl/{private.py → privates.py} +2 -2
  54. omlish/inject/impl/scopes.py +6 -2
  55. omlish/inject/injector.py +8 -4
  56. omlish/inject/inspect.py +18 -0
  57. omlish/inject/keys.py +8 -14
  58. omlish/inject/listeners.py +26 -0
  59. omlish/inject/managed.py +76 -10
  60. omlish/inject/multis.py +68 -18
  61. omlish/inject/origins.py +30 -0
  62. omlish/inject/overrides.py +5 -4
  63. omlish/inject/{private.py → privates.py} +6 -10
  64. omlish/inject/providers.py +12 -85
  65. omlish/inject/scopes.py +13 -6
  66. omlish/inject/types.py +3 -1
  67. omlish/inject/utils.py +18 -0
  68. omlish/iterators.py +69 -2
  69. omlish/lang/__init__.py +24 -9
  70. omlish/lang/cached.py +2 -2
  71. omlish/lang/classes/restrict.py +12 -1
  72. omlish/lang/classes/simple.py +18 -8
  73. omlish/lang/contextmanagers.py +13 -4
  74. omlish/lang/descriptors.py +132 -1
  75. omlish/lang/functions.py +8 -28
  76. omlish/lang/imports.py +67 -0
  77. omlish/lang/iterables.py +60 -1
  78. omlish/lang/maybes.py +3 -0
  79. omlish/lang/objects.py +38 -0
  80. omlish/lang/strings.py +25 -0
  81. omlish/lang/sys.py +9 -0
  82. omlish/lang/typing.py +42 -0
  83. omlish/lifecycles/__init__.py +34 -0
  84. omlish/lifecycles/abstract.py +43 -0
  85. omlish/lifecycles/base.py +51 -0
  86. omlish/lifecycles/contextmanagers.py +74 -0
  87. omlish/lifecycles/controller.py +116 -0
  88. omlish/lifecycles/manager.py +161 -0
  89. omlish/lifecycles/states.py +43 -0
  90. omlish/lifecycles/transitions.py +64 -0
  91. omlish/lite/__init__.py +1 -0
  92. omlish/lite/cached.py +18 -0
  93. omlish/lite/check.py +29 -0
  94. omlish/lite/contextmanagers.py +18 -0
  95. omlish/lite/json.py +30 -0
  96. omlish/lite/logs.py +52 -0
  97. omlish/lite/marshal.py +316 -0
  98. omlish/lite/reflect.py +49 -0
  99. omlish/lite/runtime.py +18 -0
  100. omlish/lite/secrets.py +19 -0
  101. omlish/lite/strings.py +25 -0
  102. omlish/lite/subprocesses.py +112 -0
  103. omlish/logs/configs.py +15 -2
  104. omlish/logs/formatters.py +7 -2
  105. omlish/marshal/__init__.py +32 -0
  106. omlish/marshal/any.py +5 -5
  107. omlish/marshal/base.py +27 -11
  108. omlish/marshal/base64.py +24 -9
  109. omlish/marshal/dataclasses.py +34 -28
  110. omlish/marshal/datetimes.py +74 -18
  111. omlish/marshal/enums.py +14 -8
  112. omlish/marshal/exceptions.py +11 -1
  113. omlish/marshal/factories.py +59 -74
  114. omlish/marshal/forbidden.py +35 -0
  115. omlish/marshal/global_.py +11 -4
  116. omlish/marshal/iterables.py +21 -24
  117. omlish/marshal/mappings.py +23 -26
  118. omlish/marshal/naming.py +4 -0
  119. omlish/marshal/numbers.py +51 -0
  120. omlish/marshal/objects.py +1 -0
  121. omlish/marshal/optionals.py +11 -12
  122. omlish/marshal/polymorphism.py +86 -21
  123. omlish/marshal/primitives.py +4 -5
  124. omlish/marshal/standard.py +13 -8
  125. omlish/marshal/uuids.py +4 -5
  126. omlish/matchfns.py +218 -0
  127. omlish/os.py +64 -0
  128. omlish/reflect/__init__.py +39 -0
  129. omlish/reflect/isinstance.py +38 -0
  130. omlish/reflect/ops.py +84 -0
  131. omlish/reflect/subst.py +110 -0
  132. omlish/reflect/types.py +275 -0
  133. omlish/secrets/__init__.py +23 -0
  134. omlish/secrets/crypto.py +132 -0
  135. omlish/secrets/marshal.py +70 -0
  136. omlish/secrets/openssl.py +207 -0
  137. omlish/secrets/passwords.py +120 -0
  138. omlish/secrets/secrets.py +299 -0
  139. omlish/secrets/subprocesses.py +42 -0
  140. omlish/sql/dbs.py +7 -6
  141. omlish/sql/duckdb.py +136 -0
  142. omlish/sql/exprs.py +12 -0
  143. omlish/sql/secrets.py +10 -0
  144. omlish/sql/sqlean.py +17 -0
  145. omlish/term.py +2 -2
  146. omlish/testing/pytest/__init__.py +3 -2
  147. omlish/testing/pytest/inject/harness.py +3 -3
  148. omlish/testing/pytest/marks.py +4 -7
  149. omlish/testing/pytest/plugins/__init__.py +1 -0
  150. omlish/testing/pytest/plugins/asyncs.py +136 -0
  151. omlish/testing/pytest/plugins/pydevd.py +1 -1
  152. omlish/testing/pytest/plugins/switches.py +54 -19
  153. omlish/text/glyphsplit.py +97 -0
  154. omlish-0.0.0.dev7.dist-info/METADATA +50 -0
  155. omlish-0.0.0.dev7.dist-info/RECORD +268 -0
  156. {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev7.dist-info}/WHEEL +1 -1
  157. omlish/reflect.py +0 -355
  158. omlish-0.0.0.dev5.dist-info/METADATA +0 -34
  159. omlish-0.0.0.dev5.dist-info/RECORD +0 -212
  160. /omlish/{asyncs/futures.py → concurrent.py} +0 -0
  161. /omlish/{configs → formats}/props.py +0 -0
  162. {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev7.dist-info}/LICENSE +0 -0
  163. {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev7.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,161 @@
1
+ import typing as ta
2
+
3
+ from .. import check
4
+ from .. import collections as col
5
+ from .. import dataclasses as dc
6
+ from .. import lang
7
+ from .abstract import AbstractLifecycle
8
+ from .base import Lifecycle
9
+ from .controller import LifecycleController
10
+ from .states import LifecycleState
11
+ from .states import LifecycleStateError
12
+ from .states import LifecycleStates
13
+
14
+
15
+ class LifecycleManager(AbstractLifecycle):
16
+
17
+ @dc.dataclass(frozen=True)
18
+ class Entry(lang.Final):
19
+ controller: LifecycleController
20
+ dependencies: ta.MutableSet['LifecycleManager.Entry'] = dc.field(default_factory=col.IdentitySet)
21
+ dependents: ta.MutableSet['LifecycleManager.Entry'] = dc.field(default_factory=col.IdentitySet)
22
+
23
+ def __init__(
24
+ self,
25
+ *,
26
+ lock: lang.DefaultLockable = None,
27
+ ) -> None:
28
+ super().__init__()
29
+
30
+ self._lock = lang.default_lock(lock, False)
31
+
32
+ self._entries_by_lifecycle: ta.MutableMapping[Lifecycle, LifecycleManager.Entry] = col.IdentityKeyDict()
33
+
34
+ self._controller = LifecycleController(self._lifecycle, lock=self._lock)
35
+
36
+ @property
37
+ def controller(self) -> LifecycleController:
38
+ return self._controller
39
+
40
+ @property
41
+ def state(self) -> LifecycleState:
42
+ return self._controller.state
43
+
44
+ @staticmethod
45
+ def _get_controller(lifecycle: Lifecycle) -> LifecycleController:
46
+ if isinstance(lifecycle, LifecycleController):
47
+ return lifecycle
48
+ # elif isinstance(lifecycle, AbstractLifecycle):
49
+ # return lifecycle.lifecycle_controller
50
+ elif isinstance(lifecycle, Lifecycle):
51
+ return LifecycleController(lifecycle)
52
+ else:
53
+ raise TypeError(lifecycle)
54
+
55
+ def _add_internal(self, lifecycle: Lifecycle, dependencies: ta.Iterable[Lifecycle]) -> Entry:
56
+ check.state(self.state < LifecycleStates.STOPPING and not self.state.is_failed)
57
+
58
+ check.isinstance(lifecycle, Lifecycle)
59
+ try:
60
+ entry = self._entries_by_lifecycle[lifecycle]
61
+ except KeyError:
62
+ controller = self._get_controller(lifecycle)
63
+ entry = self._entries_by_lifecycle[lifecycle] = LifecycleManager.Entry(controller)
64
+
65
+ for dep in dependencies:
66
+ check.isinstance(dep, Lifecycle)
67
+ dep_entry = self._add_internal(dep, [])
68
+ entry.dependencies.add(dep_entry)
69
+ dep_entry.dependents.add(entry)
70
+
71
+ return entry
72
+
73
+ def add(
74
+ self,
75
+ lifecycle: Lifecycle,
76
+ dependencies: ta.Iterable[Lifecycle] = (),
77
+ ) -> Entry:
78
+ check.state(self.state < LifecycleStates.STOPPING and not self.state.is_failed)
79
+
80
+ with self._lock():
81
+ entry = self._add_internal(lifecycle, dependencies)
82
+
83
+ if self.state >= LifecycleStates.CONSTRUCTING:
84
+ def rec(e):
85
+ if e.controller.state < LifecycleStates.CONSTRUCTED:
86
+ for dep in e.dependencies:
87
+ rec(dep)
88
+ e.controller.lifecycle_construct()
89
+ rec(entry)
90
+
91
+ if self.state >= LifecycleStates.STARTING:
92
+ def rec(e):
93
+ if e.controller.state < LifecycleStates.STARTED:
94
+ for dep in e.dependencies:
95
+ rec(dep)
96
+ e.controller.lifecycle_start()
97
+ rec(entry)
98
+
99
+ return entry
100
+
101
+ ##
102
+
103
+ @ta.override
104
+ def _lifecycle_construct(self) -> None:
105
+ def rec(entry: LifecycleManager.Entry) -> None:
106
+ for dep in entry.dependencies:
107
+ rec(dep)
108
+
109
+ if entry.controller.state.is_failed:
110
+ raise LifecycleStateError(entry.controller)
111
+
112
+ if entry.controller.state < LifecycleStates.CONSTRUCTED:
113
+ entry.controller.lifecycle_construct()
114
+
115
+ for entry in self._entries_by_lifecycle.values():
116
+ rec(entry)
117
+
118
+ @ta.override
119
+ def _lifecycle_start(self) -> None:
120
+ def rec(entry: LifecycleManager.Entry) -> None:
121
+ for dep in entry.dependencies:
122
+ rec(dep)
123
+
124
+ if entry.controller.state.is_failed:
125
+ raise LifecycleStateError(entry.controller)
126
+
127
+ if entry.controller.state < LifecycleStates.CONSTRUCTED:
128
+ entry.controller.lifecycle_construct()
129
+
130
+ if entry.controller.state < LifecycleStates.STARTED:
131
+ entry.controller.lifecycle_start()
132
+
133
+ for entry in self._entries_by_lifecycle.values():
134
+ rec(entry)
135
+
136
+ @ta.override
137
+ def _lifecycle_stop(self) -> None:
138
+ def rec(entry: LifecycleManager.Entry) -> None:
139
+ for dep in entry.dependents:
140
+ rec(dep)
141
+
142
+ if entry.controller.state.is_failed:
143
+ raise LifecycleStateError(entry.controller)
144
+
145
+ if entry.controller.state is LifecycleStates.STARTED:
146
+ entry.controller.lifecycle_stop()
147
+
148
+ for entry in self._entries_by_lifecycle.values():
149
+ rec(entry)
150
+
151
+ @ta.override
152
+ def _lifecycle_destroy(self) -> None:
153
+ def rec(entry: LifecycleManager.Entry) -> None:
154
+ for dep in entry.dependents:
155
+ rec(dep)
156
+
157
+ if entry.controller.state < LifecycleStates.DESTROYED:
158
+ entry.controller.lifecycle_destroy()
159
+
160
+ for entry in self._entries_by_lifecycle.values():
161
+ rec(entry)
@@ -0,0 +1,43 @@
1
+ import functools
2
+
3
+ from .. import check
4
+ from .. import dataclasses as dc
5
+ from .. import lang
6
+
7
+
8
+ class LifecycleStateError(Exception):
9
+ pass
10
+
11
+
12
+ @dc.dataclass(frozen=True, eq=False)
13
+ @functools.total_ordering
14
+ class LifecycleState(lang.Final):
15
+ name: str
16
+ phase: int
17
+ is_failed: bool
18
+
19
+ def __lt__(self, other):
20
+ return self.phase < check.isinstance(other, LifecycleState).phase
21
+
22
+ def __le__(self, other):
23
+ return self.phase <= check.isinstance(other, LifecycleState).phase
24
+
25
+
26
+ class LifecycleStates(lang.Namespace):
27
+ NEW = LifecycleState('NEW', 0, False)
28
+
29
+ CONSTRUCTING = LifecycleState('CONSTRUCTING', 1, False)
30
+ FAILED_CONSTRUCTING = LifecycleState('FAILED_CONSTRUCTING', 2, True)
31
+ CONSTRUCTED = LifecycleState('CONSTRUCTED', 3, False)
32
+
33
+ STARTING = LifecycleState('STARTING', 4, False)
34
+ FAILED_STARTING = LifecycleState('FAILED_STARTING', 5, True)
35
+ STARTED = LifecycleState('STARTED', 6, False)
36
+
37
+ STOPPING = LifecycleState('STOPPING', 7, False)
38
+ FAILED_STOPPING = LifecycleState('FAILED_STOPPING', 8, True)
39
+ STOPPED = LifecycleState('STOPPED', 9, False)
40
+
41
+ DESTROYING = LifecycleState('DESTROYING', 10, False)
42
+ FAILED_DESTROYING = LifecycleState('FAILED_DESTROYING', 11, True)
43
+ DESTROYED = LifecycleState('DESTROYED', 12, False)
@@ -0,0 +1,64 @@
1
+ from .. import check
2
+ from .. import dataclasses as dc
3
+ from .. import lang
4
+ from .states import LifecycleState
5
+ from .states import LifecycleStates
6
+
7
+
8
+ @dc.dataclass(frozen=True)
9
+ class LifecycleTransition(lang.Final):
10
+ old: frozenset[LifecycleState]
11
+ new_intermediate: LifecycleState
12
+ new_failed: LifecycleState
13
+ new_succeeded: LifecycleState
14
+
15
+ def __post_init__(self) -> None:
16
+ dc.maybe_post_init(super())
17
+ check.unique([*self.old, self.new_intermediate, self.new_succeeded, self.new_failed])
18
+ check.arg(all(self.new_intermediate > o for o in self.old))
19
+ check.arg(self.new_failed > self.new_intermediate)
20
+ check.arg(self.new_succeeded > self.new_failed)
21
+
22
+
23
+ class LifecycleTransitions(lang.Namespace):
24
+ CONSTRUCT = LifecycleTransition(
25
+ frozenset([LifecycleStates.NEW]),
26
+ LifecycleStates.CONSTRUCTING,
27
+ LifecycleStates.FAILED_CONSTRUCTING,
28
+ LifecycleStates.CONSTRUCTED,
29
+ )
30
+
31
+ START = LifecycleTransition(
32
+ frozenset([LifecycleStates.CONSTRUCTED]),
33
+ LifecycleStates.STARTING,
34
+ LifecycleStates.FAILED_STARTING,
35
+ LifecycleStates.STARTED,
36
+ )
37
+
38
+ STOP = LifecycleTransition(
39
+ frozenset([LifecycleStates.STARTED]),
40
+ LifecycleStates.STOPPING,
41
+ LifecycleStates.FAILED_STOPPING,
42
+ LifecycleStates.STOPPED,
43
+ )
44
+
45
+ DESTROY = LifecycleTransition(
46
+ frozenset([
47
+ LifecycleStates.NEW,
48
+
49
+ LifecycleStates.CONSTRUCTING,
50
+ LifecycleStates.FAILED_CONSTRUCTING,
51
+ LifecycleStates.CONSTRUCTED,
52
+
53
+ LifecycleStates.STARTING,
54
+ LifecycleStates.FAILED_STARTING,
55
+ LifecycleStates.STARTED,
56
+
57
+ LifecycleStates.STOPPING,
58
+ LifecycleStates.FAILED_STOPPING,
59
+ LifecycleStates.STOPPED,
60
+ ]),
61
+ LifecycleStates.DESTROYING,
62
+ LifecycleStates.FAILED_DESTROYING,
63
+ LifecycleStates.DESTROYED,
64
+ )
@@ -0,0 +1 @@
1
+ # @omlish-lite
omlish/lite/cached.py ADDED
@@ -0,0 +1,18 @@
1
+ import functools
2
+
3
+
4
+ class cached_nullary: # noqa
5
+ def __init__(self, fn):
6
+ super().__init__()
7
+ self._fn = fn
8
+ self._value = self._missing = object()
9
+ functools.update_wrapper(self, fn)
10
+
11
+ def __call__(self, *args, **kwargs): # noqa
12
+ if self._value is self._missing:
13
+ self._value = self._fn()
14
+ return self._value
15
+
16
+ def __get__(self, instance, owner): # noqa
17
+ bound = instance.__dict__[self._fn.__name__] = self.__class__(self._fn.__get__(instance, owner))
18
+ return bound
omlish/lite/check.py ADDED
@@ -0,0 +1,29 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import typing as ta
3
+
4
+
5
+ T = ta.TypeVar('T')
6
+
7
+
8
+ def check_isinstance(v: T, spec: ta.Union[ta.Type[T], tuple]) -> T:
9
+ if not isinstance(v, spec):
10
+ raise TypeError(v)
11
+ return v
12
+
13
+
14
+ def check_not_isinstance(v: T, spec: ta.Union[type, tuple]) -> T:
15
+ if isinstance(v, spec):
16
+ raise TypeError(v)
17
+ return v
18
+
19
+
20
+ def check_not_none(v: ta.Optional[T]) -> T:
21
+ if v is None:
22
+ raise ValueError
23
+ return v
24
+
25
+
26
+ def check_not(v: ta.Any) -> None:
27
+ if v:
28
+ raise ValueError(v)
29
+ return v
@@ -0,0 +1,18 @@
1
+ import contextlib
2
+
3
+
4
+ @contextlib.contextmanager
5
+ def attr_setting(obj, attr, val, *, default=None): # noqa
6
+ not_set = object()
7
+ orig = getattr(obj, attr, not_set)
8
+ try:
9
+ setattr(obj, attr, val)
10
+ if orig is not not_set:
11
+ yield orig
12
+ else:
13
+ yield default
14
+ finally:
15
+ if orig is not_set:
16
+ delattr(obj, attr)
17
+ else:
18
+ setattr(obj, attr, orig)
omlish/lite/json.py ADDED
@@ -0,0 +1,30 @@
1
+ import functools
2
+ import json
3
+ import typing as ta
4
+
5
+
6
+ ##
7
+
8
+
9
+ JSON_PRETTY_INDENT = 2
10
+
11
+ JSON_PRETTY_KWARGS: ta.Mapping[str, ta.Any] = dict(
12
+ indent=JSON_PRETTY_INDENT,
13
+ )
14
+
15
+ json_dump_pretty: ta.Callable[..., bytes] = functools.partial(json.dump, **JSON_PRETTY_KWARGS) # type: ignore
16
+ json_dumps_pretty: ta.Callable[..., str] = functools.partial(json.dumps, **JSON_PRETTY_KWARGS)
17
+
18
+
19
+ ##
20
+
21
+
22
+ JSON_COMPACT_SEPARATORS = (',', ':')
23
+
24
+ JSON_COMPACT_KWARGS: ta.Mapping[str, ta.Any] = dict(
25
+ indent=None,
26
+ separators=JSON_COMPACT_SEPARATORS,
27
+ )
28
+
29
+ json_dump_compact: ta.Callable[..., bytes] = functools.partial(json.dump, **JSON_COMPACT_KWARGS) # type: ignore
30
+ json_dumps_compact: ta.Callable[..., str] = functools.partial(json.dumps, **JSON_COMPACT_KWARGS)
omlish/lite/logs.py ADDED
@@ -0,0 +1,52 @@
1
+ """
2
+ TODO:
3
+ - debug
4
+ """
5
+ # ruff: noqa: UP007
6
+ import logging
7
+ import typing as ta
8
+
9
+ from .json import json_dumps_compact
10
+
11
+
12
+ log = logging.getLogger(__name__)
13
+
14
+
15
+ class JsonLogFormatter(logging.Formatter):
16
+
17
+ KEYS: ta.Mapping[str, bool] = {
18
+ 'name': False,
19
+ 'msg': False,
20
+ 'args': False,
21
+ 'levelname': False,
22
+ 'levelno': False,
23
+ 'pathname': False,
24
+ 'filename': False,
25
+ 'module': False,
26
+ 'exc_info': True,
27
+ 'exc_text': True,
28
+ 'stack_info': True,
29
+ 'lineno': False,
30
+ 'funcName': False,
31
+ 'created': False,
32
+ 'msecs': False,
33
+ 'relativeCreated': False,
34
+ 'thread': False,
35
+ 'threadName': False,
36
+ 'processName': False,
37
+ 'process': False,
38
+ }
39
+
40
+ def format(self, record: logging.LogRecord) -> str:
41
+ dct = {
42
+ k: v
43
+ for k, o in self.KEYS.items()
44
+ for v in [getattr(record, k)]
45
+ if not (o and v is None)
46
+ }
47
+ return json_dumps_compact(dct)
48
+
49
+
50
+ def configure_standard_logging(level: ta.Union[int, str] = logging.INFO) -> None:
51
+ logging.root.addHandler(logging.StreamHandler())
52
+ logging.root.setLevel(level)