summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorWojtek Kosior <koszko@koszko.org>2022-05-27 19:43:51 +0200
committerWojtek Kosior <koszko@koszko.org>2022-05-31 15:17:11 +0200
commitf42f5c1957e9c6ff8a61ef295b63dccead9b0ae8 (patch)
treeefc3d1eb06f6cabd95635d88f957537312985a2a /src
parent496d90f70a58c0040f8493aa8a5e4e2f106bfac7 (diff)
downloadhydrilla-builder-f42f5c1957e9c6ff8a61ef295b63dccead9b0ae8.tar.gz
hydrilla-builder-f42f5c1957e9c6ff8a61ef295b63dccead9b0ae8.zip
incorporate version 2 of Hydrilla JSON schemas
Diffstat (limited to 'src')
-rw-r--r--src/hydrilla/builder/build.py27
-rw-r--r--src/hydrilla/builder/locales/en_US/LC_MESSAGES/hydrilla-messages.po50
m---------src/hydrilla/schemas0
m---------src/hydrilla/schemas/1.x0
m---------src/hydrilla/schemas/2.x0
-rw-r--r--src/hydrilla/util/__init__.py4
-rw-r--r--src/hydrilla/util/_util.py65
7 files changed, 101 insertions, 45 deletions
diff --git a/src/hydrilla/builder/build.py b/src/hydrilla/builder/build.py
index 8354331..5de9351 100644
--- a/src/hydrilla/builder/build.py
+++ b/src/hydrilla/builder/build.py
@@ -51,7 +51,14 @@ here = Path(__file__).resolve().parent
_ = util.translation(here / 'locales').gettext
-index_validator = util.validator_for('package_source-2.schema.json')
+def index_validator(major_schema_version):
+ """
+ Create an index.json schema validator specific to the requested schema
+ version series.
+ """
+ exact_version = {1: '1.0.1', 2: '2'}[major_schema_version]
+
+ return util.validator_for(f'package_source-{exact_version}.schema.json')
schemas_root = 'https://hydrilla.koszko.org/schemas'
@@ -158,16 +165,18 @@ class Build:
if not index_json_path.is_absolute():
index_json_path = (self.srcdir / index_json_path)
- with open(index_json_path, 'rt') as index_file:
- index_json_text = index_file.read()
+ index_obj, major = util.load_instance_from_file(index_json_path)
- index_obj = json.loads(util.strip_json_comments(index_json_text))
+ if major not in (1, 2):
+ msg = _('unknown_schema_package_source_{}')\
+ .format(index_json_path)
+ raise util.UnknownSchemaError(msg)
index_desired_path = PurePosixPath('index.json')
self.files_by_path[index_desired_path] = \
- FileRef(index_desired_path, index_json_text.encode())
+ FileRef(index_desired_path, index_json_path.read_bytes())
- self._process_index_json(index_obj)
+ self._process_index_json(index_obj, major)
def _process_file(self, filename: Union[str, PurePosixPath],
piggybacked: Piggybacked,
@@ -308,14 +317,16 @@ class Build:
props_in_ref = ('type', 'identifier', 'version', 'long_name')
return dict([(prop, new_item_obj[prop]) for prop in props_in_ref])
- def _process_index_json(self, index_obj: dict):
+ def _process_index_json(self, index_obj: dict,
+ major_schema_version: int) -> None:
"""
Process 'index_obj' as contents of source package's index.json and store
in memory this source package's zipfile as well as package's individual
files and computed definitions of the source package and items defined
in it.
"""
- index_validator.validate(index_obj)
+ index_validator(major_schema_version).validate(index_obj)
+
match = re.match(r'.*-((([1-9][0-9]*|0)\.)+)schema\.json$',
index_obj['$schema'])
self.source_schema_ver = \
diff --git a/src/hydrilla/builder/locales/en_US/LC_MESSAGES/hydrilla-messages.po b/src/hydrilla/builder/locales/en_US/LC_MESSAGES/hydrilla-messages.po
index 807be87..821f74b 100644
--- a/src/hydrilla/builder/locales/en_US/LC_MESSAGES/hydrilla-messages.po
+++ b/src/hydrilla/builder/locales/en_US/LC_MESSAGES/hydrilla-messages.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: hydrilla.builder 0.1.dev16+g4e46d7f.d20220211\n"
"Report-Msgid-Bugs-To: koszko@koszko.org\n"
-"POT-Creation-Date: 2022-05-11 11:10+0200\n"
+"POT-Creation-Date: 2022-05-27 18:49+0200\n"
"PO-Revision-Date: 2022-02-12 00:00+0000\n"
"Last-Translator: Wojtek Kosior <koszko@koszko.org>\n"
"Language: en_US\n"
@@ -18,67 +18,73 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.8.0\n"
-#: src/hydrilla/builder/build.py:86 src/hydrilla/builder/local_apt.py:118
-#: src/hydrilla/builder/local_apt.py:407
+#: src/hydrilla/builder/build.py:93 src/hydrilla/builder/local_apt.py:118
+#: src/hydrilla/builder/local_apt.py:410
msgid "couldnt_execute_{}_is_it_installed"
msgstr "Could not execute '{}'. Is the tool installed and reachable via PATH?"
-#: src/hydrilla/builder/build.py:90 src/hydrilla/builder/local_apt.py:122
-#: src/hydrilla/builder/local_apt.py:411
+#: src/hydrilla/builder/build.py:97 src/hydrilla/builder/local_apt.py:122
+#: src/hydrilla/builder/local_apt.py:414
msgid "command_{}_failed"
msgstr "The following command finished execution with a non-zero exit status: {}"
-#: src/hydrilla/builder/build.py:198
+#: src/hydrilla/builder/build.py:171
+msgid "unknown_schema_package_source_{}"
+msgstr ""
+"The provided JSON at '{}' does not use any of the known package source "
+"JSON schemas."
+
+#: src/hydrilla/builder/build.py:207
msgid "path_contains_double_dot_{}"
msgstr ""
"Attempt to load '{}' which includes a forbidden parent reference ('..') "
"in the path."
-#: src/hydrilla/builder/build.py:205
+#: src/hydrilla/builder/build.py:214
msgid "loading_{}_outside_package_dir"
msgstr "Attempt to load '{}' which lies outside package source directory."
-#: src/hydrilla/builder/build.py:209
+#: src/hydrilla/builder/build.py:218
msgid "loading_reserved_index_json"
msgstr "Attempt to load 'index.json' which is a reserved filename."
-#: src/hydrilla/builder/build.py:216
+#: src/hydrilla/builder/build.py:225
msgid "referenced_file_{}_missing"
msgstr "Referenced file '{}' is missing."
-#: src/hydrilla/builder/build.py:351
+#: src/hydrilla/builder/build.py:362
msgid "report_spdx_not_in_copyright_list"
msgstr ""
"Told to generate 'report.spdx' but 'report.spdx' is not listed among "
"copyright files. Refusing to proceed."
-#: src/hydrilla/builder/build.py:422
+#: src/hydrilla/builder/build.py:433
msgid "build_package_from_srcdir_to_dstdir"
msgstr ""
"Build Hydrilla package from `scrdir` and write the resulting files under "
"`dstdir`."
-#: src/hydrilla/builder/build.py:424
+#: src/hydrilla/builder/build.py:435
msgid "source_directory_to_build_from"
msgstr "Source directory to build from."
-#: src/hydrilla/builder/build.py:426
+#: src/hydrilla/builder/build.py:437
msgid "path_instead_of_index_json"
msgstr ""
"Path to file to be processed instead of index.json (if not absolute, "
"resolved relative to srcdir)."
-#: src/hydrilla/builder/build.py:428
+#: src/hydrilla/builder/build.py:439
msgid "path_instead_for_piggyback_files"
msgstr ""
"Path to a non-standard directory with foreign packages' archive files to "
"use."
-#: src/hydrilla/builder/build.py:430
+#: src/hydrilla/builder/build.py:441
msgid "built_package_files_destination"
msgstr "Destination directory to write built package files to."
-#: src/hydrilla/builder/build.py:432
+#: src/hydrilla/builder/build.py:443
#, python-format
msgid "%(prog)s_%(version)s_license"
msgstr ""
@@ -89,7 +95,7 @@ msgstr ""
"This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law."
-#: src/hydrilla/builder/build.py:433
+#: src/hydrilla/builder/build.py:444
msgid "version_printing"
msgstr "Print version information and exit."
@@ -105,21 +111,21 @@ msgstr "## Command's standard error output ##"
msgid "distro_{}_unknown"
msgstr "Attempt to use an unknown software distribution '{}'."
-#: src/hydrilla/builder/local_apt.py:190
+#: src/hydrilla/builder/local_apt.py:191
msgid "couldnt_import_{}_is_it_installed"
msgstr ""
"Could not import '{}'. Is the module installed and visible to this Python"
" instance?"
-#: src/hydrilla/builder/local_apt.py:198
+#: src/hydrilla/builder/local_apt.py:199
msgid "gpg_couldnt_recv_key_{}"
msgstr "Could not import PGP key '{}'."
-#: src/hydrilla/builder/local_apt.py:312
+#: src/hydrilla/builder/local_apt.py:313
msgid "apt_install_output_not_understood"
msgstr "The output of an 'apt-get install' command was not understood."
-#: src/hydrilla/builder/local_apt.py:338
+#: src/hydrilla/builder/local_apt.py:339
msgid "apt_download_gave_bad_filename_{}"
msgstr "The 'apt-get download' command produced a file with unexpected name '{}'."
@@ -129,7 +135,7 @@ msgstr ""
"Attempt to load '{}' which lies outside piggybacked packages files root "
"directory."
-#: src/hydrilla/util/_util.py:79
+#: src/hydrilla/util/_util.py:86
msgid "bad_comment"
msgstr "bad comment"
diff --git a/src/hydrilla/schemas b/src/hydrilla/schemas
deleted file mode 160000
-Subproject 4b4da5a02bc311603469eea7b3dfd4f1bbb911f
diff --git a/src/hydrilla/schemas/1.x b/src/hydrilla/schemas/1.x
new file mode 160000
+Subproject 09634f3446866f712a022327683b1149d8f46bf
diff --git a/src/hydrilla/schemas/2.x b/src/hydrilla/schemas/2.x
new file mode 160000
+Subproject 6b6ae219929dc1d47e1dff2a780784b78bd825b
diff --git a/src/hydrilla/util/__init__.py b/src/hydrilla/util/__init__.py
index fadb81c..65367c3 100644
--- a/src/hydrilla/util/__init__.py
+++ b/src/hydrilla/util/__init__.py
@@ -4,5 +4,5 @@
#
# Available under the terms of Creative Commons Zero v1.0 Universal.
-from ._util import strip_json_comments, normalize_version, parse_version, \
- version_string, validator_for, translation
+from ._util import normalize_version, parse_version, version_string, \
+ validator_for, load_instance_from_file, translation, UnknownSchemaError
diff --git a/src/hydrilla/util/_util.py b/src/hydrilla/util/_util.py
index 778e78f..815b7fd 100644
--- a/src/hydrilla/util/_util.py
+++ b/src/hydrilla/util/_util.py
@@ -39,6 +39,13 @@ from jsonschema import RefResolver, Draft7Validator
here = Path(__file__).resolve().parent
+class UnknownSchemaError(Exception):
+ """
+ Exception used to record problems with JSON documents for which not even
+ the appropriate validation schema could be determined.
+ """
+ pass
+
_strip_comment_re = re.compile(r'''
^ # match from the beginning of each line
( # catch the part before '//' comment
@@ -111,27 +118,59 @@ def version_string(ver: list[int], rev: Optional[int]=None) -> str:
return '.'.join([str(n) for n in ver]) + ('' if rev is None else f'-{rev}')
schemas = {}
-for path in (here.parent / 'schemas').glob('*-1.0.1.schema.json'):
- schema = json.loads(path.read_text())
- schemas[schema['$id']] = schema
+for series_dir in (here.parent / 'schemas').glob('*.x'):
+ for path in series_dir.glob("*.schema.json"):
+ schema = json.loads(path.read_text())
+ schemas[schema['$id']] = schema
common_schema_filename = 'common_definitions-1.schema.json'
common_schema_path = here.parent / "schemas" / common_schema_filename
-resolver = RefResolver(
- base_uri=f'file://{str(common_schema_path)}',
- referrer=f'https://hydrilla.koszko.org/{common_schema_filename}',
- store=schemas
-)
+def validator_for(schema: Union[str, dict]) -> Draft7Validator:
+ """
+ Prepare a validator for the provided schema.
-def validator_for(schema_filename: str) -> Draft7Validator:
+ Other schemas under '../schemas' can be referenced.
"""
- Prepare a validator for one of the schemas in '../schemas'.
+ if isinstance(schema, str):
+ schema = schemas[f'https://hydrilla.koszko.org/schemas/{schema}']
+
+ resolver = RefResolver(
+ base_uri=schema['$id'],
+ referrer=schema,
+ handlers={'https': lambda uri: schemas[uri]}
+ )
+
+ return Draft7Validator(schema, resolver=resolver)
+
+_major_version_re = re.compile(r'''
+-
+(?P<major>[1-9][0-9]*)
+(?: # this repeated group matches the remaining version numbers
+ \.
+ (?:[1-9][0-9]*|0)
+)*
+\.schema\.json
+$
+''', re.VERBOSE)
+
+def load_instance_from_file(path: Path) -> tuple[dict, Optional[int]]:
+ """
+ Open a file and load its contents as a JSON document (with additional
+ '//' comments support). Then parse its "$schema" property (if present)
+ and return a tuple of the document instance and the major number of
+ schema version.
- This function is not thread-safe.
+ If no schema version number can be extracted, None is used instead.
"""
- return Draft7Validator(resolver.resolve(schema_filename)[1],
- resolver=resolver)
+ instance = json.loads(strip_json_comments(path.read_text()))
+ major = None
+
+ if type(instance) is dict and type(instance.get('$schema')) is str:
+ match = _major_version_re.search(instance.get('$schema'))
+ major = match and int(match.group('major'))
+
+ return instance, major
def translation(localedir: Union[Path, str], lang: Optional[str]=None) \
-> gettext.GNUTranslations: