diff options
author | Wojtek Kosior <koszko@koszko.org> | 2022-04-25 19:46:54 +0200 |
---|---|---|
committer | Wojtek Kosior <koszko@koszko.org> | 2022-04-25 19:46:54 +0200 |
commit | 6bc04f8097e44e55cbf543d811ccd90671faedbc (patch) | |
tree | bf8b43dfbb8a1f3e7ffe1439a304aa33bd32bf24 /src/hydrilla/util/_util.py | |
parent | 68d2102c7f791b8607638167ed4aadb5d71dea1b (diff) | |
download | hydrilla-builder-6bc04f8097e44e55cbf543d811ccd90671faedbc.tar.gz hydrilla-builder-6bc04f8097e44e55cbf543d811ccd90671faedbc.zip |
New upstream version 1.0upstream/1.0
Diffstat (limited to 'src/hydrilla/util/_util.py')
-rw-r--r-- | src/hydrilla/util/_util.py | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/src/hydrilla/util/_util.py b/src/hydrilla/util/_util.py new file mode 100644 index 0000000..778e78f --- /dev/null +++ b/src/hydrilla/util/_util.py @@ -0,0 +1,162 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later + +# Building Hydrilla packages. +# +# This file is part of Hydrilla +# +# Copyright (C) 2021, 2022 Wojtek Kosior +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. +# +# +# I, Wojtek Kosior, thereby promise not to sue for violation of this +# file's license. Although I request that you do not make use this code +# in a proprietary program, I am not going to enforce this in court. + +# Enable using with Python 3.7. +from __future__ import annotations + +import re +import json +import locale +import gettext + +from pathlib import Path +from typing import Optional, Union + +from jsonschema import RefResolver, Draft7Validator + +here = Path(__file__).resolve().parent + +_strip_comment_re = re.compile(r''' +^ # match from the beginning of each line +( # catch the part before '//' comment + (?: # this group matches either a string or a single out-of-string character + [^"/] | + " + (?: # this group matches any in-a-string character + [^"\\] | # match any normal character + \\[^u] | # match any escaped character like '\f' or '\n' + \\u[a-fA-F0-9]{4} # match an escape + )* + " + )* +) +# expect either end-of-line or a comment: +# * unterminated strings will cause matching to fail +# * bad comment (with '/' instead of '//') will be indicated by second group +# having length 1 instead of 2 or 0 +(//?|$) +''', re.VERBOSE) + +def strip_json_comments(text: str) -> str: + """ + Accept JSON text with optional C++-style ('//') comments and return the text + with comments removed. Consecutive slashes inside strings are handled + properly. A spurious single slash ('/') shall generate an error. Errors in + JSON itself shall be ignored. + """ + processed = 0 + stripped_text = [] + for line in text.split('\n'): + match = _strip_comment_re.match(line) + + if match is None: # unterminated string + # ignore this error, let json module report it + stripped = line + elif len(match[2]) == 1: + raise json.JSONDecodeError(_('bad_comment'), text, + processed + len(match[1])) + else: + stripped = match[1] + + stripped_text.append(stripped) + processed += len(line) + 1 + + return '\n'.join(stripped_text) + +def normalize_version(ver: list[int]) -> list[int]: + """Strip right-most zeroes from 'ver'. The original list is not modified.""" + new_len = 0 + for i, num in enumerate(ver): + if num != 0: + new_len = i + 1 + + return ver[:new_len] + +def parse_version(ver_str: str) -> list[int]: + """ + Convert 'ver_str' into an array representation, e.g. for ver_str="4.6.13.0" + return [4, 6, 13, 0]. + """ + return [int(num) for num in ver_str.split('.')] + +def version_string(ver: list[int], rev: Optional[int]=None) -> str: + """ + Produce version's string representation (optionally with revision), like: + 1.2.3-5 + No version normalization is performed. + """ + 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 + +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_filename: str) -> Draft7Validator: + """ + Prepare a validator for one of the schemas in '../schemas'. + + This function is not thread-safe. + """ + return Draft7Validator(resolver.resolve(schema_filename)[1], + resolver=resolver) + +def translation(localedir: Union[Path, str], lang: Optional[str]=None) \ + -> gettext.GNUTranslations: + """ + Configure translations for domain 'hydrilla-messages' and return the object + that represents them. + + If `lang` is set, look for translations for `lang`. Otherwise, try to + determine system's default language and use that. + """ + # https://stackoverflow.com/questions/3425294/how-to-detect-the-os-default-language-in-python + # But I am not going to surrender to Microbugs' nonfree, crappy OS to test + # it, to the lines inside try: may fail. + if lang is None: + try: + from ctypes.windll import kernel32 as windll + lang = locale.windows_locale[windll.GetUserDefaultUILanguage()] + except: + lang = locale.getdefaultlocale()[0] or 'en_US' + + localedir = Path(localedir) + if not (localedir / lang).is_dir(): + lang = 'en_US' + + return gettext.translation('hydrilla-messages', localedir=localedir, + languages=[lang]) + +_ = translation(here.parent / 'builder' / 'locales').gettext |