-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Validate dependencies.yaml using jsonschema (#29)
This PR enables validating the contents of a dependencies.yaml file directly without doing any processing. The schema is encoded using [JSON Schema](https://json-schema.org/) and validated using [the Python implementation](https://python-jsonschema.readthedocs.io/). The new Python code is fairly minimal, and it would be even shorter except that I leveraged the object-oriented API to show all errors in a file instead of simply showing the first error using `jsonschema.validate`. The majority of the new lines are from the schema definition. The validation is injected into the normal CLI usage so that schemas are always validated before dependency files are generated, ensuring that developers see useful errors about why their dependencies.yaml file is invalid rather than opaque runtime errors when dfg fails to use the file. --------- Co-authored-by: Simon Adorf <[email protected]>
- Loading branch information
Showing
10 changed files
with
310 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
include src/rapids_dependency_file_generator/schema.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
32 changes: 32 additions & 0 deletions
32
src/rapids_dependency_file_generator/rapids_dependency_file_validator.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
"""Logic for validating dependency files.""" | ||
|
||
import json | ||
import textwrap | ||
|
||
import jsonschema | ||
import pkg_resources | ||
from jsonschema.exceptions import best_match | ||
|
||
SCHEMA = json.loads(pkg_resources.resource_string(__name__, "schema.json")) | ||
|
||
|
||
def validate_dependencies(dependencies): | ||
"""Valid a dictionary against the dependencies.yaml spec. | ||
Parameters | ||
---------- | ||
dependencies : dict | ||
The parsed dependencies.yaml file. | ||
Raises | ||
------ | ||
jsonschema.exceptions.ValidationError | ||
If the dependencies do not conform to the schema | ||
""" | ||
validator = jsonschema.Draft7Validator(SCHEMA) | ||
errors = list(validator.iter_errors(dependencies)) | ||
if len(errors) > 0: | ||
print("The provided dependency file contains schema errors.") | ||
best_matching_error = best_match(errors) | ||
print("\n", textwrap.indent(str(best_matching_error), "\t"), "\n") | ||
raise RuntimeError("The provided dependencies data is invalid.") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
{ | ||
"$schema": "http://json-schema.org/draft-07/schema#", | ||
"$id": "https://raw.githubusercontent.com/rapidsai/dependency-file-generator/v1.0.0/src/rapids_dependency_file_generator/schema.json", | ||
"type": "object", | ||
"title": "RAPIDS Package Dependency Specification Format", | ||
"description": "Consolidated specification of RAPIDS project dependencies", | ||
"properties": { | ||
"files": { | ||
"type": "object", | ||
"patternProperties": { | ||
".*": { | ||
"type": "object", | ||
"properties": { | ||
"output": {"$ref": "#/$defs/outputs"}, | ||
"includes": {"type": "array", "items": {"type": "string"}}, | ||
"matrix": {"$ref": "#/$defs/matrix"}, | ||
"requirements_dir": {"type": "string"}, | ||
"conda_dir": {"type": "string"} | ||
}, | ||
"additionalProperties": false, | ||
"required": ["output", "includes"] | ||
} | ||
}, | ||
"minProperties": 1 | ||
}, | ||
"dependencies": { | ||
"type": "object", | ||
"patternProperties": { | ||
".*": { | ||
"type": "object", | ||
"properties": { | ||
"common": { | ||
"type": "array", | ||
"items": { | ||
"type": "object", | ||
"properties": { | ||
"output_types": {"$ref": "#/$defs/outputs"}, | ||
"packages": {"$ref": "#/$defs/packages"} | ||
}, | ||
"required": ["output_types", "packages"], | ||
"additionalProperties": false | ||
} | ||
}, | ||
"specific": { | ||
"type": "array", | ||
"items": { | ||
"type": "object", | ||
"properties": { | ||
"output_types": {"$ref": "#/$defs/outputs"}, | ||
"matrices": {"$ref": "#/$defs/matrices"} | ||
}, | ||
"required": ["output_types", "matrices"], | ||
"additionalProperties": false | ||
} | ||
} | ||
}, | ||
"minProperties": 1, | ||
"additionalProperties": false | ||
} | ||
} | ||
}, | ||
"channels": {"$ref": "#/$defs/channels"} | ||
}, | ||
"required": ["files", "dependencies"], | ||
"additionalProperties": false, | ||
"$defs": { | ||
"channel": { | ||
"type": "string", | ||
"format": "iri-reference" | ||
}, | ||
"channel-list": { | ||
"type": "array", | ||
"items": { | ||
"$ref": "#/$defs/channel" | ||
} | ||
}, | ||
"channels": { | ||
"$oneOf": [ | ||
{"$ref": "#/$defs/channel"}, | ||
{"$ref": "#/$defs/channel-list"} | ||
] | ||
}, | ||
"matrix": { | ||
"type": "object", | ||
"patternProperties": { | ||
".*": { | ||
"type": "array", | ||
"items": {"type": "string"} | ||
} | ||
} | ||
}, | ||
"matrix-matcher": { | ||
"type": "object", | ||
"properties": { | ||
"matrix": { | ||
"oneOf": [ | ||
{ | ||
"type": "object", | ||
"patternProperties": { | ||
".*": {"type": "string"} | ||
} | ||
}, | ||
{"type": "null"} | ||
] | ||
}, | ||
"packages": {"oneOf": [ | ||
{"$ref": "#/$defs/requirements"}, | ||
{"type": "null"} | ||
]} | ||
}, | ||
"requiredProperties": ["matrix", "packages"], | ||
"additionalProperties": false | ||
}, | ||
"matrices": { | ||
"type": "array", | ||
"items": {"$ref": "#/$defs/matrix-matcher"} | ||
}, | ||
"output-types": { | ||
"enum": ["conda", "requirements"] | ||
}, | ||
"output-types-array": { | ||
"type": "array", | ||
"item": {"$ref": "#/$defs/output-types"} | ||
}, | ||
"outputs": { | ||
"oneOf": [ | ||
{"$ref": "#/$defs/output-types"}, | ||
{"$ref": "#/$defs/output-types-array"}, | ||
{"const": "none"} | ||
] | ||
}, | ||
"packages": { | ||
"type": "array", | ||
"items": { | ||
"oneOf": [ | ||
{"$ref": "#/$defs/requirement"}, | ||
{"$ref": "#/$defs/pip-requirements"} | ||
] | ||
} | ||
}, | ||
"requirement": { | ||
"type": "string" | ||
}, | ||
"requirements": { | ||
"type": "array", | ||
"items": { | ||
"$ref": "#/$defs/requirement" | ||
}, | ||
"minItems": 1 | ||
|
||
}, | ||
"pip-requirements": { | ||
"type": "object", | ||
"properties": { | ||
"pip": {"$ref": "#/$defs/requirements"} | ||
}, | ||
"additionalProperties": false, | ||
"required": ["pip"] | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import pytest | ||
|
||
from rapids_dependency_file_generator.rapids_dependency_file_validator import SCHEMA | ||
|
||
|
||
@pytest.fixture(scope="session") | ||
def schema(): | ||
return SCHEMA |
46 changes: 46 additions & 0 deletions
46
tests/examples/invalid/invalid-requirement/dependencies.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
files: | ||
build: | ||
output: conda | ||
conda_dir: output/actual | ||
matrix: | ||
cuda: ["11.5", "11.6"] | ||
arch: [x86_64] | ||
includes: | ||
- build | ||
channels: | ||
- rapidsai | ||
- conda-forge | ||
dependencies: | ||
build: | ||
common: | ||
- output_types: [conda, requirements] | ||
packages: | ||
- clang=11.1.0 | ||
- spdlog>=1.8.5,<1.9 | ||
- output_types: conda | ||
packages: | ||
- pip | ||
- pip: | ||
- git+https://github.com/python-streamz/streamz.git@master | ||
specific: | ||
- output_types: [conda, requirements] | ||
matrices: | ||
- matrix: | ||
cuda: "11.5" | ||
packages: | ||
- 1234 | ||
- cuda-python>=11.5,<11.7.1 | ||
- matrix: | ||
cuda: "11.6" | ||
packages: | ||
- cuda-python>=11.6,<11.7.1 | ||
- output_types: conda | ||
matrices: | ||
- matrix: | ||
cuda: "11.5" | ||
packages: | ||
- cudatoolkit=11.5 | ||
- matrix: | ||
cuda: "11.6" | ||
packages: | ||
- cudatoolkit=11.6 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import jsonschema | ||
|
||
|
||
def test_schema_is_valid(schema): | ||
jsonschema.Draft7Validator.check_schema(schema) |