-
Notifications
You must be signed in to change notification settings - Fork 85
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Alternative Schema Locations (#1753)
- Refactors * Migrate from `pkg_resources` to `importlib`. See importlib's migration guide. https://importlib-resources.readthedocs.io/en/latest/migration.html * Add importlib_resources as core dependency for Pythons older than 3.9. * `load_schema` will handle functionality for finding the schema locations locally in multiple places. * `load_schema` will not handle `version` identification (that is done through `validate` only). - Features * Support overriding the paths for finding schemas, using the `pyhf` installed location as a base via `pyhf.schema.variables.path`. * Add support for offline access to currently supported version of schemas (via extra `load_schema` commands). * Add `SchemaNotFound` exception for when a schema cannot be found locally (outside of `jsonschema.RefResolver` calls) - Python API * `pyhf.schema` introduced as a new API for all things schema-related. * `pyhf.schema.version`, `pyhf.schema.path`, `pyhf.schema.load_schema`, and `pyhf.schema.validate` are migrated over from `pyhf.utils`.
- Loading branch information
Showing
33 changed files
with
719 additions
and
128 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
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
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,68 @@ | ||
""" | ||
See :class:`~pyhf.schema.Schema` for documentation. | ||
""" | ||
import pathlib | ||
import sys | ||
from pyhf.schema.loader import load_schema | ||
from pyhf.schema.validator import validate | ||
from pyhf.schema import variables | ||
|
||
__all__ = [ | ||
"load_schema", | ||
"validate", | ||
"path", | ||
"version", | ||
] | ||
|
||
|
||
def __dir__(): | ||
return __all__ | ||
|
||
|
||
class Schema(sys.modules[__name__].__class__): | ||
""" | ||
A module-level wrapper around :mod:`pyhf.schema` which will provide additional functionality for interacting with schemas. | ||
Example: | ||
>>> import pyhf.schema | ||
>>> import pathlib | ||
>>> curr_path = pyhf.schema.path | ||
>>> curr_path # doctest: +ELLIPSIS | ||
PosixPath('.../pyhf/schemas') | ||
>>> pyhf.schema(pathlib.Path('/home/root/my/new/path')) | ||
>>> pyhf.schema.path | ||
PosixPath('/home/root/my/new/path') | ||
>>> pyhf.schema(curr_path) | ||
>>> pyhf.schema.path # doctest: +ELLIPSIS | ||
PosixPath('.../pyhf/schemas') | ||
""" | ||
|
||
def __call__(self, new_path: pathlib.Path): | ||
""" | ||
Change the local search path for finding schemas locally. | ||
Args: | ||
new_path (pathlib.Path): Path to folder containing the schemas | ||
Returns: | ||
None | ||
""" | ||
variables.schemas = new_path | ||
|
||
@property | ||
def path(self): | ||
""" | ||
The local path for schemas. | ||
""" | ||
return variables.schemas | ||
|
||
@property | ||
def version(self): | ||
""" | ||
The default version used for finding schemas. | ||
""" | ||
return variables.SCHEMA_VERSION | ||
|
||
|
||
sys.modules[__name__].__class__ = 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
from pathlib import Path | ||
import sys | ||
import json | ||
import pyhf.exceptions | ||
from pyhf.schema import variables | ||
|
||
# importlib.resources.as_file wasn't added until Python 3.9 | ||
# c.f. https://docs.python.org/3.9/library/importlib.html#importlib.resources.as_file | ||
if sys.version_info >= (3, 9): | ||
from importlib import resources | ||
else: | ||
import importlib_resources as resources | ||
|
||
|
||
def load_schema(schema_id: str): | ||
""" | ||
Get a schema by relative path from cache, or load it into the cache and return. | ||
Args: | ||
schema_id (str): Relative path to schema from :attr:`pyhf.schema.path` | ||
Returns: | ||
schema (dict): The loaded schema. | ||
""" | ||
try: | ||
return variables.SCHEMA_CACHE[ | ||
f'{Path(variables.SCHEMA_BASE).joinpath(schema_id)}' | ||
] | ||
except KeyError: | ||
pass | ||
|
||
ref = variables.schemas.joinpath(schema_id) | ||
with resources.as_file(ref) as path: | ||
if not path.exists(): | ||
raise pyhf.exceptions.SchemaNotFound( | ||
f'The schema {schema_id} was not found. Do you have the right version or the right path? {path}' | ||
) | ||
with path.open() as json_schema: | ||
schema = json.load(json_schema) | ||
variables.SCHEMA_CACHE[schema['$id']] = schema | ||
return variables.SCHEMA_CACHE[schema['$id']] |
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,41 @@ | ||
import jsonschema | ||
import pyhf.exceptions | ||
from pyhf.schema.loader import load_schema | ||
from pyhf.schema import variables | ||
from typing import Union | ||
|
||
|
||
def validate(spec: dict, schema_name: str, version: Union[str, None] = None): | ||
""" | ||
Validate a provided specification against a schema. | ||
Args: | ||
spec (dict): The specification to validate. | ||
schema_name (str): The name of the schema to use. | ||
version (None or str): The version to use if not the default from :attr:`pyhf.schema.version`. | ||
Returns: | ||
None: schema validated fine | ||
Raises: | ||
pyhf.exceptions.InvalidSpecification: the specification is invalid | ||
""" | ||
|
||
version = version or variables.SCHEMA_VERSION | ||
|
||
schema = load_schema(f'{version}/{schema_name}') | ||
|
||
# note: trailing slash needed for RefResolver to resolve correctly | ||
resolver = jsonschema.RefResolver( | ||
base_uri=f"file://{variables.schemas}/", | ||
referrer=f"{version}/{schema_name}", | ||
store=variables.SCHEMA_CACHE, | ||
) | ||
validator = jsonschema.Draft6Validator( | ||
schema, resolver=resolver, format_checker=None | ||
) | ||
|
||
try: | ||
return validator.validate(spec) | ||
except jsonschema.ValidationError as err: | ||
raise pyhf.exceptions.InvalidSpecification(err, schema_name) |
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,13 @@ | ||
import sys | ||
|
||
# importlib.resources.as_file wasn't added until Python 3.9 | ||
# c.f. https://docs.python.org/3.9/library/importlib.html#importlib.resources.as_file | ||
if sys.version_info >= (3, 9): | ||
from importlib import resources | ||
else: | ||
import importlib_resources as resources | ||
schemas = resources.files('pyhf') / "schemas" | ||
|
||
SCHEMA_CACHE = {} | ||
SCHEMA_BASE = "https://scikit-hep.org/pyhf/schemas/" | ||
SCHEMA_VERSION = '1.0.0' |
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
Oops, something went wrong.