omdev 0.0.0.dev392__py3-none-any.whl → 0.0.0.dev394__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.
- omdev/.manifests.json +11 -0
- omdev/cli/main.py +5 -2
- omdev/manifests/__init__.py +0 -1
- omdev/manifests/_dumping.py +1586 -0
- omdev/manifests/{build.py → building.py} +142 -56
- omdev/manifests/dumping.py +109 -73
- omdev/manifests/main.py +11 -10
- omdev/precheck/manifests.py +1 -1
- omdev/tools/git/messages.py +2 -2
- {omdev-0.0.0.dev392.dist-info → omdev-0.0.0.dev394.dist-info}/METADATA +2 -2
- {omdev-0.0.0.dev392.dist-info → omdev-0.0.0.dev394.dist-info}/RECORD +15 -14
- {omdev-0.0.0.dev392.dist-info → omdev-0.0.0.dev394.dist-info}/WHEEL +0 -0
- {omdev-0.0.0.dev392.dist-info → omdev-0.0.0.dev394.dist-info}/entry_points.txt +0 -0
- {omdev-0.0.0.dev392.dist-info → omdev-0.0.0.dev394.dist-info}/licenses/LICENSE +0 -0
- {omdev-0.0.0.dev392.dist-info → omdev-0.0.0.dev394.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,3 @@
|
|
1
|
-
# ruff: noqa: UP006 UP007 UP045
|
2
1
|
"""
|
3
2
|
TODO:
|
4
3
|
- verify classes instantiate
|
@@ -6,6 +5,10 @@ TODO:
|
|
6
5
|
- roundtrip flexibility regarding json-ness - tuples vs lists vs sets vs frozensets etc
|
7
6
|
- kill _MANIFEST_GLOBAL_PATS lol, ast walk
|
8
7
|
- garbage skip_pat doesn't handle multiline decos, etc
|
8
|
+
- relative paths
|
9
|
+
- is this lite? or not?
|
10
|
+
- can this run externally? or not? what does it have to import?
|
11
|
+
- has to import manifest classes, but not modules with manifest magics
|
9
12
|
|
10
13
|
See (entry_points):
|
11
14
|
- https://github.com/pytest-dev/pluggy/blob/main/src/pluggy/_manager.py#L405
|
@@ -34,16 +37,17 @@ from omlish.lite.imports import import_attr
|
|
34
37
|
from omlish.lite.json import json_dumps_pretty
|
35
38
|
from omlish.lite.logs import log
|
36
39
|
from omlish.manifests.base import ModAttrManifest
|
37
|
-
from omlish.manifests.
|
40
|
+
from omlish.manifests.globals import MANIFEST_LOADER
|
38
41
|
from omlish.manifests.types import Manifest
|
39
42
|
from omlish.manifests.types import ManifestOrigin
|
40
43
|
|
41
44
|
from .. import magic
|
42
|
-
from .dumping import _ModuleManifestDumper
|
43
45
|
|
44
46
|
|
45
47
|
T = ta.TypeVar('T')
|
46
48
|
|
49
|
+
ManifestDumperTarget = ta.Union['InlineManifestDumperTarget', 'AttrManifestDumperTarget'] # ta.TypeAlias
|
50
|
+
|
47
51
|
|
48
52
|
##
|
49
53
|
|
@@ -58,7 +62,7 @@ _NAME_PAT_PART = rf'(?P<name>{_IDENT_PAT_PART})'
|
|
58
62
|
@dc.dataclass(frozen=True)
|
59
63
|
class _ManifestGlobalPat:
|
60
64
|
name_pat: re.Pattern
|
61
|
-
skip_pat:
|
65
|
+
skip_pat: re.Pattern | None = None
|
62
66
|
|
63
67
|
|
64
68
|
_MANIFEST_GLOBAL_PATS: ta.Sequence[_ManifestGlobalPat] = [
|
@@ -87,45 +91,57 @@ _INLINE_MANIFEST_CLS_NAME_PAT = re.compile(r'^(?P<cls_name>[_a-zA-Z][_a-zA-Z0-9.
|
|
87
91
|
##
|
88
92
|
|
89
93
|
|
94
|
+
class InlineManifestDumperTarget(ta.TypedDict):
|
95
|
+
origin: ta.Mapping[str, ta.Any]
|
96
|
+
kind: ta.Literal['inline']
|
97
|
+
cls_mod_name: str
|
98
|
+
cls_qualname: str
|
99
|
+
init_src: str
|
100
|
+
kwargs: ta.Mapping[str, ta.Any]
|
101
|
+
|
102
|
+
|
103
|
+
class AttrManifestDumperTarget(ta.TypedDict):
|
104
|
+
origin: ta.Mapping[str, ta.Any]
|
105
|
+
kind: ta.Literal['attr']
|
106
|
+
attr: str
|
107
|
+
|
108
|
+
|
109
|
+
##
|
110
|
+
|
111
|
+
|
90
112
|
@cached_nullary
|
91
|
-
def
|
92
|
-
|
113
|
+
def _module_manifest_dumper_payload_src() -> str:
|
114
|
+
from . import _dumping
|
115
|
+
return inspect.getsource(_dumping)
|
93
116
|
|
94
117
|
|
95
118
|
class ManifestBuilder:
|
96
119
|
def __init__(
|
97
120
|
self,
|
98
|
-
|
121
|
+
base_dir: str,
|
99
122
|
concurrency: int = 8,
|
100
123
|
*,
|
101
|
-
|
124
|
+
subprocess_kwargs: ta.Mapping[str, ta.Any] | None = None,
|
125
|
+
module_dumper_payload_src: str | None = None,
|
102
126
|
) -> None:
|
103
127
|
super().__init__()
|
104
128
|
|
105
|
-
self.
|
129
|
+
self._base_dir = base_dir
|
130
|
+
self._subprocess_kwargs = subprocess_kwargs
|
131
|
+
self._module_dumper_payload_src = module_dumper_payload_src
|
132
|
+
|
106
133
|
self._sem = asyncio.Semaphore(concurrency)
|
107
|
-
self._write = write
|
108
134
|
|
109
|
-
|
110
|
-
await self._sem.acquire()
|
111
|
-
try:
|
112
|
-
try:
|
113
|
-
return await fn(*args, **kwargs)
|
114
|
-
except Exception: # noqa
|
115
|
-
log.exception('Exception in task: %s, %r, %r', fn, args, kwargs)
|
116
|
-
raise
|
117
|
-
finally:
|
118
|
-
self._sem.release()
|
135
|
+
#
|
119
136
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
) -> ta.Sequence[Manifest]:
|
127
|
-
log.info('Extracting manifests from file %s', file)
|
137
|
+
@dc.dataclass(frozen=True)
|
138
|
+
class FileModule:
|
139
|
+
file: str
|
140
|
+
|
141
|
+
mod_name: str
|
142
|
+
mod_base: str
|
128
143
|
|
144
|
+
def build_file_module(self, file: str) -> FileModule:
|
129
145
|
if not file.endswith('.py'):
|
130
146
|
raise Exception(file)
|
131
147
|
|
@@ -134,31 +150,41 @@ class ManifestBuilder:
|
|
134
150
|
if mod_base != (first_dir := file.split(os.path.sep)[0]):
|
135
151
|
raise Exception(f'Unexpected module base: {mod_base=} != {first_dir=}')
|
136
152
|
|
137
|
-
|
153
|
+
return ManifestBuilder.FileModule(
|
154
|
+
file=file,
|
155
|
+
|
156
|
+
mod_name=mod_name,
|
157
|
+
mod_base=mod_base,
|
158
|
+
)
|
159
|
+
|
160
|
+
#
|
161
|
+
|
162
|
+
def collect_module_manifest_targets(self, fm: FileModule) -> list[ManifestDumperTarget]:
|
163
|
+
with open(os.path.join(self._base_dir, fm.file)) as f: # noqa
|
138
164
|
src = f.read()
|
139
165
|
|
140
166
|
lines = src.splitlines(keepends=True)
|
141
167
|
|
142
168
|
def prepare(s: str) -> ta.Any:
|
143
169
|
if s.startswith('$.'):
|
144
|
-
s = f'{mod_base}.{s[2:]}'
|
170
|
+
s = f'{fm.mod_base}.{s[2:]}'
|
145
171
|
return magic.py_compile_magic_preparer(s)
|
146
172
|
|
147
173
|
magics = magic.find_magic(
|
148
174
|
magic.PY_MAGIC_STYLE,
|
149
175
|
lines,
|
150
|
-
file=file,
|
176
|
+
file=fm.file,
|
151
177
|
keys={MANIFEST_MAGIC_KEY},
|
152
178
|
preparer=prepare,
|
153
179
|
)
|
154
180
|
|
155
|
-
origins:
|
156
|
-
targets:
|
181
|
+
origins: list[ManifestOrigin] = []
|
182
|
+
targets: list[ManifestDumperTarget] = []
|
157
183
|
for m in magics:
|
158
184
|
if m.body:
|
159
185
|
body = m.body
|
160
186
|
if body.startswith('$.'):
|
161
|
-
body = f'{mod_base}.{body[2:]}'
|
187
|
+
body = f'{fm.mod_base}.{body[2:]}'
|
162
188
|
|
163
189
|
pat_match = check.not_none(_INLINE_MANIFEST_CLS_NAME_PAT.match(body))
|
164
190
|
cls_name = check.non_empty_str(pat_match.groupdict()['cls_name'])
|
@@ -184,15 +210,15 @@ class ManifestBuilder:
|
|
184
210
|
if issubclass(cls, ModAttrManifest):
|
185
211
|
attr_name = extract_manifest_target_name(lines, m.end_line)
|
186
212
|
inl_kw.update({
|
187
|
-
'mod_name': mod_name,
|
213
|
+
'mod_name': fm.mod_name,
|
188
214
|
'attr_name': attr_name,
|
189
215
|
})
|
190
216
|
|
191
217
|
origin = ManifestOrigin(
|
192
|
-
module='.'.join(['', *mod_name.split('.')[1:]]),
|
218
|
+
module='.'.join(['', *fm.mod_name.split('.')[1:]]),
|
193
219
|
attr=None,
|
194
220
|
|
195
|
-
file=file,
|
221
|
+
file=fm.file,
|
196
222
|
line=m.start_line,
|
197
223
|
)
|
198
224
|
|
@@ -210,10 +236,10 @@ class ManifestBuilder:
|
|
210
236
|
attr_name = extract_manifest_target_name(lines, m.end_line)
|
211
237
|
|
212
238
|
origin = ManifestOrigin(
|
213
|
-
module='.'.join(['', *mod_name.split('.')[1:]]),
|
239
|
+
module='.'.join(['', *fm.mod_name.split('.')[1:]]),
|
214
240
|
attr=attr_name,
|
215
241
|
|
216
|
-
file=file,
|
242
|
+
file=fm.file,
|
217
243
|
line=m.start_line,
|
218
244
|
)
|
219
245
|
|
@@ -230,9 +256,27 @@ class ManifestBuilder:
|
|
230
256
|
if (dups := [k for k, v in collections.Counter(o.attr for o in origins if o.attr is not None).items() if v > 1]): # noqa
|
231
257
|
raise Exception(f'Duplicate attrs: {dups}')
|
232
258
|
|
259
|
+
return targets
|
260
|
+
|
261
|
+
#
|
262
|
+
|
263
|
+
async def _dump_module_manifests(
|
264
|
+
self,
|
265
|
+
fm: FileModule,
|
266
|
+
targets: ta.Sequence[ManifestDumperTarget],
|
267
|
+
*,
|
268
|
+
shell_wrap: bool = True,
|
269
|
+
warn_threshold_s: float | None = 1.,
|
270
|
+
):
|
271
|
+
dumper_payload_src: str
|
272
|
+
if self._module_dumper_payload_src is not None:
|
273
|
+
dumper_payload_src = self._module_dumper_payload_src
|
274
|
+
else:
|
275
|
+
dumper_payload_src = _module_manifest_dumper_payload_src()
|
276
|
+
|
233
277
|
subproc_src = '\n\n'.join([
|
234
|
-
|
235
|
-
f'_ModuleManifestDumper({mod_name!r})({", ".join(repr(tgt) for tgt in targets)})\n',
|
278
|
+
dumper_payload_src,
|
279
|
+
f'_ModuleManifestDumper({fm.mod_name!r})({", ".join(repr(tgt) for tgt in targets)})\n',
|
236
280
|
])
|
237
281
|
|
238
282
|
args = [
|
@@ -246,7 +290,12 @@ class ManifestBuilder:
|
|
246
290
|
|
247
291
|
start_time = time.time()
|
248
292
|
|
249
|
-
proc = await asyncio.create_subprocess_exec(
|
293
|
+
proc = await asyncio.create_subprocess_exec(
|
294
|
+
*args,
|
295
|
+
stdout=subprocess.PIPE,
|
296
|
+
**(self._subprocess_kwargs or {}),
|
297
|
+
)
|
298
|
+
|
250
299
|
subproc_out, _ = await proc.communicate()
|
251
300
|
if proc.returncode:
|
252
301
|
raise Exception('Subprocess failed')
|
@@ -254,18 +303,21 @@ class ManifestBuilder:
|
|
254
303
|
end_time = time.time()
|
255
304
|
|
256
305
|
if warn_threshold_s is not None and (elapsed_time := (end_time - start_time)) >= warn_threshold_s:
|
257
|
-
log.warning('Manifest extraction took a long time: %s, %.2f s', file, elapsed_time)
|
306
|
+
log.warning('Manifest extraction took a long time: %s, %.2f s', fm.file, elapsed_time)
|
258
307
|
|
259
308
|
sp_lines = subproc_out.decode().strip().splitlines()
|
260
309
|
if len(sp_lines) != 1:
|
261
310
|
raise Exception('Unexpected subprocess output')
|
262
311
|
|
263
312
|
sp_outs = json.loads(sp_lines[0])
|
264
|
-
|
265
|
-
# if set(dct) != set(attrs):
|
266
|
-
# raise Exception('Unexpected subprocess output keys')
|
313
|
+
return sp_outs
|
267
314
|
|
268
|
-
|
315
|
+
def _process_module_dump_output(
|
316
|
+
self,
|
317
|
+
fm: FileModule,
|
318
|
+
sp_outs: ta.Sequence[ta.Mapping[str, ta.Any]],
|
319
|
+
) -> list[Manifest]:
|
320
|
+
out: list[Manifest] = []
|
269
321
|
for sp_out in sp_outs:
|
270
322
|
value = sp_out['value']
|
271
323
|
|
@@ -278,7 +330,7 @@ class ManifestBuilder:
|
|
278
330
|
|
279
331
|
[(key, value_dct)] = value.items()
|
280
332
|
kb, _, kr = key[1:].partition('.') # noqa
|
281
|
-
if kb == mod_base: # noqa
|
333
|
+
if kb == fm.mod_base: # noqa
|
282
334
|
key = f'$.{kr}'
|
283
335
|
value = {key: value_dct}
|
284
336
|
|
@@ -289,11 +341,45 @@ class ManifestBuilder:
|
|
289
341
|
|
290
342
|
return out
|
291
343
|
|
344
|
+
#
|
345
|
+
|
346
|
+
async def build_module_manifests(self, file: str) -> ta.Sequence[Manifest]:
|
347
|
+
log.info('Extracting manifests from file %s', file)
|
348
|
+
|
349
|
+
fm = self.build_file_module(file)
|
350
|
+
|
351
|
+
targets = self.collect_module_manifest_targets(fm)
|
352
|
+
|
353
|
+
sp_outs = await self._dump_module_manifests(fm, targets)
|
354
|
+
|
355
|
+
# FIXME:
|
356
|
+
# if set(dct) != set(attrs):
|
357
|
+
# raise Exception('Unexpected subprocess output keys')
|
358
|
+
|
359
|
+
out = self._process_module_dump_output(fm, sp_outs)
|
360
|
+
|
361
|
+
return out
|
362
|
+
|
363
|
+
#
|
364
|
+
|
365
|
+
async def _spawn(self, fn: ta.Callable[..., ta.Awaitable[T]], *args: ta.Any, **kwargs: ta.Any) -> T:
|
366
|
+
await self._sem.acquire()
|
367
|
+
try:
|
368
|
+
try:
|
369
|
+
return await fn(*args, **kwargs)
|
370
|
+
except Exception: # noqa
|
371
|
+
log.exception('Exception in task: %s, %r, %r', fn, args, kwargs)
|
372
|
+
raise
|
373
|
+
finally:
|
374
|
+
self._sem.release()
|
375
|
+
|
292
376
|
async def build_package_manifests(
|
293
377
|
self,
|
294
378
|
name: str,
|
295
|
-
|
296
|
-
|
379
|
+
*,
|
380
|
+
write: bool = False,
|
381
|
+
) -> list[Manifest]:
|
382
|
+
pkg_dir = os.path.join(self._base_dir, name)
|
297
383
|
if not os.path.isdir(pkg_dir) or not os.path.isfile(os.path.join(pkg_dir, '__init__.py')):
|
298
384
|
raise Exception(pkg_dir)
|
299
385
|
|
@@ -302,15 +388,15 @@ class ManifestBuilder:
|
|
302
388
|
[pkg_dir],
|
303
389
|
keys=[MANIFEST_MAGIC_KEY],
|
304
390
|
))
|
305
|
-
manifests:
|
391
|
+
manifests: list[Manifest] = list(itertools.chain.from_iterable(await asyncio.gather(*[
|
306
392
|
self._spawn(
|
307
393
|
self.build_module_manifests,
|
308
|
-
os.path.relpath(file, self.
|
394
|
+
os.path.relpath(file, self._base_dir),
|
309
395
|
)
|
310
396
|
for file in files
|
311
397
|
])))
|
312
398
|
|
313
|
-
if
|
399
|
+
if write:
|
314
400
|
with open(os.path.join(pkg_dir, '.manifests.json'), 'w') as f: # noqa
|
315
401
|
f.write(json_dumps_pretty([dc.asdict(m) for m in manifests]))
|
316
402
|
f.write('\n')
|
@@ -323,9 +409,9 @@ class ManifestBuilder:
|
|
323
409
|
|
324
410
|
def check_package_manifests(
|
325
411
|
name: str,
|
326
|
-
|
412
|
+
base_dir: str,
|
327
413
|
) -> None:
|
328
|
-
pkg_dir = os.path.join(
|
414
|
+
pkg_dir = os.path.join(base_dir, name)
|
329
415
|
if not os.path.isdir(pkg_dir) or not os.path.isfile(os.path.join(pkg_dir, '__init__.py')):
|
330
416
|
raise Exception(pkg_dir)
|
331
417
|
|
@@ -342,4 +428,4 @@ def check_package_manifests(
|
|
342
428
|
if key.startswith('$.'):
|
343
429
|
key = f'${name}{key[1:]}'
|
344
430
|
cls = MANIFEST_LOADER.load_cls(key)
|
345
|
-
value = cls
|
431
|
+
value = MANIFEST_LOADER.instantiate_cls(cls, **value_dct) # noqa
|
omdev/manifests/dumping.py
CHANGED
@@ -1,30 +1,99 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007 UP037 UP045
|
2
|
+
# @omlish-lite
|
3
|
+
# @omlish-amalg _dumping.py
|
4
|
+
import collections.abc
|
5
|
+
import dataclasses as dc
|
6
|
+
import functools
|
7
|
+
import importlib
|
8
|
+
import json
|
2
9
|
import typing as ta
|
3
10
|
|
11
|
+
from omlish.lite.cached import cached_nullary
|
12
|
+
from omlish.lite.marshal import marshal_obj
|
13
|
+
from omlish.lite.marshal import unmarshal_obj
|
14
|
+
|
4
15
|
|
5
16
|
##
|
6
17
|
|
7
18
|
|
8
19
|
class _ModuleManifestDumper:
|
9
|
-
def __init__(
|
20
|
+
def __init__(
|
21
|
+
self,
|
22
|
+
spec: str,
|
23
|
+
*,
|
24
|
+
output: ta.Optional[ta.Callable[[str], None]] = None,
|
25
|
+
) -> None:
|
10
26
|
super().__init__()
|
11
27
|
|
12
28
|
self._spec = spec
|
13
|
-
|
29
|
+
if output is None:
|
30
|
+
output = print
|
31
|
+
self._output = output
|
32
|
+
|
33
|
+
#
|
34
|
+
|
35
|
+
@cached_nullary
|
36
|
+
def _mod(self) -> ta.Any:
|
37
|
+
return importlib.import_module(self._spec)
|
38
|
+
|
39
|
+
#
|
40
|
+
|
41
|
+
def _build_manifest_dct(self, manifest: ta.Any) -> ta.Mapping[str, ta.Any]:
|
42
|
+
manifest_json = json.dumps(marshal_obj(manifest))
|
43
|
+
manifest_dct = json.loads(manifest_json)
|
44
|
+
|
45
|
+
rt_manifest: ta.Any = unmarshal_obj(manifest_dct, type(manifest))
|
46
|
+
rt_manifest_json: ta.Any = json.dumps(marshal_obj(rt_manifest))
|
47
|
+
rt_manifest_dct: ta.Any = json.loads(rt_manifest_json)
|
48
|
+
if rt_manifest_dct != manifest_dct:
|
49
|
+
raise Exception(
|
50
|
+
f'Manifest failed to roundtrip: '
|
51
|
+
f'{manifest} => {manifest_dct} != {rt_manifest} => {rt_manifest_dct}',
|
52
|
+
)
|
53
|
+
|
54
|
+
return manifest_dct
|
55
|
+
|
56
|
+
#
|
57
|
+
|
58
|
+
def _load_attr_manifest(self, target: dict) -> dict:
|
59
|
+
attr = target['attr']
|
60
|
+
manifest = getattr(self._mod(), attr)
|
61
|
+
|
62
|
+
if dc.is_dataclass(manifest):
|
63
|
+
# Support static dataclasses
|
64
|
+
if isinstance(manifest, type):
|
65
|
+
manifest = manifest()
|
66
|
+
|
67
|
+
manifest_dct = self._build_manifest_dct(manifest)
|
68
|
+
|
69
|
+
cls = type(manifest)
|
70
|
+
key = f'${cls.__module__}.{cls.__qualname__}'
|
71
|
+
|
72
|
+
return {key: manifest_dct}
|
73
|
+
|
74
|
+
elif isinstance(manifest, collections.abc.Mapping):
|
75
|
+
[(key, manifest_dct)] = manifest.items()
|
76
|
+
if not key.startswith('$'): # noqa
|
77
|
+
raise Exception(f'Bad key: {key}')
|
14
78
|
|
15
|
-
|
16
|
-
|
17
|
-
return mod
|
79
|
+
if not isinstance(manifest_dct, collections.abc.Mapping):
|
80
|
+
raise Exception(f'Bad value: {manifest_dct}')
|
18
81
|
|
19
|
-
|
82
|
+
manifest_json = json.dumps(manifest_dct)
|
20
83
|
|
21
|
-
|
84
|
+
rt_manifest_dct = json.loads(manifest_json)
|
85
|
+
if rt_manifest_dct != manifest_dct:
|
86
|
+
raise Exception(f'Manifest failed to roundtrip: {manifest_dct} != {rt_manifest_dct}')
|
22
87
|
|
23
|
-
|
24
|
-
|
88
|
+
return {key: manifest_dct}
|
89
|
+
|
90
|
+
else:
|
91
|
+
raise TypeError(f'Manifest must be dataclass or mapping: {manifest!r}')
|
92
|
+
|
93
|
+
#
|
25
94
|
|
26
95
|
class _LazyGlobals(dict):
|
27
|
-
def __init__(self, get_missing:
|
96
|
+
def __init__(self, get_missing: ta.Callable[[str], ta.Any]) -> None:
|
28
97
|
super().__init__()
|
29
98
|
|
30
99
|
self.__get_missing = get_missing
|
@@ -32,79 +101,46 @@ class _ModuleManifestDumper:
|
|
32
101
|
def __missing__(self, key):
|
33
102
|
return self.__get_missing(key)
|
34
103
|
|
35
|
-
def
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
104
|
+
def _load_inline_manifest(self, target: dict) -> dict:
|
105
|
+
cls: ta.Any = importlib.import_module(target['cls_mod_name'])
|
106
|
+
for p in target['cls_qualname'].split('.'):
|
107
|
+
cls = getattr(cls, p)
|
108
|
+
if not isinstance(cls, type) or not dc.is_dataclass(cls):
|
109
|
+
raise TypeError(cls)
|
41
110
|
|
42
|
-
cls
|
111
|
+
cls_fac = functools.partial(cls, **target['kwargs'])
|
112
|
+
eval_attr_name = '__manifest_factory__'
|
43
113
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
if target['kind'] == 'attr':
|
49
|
-
attr = target['attr']
|
50
|
-
manifest = getattr(self._mod(), attr)
|
114
|
+
inl_glo = self._LazyGlobals(lambda k: getattr(self._mod(), k))
|
115
|
+
inl_glo.update({
|
116
|
+
eval_attr_name: cls_fac,
|
117
|
+
})
|
51
118
|
|
52
|
-
|
53
|
-
|
54
|
-
if isinstance(manifest, type):
|
55
|
-
manifest = manifest()
|
119
|
+
inl_src = eval_attr_name + target['init_src']
|
120
|
+
inl_code = compile(inl_src, '<magic>', 'eval')
|
56
121
|
|
57
|
-
|
58
|
-
manifest_json = json.dumps(dc.asdict(manifest))
|
59
|
-
manifest_dct = json.loads(manifest_json)
|
122
|
+
manifest = eval(inl_code, inl_glo) # noqa
|
60
123
|
|
61
|
-
|
62
|
-
if rt_manifest != manifest:
|
63
|
-
raise Exception(f'Manifest failed to roundtrip: {manifest} => {manifest_dct} != {rt_manifest}')
|
124
|
+
manifest_dct = self._build_manifest_dct(manifest)
|
64
125
|
|
65
|
-
|
66
|
-
|
126
|
+
key = f'${cls.__module__}.{cls.__qualname__}'
|
127
|
+
return {key: manifest_dct}
|
67
128
|
|
68
|
-
|
69
|
-
[(key, manifest_dct)] = manifest.items()
|
70
|
-
if not key.startswith('$'): # noqa
|
71
|
-
raise Exception(f'Bad key: {key}')
|
129
|
+
#
|
72
130
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
out_value = {key: manifest_dct}
|
131
|
+
def __call__(
|
132
|
+
self,
|
133
|
+
*targets: dict, # .build.ManifestDumperTarget
|
134
|
+
) -> None:
|
135
|
+
out = []
|
136
|
+
for target in targets:
|
137
|
+
origin = target['origin']
|
82
138
|
|
83
|
-
|
84
|
-
|
139
|
+
if target['kind'] == 'attr':
|
140
|
+
out_value = self._load_attr_manifest(target)
|
85
141
|
|
86
142
|
elif target['kind'] == 'inline':
|
87
|
-
|
88
|
-
for p in target['cls_qualname'].split('.'):
|
89
|
-
cls = getattr(cls, p)
|
90
|
-
if not isinstance(cls, type) or not dc.is_dataclass(cls):
|
91
|
-
raise TypeError(cls)
|
92
|
-
|
93
|
-
cls_fac = functools.partial(cls, **target['kwargs'])
|
94
|
-
eval_attr_name = '__manifest_factory__'
|
95
|
-
|
96
|
-
inl_glo = self._LazyGlobals(lambda k: getattr(self._mod(), k))
|
97
|
-
inl_glo.update({
|
98
|
-
eval_attr_name: cls_fac,
|
99
|
-
})
|
100
|
-
|
101
|
-
inl_src = eval_attr_name + target['init_src']
|
102
|
-
inl_code = compile(inl_src, '<magic>', 'eval')
|
103
|
-
manifest = eval(inl_code, inl_glo) # noqa
|
104
|
-
manifest_json = json.dumps(dc.asdict(manifest))
|
105
|
-
manifest_dct = json.loads(manifest_json)
|
106
|
-
key = f'${cls.__module__}.{cls.__qualname__}'
|
107
|
-
out_value = {key: manifest_dct}
|
143
|
+
out_value = self._load_inline_manifest(target)
|
108
144
|
|
109
145
|
else:
|
110
146
|
raise ValueError(target)
|
@@ -115,4 +151,4 @@ class _ModuleManifestDumper:
|
|
115
151
|
})
|
116
152
|
|
117
153
|
out_json = json.dumps(out, indent=None, separators=(',', ':'))
|
118
|
-
|
154
|
+
self._output(out_json)
|
omdev/manifests/main.py
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
# ruff: noqa: UP006 UP007 UP045
|
2
1
|
import argparse
|
3
2
|
import asyncio
|
4
3
|
import dataclasses as dc
|
@@ -8,14 +7,14 @@ import os.path
|
|
8
7
|
from omlish.lite.json import json_dumps_pretty
|
9
8
|
from omlish.logs.standard import configure_standard_logging
|
10
9
|
|
11
|
-
from .
|
12
|
-
from .
|
10
|
+
from .building import ManifestBuilder
|
11
|
+
from .building import check_package_manifests
|
13
12
|
|
14
13
|
|
15
14
|
##
|
16
15
|
|
17
16
|
|
18
|
-
def
|
17
|
+
def _get_base_dir(args) -> str:
|
19
18
|
if args.base is not None:
|
20
19
|
base = args.base
|
21
20
|
else:
|
@@ -27,18 +26,20 @@ def _get_base(args) -> str:
|
|
27
26
|
|
28
27
|
|
29
28
|
def _gen_cmd(args) -> None:
|
30
|
-
|
29
|
+
base_dir = _get_base_dir(args)
|
31
30
|
|
32
31
|
jobs = args.jobs or int(max(mp.cpu_count() // 1.5, 1))
|
33
32
|
builder = ManifestBuilder(
|
34
|
-
|
33
|
+
base_dir,
|
35
34
|
jobs,
|
36
|
-
write=args.write or False,
|
37
35
|
)
|
38
36
|
|
39
37
|
async def do():
|
40
38
|
return await asyncio.gather(*[
|
41
|
-
builder.build_package_manifests(
|
39
|
+
builder.build_package_manifests(
|
40
|
+
pkg,
|
41
|
+
write=bool(args.write),
|
42
|
+
)
|
42
43
|
for pkg in args.package
|
43
44
|
])
|
44
45
|
|
@@ -49,12 +50,12 @@ def _gen_cmd(args) -> None:
|
|
49
50
|
|
50
51
|
|
51
52
|
def _check_cmd(args) -> None:
|
52
|
-
|
53
|
+
base_dir = _get_base_dir(args)
|
53
54
|
|
54
55
|
for pkg in args.package:
|
55
56
|
check_package_manifests(
|
56
57
|
pkg,
|
57
|
-
|
58
|
+
base_dir,
|
58
59
|
)
|
59
60
|
|
60
61
|
|
omdev/precheck/manifests.py
CHANGED
omdev/tools/git/messages.py
CHANGED
@@ -6,7 +6,7 @@ from omlish import cached
|
|
6
6
|
from omlish import check
|
7
7
|
from omlish import dataclasses as dc
|
8
8
|
from omlish import lang
|
9
|
-
from omlish.manifests import
|
9
|
+
from omlish.manifests import globals as manifest_globals
|
10
10
|
from omlish.manifests.base import ModAttrManifest
|
11
11
|
from omlish.manifests.base import NameAliasesManifest
|
12
12
|
from omlish.manifests.static import StaticModAttrManifest
|
@@ -54,7 +54,7 @@ class StaticGitMessageGeneratorManifest(StaticModAttrManifest, GitMessageGenerat
|
|
54
54
|
|
55
55
|
@cached.function
|
56
56
|
def load_message_generator_manifests() -> ta.Sequence[GitMessageGeneratorManifest]:
|
57
|
-
ldr =
|
57
|
+
ldr = manifest_globals.MANIFEST_LOADER
|
58
58
|
pkgs = ldr.scan_or_discover_pkgs(fallback_root=os.getcwd())
|
59
59
|
mfs = ldr.load(*pkgs, only=[GitMessageGeneratorManifest])
|
60
60
|
return [mf.value for mf in mfs]
|