omlish 0.0.0.dev25__py3-none-any.whl → 0.0.0.dev27__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- omlish/__about__.py +8 -5
- omlish/bootstrap/diag.py +25 -0
- omlish/bootstrap/harness.py +3 -2
- omlish/check.py +1 -1
- omlish/collections/__init__.py +1 -0
- omlish/collections/utils.py +7 -2
- omlish/dataclasses/__init__.py +4 -4
- omlish/dataclasses/impl/api.py +5 -3
- omlish/dataclasses/impl/exceptions.py +2 -2
- omlish/dataclasses/impl/fields.py +7 -4
- omlish/dataclasses/impl/init.py +8 -8
- omlish/dataclasses/impl/metadata.py +3 -3
- omlish/dataclasses/impl/params.py +4 -3
- omlish/diag/replserver/server.py +17 -2
- omlish/docker.py +74 -6
- omlish/formats/yaml.py +10 -0
- omlish/graphs/dot/__init__.py +31 -19
- omlish/graphs/dot/make.py +16 -0
- omlish/graphs/dot/rendering.py +9 -8
- omlish/http/cookies.py +2 -1
- omlish/inject/impl/scopes.py +2 -0
- omlish/inject/keys.py +1 -1
- omlish/inject/multis.py +4 -4
- omlish/inject/providers.py +1 -1
- omlish/lang/__init__.py +8 -0
- omlish/lang/classes/__init__.py +3 -0
- omlish/lang/classes/restrict.py +25 -4
- omlish/lang/classes/simple.py +0 -4
- omlish/lang/classes/virtual.py +6 -4
- omlish/lang/datetimes.py +9 -0
- omlish/lang/resources.py +29 -10
- omlish/lite/check.py +6 -0
- omlish/lite/logs.py +30 -25
- omlish/lite/secrets.py +3 -1
- omlish/logs/configs.py +2 -2
- omlish/marshal/dataclasses.py +1 -1
- omlish/secrets/secrets.py +1 -1
- omlish/stats.py +1 -1
- {omlish-0.0.0.dev25.dist-info → omlish-0.0.0.dev27.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev25.dist-info → omlish-0.0.0.dev27.dist-info}/RECORD +44 -42
- {omlish-0.0.0.dev25.dist-info → omlish-0.0.0.dev27.dist-info}/WHEEL +1 -1
- /omlish/{_manifests.json → .manifests.json} +0 -0
- {omlish-0.0.0.dev25.dist-info → omlish-0.0.0.dev27.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev25.dist-info → omlish-0.0.0.dev27.dist-info}/top_level.txt +0 -0
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(
|
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(
|
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(
|
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(
|
63
|
+
multi_key: Key = dc.xfield(validate=_check_map_multi_key)
|
64
64
|
|
65
65
|
|
66
66
|
##
|
omlish/inject/providers.py
CHANGED
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,
|
omlish/lang/classes/__init__.py
CHANGED
@@ -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
|
|
omlish/lang/classes/restrict.py
CHANGED
@@ -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
|
omlish/lang/classes/simple.py
CHANGED
@@ -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]
|
omlish/lang/classes/virtual.py
CHANGED
@@ -40,7 +40,12 @@ class _VirtualMeta(abc.ABCMeta):
|
|
40
40
|
if absv is not v:
|
41
41
|
namespace[k] = absv
|
42
42
|
|
43
|
-
reqs = {
|
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
|
|
omlish/lang/datetimes.py
ADDED
omlish/lang/resources.py
CHANGED
@@ -11,7 +11,7 @@ class RelativeResource(ta.NamedTuple):
|
|
11
11
|
|
12
12
|
|
13
13
|
def get_relative_resources(
|
14
|
-
|
14
|
+
path: str = '',
|
15
15
|
*,
|
16
16
|
globals: ta.Mapping[str, ta.Any] | None = None, # noqa
|
17
17
|
package: str | None = None,
|
@@ -23,13 +23,29 @@ def get_relative_resources(
|
|
23
23
|
if not file:
|
24
24
|
file = globals.get('__file__')
|
25
25
|
|
26
|
+
#
|
27
|
+
|
28
|
+
if os.sep in path:
|
29
|
+
raise ValueError(path) # noqa
|
30
|
+
if not path.startswith('.'):
|
31
|
+
path = '.' + path
|
32
|
+
if set(path) - {'.'}:
|
33
|
+
dot_pos = next(i for i in range(len(path)) if path[i] != '.')
|
34
|
+
num_up = dot_pos - 1
|
35
|
+
path_parts = path[dot_pos:].split('.')
|
36
|
+
else:
|
37
|
+
num_up = len(path) - 1
|
38
|
+
path_parts = []
|
39
|
+
|
40
|
+
#
|
41
|
+
|
26
42
|
lst: list[RelativeResource] = []
|
27
43
|
|
28
44
|
if package:
|
29
|
-
|
30
|
-
if
|
31
|
-
|
32
|
-
|
45
|
+
pkg_parts = package.split('.')
|
46
|
+
if num_up:
|
47
|
+
pkg_parts = pkg_parts[:-num_up]
|
48
|
+
anchor = '.'.join([*pkg_parts, *path_parts])
|
33
49
|
for pf in importlib.resources.files(anchor).iterdir():
|
34
50
|
lst.append(RelativeResource(
|
35
51
|
name=pf.name,
|
@@ -38,16 +54,19 @@ def get_relative_resources(
|
|
38
54
|
))
|
39
55
|
|
40
56
|
elif file:
|
41
|
-
|
42
|
-
|
43
|
-
|
57
|
+
base_dir = os.path.dirname(file)
|
58
|
+
dst_dir = os.path.join(
|
59
|
+
base_dir,
|
60
|
+
*(['..'] * num_up),
|
61
|
+
*path_parts,
|
62
|
+
)
|
44
63
|
|
45
64
|
def _read_file(fp: str) -> bytes:
|
46
65
|
with open(fp, 'rb') as f:
|
47
66
|
return f.read()
|
48
67
|
|
49
|
-
for ff in os.listdir(
|
50
|
-
ff = os.path.join(
|
68
|
+
for ff in os.listdir(dst_dir):
|
69
|
+
ff = os.path.join(dst_dir, ff)
|
51
70
|
lst.append(RelativeResource(
|
52
71
|
name=os.path.basename(ff),
|
53
72
|
is_file=os.path.isfile(ff),
|
omlish/lite/check.py
CHANGED
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
|
-
|
211
|
+
force: bool = False,
|
212
212
|
) -> ta.Optional[StandardLogHandler]:
|
213
|
-
|
214
|
-
|
213
|
+
logging._acquireLock() # type: ignore # noqa
|
214
|
+
try:
|
215
|
+
if target is None:
|
216
|
+
target = logging.root
|
215
217
|
|
216
|
-
|
218
|
+
#
|
217
219
|
|
218
|
-
|
219
|
-
|
220
|
-
|
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
|
-
|
226
|
+
handler = logging.StreamHandler()
|
225
227
|
|
226
|
-
|
228
|
+
#
|
227
229
|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
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
|
-
|
241
|
+
#
|
238
242
|
|
239
|
-
|
243
|
+
target.addHandler(handler)
|
240
244
|
|
241
|
-
|
245
|
+
#
|
242
246
|
|
243
|
-
|
247
|
+
if level is not None:
|
248
|
+
target.setLevel(level)
|
244
249
|
|
245
|
-
|
246
|
-
target.setLevel(level)
|
250
|
+
#
|
247
251
|
|
248
|
-
|
252
|
+
return StandardLogHandler(handler)
|
249
253
|
|
250
|
-
|
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
|
-
|
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
|
-
|
45
|
+
force=force,
|
46
46
|
)
|
47
47
|
|
48
48
|
if handler is None:
|
omlish/marshal/dataclasses.py
CHANGED
@@ -24,7 +24,7 @@ from .objects import ObjectUnmarshaler
|
|
24
24
|
|
25
25
|
|
26
26
|
def get_dataclass_metadata(ty: type) -> ObjectMetadata:
|
27
|
-
return check.
|
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
omlish/stats.py
CHANGED