Coverage for gws-app/gws/spec/generator/manifest.py: 58%
48 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-05 13:35 +0200
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-05 13:35 +0200
1"""Reader for application manifest files (``MANIFEST.json``)."""
3import json
4import os
7class Error(Exception):
8 """Raised when a manifest cannot be parsed."""
10 pass
13def from_path(path):
14 """Read a manifest file.
16 Args:
17 path: Path to the manifest file.
19 Returns:
20 A dict with the manifest keys; missing keys get defaults.
22 Raises:
23 ``Error``: If the manifest is not valid JSON or a value is invalid.
24 """
26 with open(path, 'rt', encoding='utf8') as fp:
27 return from_text(fp.read(), path)
30def from_text(text, path):
31 """Parse a manifest from text.
33 Lines starting with ``//`` or ``#`` are ignored. Relative plugin and
34 ``tsConfig`` paths are resolved against the directory of ``path``.
36 Args:
37 text: Manifest JSON text.
38 path: Path to the manifest file.
40 Returns:
41 A dict with the manifest keys; missing keys get defaults.
43 Raises:
44 ``Error``: If the text is not valid JSON or a value is invalid.
45 """
47 lines = []
48 for s in text.split('\n'):
49 # we allow // or # comments in json
50 if s.strip().startswith(('//', '#')):
51 s = ''
52 lines.append(s)
54 try:
55 dct = json.loads('\n'.join(lines))
56 except Exception as exc:
57 raise Error('invalid json') from exc
59 return _parse(dct, path)
62##
64def _version(val, dct, path):
65 p = [int(s) for s in val.split('.')]
66 return '.'.join(str(s) for s in p)
69def _plugins(val, dct, path):
70 plugins = []
72 for p in val:
73 plugin_path = _relpath(p['path'], dct, path)
74 plugin_name = p.get('name') or os.path.basename(plugin_path)
75 plugins.append({'name': plugin_name, 'path': plugin_path})
77 return plugins
80def _relpath(val, dct, path):
81 basedir = os.path.dirname(path)
82 return val if os.path.isabs(val) else os.path.abspath(os.path.join(basedir, val))
85def _strlist(val, dct, path):
86 return [str(s) for s in val]
89def _str(val, dct, path):
90 return str(val)
93def _bool(val, dct, path):
94 return bool(val)
97_KEYS = [
98 ('uid', _str, None),
99 ('release', _version, None),
100 ('locales', _strlist, []),
101 ('plugins', _plugins, []),
102 ('tsConfig', _relpath, ''),
103 ('excludePlugins', _strlist, []),
104 ('withFallbackConfig', _bool, False),
105 ('withStrictConfig', _bool, False),
106]
109def _parse(dct, path):
110 res = {}
112 for key, fn, default in _KEYS:
113 if key not in dct:
114 res[key] = default
115 else:
116 try:
117 res[key] = fn(dct[key], dct, path)
118 except Exception as exc:
119 raise Error(f'invalid value for key {key!r} in {path!r}') from exc
121 return res