omdev 0.0.0.dev30__py3-none-any.whl → 0.0.0.dev32__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 +122 -1
- omdev/__about__.py +9 -0
- omdev/amalg/__main__.py +7 -0
- omdev/amalg/amalg.py +4 -4
- omdev/cexts/_boilerplate.cc +1 -1
- omdev/cexts/cmake.py +5 -0
- omdev/cexts/magic.py +1 -1
- omdev/classdot.py +6 -0
- omdev/cli/__init__.py +1 -0
- omdev/cli/__main__.py +4 -0
- omdev/cli/main.py +59 -0
- omdev/cli/types.py +7 -0
- omdev/interp/__main__.py +7 -0
- omdev/interp/cli.py +13 -5
- omdev/interp/pyenv.py +53 -4
- omdev/manifests/__init__.py +2 -0
- omdev/{manifests.py → manifests/build.py} +97 -34
- omdev/manifests/load.py +149 -0
- omdev/manifests/types.py +16 -0
- omdev/precheck/precheck.py +6 -0
- omdev/pyproject/__main__.py +7 -0
- omdev/pyproject/cli.py +2 -2
- omdev/pyproject/pkg.py +13 -2
- omdev/pyproject/reqs.py +1 -1
- omdev/scripts/interp.py +64 -9
- omdev/scripts/pyproject.py +77 -11
- omdev/secrets.py +12 -0
- omdev/toml/writer.py +8 -1
- omdev/tools/dockertools.py +6 -0
- omdev/tools/gittools.py +6 -0
- omdev/tools/piptools.py +6 -0
- omdev/tools/sqlrepl.py +6 -0
- {omdev-0.0.0.dev30.dist-info → omdev-0.0.0.dev32.dist-info}/METADATA +2 -2
- {omdev-0.0.0.dev30.dist-info → omdev-0.0.0.dev32.dist-info}/RECORD +38 -29
- omdev-0.0.0.dev32.dist-info/entry_points.txt +2 -0
- {omdev-0.0.0.dev30.dist-info → omdev-0.0.0.dev32.dist-info}/LICENSE +0 -0
- {omdev-0.0.0.dev30.dist-info → omdev-0.0.0.dev32.dist-info}/WHEEL +0 -0
- {omdev-0.0.0.dev30.dist-info → omdev-0.0.0.dev32.dist-info}/top_level.txt +0 -0
omdev/.manifests.json
CHANGED
|
@@ -1 +1,122 @@
|
|
|
1
|
-
[
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"module": ".classdot",
|
|
4
|
+
"attr": "_CLI_MODULE",
|
|
5
|
+
"file": "omdev/classdot.py",
|
|
6
|
+
"line": 62,
|
|
7
|
+
"value": {
|
|
8
|
+
"$.cli.types.CliModule": {
|
|
9
|
+
"cmd_name": "classdot",
|
|
10
|
+
"mod_name": "omdev.classdot"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"module": ".tools.piptools",
|
|
16
|
+
"attr": "_CLI_MODULE",
|
|
17
|
+
"file": "omdev/tools/piptools.py",
|
|
18
|
+
"line": 51,
|
|
19
|
+
"value": {
|
|
20
|
+
"$.cli.types.CliModule": {
|
|
21
|
+
"cmd_name": "pip",
|
|
22
|
+
"mod_name": "omdev.tools.piptools"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"module": ".tools.gittools",
|
|
28
|
+
"attr": "_CLI_MODULE",
|
|
29
|
+
"file": "omdev/tools/gittools.py",
|
|
30
|
+
"line": 22,
|
|
31
|
+
"value": {
|
|
32
|
+
"$.cli.types.CliModule": {
|
|
33
|
+
"cmd_name": "git",
|
|
34
|
+
"mod_name": "omdev.tools.gittools"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"module": ".tools.dockertools",
|
|
40
|
+
"attr": "_CLI_MODULE",
|
|
41
|
+
"file": "omdev/tools/dockertools.py",
|
|
42
|
+
"line": 183,
|
|
43
|
+
"value": {
|
|
44
|
+
"$.cli.types.CliModule": {
|
|
45
|
+
"cmd_name": "docker",
|
|
46
|
+
"mod_name": "omdev.tools.dockertools"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"module": ".tools.sqlrepl",
|
|
52
|
+
"attr": "_CLI_MODULE",
|
|
53
|
+
"file": "omdev/tools/sqlrepl.py",
|
|
54
|
+
"line": 193,
|
|
55
|
+
"value": {
|
|
56
|
+
"$.cli.types.CliModule": {
|
|
57
|
+
"cmd_name": "sqlrepl",
|
|
58
|
+
"mod_name": "omdev.tools.sqlrepl"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"module": ".interp.__main__",
|
|
64
|
+
"attr": "_CLI_MODULE",
|
|
65
|
+
"file": "omdev/interp/__main__.py",
|
|
66
|
+
"line": 4,
|
|
67
|
+
"value": {
|
|
68
|
+
"$.cli.types.CliModule": {
|
|
69
|
+
"cmd_name": "interp",
|
|
70
|
+
"mod_name": "omdev.interp.__main__"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"module": ".pyproject.__main__",
|
|
76
|
+
"attr": "_CLI_MODULE",
|
|
77
|
+
"file": "omdev/pyproject/__main__.py",
|
|
78
|
+
"line": 4,
|
|
79
|
+
"value": {
|
|
80
|
+
"$.cli.types.CliModule": {
|
|
81
|
+
"cmd_name": "pyproject",
|
|
82
|
+
"mod_name": "omdev.pyproject.__main__"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"module": ".cexts.cmake",
|
|
88
|
+
"attr": "_CLI_MODULE",
|
|
89
|
+
"file": "omdev/cexts/cmake.py",
|
|
90
|
+
"line": 323,
|
|
91
|
+
"value": {
|
|
92
|
+
"$.cli.types.CliModule": {
|
|
93
|
+
"cmd_name": "cmake",
|
|
94
|
+
"mod_name": "omdev.cexts.cmake"
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"module": ".precheck.precheck",
|
|
100
|
+
"attr": "_CLI_MODULE",
|
|
101
|
+
"file": "omdev/precheck/precheck.py",
|
|
102
|
+
"line": 90,
|
|
103
|
+
"value": {
|
|
104
|
+
"$.cli.types.CliModule": {
|
|
105
|
+
"cmd_name": "precheck",
|
|
106
|
+
"mod_name": "omdev.precheck.precheck"
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"module": ".amalg.__main__",
|
|
112
|
+
"attr": "_CLI_MODULE",
|
|
113
|
+
"file": "omdev/amalg/__main__.py",
|
|
114
|
+
"line": 4,
|
|
115
|
+
"value": {
|
|
116
|
+
"$.cli.types.CliModule": {
|
|
117
|
+
"cmd_name": "amalg",
|
|
118
|
+
"mod_name": "omdev.amalg.__main__"
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
]
|
omdev/__about__.py
CHANGED
|
@@ -35,6 +35,15 @@ class Project(ProjectBase):
|
|
|
35
35
|
],
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
entry_points = {
|
|
39
|
+
'omlish.manifests': {name: name},
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
# FIXME: omdev-cli ?
|
|
43
|
+
# scripts = {
|
|
44
|
+
# 'om': f'{name}.cli.main:_main [cli]',
|
|
45
|
+
# }
|
|
46
|
+
|
|
38
47
|
|
|
39
48
|
class Setuptools(SetuptoolsBase):
|
|
40
49
|
cexts = True
|
omdev/amalg/__main__.py
CHANGED
omdev/amalg/amalg.py
CHANGED
|
@@ -4,9 +4,9 @@ Conventions:
|
|
|
4
4
|
- must import 'from' items for local modules
|
|
5
5
|
|
|
6
6
|
TODO:
|
|
7
|
-
-
|
|
7
|
+
- !! strip manifests? or relegate them to a separate tiny module ala __main__.py?
|
|
8
|
+
- # @omlish-no-amalg ? in cli.types? will strip stmt (more than 1 line) following @manifest, so shouldn't import
|
|
8
9
|
- more sanity checks lol
|
|
9
|
-
- flake8 / ruff mgmt
|
|
10
10
|
- typealias - support # noqa, other comments, and lamely support multiline by just stealing lines till it parses
|
|
11
11
|
- remove `if __name__ == '__main__':` blocks - thus, convention: no def _main() for these
|
|
12
12
|
|
|
@@ -339,8 +339,8 @@ RUFF_DISABLES: ta.AbstractSet[str] = {
|
|
|
339
339
|
'UP036', # outdated-version-block
|
|
340
340
|
}
|
|
341
341
|
|
|
342
|
-
OUTPUT_COMMENT = '# @
|
|
343
|
-
SCAN_COMMENT = '# @
|
|
342
|
+
OUTPUT_COMMENT = '# @omlish-amalg-output '
|
|
343
|
+
SCAN_COMMENT = '# @omlish-amalg '
|
|
344
344
|
|
|
345
345
|
|
|
346
346
|
def gen_amalg(
|
omdev/cexts/_boilerplate.cc
CHANGED
omdev/cexts/cmake.py
CHANGED
|
@@ -38,6 +38,7 @@ from omlish import logs
|
|
|
38
38
|
|
|
39
39
|
from .. import cmake
|
|
40
40
|
from .. import findmagic
|
|
41
|
+
from ..cli import CliModule
|
|
41
42
|
from .magic import CextMagic
|
|
42
43
|
|
|
43
44
|
|
|
@@ -319,6 +320,10 @@ def _gen_cmd(args) -> None:
|
|
|
319
320
|
cpg.run()
|
|
320
321
|
|
|
321
322
|
|
|
323
|
+
# @omlish-manifest
|
|
324
|
+
_CLI_MODULE = CliModule('cmake', __name__)
|
|
325
|
+
|
|
326
|
+
|
|
322
327
|
def _main(argv=None) -> None:
|
|
323
328
|
logs.configure_standard_logging('INFO')
|
|
324
329
|
|
omdev/cexts/magic.py
CHANGED
omdev/classdot.py
CHANGED
|
@@ -7,6 +7,8 @@ import typing as ta
|
|
|
7
7
|
|
|
8
8
|
from omlish.graphs import dot
|
|
9
9
|
|
|
10
|
+
from .cli import CliModule
|
|
11
|
+
|
|
10
12
|
|
|
11
13
|
def gen_class_dot(roots: ta.Iterable[type]) -> dot.Graph:
|
|
12
14
|
roots = set(roots)
|
|
@@ -57,5 +59,9 @@ def _main() -> None:
|
|
|
57
59
|
dot.open_dot(dot.render(scd), sleep_s=1.)
|
|
58
60
|
|
|
59
61
|
|
|
62
|
+
# @omlish-manifest
|
|
63
|
+
_CLI_MODULE = CliModule('classdot', __name__)
|
|
64
|
+
|
|
65
|
+
|
|
60
66
|
if __name__ == '__main__':
|
|
61
67
|
_main()
|
omdev/cli/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .types import CliModule # noqa
|
omdev/cli/__main__.py
ADDED
omdev/cli/main.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TODO:
|
|
3
|
+
- allow manually specifying manifest packages
|
|
4
|
+
- omlish.bootstrap always
|
|
5
|
+
- https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#creating-executable-scripts
|
|
6
|
+
- https://packaging.python.org/en/latest/specifications/entry-points/#entry-points
|
|
7
|
+
"""
|
|
8
|
+
import argparse
|
|
9
|
+
import functools
|
|
10
|
+
import os
|
|
11
|
+
import runpy
|
|
12
|
+
import sys
|
|
13
|
+
|
|
14
|
+
from omlish import check
|
|
15
|
+
|
|
16
|
+
from ..manifests.load import ManifestLoader
|
|
17
|
+
from .types import CliModule
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _main() -> None:
|
|
21
|
+
cms: list[CliModule] = []
|
|
22
|
+
|
|
23
|
+
ldr = ManifestLoader.from_entry_point(globals())
|
|
24
|
+
|
|
25
|
+
pkgs = ldr.discover()
|
|
26
|
+
if not pkgs:
|
|
27
|
+
pkgs = []
|
|
28
|
+
for n in os.listdir(os.getcwd()):
|
|
29
|
+
if os.path.isdir(n) and os.path.exists(os.path.join(n, '__init__.py')):
|
|
30
|
+
pkgs.append(n)
|
|
31
|
+
|
|
32
|
+
for m in ldr.load(*pkgs, only=[CliModule]):
|
|
33
|
+
cms.append(check.isinstance(m.value, CliModule))
|
|
34
|
+
|
|
35
|
+
parser = argparse.ArgumentParser()
|
|
36
|
+
subparsers = parser.add_subparsers()
|
|
37
|
+
|
|
38
|
+
def run(cm: CliModule) -> None:
|
|
39
|
+
sys.argv = [cm.cmd_name, *(args.args or ())]
|
|
40
|
+
runpy._run_module_as_main(cm.mod_name) # type: ignore # noqa
|
|
41
|
+
|
|
42
|
+
seen: set[str] = set()
|
|
43
|
+
for cm in cms:
|
|
44
|
+
if cm.cmd_name in seen:
|
|
45
|
+
raise NameError(cm)
|
|
46
|
+
|
|
47
|
+
cmd_parser = subparsers.add_parser(cm.cmd_name)
|
|
48
|
+
cmd_parser.add_argument('args', nargs=argparse.REMAINDER)
|
|
49
|
+
cmd_parser.set_defaults(func=functools.partial(run, cm))
|
|
50
|
+
|
|
51
|
+
args = parser.parse_args()
|
|
52
|
+
if not getattr(args, 'func', None):
|
|
53
|
+
parser.print_help()
|
|
54
|
+
else:
|
|
55
|
+
args.func()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
if __name__ == '__main__':
|
|
59
|
+
_main()
|
omdev/cli/types.py
ADDED
omdev/interp/__main__.py
CHANGED
omdev/interp/cli.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
# @
|
|
2
|
+
# @omlish-amalg ../scripts/interp.py
|
|
3
3
|
# ruff: noqa: UP007
|
|
4
4
|
"""
|
|
5
5
|
TODO:
|
|
@@ -15,6 +15,8 @@ from omlish.lite.logs import configure_standard_logging
|
|
|
15
15
|
from omlish.lite.runtime import check_runtime_version
|
|
16
16
|
|
|
17
17
|
from .resolvers import DEFAULT_INTERP_RESOLVER
|
|
18
|
+
from .resolvers import INTERP_PROVIDER_TYPES_BY_NAME
|
|
19
|
+
from .resolvers import InterpResolver
|
|
18
20
|
from .types import InterpSpecifier
|
|
19
21
|
|
|
20
22
|
|
|
@@ -25,9 +27,13 @@ def _list_cmd(args) -> None:
|
|
|
25
27
|
|
|
26
28
|
|
|
27
29
|
def _resolve_cmd(args) -> None:
|
|
28
|
-
|
|
30
|
+
if args.provider:
|
|
31
|
+
p = INTERP_PROVIDER_TYPES_BY_NAME[args.provider]()
|
|
32
|
+
r = InterpResolver([(p.name, p)])
|
|
33
|
+
else:
|
|
34
|
+
r = DEFAULT_INTERP_RESOLVER
|
|
29
35
|
s = InterpSpecifier.parse(args.version)
|
|
30
|
-
print(check_not_none(r.resolve(s)).exe)
|
|
36
|
+
print(check_not_none(r.resolve(s, install=bool(args.install))).exe)
|
|
31
37
|
|
|
32
38
|
|
|
33
39
|
def _build_parser() -> argparse.ArgumentParser:
|
|
@@ -37,12 +43,14 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
37
43
|
|
|
38
44
|
parser_list = subparsers.add_parser('list')
|
|
39
45
|
parser_list.add_argument('version')
|
|
40
|
-
parser_list.add_argument('--debug', action='store_true')
|
|
46
|
+
parser_list.add_argument('-d', '--debug', action='store_true')
|
|
41
47
|
parser_list.set_defaults(func=_list_cmd)
|
|
42
48
|
|
|
43
49
|
parser_resolve = subparsers.add_parser('resolve')
|
|
44
50
|
parser_resolve.add_argument('version')
|
|
45
|
-
parser_resolve.add_argument('
|
|
51
|
+
parser_resolve.add_argument('-p', '--provider')
|
|
52
|
+
parser_resolve.add_argument('-d', '--debug', action='store_true')
|
|
53
|
+
parser_resolve.add_argument('-i', '--install', action='store_true')
|
|
46
54
|
parser_resolve.set_defaults(func=_resolve_cmd)
|
|
47
55
|
|
|
48
56
|
return parser
|
omdev/interp/pyenv.py
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
"""
|
|
2
2
|
TODO:
|
|
3
3
|
- custom tags
|
|
4
|
+
- 'aliases'
|
|
5
|
+
- https://github.com/pyenv/pyenv/pull/2966
|
|
6
|
+
- https://github.com/pyenv/pyenv/issues/218 (lol)
|
|
7
|
+
- probably need custom (temp?) definition file
|
|
8
|
+
- *or* python-build directly just into the versions dir?
|
|
4
9
|
- optionally install / upgrade pyenv itself
|
|
5
10
|
- new vers dont need these custom mac opts, only run on old vers
|
|
6
11
|
"""
|
|
@@ -122,8 +127,33 @@ class PyenvInstallOpts:
|
|
|
122
127
|
)
|
|
123
128
|
|
|
124
129
|
|
|
125
|
-
|
|
130
|
+
# TODO: https://github.com/pyenv/pyenv/blob/master/plugins/python-build/README.md#building-for-maximum-performance
|
|
131
|
+
DEFAULT_PYENV_INSTALL_OPTS = PyenvInstallOpts(
|
|
132
|
+
opts=[
|
|
133
|
+
'-s',
|
|
134
|
+
'-v',
|
|
135
|
+
'-k',
|
|
136
|
+
],
|
|
137
|
+
conf_opts=[
|
|
138
|
+
'--enable-loadable-sqlite-extensions',
|
|
139
|
+
|
|
140
|
+
# '--enable-shared',
|
|
141
|
+
|
|
142
|
+
'--enable-optimizations',
|
|
143
|
+
'--with-lto',
|
|
144
|
+
|
|
145
|
+
# '--enable-profiling', # ?
|
|
146
|
+
|
|
147
|
+
# '--enable-ipv6', # ?
|
|
148
|
+
],
|
|
149
|
+
cflags=[
|
|
150
|
+
# '-march=native',
|
|
151
|
+
# '-mtune=native',
|
|
152
|
+
],
|
|
153
|
+
)
|
|
154
|
+
|
|
126
155
|
DEBUG_PYENV_INSTALL_OPTS = PyenvInstallOpts(opts=['-g'])
|
|
156
|
+
|
|
127
157
|
THREADED_PYENV_INSTALL_OPTS = PyenvInstallOpts(conf_opts=['--disable-gil'])
|
|
128
158
|
|
|
129
159
|
|
|
@@ -222,6 +252,7 @@ class PyenvVersionInstaller:
|
|
|
222
252
|
opts: ta.Optional[PyenvInstallOpts] = None,
|
|
223
253
|
interp_opts: InterpOpts = InterpOpts(),
|
|
224
254
|
*,
|
|
255
|
+
install_name: ta.Optional[str] = None,
|
|
225
256
|
no_default_opts: bool = False,
|
|
226
257
|
pyenv: Pyenv = Pyenv(),
|
|
227
258
|
) -> None:
|
|
@@ -242,6 +273,7 @@ class PyenvVersionInstaller:
|
|
|
242
273
|
self._version = version
|
|
243
274
|
self._opts = opts
|
|
244
275
|
self._interp_opts = interp_opts
|
|
276
|
+
self._given_install_name = install_name
|
|
245
277
|
|
|
246
278
|
self._no_default_opts = no_default_opts
|
|
247
279
|
self._pyenv = pyenv
|
|
@@ -256,6 +288,8 @@ class PyenvVersionInstaller:
|
|
|
256
288
|
|
|
257
289
|
@cached_nullary
|
|
258
290
|
def install_name(self) -> str:
|
|
291
|
+
if self._given_install_name is not None:
|
|
292
|
+
return self._given_install_name
|
|
259
293
|
return self._version + ('-debug' if self._interp_opts.debug else '')
|
|
260
294
|
|
|
261
295
|
@cached_nullary
|
|
@@ -275,11 +309,26 @@ class PyenvVersionInstaller:
|
|
|
275
309
|
v += ' ' + os.environ[k]
|
|
276
310
|
env[k] = v
|
|
277
311
|
|
|
278
|
-
|
|
279
|
-
self._pyenv.exe(),
|
|
280
|
-
'install',
|
|
312
|
+
conf_args = [
|
|
281
313
|
*self._opts.opts,
|
|
282
314
|
self._version,
|
|
315
|
+
]
|
|
316
|
+
|
|
317
|
+
if self._given_install_name is not None:
|
|
318
|
+
full_args = [
|
|
319
|
+
os.path.join(check_not_none(self._pyenv.root()), 'plugins', 'python-build', 'bin', 'python-build'),
|
|
320
|
+
*conf_args,
|
|
321
|
+
self.install_dir(),
|
|
322
|
+
]
|
|
323
|
+
else:
|
|
324
|
+
full_args = [
|
|
325
|
+
self._pyenv.exe(),
|
|
326
|
+
'install',
|
|
327
|
+
*conf_args,
|
|
328
|
+
]
|
|
329
|
+
|
|
330
|
+
subprocess_check_call(
|
|
331
|
+
*full_args,
|
|
283
332
|
env=env,
|
|
284
333
|
)
|
|
285
334
|
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
3
|
-
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
- also can do prechecks!
|
|
2
|
+
TODO:
|
|
3
|
+
- relative keys - if startswith self pkg then `$.foo.bar`
|
|
4
|
+
|
|
5
|
+
- See (entry_points):
|
|
6
|
+
- https://github.com/pytest-dev/pluggy/blob/main/src/pluggy/_manager.py#L405
|
|
7
|
+
- https://docs.pytest.org/en/7.1.x/how-to/writing_plugins.html#setuptools-entry-points
|
|
8
|
+
- https://packaging.python.org/en/latest/specifications/entry-points/
|
|
9
|
+
- https://packaging.python.org/en/latest/guides/creating-and-discovering-plugins/
|
|
10
|
+
- [project.entry-points.omlish-manifests] \n omdev = omdev
|
|
12
11
|
"""
|
|
13
12
|
# ruff: noqa: UP006 UP007
|
|
14
|
-
# @omlish-lite
|
|
15
13
|
import argparse
|
|
16
14
|
import collections
|
|
17
15
|
import dataclasses as dc
|
|
@@ -30,32 +28,23 @@ from omlish.lite.json import json_dumps_pretty
|
|
|
30
28
|
from omlish.lite.logs import configure_standard_logging
|
|
31
29
|
from omlish.lite.logs import log
|
|
32
30
|
|
|
33
|
-
from
|
|
31
|
+
from .. import findmagic
|
|
32
|
+
from .load import ManifestLoader
|
|
33
|
+
from .types import Manifest
|
|
34
|
+
from .types import ManifestOrigin
|
|
34
35
|
|
|
35
36
|
|
|
36
37
|
##
|
|
37
38
|
|
|
38
39
|
|
|
39
|
-
@dc.dataclass(frozen=True)
|
|
40
|
-
class ManifestOrigin:
|
|
41
|
-
module: str
|
|
42
|
-
attr: str
|
|
43
|
-
|
|
44
|
-
file: str
|
|
45
|
-
line: int
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
@dc.dataclass(frozen=True)
|
|
49
|
-
class Manifest(ManifestOrigin):
|
|
50
|
-
value: ta.Any
|
|
51
|
-
|
|
52
|
-
|
|
53
40
|
MANIFEST_MAGIC = '# @omlish-manifest'
|
|
54
41
|
|
|
55
42
|
_MANIFEST_GLOBAL_PAT = re.compile(r'^(?P<name>[A-Za-z_][A-Za-z0-9_]*)\s*=.*')
|
|
56
43
|
|
|
57
44
|
|
|
58
45
|
def _dump_module_manifests(spec: str, *attrs: str) -> None:
|
|
46
|
+
import collections.abc
|
|
47
|
+
import dataclasses as dc # noqa
|
|
59
48
|
import importlib
|
|
60
49
|
import json
|
|
61
50
|
|
|
@@ -65,13 +54,35 @@ def _dump_module_manifests(spec: str, *attrs: str) -> None:
|
|
|
65
54
|
for attr in attrs:
|
|
66
55
|
manifest = getattr(mod, attr)
|
|
67
56
|
|
|
68
|
-
|
|
69
|
-
|
|
57
|
+
if dc.is_dataclass(manifest):
|
|
58
|
+
cls = type(manifest)
|
|
59
|
+
manifest_json = json.dumps(dc.asdict(manifest)) # type: ignore
|
|
60
|
+
manifest_dct = json.loads(manifest_json)
|
|
61
|
+
|
|
62
|
+
rt_manifest = cls(**manifest_dct) # type: ignore
|
|
63
|
+
if rt_manifest != manifest:
|
|
64
|
+
raise Exception(f'Manifest failed to roundtrip: {manifest} -> {manifest_dct} != {rt_manifest}')
|
|
65
|
+
|
|
66
|
+
key = f'${cls.__module__}.{cls.__qualname__}'
|
|
67
|
+
out[attr] = {key: manifest_dct}
|
|
68
|
+
|
|
69
|
+
elif isinstance(manifest, collections.abc.Mapping):
|
|
70
|
+
[(key, manifest_dct)] = manifest.items()
|
|
71
|
+
if not key.startswith('$'): # noqa
|
|
72
|
+
raise Exception(f'Bad key: {key}')
|
|
73
|
+
|
|
74
|
+
if not isinstance(manifest_dct, collections.abc.Mapping):
|
|
75
|
+
raise Exception(f'Bad value: {manifest_dct}')
|
|
70
76
|
|
|
71
|
-
|
|
72
|
-
|
|
77
|
+
manifest_json = json.dumps(manifest_dct)
|
|
78
|
+
rt_manifest_dct = json.loads(manifest_json)
|
|
79
|
+
if manifest_dct != rt_manifest_dct:
|
|
80
|
+
raise Exception(f'Manifest failed to roundtrip: {manifest_dct} != {rt_manifest_dct}')
|
|
73
81
|
|
|
74
|
-
|
|
82
|
+
out[attr] = {key: manifest_dct}
|
|
83
|
+
|
|
84
|
+
else:
|
|
85
|
+
raise TypeError(f'Manifest must be dataclass or mapping: {manifest!r}')
|
|
75
86
|
|
|
76
87
|
out_json = json.dumps(out, indent=None, separators=(',', ':'))
|
|
77
88
|
print(out_json)
|
|
@@ -163,9 +174,16 @@ def build_module_manifests(
|
|
|
163
174
|
|
|
164
175
|
if not (
|
|
165
176
|
isinstance(value, ta.Mapping) and
|
|
177
|
+
len(value) == 1 and
|
|
166
178
|
all(isinstance(k, str) and k.startswith('$') and len(k) > 1 for k in value)
|
|
167
179
|
):
|
|
168
|
-
raise TypeError(f'Manifests must be
|
|
180
|
+
raise TypeError(f'Manifests must be mappings of strings starting with $: {value!r}')
|
|
181
|
+
|
|
182
|
+
[(key, value_dct)] = value.items()
|
|
183
|
+
kb, _, kr = key[1:].partition('.') # noqa
|
|
184
|
+
if kb == mod_base: # noqa
|
|
185
|
+
key = f'$.{kr}'
|
|
186
|
+
value = {key: value_dct}
|
|
169
187
|
|
|
170
188
|
out.append(Manifest(
|
|
171
189
|
**dc.asdict(o),
|
|
@@ -205,8 +223,36 @@ def build_package_manifests(
|
|
|
205
223
|
##
|
|
206
224
|
|
|
207
225
|
|
|
226
|
+
def check_package_manifests(
|
|
227
|
+
name: str,
|
|
228
|
+
base: str,
|
|
229
|
+
) -> None:
|
|
230
|
+
pkg_dir = os.path.join(base, name)
|
|
231
|
+
if not os.path.isdir(pkg_dir) or not os.path.isfile(os.path.join(pkg_dir, '__init__.py')):
|
|
232
|
+
raise Exception(pkg_dir)
|
|
233
|
+
|
|
234
|
+
manifests_file = os.path.join(pkg_dir, '.manifests.json')
|
|
235
|
+
if not os.path.isfile(manifests_file):
|
|
236
|
+
raise Exception(f'No manifests file: {manifests_file}')
|
|
237
|
+
|
|
238
|
+
with open(manifests_file) as f:
|
|
239
|
+
manifests_json = json.load(f)
|
|
240
|
+
|
|
241
|
+
ldr = ManifestLoader()
|
|
242
|
+
for entry in manifests_json:
|
|
243
|
+
manifest = Manifest(**entry)
|
|
244
|
+
[(key, value_dct)] = manifest.value.items()
|
|
245
|
+
if key.startswith('$.'):
|
|
246
|
+
key = f'${name}{key[1:]}'
|
|
247
|
+
cls = ldr.load_cls(key)
|
|
248
|
+
value = cls(**value_dct) # noqa
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
##
|
|
252
|
+
|
|
253
|
+
|
|
208
254
|
if __name__ == '__main__':
|
|
209
|
-
def
|
|
255
|
+
def _get_base(args) -> str:
|
|
210
256
|
if args.base is not None:
|
|
211
257
|
base = args.base
|
|
212
258
|
else:
|
|
@@ -214,6 +260,10 @@ if __name__ == '__main__':
|
|
|
214
260
|
base = os.path.abspath(base)
|
|
215
261
|
if not os.path.isdir(base):
|
|
216
262
|
raise RuntimeError(base)
|
|
263
|
+
return base
|
|
264
|
+
|
|
265
|
+
def _gen_cmd(args) -> None:
|
|
266
|
+
base = _get_base(args)
|
|
217
267
|
|
|
218
268
|
for pkg in args.package:
|
|
219
269
|
ms = build_package_manifests(
|
|
@@ -224,6 +274,15 @@ if __name__ == '__main__':
|
|
|
224
274
|
if not args.quiet:
|
|
225
275
|
print(json_dumps_pretty([dc.asdict(m) for m in ms]))
|
|
226
276
|
|
|
277
|
+
def _check_cmd(args) -> None:
|
|
278
|
+
base = _get_base(args)
|
|
279
|
+
|
|
280
|
+
for pkg in args.package:
|
|
281
|
+
check_package_manifests(
|
|
282
|
+
pkg,
|
|
283
|
+
base,
|
|
284
|
+
)
|
|
285
|
+
|
|
227
286
|
def _main(argv=None) -> None:
|
|
228
287
|
configure_standard_logging('INFO')
|
|
229
288
|
|
|
@@ -235,9 +294,13 @@ if __name__ == '__main__':
|
|
|
235
294
|
parser_gen.add_argument('-w', '--write', action='store_true')
|
|
236
295
|
parser_gen.add_argument('-q', '--quiet', action='store_true')
|
|
237
296
|
parser_gen.add_argument('package', nargs='*')
|
|
238
|
-
|
|
239
297
|
parser_gen.set_defaults(func=_gen_cmd)
|
|
240
298
|
|
|
299
|
+
parser_check = subparsers.add_parser('check')
|
|
300
|
+
parser_check.add_argument('-b', '--base')
|
|
301
|
+
parser_check.add_argument('package', nargs='*')
|
|
302
|
+
parser_check.set_defaults(func=_check_cmd)
|
|
303
|
+
|
|
241
304
|
args = parser.parse_args(argv)
|
|
242
305
|
if not getattr(args, 'func', None):
|
|
243
306
|
parser.print_help()
|