diff --git a/.gitignore b/.gitignore index 0c4a6b122..ec4149629 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,157 @@ -_cache -_static -_templates +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ *.egg-info/ -build -dist +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock -TODO +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintainted in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# User defined +_cache +_static +_templates diff --git a/jsonschema/protocols.py b/jsonschema/protocols.py index ea0044696..96e3885e1 100644 --- a/jsonschema/protocols.py +++ b/jsonschema/protocols.py @@ -79,6 +79,10 @@ class Validator(Protocol): #: :validator:`type` properties in JSON schemas. TYPE_CHECKER: ClassVar[jsonschema.TypeChecker] + #: A `jsonschema.FormatChecker` that will be used when validating + #: :validator:`format` properties in JSON schemas. + FORMAT_CHECKER: ClassVar[jsonschema.FormatChecker] + #: The schema that was passed in when initializing the object. schema: dict | bool diff --git a/jsonschema/validators.py b/jsonschema/validators.py index ef8f082d5..67ba1d96a 100644 --- a/jsonschema/validators.py +++ b/jsonschema/validators.py @@ -17,6 +17,7 @@ import attr from jsonschema import ( + _format, _legacy_validators, _types, _utils, @@ -108,6 +109,7 @@ def create( validators=(), version=None, type_checker=_types.draft7_type_checker, + format_checker=_format.draft7_format_checker, id_of=_id_of, applicable_validators=lambda schema: schema.items(), ): @@ -148,6 +150,13 @@ def create( If unprovided, a `jsonschema.TypeChecker` will be created with a set of default types typical of JSON Schema drafts. + format_checker (jsonschema.FormatChecker): + + a format checker, used when applying the :validator:`format` validator. + + If unprovided, a `jsonschema.FormatChecker` will be created + with a set of default formats typical of JSON Schema drafts. + id_of (collections.abc.Callable): A function that given a schema, returns its ID. @@ -162,6 +171,8 @@ def create( a new `jsonschema.protocols.Validator` class """ + # rename to not clash with "format_checker" argument of `Validator.__init__()` + fmt_checker = format_checker @attr.s class Validator: @@ -169,6 +180,7 @@ class Validator: VALIDATORS = dict(validators) META_SCHEMA = dict(meta_schema) TYPE_CHECKER = type_checker + FORMAT_CHECKER = fmt_checker ID_OF = staticmethod(id_of) schema = attr.ib(repr=reprlib.repr) @@ -283,7 +295,7 @@ def is_valid(self, instance, _schema=None): return Validator -def extend(validator, validators=(), version=None, type_checker=None): +def extend(validator, validators=(), version=None, type_checker=None, format_checker=None): """ Create a new validator class by extending an existing one. @@ -321,6 +333,13 @@ def extend(validator, validators=(), version=None, type_checker=None): If unprovided, the type checker of the extended `jsonschema.protocols.Validator` will be carried along. + format_checker (jsonschema.FormatChecker): + + a format checker, used when applying the :validator:`format` validator. + + If unprovided, the format checker of the extended + `jsonschema.protocols.Validator` will be carried along. + Returns: a new `jsonschema.protocols.Validator` class extending the one @@ -342,11 +361,14 @@ def extend(validator, validators=(), version=None, type_checker=None): if type_checker is None: type_checker = validator.TYPE_CHECKER + if format_checker is None: + format_checker = validator.FORMAT_CHECKER return create( meta_schema=validator.META_SCHEMA, validators=all_validators, version=version, type_checker=type_checker, + format_checker=format_checker, id_of=validator.ID_OF, ) @@ -377,6 +399,7 @@ def extend(validator, validators=(), version=None, type_checker=None): "uniqueItems": _validators.uniqueItems, }, type_checker=_types.draft3_type_checker, + format_checker=_format.draft3_format_checker, version="draft3", id_of=lambda schema: schema.get("id", ""), applicable_validators=_legacy_validators.ignore_ref_siblings, @@ -413,6 +436,7 @@ def extend(validator, validators=(), version=None, type_checker=None): "uniqueItems": _validators.uniqueItems, }, type_checker=_types.draft4_type_checker, + format_checker=_format.draft4_format_checker, version="draft4", id_of=lambda schema: schema.get("id", ""), applicable_validators=_legacy_validators.ignore_ref_siblings, @@ -454,6 +478,7 @@ def extend(validator, validators=(), version=None, type_checker=None): "uniqueItems": _validators.uniqueItems, }, type_checker=_types.draft6_type_checker, + format_checker=_format.draft6_format_checker, version="draft6", applicable_validators=_legacy_validators.ignore_ref_siblings, ) @@ -495,6 +520,7 @@ def extend(validator, validators=(), version=None, type_checker=None): "uniqueItems": _validators.uniqueItems, }, type_checker=_types.draft7_type_checker, + format_checker=_format.draft7_format_checker, version="draft7", applicable_validators=_legacy_validators.ignore_ref_siblings, ) @@ -540,6 +566,7 @@ def extend(validator, validators=(), version=None, type_checker=None): "uniqueItems": _validators.uniqueItems, }, type_checker=_types.draft201909_type_checker, + format_checker=_format.draft201909_format_checker, version="draft2019-09", ) @@ -585,6 +612,7 @@ def extend(validator, validators=(), version=None, type_checker=None): "uniqueItems": _validators.uniqueItems, }, type_checker=_types.draft202012_type_checker, + format_checker=_format.draft202012_format_checker, version="draft2020-12", )