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 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
@@ -1,3 +1,10 @@
1
+ from ..cli import CliModule
2
+
3
+
4
+ # @omlish-manifest
5
+ _CLI_MODULE = CliModule('amalg', __name__)
6
+
7
+
1
8
  if __name__ == '__main__':
2
9
  from .amalg import _main
3
10
 
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
- - check 3.8 compat
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 = '# @omdev-amalg-output '
343
- SCAN_COMMENT = '# @omdev-amalg '
342
+ OUTPUT_COMMENT = '# @omlish-amalg-output '
343
+ SCAN_COMMENT = '# @omlish-amalg '
344
344
 
345
345
 
346
346
  def gen_amalg(
@@ -1,4 +1,4 @@
1
- // @omdev-cext
1
+ // @omlish-cext
2
2
  #define PY_SSIZE_T_CLEAN
3
3
  #include "Python.h"
4
4
  #include "structmember.h"
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
@@ -1,7 +1,7 @@
1
1
  # @omlish-lite
2
2
 
3
3
  class CextMagic:
4
- MAGIC = '@omdev-cext'
4
+ MAGIC = '@omlish-cext'
5
5
  MAGIC_COMMENT = f'// {MAGIC}'
6
6
 
7
7
  FILE_EXTENSIONS = ('c', 'cc', 'cpp')
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
@@ -0,0 +1,4 @@
1
+ if __name__ == '__main__':
2
+ from .main import _main
3
+
4
+ _main()
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
@@ -0,0 +1,7 @@
1
+ import dataclasses as dc
2
+
3
+
4
+ @dc.dataclass(frozen=True)
5
+ class CliModule:
6
+ cmd_name: str
7
+ mod_name: str
omdev/interp/__main__.py CHANGED
@@ -1,3 +1,10 @@
1
+ from ..cli import CliModule
2
+
3
+
4
+ # @omlish-manifest
5
+ _CLI_MODULE = CliModule('interp', __name__)
6
+
7
+
1
8
  if __name__ == '__main__':
2
9
  from .cli import _main
3
10
 
omdev/interp/cli.py CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- # @omdev-amalg ../scripts/interp.py
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
- r = DEFAULT_INTERP_RESOLVER
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('--debug', action='store_true')
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
- DEFAULT_PYENV_INSTALL_OPTS = PyenvInstallOpts(opts=['-s', '-v'])
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
- subprocess_check_call(
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
 
@@ -0,0 +1,2 @@
1
+ # @omlish-lite
2
+ from .types import Manifest # noqa
@@ -1,17 +1,15 @@
1
1
  """
2
- !!! manifests! get-manifest, .manifest.json
3
- - dumb dicts, root keys are 'types'
4
- - get put in _manifest.py, root level dict or smth
5
- - IMPORT files w comment
6
- - comment must immediately precede a global val setter
7
- - val is grabbed from imported module dict by name
8
- - value is repr'd somehow (roundtrip checked) (naw, json lol)
9
- - dumped in _manifest.py
10
- - # @omlish-manifest \n _CACHE_MANIFEST = {'cache': {'name': 'llm', …
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 . import findmagic
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
- manifest_json = json.dumps(manifest)
69
- rt_manifest = json.loads(manifest_json)
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
- if rt_manifest != manifest:
72
- raise Exception(f'Manifest failed to roundtrip: {manifest} != {rt_manifest}')
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
- out[attr] = rt_manifest
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 mapping of strings starting with $: {value!r}')
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 _gen_cmd(args) -> None:
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()