diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/hydrilla/builder/build.py | 27 | ||||
-rw-r--r-- | src/hydrilla/builder/locales/en_US/LC_MESSAGES/hydrilla-messages.po | 50 | ||||
m--------- | src/hydrilla/schemas | 0 | ||||
m--------- | src/hydrilla/schemas/1.x | 0 | ||||
m--------- | src/hydrilla/schemas/2.x | 0 | ||||
-rw-r--r-- | src/hydrilla/util/__init__.py | 4 | ||||
-rw-r--r-- | src/hydrilla/util/_util.py | 65 |
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: |