Project

General

Profile

Download (4.21 KB) Statistics
| Branch: | Tag: | Revision:

hydrilla-builder / src / hydrilla / util / _util.py @ 456ad6c0

1
# SPDX-License-Identifier: AGPL-3.0-or-later
2

    
3
# Building Hydrilla packages.
4
#
5
# This file is part of Hydrilla
6
#
7
# Copyright (C) 2021, 2022 Wojtek Kosior
8
#
9
# This program is free software: you can redistribute it and/or modify
10
# it under the terms of the GNU Affero General Public License as
11
# published by the Free Software Foundation, either version 3 of the
12
# License, or (at your option) any later version.
13
#
14
# This program is distributed in the hope that it will be useful,
15
# but WITHOUT ANY WARRANTY; without even the implied warranty of
16
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
# GNU Affero General Public License for more details.
18
#
19
# You should have received a copy of the GNU Affero General Public License
20
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
21
#
22
#
23
# I, Wojtek Kosior, thereby promise not to sue for violation of this
24
# file's license. Although I request that you do not make use this code
25
# in a proprietary program, I am not going to enforce this in court.
26

    
27
import re
28
import json
29

    
30
from pathlib import Path
31
from typing import Optional
32

    
33
from jsonschema import RefResolver, Draft7Validator
34

    
35
here = Path(__file__).resolve().parent
36

    
37
_strip_comment_re = re.compile(r'''
38
^ # match from the beginning of each line
39
( # catch the part before '//' comment
40
  (?: # this group matches either a string or a single out-of-string character
41
    [^"/] |
42
    "
43
    (?: # this group matches any in-a-string character
44
      [^"\\] |          # match any normal character
45
      \\[^u] |          # match any escaped character like '\f' or '\n'
46
      \\u[a-fA-F0-9]{4} # match an escape
47
    )*
48
    "
49
  )*
50
)
51
# expect either end-of-line or a comment:
52
# * unterminated strings will cause matching to fail
53
# * bad comment (with '/' instead of '//') will be indicated by second group
54
#   having length 1 instead of 2 or 0
55
(//?|$)
56
''', re.VERBOSE)
57

    
58
def strip_json_comments(text: str) -> str:
59
    """
60
    Accept JSON text with optional C++-style ('//') comments and return the text
61
    with comments removed. Consecutive slashes inside strings are handled
62
    properly. A spurious single slash ('/') shall generate an error. Errors in
63
    JSON itself shall be ignored.
64
    """
65
    processed = 0
66
    stripped_text = []
67
    for line in text.split('\n'):
68
        match = _strip_comment_re.match(line)
69

    
70
        if match is None: # unterminated string
71
            # ignore this error, let json module report it
72
            stripped = line
73
        elif len(match[2]) == 1:
74
            raise json.JSONDecodeError('bad comment', text,
75
                                       processed + len(match[1]))
76
        else:
77
            stripped = match[1]
78

    
79
        stripped_text.append(stripped)
80
        processed += len(line) + 1
81

    
82
    return '\n'.join(stripped_text)
83

    
84
def normalize_version(ver: list[int]) -> list[int]:
85
    """Strip right-most zeroes from 'ver'. The original list is not modified."""
86
    new_len = 0
87
    for i, num in enumerate(ver):
88
        if num != 0:
89
            new_len = i + 1
90

    
91
    return ver[:new_len]
92

    
93
def parse_version(ver_str: str) -> list[int]:
94
    """
95
    Convert 'ver_str' into an array representation, e.g. for ver_str="4.6.13.0"
96
    return [4, 6, 13, 0].
97
    """
98
    return [int(num) for num in ver_str.split('.')]
99

    
100
def version_string(ver: list[int], rev: Optional[int]=None) -> str:
101
    """
102
    Produce version's string representation (optionally with revision), like:
103
        1.2.3-5
104
    No version normalization is performed.
105
    """
106
    return '.'.join([str(n) for n in ver]) + ('' if rev is None else f'-{rev}')
107

    
108
schemas = {}
109
for path in (here.parent / 'schemas').glob('*-1.schema.json'):
110
    schema = json.loads(path.read_text())
111
    schemas[schema['$id']] = schema
112

    
113
common_schema_filename = 'common_definitions-1.schema.json'
114
common_schema_path = here.parent / "schemas" / common_schema_filename
115

    
116
resolver = RefResolver(
117
    base_uri=f'file://{str(common_schema_path)}',
118
    referrer=f'https://hydrilla.koszko.org/{common_schema_filename}',
119
    store=schemas
120
)
121

    
122
def validator_for(schema_filename: str) -> Draft7Validator:
123
    """
124
    Prepare a validator for one of the schemas in '../schemas'.
125

    
126
    This function is not thread-safe.
127
    """
128
    return Draft7Validator(resolver.resolve(schema_filename)[1],
129
                           resolver=resolver)
(2-2/2)