Skip to content

Commit 8f4b253

Browse files
kartbencfriedt
authored andcommitted
scripts: replace pykwalify with jsonschema
Converted `arch-schema.yml`, `board-schema.yml`, and `soc-schema.yml` from PyYAML format to JSON Schema format, including baking in some of the validation rules that were deferred to ad hoc Python right into the schemas (e.g. mutual exclusivity of certain fields). Signed-off-by: Benjamin Cabé <[email protected]>
1 parent d7a5d84 commit 8f4b253

11 files changed

+676
-387
lines changed

scripts/list_boards.py

Lines changed: 11 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,24 @@
1111
from dataclasses import dataclass, field
1212
from pathlib import Path
1313

14+
import jsonschema
1415
import list_hardware
15-
import pykwalify.core
1616
import yaml
17+
from jsonschema.exceptions import best_match
1718
from list_hardware import unique_paths
1819

1920
try:
2021
from yaml import CSafeLoader as SafeLoader
2122
except ImportError:
2223
from yaml import SafeLoader
2324

24-
BOARD_SCHEMA_PATH = str(Path(__file__).parent / 'schemas' / 'board-schema.yml')
25+
BOARD_SCHEMA_PATH = str(Path(__file__).parent / 'schemas' / 'board-schema.yaml')
2526
with open(BOARD_SCHEMA_PATH) as f:
2627
board_schema = yaml.load(f.read(), Loader=SafeLoader)
2728

28-
BOARD_VALIDATOR = pykwalify.core.Core(schema_data=board_schema, source_data={})
29+
validator_class = jsonschema.validators.validator_for(board_schema)
30+
validator_class.check_schema(board_schema)
31+
board_validator = validator_class(board_schema)
2932

3033
BOARD_YML = 'board.yml'
3134

@@ -231,23 +234,14 @@ def load_v2_boards(board_name, board_yml, systems):
231234
with board_yml.open('r', encoding='utf-8') as f:
232235
b = yaml.load(f.read(), Loader=SafeLoader)
233236

234-
try:
235-
BOARD_VALIDATOR.source = b
236-
BOARD_VALIDATOR.validate()
237-
except pykwalify.errors.SchemaError as e:
238-
sys.exit(f'ERROR: Malformed "build" section in file: {board_yml.as_posix()}\n{e}')
239-
240-
mutual_exclusive = {'board', 'boards'}
241-
if len(mutual_exclusive - b.keys()) < 1:
242-
sys.exit(f'ERROR: Malformed content in file: {board_yml.as_posix()}\n'
243-
f'{mutual_exclusive} are mutual exclusive at this level.')
237+
errors = list(board_validator.iter_errors(b))
238+
if errors:
239+
sys.exit('ERROR: Malformed board YAML file: '
240+
f'{board_yml.as_posix()}\n'
241+
f'{best_match(errors).message} in {best_match(errors).json_path}')
244242

245243
board_array = b.get('boards', [b.get('board', None)])
246244
for board in board_array:
247-
mutual_exclusive = {'name', 'extend'}
248-
if len(mutual_exclusive - board.keys()) < 1:
249-
sys.exit(f'ERROR: Malformed "board" section in file: {board_yml.as_posix()}\n'
250-
f'{mutual_exclusive} are mutual exclusive at this level.')
251245

252246
# This is a extending an existing board, place in array to allow later processing.
253247
if 'extend' in board:
@@ -260,19 +254,6 @@ def load_v2_boards(board_name, board_yml, systems):
260254
# Not the board we're looking for, ignore.
261255
continue
262256

263-
board_revision = board.get('revision')
264-
if board_revision is not None and board_revision.get('format') != 'custom':
265-
if board_revision.get('default') is None:
266-
sys.exit(f'ERROR: Malformed "board" section in file: {board_yml.as_posix()}\n'
267-
"Cannot find required key 'default'. Path: '/board/revision.'")
268-
if board_revision.get('revisions') is None:
269-
sys.exit(f'ERROR: Malformed "board" section in file: {board_yml.as_posix()}\n'
270-
"Cannot find required key 'revisions'. Path: '/board/revision.'")
271-
272-
mutual_exclusive = {'socs', 'variants'}
273-
if len(mutual_exclusive - board.keys()) < 1:
274-
sys.exit(f'ERROR: Malformed "board" section in file: {board_yml.as_posix()}\n'
275-
f'{mutual_exclusive} are mutual exclusive at this level.')
276257
socs = [Soc.from_soc(systems.get_soc(s['name']), s.get('variants', []))
277258
for s in board.get('socs', {})]
278259

scripts/list_hardware.py

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,31 @@
99
from dataclasses import dataclass
1010
from pathlib import Path, PurePath
1111

12-
import pykwalify.core
12+
import jsonschema
1313
import yaml
14+
from jsonschema.exceptions import best_match
1415

1516
try:
1617
from yaml import CSafeLoader as SafeLoader
1718
except ImportError:
1819
from yaml import SafeLoader
1920

2021

21-
SOC_SCHEMA_PATH = str(Path(__file__).parent / 'schemas' / 'soc-schema.yml')
22+
SOC_SCHEMA_PATH = str(Path(__file__).parent / 'schemas' / 'soc-schema.yaml')
2223
with open(SOC_SCHEMA_PATH) as f:
2324
soc_schema = yaml.load(f.read(), Loader=SafeLoader)
2425

25-
SOC_VALIDATOR = pykwalify.core.Core(schema_data=soc_schema, source_data={})
26-
27-
ARCH_SCHEMA_PATH = str(Path(__file__).parent / 'schemas' / 'arch-schema.yml')
26+
ARCH_SCHEMA_PATH = str(Path(__file__).parent / 'schemas' / 'arch-schema.yaml')
2827
with open(ARCH_SCHEMA_PATH) as f:
2928
arch_schema = yaml.load(f.read(), Loader=SafeLoader)
3029

31-
ARCH_VALIDATOR = pykwalify.core.Core(schema_data=arch_schema, source_data={})
30+
validator_class = jsonschema.validators.validator_for(soc_schema)
31+
validator_class.check_schema(soc_schema)
32+
soc_validator = validator_class(soc_schema)
33+
34+
validator_class = jsonschema.validators.validator_for(arch_schema)
35+
validator_class.check_schema(arch_schema)
36+
arch_validator = validator_class(arch_schema)
3237

3338
SOC_YML = 'soc.yml'
3439
ARCHS_YML_PATH = PurePath('arch/archs.yml')
@@ -44,12 +49,12 @@ def __init__(self, folder='', soc_yaml=None):
4449
if soc_yaml is None:
4550
return
4651

47-
try:
48-
data = yaml.load(soc_yaml, Loader=SafeLoader)
49-
SOC_VALIDATOR.source = data
50-
SOC_VALIDATOR.validate()
51-
except (yaml.YAMLError, pykwalify.errors.SchemaError) as e:
52-
sys.exit(f'ERROR: Malformed yaml {soc_yaml.as_posix()}', e)
52+
data = yaml.load(soc_yaml, Loader=SafeLoader)
53+
errors = list(soc_validator.iter_errors(data))
54+
if errors:
55+
sys.exit('ERROR: Malformed soc YAML file: \n'
56+
f'{soc_yaml}\n'
57+
f'{best_match(errors).message} in {best_match(errors).json_path}')
5358

5459
for f in data.get('family', []):
5560
family = Family(f['name'], [folder], [], [])
@@ -82,10 +87,6 @@ def __init__(self, folder='', soc_yaml=None):
8287
self._socs.extend(socs)
8388

8489
for soc in data.get('socs', []):
85-
mutual_exclusive = {'name', 'extend'}
86-
if len(mutual_exclusive - soc.keys()) < 1:
87-
sys.exit(f'ERROR: Malformed content in SoC file: {soc_yaml}\n'
88-
f'{mutual_exclusive} are mutual exclusive at this level.')
8990
if soc.get('name') is not None:
9091
self._socs.append(Soc(soc['name'], [c['name'] for c in soc.get('cpuclusters', [])],
9192
[folder], '', ''))
@@ -94,8 +95,9 @@ def __init__(self, folder='', soc_yaml=None):
9495
[c['name'] for c in soc.get('cpuclusters', [])],
9596
[folder], '', ''))
9697
else:
98+
# This should not happen if schema validation passed
9799
sys.exit(f'ERROR: Malformed "socs" section in SoC file: {soc_yaml}\n'
98-
f'Cannot find one of required keys {mutual_exclusive}.')
100+
f'SoC entry must have either "name" or "extend" property.')
99101

100102
# Ensure that any runner configuration matches socs and cpuclusters declared in the same
101103
# soc.yml file
@@ -217,11 +219,11 @@ def find_v2_archs(args):
217219
with Path(archs_yml).open('r', encoding='utf-8') as f:
218220
archs = yaml.load(f.read(), Loader=SafeLoader)
219221

220-
try:
221-
ARCH_VALIDATOR.source = archs
222-
ARCH_VALIDATOR.validate()
223-
except pykwalify.errors.SchemaError as e:
224-
sys.exit(f'ERROR: Malformed "build" section in file: {archs_yml.as_posix()}\n{e}')
222+
errors = list(arch_validator.iter_errors(archs))
223+
if errors:
224+
sys.exit('ERROR: Malformed arch YAML file: '
225+
f'{archs_yml.as_posix()}\n'
226+
f'{best_match(errors).message} in {best_match(errors).json_path}')
225227

226228
if args.arch is not None:
227229
archs = {'archs': list(filter(

scripts/requirements-actions.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ gitlint-core>=0.19.1
1010
gitpython>=3.1.41
1111
ijson
1212
intelhex
13+
jsonschema
1314
junit2html
1415
junitparser>=4.0.1
1516
mypy

0 commit comments

Comments
 (0)