-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Validation of User Configuration #28
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
804b50d
feat: validation for pipeline dictionary
pgierz 79c0c68
fixes tests
pgierz 8abd771
feat: hooks up pipeline validator
pgierz b61e7b9
feat: rules validator
pgierz 6dee697
fixes last tests for cerberus
pgierz 06270e1
Merge branch 'main' into feat/cerberus
pgierz f424a25
fix: allowed input_source and input_type is handled by cerberus
pgierz ce92a48
proper docs for cerberus
pgierz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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
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,111 @@ | ||
""" | ||
Provides validation of user configuration files by checking against a schema. | ||
""" | ||
|
||
import importlib | ||
|
||
from cerberus import Validator | ||
|
||
|
||
class PipelineValidator(Validator): | ||
""" | ||
Validator for pipeline configuration. | ||
|
||
See Also | ||
-------- | ||
* https://cerberus-sanhe.readthedocs.io/customize.html#class-based-custom-validators | ||
""" | ||
|
||
def _validate_is_qualname(self, is_qualname, field, value): | ||
"""Test if a string is a Python qualname. | ||
|
||
The rule's arguments are validated against this schema: | ||
{'type': 'boolean'}. This means that you can use a boolean value | ||
for the schema argument "is_qualname" in your rule definition. | ||
""" | ||
if is_qualname and not isinstance(value, str): | ||
self._error(field, "Must be a string") | ||
if is_qualname: | ||
parts = value.split(".") | ||
module_name, attr_name = ".".join(parts[:-1]), parts[-1] | ||
try: | ||
module = importlib.import_module(module_name) | ||
if not hasattr(module, attr_name): | ||
self._error(field, "Must be a valid Python qualname") | ||
except (ImportError, ModuleNotFoundError): | ||
self._error(field, "Must be a valid Python qualname") | ||
|
||
def _validate(self, document): | ||
super()._validate(document) | ||
if "steps" not in document and "uses" not in document: | ||
self._error( | ||
"document", 'At least one of "steps" or "uses" must be specified' | ||
) | ||
|
||
|
||
PIPELINES_SCHEMA = { | ||
"pipelines": { | ||
"type": "list", | ||
"schema": { | ||
"type": "dict", | ||
"schema": { | ||
"name": {"type": "string", "required": False}, | ||
"uses": {"type": "string", "excludes": "steps"}, | ||
"steps": { | ||
"type": "list", | ||
"excludes": "uses", | ||
"schema": {"type": "string", "is_qualname": True}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
} | ||
"""dict : Schema for validating pipelines configuration.""" | ||
|
||
PIPELINES_VALIDATOR = PipelineValidator(PIPELINES_SCHEMA) | ||
"""Validator : Validator for pipelines configuration.""" | ||
|
||
RULES_SCHEMA = { | ||
"rules": { | ||
"type": "list", | ||
"schema": { | ||
"type": "dict", | ||
"schema": { | ||
"name": {"type": "string", "required": False}, | ||
"cmor_variable": {"type": "string", "required": True}, | ||
"input_type": { | ||
"type": "string", | ||
"required": False, | ||
"allowed": [ | ||
"xr.DataArray", | ||
"xr.Dataset", | ||
], | ||
}, | ||
"input_source": { | ||
"type": "string", | ||
"required": False, | ||
"allowed": [ | ||
"xr_tutorial", | ||
], | ||
}, | ||
"input_patterns": { | ||
"type": "list", | ||
"schema": {"type": "string"}, | ||
"required": True, | ||
}, | ||
"enabled": {"type": "boolean", "required": False}, | ||
"description": {"type": "string", "required": False}, | ||
"pipelines": { | ||
"type": "list", | ||
# FIXME(PG): Should cross-check with pipelines. | ||
"schema": {"type": "string"}, | ||
}, | ||
"cmor_units": {"type": "string", "required": False}, | ||
# FIXME(PS): How is it currently defined? | ||
"model_units": {"type": "string", "required": False}, | ||
}, | ||
}, | ||
}, | ||
} | ||
"""dict : Schema for validating rules configuration.""" | ||
RULES_VALIDATOR = Validator(RULES_SCHEMA) |
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,49 @@ | ||
import pytest | ||
|
||
from pymorize.validate import PIPELINES_SCHEMA, PipelineValidator | ||
|
||
|
||
@pytest.fixture | ||
def validator(): | ||
return PipelineValidator(PIPELINES_SCHEMA) | ||
|
||
|
||
def test_initialize(validator): | ||
assert validator.schema == PIPELINES_SCHEMA | ||
|
||
|
||
def test_is_qualname(validator): | ||
# Test with valid qualname | ||
validator._validate_is_qualname(True, "field", "os.path.join") | ||
|
||
|
||
def test_is_qualname_error(validator): | ||
# Test with invalid qualname | ||
with pytest.raises(Exception): | ||
validator._validate_is_qualname(True, "field", "non.existent.module") | ||
|
||
|
||
def test_validate(validator): | ||
# Test with valid document | ||
document = {"pipelines": [{"steps": ["os.path.join"]}]} | ||
assert validator.validate(document) | ||
|
||
|
||
def test_validate_neither_steps_nor_uses(validator): | ||
# Test with invalid document (neither 'steps' nor 'uses' specified) | ||
document = {"name": "test"} | ||
valid_document = validator.validate(document) | ||
assert valid_document is False | ||
# with pytest.raises( | ||
# Exception, match='At least one of "steps" or "uses" must be specified' | ||
# ): | ||
# validator.validate(document) | ||
|
||
|
||
def test_validate_error_non_qualname(validator): | ||
# Test with invalid pipeline configuration (invalid 'steps' qualname) | ||
pipelines = {"pipelines": [{"name": "test", "steps": ["non.existent.module"]}]} | ||
valid_document = validator.validate(pipelines) | ||
assert valid_document is False | ||
# with pytest.raises(Exception, match="Must be a valid Python qualname"): | ||
# validator.validate(pipelines) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@siligam: This still needs to be added correctly for
units.py
. I wasn't sure how you had designed it.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pgierz "model_units" is referred as "source_units" in units.py. It is a optional parameter. If it is not provided, it reads the model units from xarray dataset. At times, user prefers to use some other unit instead of units from xarray dataset. In this case, "source_units" takes precedence if provided.
The setting "model_units" : {"required": False} is apt.