omlish 0.0.0.dev23__py3-none-any.whl → 0.0.0.dev24__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 +2 -2
- omlish/bootstrap/diag.py +2 -2
- omlish/check.py +202 -24
- omlish/collections/__init__.py +2 -2
- omlish/collections/utils.py +3 -3
- omlish/concurrent/threadlets.py +5 -0
- omlish/dataclasses/__init__.py +1 -0
- omlish/dataclasses/impl/init.py +10 -2
- omlish/dataclasses/impl/metadata.py +3 -3
- omlish/dataclasses/impl/reflect.py +1 -1
- omlish/dataclasses/utils.py +16 -3
- omlish/diag/pycharm.py +69 -10
- omlish/docker.py +16 -11
- omlish/genmachine.py +2 -1
- omlish/graphs/trees.py +1 -1
- omlish/lang/__init__.py +13 -1
- omlish/lang/cached.py +5 -2
- omlish/lang/descriptors.py +33 -16
- omlish/lang/resources.py +60 -0
- omlish/lite/logs.py +133 -4
- omlish/logs/__init__.py +17 -2
- omlish/logs/configs.py +13 -1
- omlish/logs/formatters.py +0 -1
- omlish/marshal/__init__.py +2 -0
- omlish/marshal/helpers.py +27 -0
- omlish/specs/jsonschema/keywords/base.py +2 -2
- omlish/specs/jsonschema/keywords/parse.py +1 -1
- omlish/stats.py +1 -0
- {omlish-0.0.0.dev23.dist-info → omlish-0.0.0.dev24.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev23.dist-info → omlish-0.0.0.dev24.dist-info}/RECORD +33 -32
- {omlish-0.0.0.dev23.dist-info → omlish-0.0.0.dev24.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev23.dist-info → omlish-0.0.0.dev24.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev23.dist-info → omlish-0.0.0.dev24.dist-info}/top_level.txt +0 -0
omlish/genmachine.py
CHANGED
omlish/graphs/trees.py
CHANGED
@@ -190,7 +190,7 @@ class BasicTreeAnalysis(ta.Generic[NodeT]):
|
|
190
190
|
e: ta.Any
|
191
191
|
d: ta.Any
|
192
192
|
if identity:
|
193
|
-
e, d = id, col.
|
193
|
+
e, d = id, col.make_map(((id(n), n) for n, _ in pairs), strict=True)
|
194
194
|
else:
|
195
195
|
e, d = lang.identity, lang.identity
|
196
196
|
tsd = {e(n): {e(p)} for n, p in parents_by_node.items()}
|
omlish/lang/__init__.py
CHANGED
@@ -79,7 +79,7 @@ from .descriptors import ( # noqa
|
|
79
79
|
unwrap_func,
|
80
80
|
unwrap_func_with_partials,
|
81
81
|
unwrap_method_descriptors,
|
82
|
-
|
82
|
+
update_wrapper,
|
83
83
|
)
|
84
84
|
|
85
85
|
from .exceptions import ( # noqa
|
@@ -154,6 +154,18 @@ from .objects import ( # noqa
|
|
154
154
|
super_meta,
|
155
155
|
)
|
156
156
|
|
157
|
+
from .resolving import ( # noqa
|
158
|
+
Resolvable,
|
159
|
+
ResolvableClassNameError,
|
160
|
+
get_cls_fqcn,
|
161
|
+
get_fqcn_cls,
|
162
|
+
)
|
163
|
+
|
164
|
+
from .resources import ( # noqa
|
165
|
+
RelativeResource,
|
166
|
+
get_relative_resources,
|
167
|
+
)
|
168
|
+
|
157
169
|
from .strings import ( # noqa
|
158
170
|
BOOL_FALSE_STRINGS,
|
159
171
|
BOOL_STRINGS,
|
omlish/lang/cached.py
CHANGED
@@ -220,7 +220,7 @@ def cached_function(fn=None, **kwargs): # noqa
|
|
220
220
|
##
|
221
221
|
|
222
222
|
|
223
|
-
class _CachedProperty:
|
223
|
+
class _CachedProperty(property):
|
224
224
|
def __init__(
|
225
225
|
self,
|
226
226
|
fn,
|
@@ -229,9 +229,9 @@ class _CachedProperty:
|
|
229
229
|
ignore_if=lambda _: False,
|
230
230
|
clear_on_init=False,
|
231
231
|
):
|
232
|
-
super().__init__()
|
233
232
|
if isinstance(fn, property):
|
234
233
|
fn = fn.fget
|
234
|
+
super().__init__(fn)
|
235
235
|
self._fn = fn
|
236
236
|
self._ignore_if = ignore_if
|
237
237
|
self._name = name
|
@@ -265,6 +265,9 @@ class _CachedProperty:
|
|
265
265
|
return
|
266
266
|
raise TypeError(self._name)
|
267
267
|
|
268
|
+
def __delete__(self, instance):
|
269
|
+
raise TypeError
|
270
|
+
|
268
271
|
|
269
272
|
def cached_property(fn=None, **kwargs): # noqa
|
270
273
|
if fn is None:
|
omlish/lang/descriptors.py
CHANGED
@@ -91,21 +91,38 @@ def unwrap_func_with_partials(fn: ta.Callable) -> tuple[ta.Callable, list[functo
|
|
91
91
|
##
|
92
92
|
|
93
93
|
|
94
|
-
|
94
|
+
def update_wrapper(
|
95
|
+
wrapper: T,
|
96
|
+
wrapped: ta.Any,
|
97
|
+
assigned: ta.Iterable[str] = functools.WRAPPER_ASSIGNMENTS,
|
98
|
+
updated: ta.Iterable[str] = functools.WRAPPER_UPDATES,
|
99
|
+
*,
|
100
|
+
filter: ta.Iterable[str] | None = None, # noqa
|
101
|
+
getattr: ta.Callable = getattr, # noqa
|
102
|
+
setattr: ta.Callable = setattr, # noqa
|
103
|
+
) -> T:
|
104
|
+
if filter:
|
105
|
+
if isinstance(filter, str):
|
106
|
+
filter = [filter] # noqa
|
107
|
+
assigned = tuple(a for a in assigned if a not in filter)
|
108
|
+
updated = tuple(a for a in updated if a not in filter)
|
109
|
+
|
110
|
+
for attr in assigned:
|
111
|
+
try:
|
112
|
+
value = getattr(wrapped, attr)
|
113
|
+
except AttributeError:
|
114
|
+
pass
|
115
|
+
else:
|
116
|
+
setattr(wrapper, attr, value)
|
117
|
+
|
118
|
+
for attr in updated:
|
119
|
+
getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
|
95
120
|
|
121
|
+
# Issue #17482: set __wrapped__ last so we don't inadvertently copy it from the wrapped function when updating
|
122
|
+
# __dict__
|
123
|
+
setattr(wrapper, '__wrapped__', wrapped)
|
96
124
|
|
97
|
-
|
98
|
-
wrapper,
|
99
|
-
wrapped,
|
100
|
-
assigned=functools.WRAPPER_ASSIGNMENTS,
|
101
|
-
updated=WRAPPER_UPDATES_EXCEPT_DICT,
|
102
|
-
):
|
103
|
-
return functools.update_wrapper(
|
104
|
-
wrapper,
|
105
|
-
wrapped,
|
106
|
-
assigned=assigned,
|
107
|
-
updated=updated,
|
108
|
-
)
|
125
|
+
return wrapper
|
109
126
|
|
110
127
|
|
111
128
|
##
|
@@ -118,7 +135,7 @@ class _decorator_descriptor: # noqa
|
|
118
135
|
if not _DECORATOR_HANDLES_UNBOUND_METHODS:
|
119
136
|
def __init__(self, wrapper, fn):
|
120
137
|
self._wrapper, self._fn = wrapper, fn
|
121
|
-
|
138
|
+
update_wrapper(self, fn, filter='__dict__')
|
122
139
|
|
123
140
|
def __get__(self, instance, owner=None):
|
124
141
|
return functools.update_wrapper(functools.partial(self._wrapper, fn := self._fn.__get__(instance, owner)), fn) # noqa
|
@@ -127,7 +144,7 @@ class _decorator_descriptor: # noqa
|
|
127
144
|
def __init__(self, wrapper, fn):
|
128
145
|
self._wrapper, self._fn = wrapper, fn
|
129
146
|
self._md = _has_method_descriptor(fn)
|
130
|
-
|
147
|
+
update_wrapper(self, fn, filter='__dict__')
|
131
148
|
|
132
149
|
def __get__(self, instance, owner=None):
|
133
150
|
fn = self._fn.__get__(instance, owner)
|
@@ -157,7 +174,7 @@ class _decorator_descriptor: # noqa
|
|
157
174
|
class _decorator: # noqa
|
158
175
|
def __init__(self, wrapper):
|
159
176
|
self._wrapper = wrapper
|
160
|
-
|
177
|
+
update_wrapper(self, wrapper, filter='__dict__')
|
161
178
|
|
162
179
|
def __repr__(self):
|
163
180
|
return f'{self.__class__.__name__}<{self._wrapper}>'
|
omlish/lang/resources.py
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
import functools
|
2
|
+
import importlib.resources
|
3
|
+
import os.path
|
4
|
+
import typing as ta
|
5
|
+
|
6
|
+
|
7
|
+
class RelativeResource(ta.NamedTuple):
|
8
|
+
name: str
|
9
|
+
is_file: bool
|
10
|
+
read_bytes: ta.Callable[[], bytes]
|
11
|
+
|
12
|
+
|
13
|
+
def get_relative_resources(
|
14
|
+
prefix: str | None = None,
|
15
|
+
*,
|
16
|
+
globals: ta.Mapping[str, ta.Any] | None = None, # noqa
|
17
|
+
package: str | None = None,
|
18
|
+
file: str | None = None,
|
19
|
+
) -> ta.Mapping[str, RelativeResource]:
|
20
|
+
if globals is not None:
|
21
|
+
if not package:
|
22
|
+
package = globals.get('__package__')
|
23
|
+
if not file:
|
24
|
+
file = globals.get('__file__')
|
25
|
+
|
26
|
+
lst: list[RelativeResource] = []
|
27
|
+
|
28
|
+
if package:
|
29
|
+
anchor = package
|
30
|
+
if prefix:
|
31
|
+
anchor += '.' + prefix.replace(os.sep, '.')
|
32
|
+
|
33
|
+
for pf in importlib.resources.files(anchor).iterdir():
|
34
|
+
lst.append(RelativeResource(
|
35
|
+
name=pf.name,
|
36
|
+
is_file=pf.is_file(),
|
37
|
+
read_bytes=pf.read_bytes if pf.is_file() else None, # type: ignore
|
38
|
+
))
|
39
|
+
|
40
|
+
elif file:
|
41
|
+
path = os.path.dirname(file)
|
42
|
+
if prefix:
|
43
|
+
path = os.path.join(path, prefix.replace('.', os.sep))
|
44
|
+
|
45
|
+
def _read_file(fp: str) -> bytes:
|
46
|
+
with open(fp, 'rb') as f:
|
47
|
+
return f.read()
|
48
|
+
|
49
|
+
for ff in os.listdir(path):
|
50
|
+
ff = os.path.join(path, ff)
|
51
|
+
lst.append(RelativeResource(
|
52
|
+
name=os.path.basename(ff),
|
53
|
+
is_file=os.path.isfile(ff),
|
54
|
+
read_bytes=functools.partial(_read_file, ff),
|
55
|
+
))
|
56
|
+
|
57
|
+
else:
|
58
|
+
raise RuntimeError('no package or file specified')
|
59
|
+
|
60
|
+
return {r.name: r for r in lst}
|
omlish/lite/logs.py
CHANGED
@@ -97,13 +97,134 @@ class StandardLogFormatter(logging.Formatter):
|
|
97
97
|
##
|
98
98
|
|
99
99
|
|
100
|
+
class ProxyLogFilterer(logging.Filterer):
|
101
|
+
def __init__(self, underlying: logging.Filterer) -> None: # noqa
|
102
|
+
self._underlying = underlying
|
103
|
+
|
104
|
+
@property
|
105
|
+
def underlying(self) -> logging.Filterer:
|
106
|
+
return self._underlying
|
107
|
+
|
108
|
+
@property
|
109
|
+
def filters(self):
|
110
|
+
return self._underlying.filters
|
111
|
+
|
112
|
+
@filters.setter
|
113
|
+
def filters(self, filters):
|
114
|
+
self._underlying.filters = filters
|
115
|
+
|
116
|
+
def addFilter(self, filter): # noqa
|
117
|
+
self._underlying.addFilter(filter)
|
118
|
+
|
119
|
+
def removeFilter(self, filter): # noqa
|
120
|
+
self._underlying.removeFilter(filter)
|
121
|
+
|
122
|
+
def filter(self, record):
|
123
|
+
return self._underlying.filter(record)
|
124
|
+
|
125
|
+
|
126
|
+
class ProxyLogHandler(ProxyLogFilterer, logging.Handler):
|
127
|
+
def __init__(self, underlying: logging.Handler) -> None: # noqa
|
128
|
+
ProxyLogFilterer.__init__(self, underlying)
|
129
|
+
|
130
|
+
_underlying: logging.Handler
|
131
|
+
|
132
|
+
@property
|
133
|
+
def underlying(self) -> logging.Handler:
|
134
|
+
return self._underlying
|
135
|
+
|
136
|
+
def get_name(self):
|
137
|
+
return self._underlying.get_name()
|
138
|
+
|
139
|
+
def set_name(self, name):
|
140
|
+
self._underlying.set_name(name)
|
141
|
+
|
142
|
+
@property
|
143
|
+
def name(self):
|
144
|
+
return self._underlying.name
|
145
|
+
|
146
|
+
@property
|
147
|
+
def level(self):
|
148
|
+
return self._underlying.level
|
149
|
+
|
150
|
+
@level.setter
|
151
|
+
def level(self, level):
|
152
|
+
self._underlying.level = level
|
153
|
+
|
154
|
+
@property
|
155
|
+
def formatter(self):
|
156
|
+
return self._underlying.formatter
|
157
|
+
|
158
|
+
@formatter.setter
|
159
|
+
def formatter(self, formatter):
|
160
|
+
self._underlying.formatter = formatter
|
161
|
+
|
162
|
+
def createLock(self):
|
163
|
+
self._underlying.createLock()
|
164
|
+
|
165
|
+
def acquire(self):
|
166
|
+
self._underlying.acquire()
|
167
|
+
|
168
|
+
def release(self):
|
169
|
+
self._underlying.release()
|
170
|
+
|
171
|
+
def setLevel(self, level):
|
172
|
+
self._underlying.setLevel(level)
|
173
|
+
|
174
|
+
def format(self, record):
|
175
|
+
return self._underlying.format(record)
|
176
|
+
|
177
|
+
def emit(self, record):
|
178
|
+
self._underlying.emit(record)
|
179
|
+
|
180
|
+
def handle(self, record):
|
181
|
+
return self._underlying.handle(record)
|
182
|
+
|
183
|
+
def setFormatter(self, fmt):
|
184
|
+
self._underlying.setFormatter(fmt)
|
185
|
+
|
186
|
+
def flush(self):
|
187
|
+
self._underlying.flush()
|
188
|
+
|
189
|
+
def close(self):
|
190
|
+
self._underlying.close()
|
191
|
+
|
192
|
+
def handleError(self, record):
|
193
|
+
self._underlying.handleError(record)
|
194
|
+
|
195
|
+
|
196
|
+
##
|
197
|
+
|
198
|
+
|
199
|
+
class StandardLogHandler(ProxyLogHandler):
|
200
|
+
pass
|
201
|
+
|
202
|
+
|
203
|
+
##
|
204
|
+
|
205
|
+
|
100
206
|
def configure_standard_logging(
|
101
207
|
level: ta.Union[int, str] = logging.INFO,
|
102
208
|
*,
|
103
209
|
json: bool = False,
|
104
|
-
|
210
|
+
target: ta.Optional[logging.Logger] = None,
|
211
|
+
no_check: bool = False,
|
212
|
+
) -> ta.Optional[StandardLogHandler]:
|
213
|
+
if target is None:
|
214
|
+
target = logging.root
|
215
|
+
|
216
|
+
#
|
217
|
+
|
218
|
+
if not no_check:
|
219
|
+
if any(isinstance(h, StandardLogHandler) for h in list(target.handlers)):
|
220
|
+
return None
|
221
|
+
|
222
|
+
#
|
223
|
+
|
105
224
|
handler = logging.StreamHandler()
|
106
225
|
|
226
|
+
#
|
227
|
+
|
107
228
|
formatter: logging.Formatter
|
108
229
|
if json:
|
109
230
|
formatter = JsonLogFormatter()
|
@@ -111,11 +232,19 @@ def configure_standard_logging(
|
|
111
232
|
formatter = StandardLogFormatter(StandardLogFormatter.build_log_format(STANDARD_LOG_FORMAT_PARTS))
|
112
233
|
handler.setFormatter(formatter)
|
113
234
|
|
235
|
+
#
|
236
|
+
|
114
237
|
handler.addFilter(TidLogFilter())
|
115
238
|
|
116
|
-
|
239
|
+
#
|
240
|
+
|
241
|
+
target.addHandler(handler)
|
242
|
+
|
243
|
+
#
|
117
244
|
|
118
245
|
if level is not None:
|
119
|
-
|
246
|
+
target.setLevel(level)
|
247
|
+
|
248
|
+
#
|
120
249
|
|
121
|
-
return handler
|
250
|
+
return StandardLogHandler(handler)
|
omlish/logs/__init__.py
CHANGED
@@ -4,8 +4,6 @@ from .configs import ( # noqa
|
|
4
4
|
|
5
5
|
from .formatters import ( # noqa
|
6
6
|
ColorLogFormatter,
|
7
|
-
JsonLogFormatter,
|
8
|
-
StandardLogFormatter,
|
9
7
|
)
|
10
8
|
|
11
9
|
from .handlers import ( # noqa
|
@@ -15,3 +13,20 @@ from .handlers import ( # noqa
|
|
15
13
|
from .utils import ( # noqa
|
16
14
|
error_logging,
|
17
15
|
)
|
16
|
+
|
17
|
+
|
18
|
+
##
|
19
|
+
|
20
|
+
|
21
|
+
from ..lite.logs import ( # noqa
|
22
|
+
TidLogFilter,
|
23
|
+
JsonLogFormatter,
|
24
|
+
|
25
|
+
STANDARD_LOG_FORMAT_PARTS,
|
26
|
+
StandardLogFormatter,
|
27
|
+
|
28
|
+
ProxyLogFilterer,
|
29
|
+
ProxyLogHandler,
|
30
|
+
|
31
|
+
StandardLogHandler,
|
32
|
+
)
|
omlish/logs/configs.py
CHANGED
@@ -2,6 +2,7 @@ import dataclasses as dc
|
|
2
2
|
import logging
|
3
3
|
import typing as ta
|
4
4
|
|
5
|
+
from ..lite.logs import StandardLogHandler
|
5
6
|
from ..lite.logs import configure_standard_logging as configure_lite_standard_logging
|
6
7
|
from .noisy import silence_noisy_loggers
|
7
8
|
|
@@ -34,12 +35,23 @@ def configure_standard_logging(
|
|
34
35
|
level: ta.Any = None,
|
35
36
|
*,
|
36
37
|
json: bool = False,
|
37
|
-
|
38
|
+
target: logging.Logger | None = None,
|
39
|
+
no_check: bool = False,
|
40
|
+
) -> StandardLogHandler | None:
|
38
41
|
handler = configure_lite_standard_logging(
|
39
42
|
level,
|
40
43
|
json=json,
|
44
|
+
target=target,
|
45
|
+
no_check=no_check,
|
41
46
|
)
|
42
47
|
|
48
|
+
if handler is None:
|
49
|
+
return None
|
50
|
+
|
51
|
+
#
|
52
|
+
|
43
53
|
silence_noisy_loggers()
|
44
54
|
|
55
|
+
#
|
56
|
+
|
45
57
|
return handler
|
omlish/logs/formatters.py
CHANGED
omlish/marshal/__init__.py
CHANGED
omlish/marshal/helpers.py
CHANGED
@@ -2,11 +2,24 @@ import typing as ta
|
|
2
2
|
|
3
3
|
from .. import dataclasses as dc
|
4
4
|
from .objects import FieldMetadata
|
5
|
+
from .objects import ObjectMetadata
|
5
6
|
|
6
7
|
|
7
8
|
T = ta.TypeVar('T')
|
8
9
|
|
9
10
|
|
11
|
+
def update_field_metadata(**kwargs: ta.Any) -> dc.field_modifier:
|
12
|
+
@dc.field_modifier
|
13
|
+
def inner(f: dc.Field) -> dc.Field:
|
14
|
+
return dc.update_field_metadata(f, {
|
15
|
+
FieldMetadata: dc.replace(
|
16
|
+
f.metadata.get(FieldMetadata, FieldMetadata()),
|
17
|
+
**kwargs,
|
18
|
+
),
|
19
|
+
})
|
20
|
+
return inner
|
21
|
+
|
22
|
+
|
10
23
|
def update_fields_metadata(
|
11
24
|
fields: ta.Iterable[str] | None = None,
|
12
25
|
**kwargs: ta.Any,
|
@@ -20,3 +33,17 @@ def update_fields_metadata(
|
|
20
33
|
})
|
21
34
|
|
22
35
|
return dc.update_fields(inner, fields)
|
36
|
+
|
37
|
+
|
38
|
+
def update_object_metadata(
|
39
|
+
cls: type | None = None,
|
40
|
+
**kwargs: ta.Any,
|
41
|
+
):
|
42
|
+
def inner(cls):
|
43
|
+
return dc.update_class_metadata(cls, ObjectMetadata(**kwargs))
|
44
|
+
|
45
|
+
if cls is not None:
|
46
|
+
inner(cls)
|
47
|
+
return cls
|
48
|
+
else:
|
49
|
+
return inner
|
@@ -37,12 +37,12 @@ class Keywords(lang.Final):
|
|
37
37
|
@cached.property
|
38
38
|
@dc.init
|
39
39
|
def by_type(self) -> ta.Mapping[type[Keyword], Keyword]:
|
40
|
-
return col.
|
40
|
+
return col.make_map_by(type, self.lst, strict=True) # noqa
|
41
41
|
|
42
42
|
@cached.property
|
43
43
|
@dc.init
|
44
44
|
def by_tag(self) -> ta.Mapping[str, Keyword]:
|
45
|
-
return col.
|
45
|
+
return col.make_map_by(operator.attrgetter('tag'), self.lst, strict=True) # noqa
|
46
46
|
|
47
47
|
def __getitem__(self, item: type[KeywordT] | str) -> KeywordT:
|
48
48
|
if isinstance(item, type):
|
@@ -24,7 +24,7 @@ KeywordT = ta.TypeVar('KeywordT', bound=Keyword)
|
|
24
24
|
##
|
25
25
|
|
26
26
|
|
27
|
-
KEYWORD_TYPES_BY_TAG: ta.Mapping[str, type[Keyword]] = col.
|
27
|
+
KEYWORD_TYPES_BY_TAG: ta.Mapping[str, type[Keyword]] = col.make_map_by( # noqa
|
28
28
|
operator.attrgetter('tag'),
|
29
29
|
(cls for cls in lang.deep_subclasses(Keyword) if not lang.is_abstract_class(cls)),
|
30
30
|
strict=True,
|
omlish/stats.py
CHANGED