From 7cfa7908fbdaff7f14332c57f70efb40adf7ac93 Mon Sep 17 00:00:00 2001 From: Stephen Rosen Date: Wed, 7 Dec 2022 15:24:03 +0000 Subject: [PATCH] Harmonize annotations with python/typeshed#8608 python/typeshed#8608 introduced annotations for `create` which are not fully reflected here. In order to reflect that state into `jsonschema`, a new module, `jsonschema._typing` is introduced. The purpose of `_typing` is to be a singular place for the library to define type aliases and any typing-related utilities which may be needed. This will let us use aliases like `_typing.JsonValue` in many locations where any valid JSON datatype is accepted. The definitions can be refined over time as necessary. Initial definitions in `_typing` are: - Schema (any JSON object) - JsonObject (any JSON object) - JsonValue (any JSON value, including objects or sequences) `Schema` is just another name for `JsonObject`. Perhaps it is not needed, but the name may help clarify things to a reader. It is not obvious at present whether or not it is a good or bad idea to notate it as such, but a similar Schema alias is defined in typeshed and seems to be working there to annotate things accurately. These types are using `Mapping` and `Sequence` rather than `dict` or `list`. The rationale is that `jsonschema`'s logic does not dictate that the objects used *must* be defined in stdlib types or subtypes thereof. For example, a `collections.UserDict` could be used and should be accepted by the library (`UserDict` wraps but does not inherit from `dict`.) --- jsonschema/_typing.py | 14 ++++++++++++++ jsonschema/validators.py | 24 ++++++++++++++++-------- 2 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 jsonschema/_typing.py diff --git a/jsonschema/_typing.py b/jsonschema/_typing.py new file mode 100644 index 000000000..211344c03 --- /dev/null +++ b/jsonschema/_typing.py @@ -0,0 +1,14 @@ +""" +Type aliases and utilities to help manage `typing` usage and type annotations. +""" + +from collections.abc import Mapping, Sequence +import typing + +Schema = Mapping[str, typing.Any] + +JsonObject = Mapping[str, typing.Any] + +JsonValue = typing.Union[ + JsonObject, Sequence[typing.Any], str, int, float, bool, None +] diff --git a/jsonschema/validators.py b/jsonschema/validators.py index 66e803ea2..9ff631c6d 100644 --- a/jsonschema/validators.py +++ b/jsonschema/validators.py @@ -23,6 +23,7 @@ _format, _legacy_validators, _types, + _typing, _utils, _validators, exceptions, @@ -30,6 +31,11 @@ _UNSET = _utils.Unset() +_ValidatorCallback = typing.Callable[ + [typing.Any, typing.Any, _typing.JsonValue, _typing.JsonObject], + typing.Iterator[exceptions.ValidationError], +] + _VALIDATORS: dict[str, typing.Any] = {} _META_SCHEMAS = _utils.URIDict() _VOCABULARIES: list[tuple[str, typing.Any]] = [] @@ -114,13 +120,15 @@ def _store_schema_list(): def create( - meta_schema, - validators=(), + meta_schema: _typing.Schema, + validators: Mapping[str, _ValidatorCallback] | tuple[()] = (), version=None, - type_checker=_types.draft202012_type_checker, - format_checker=_format.draft202012_format_checker, - id_of=_id_of, - applicable_validators=methodcaller("items"), + type_checker: _types.TypeChecker = _types.draft202012_type_checker, + format_checker: _format.FormatChecker = _format.draft202012_format_checker, + id_of: typing.Callable[[_typing.Schema], str] = _id_of, + applicable_validators: typing.Callable[ + [_typing.Schema], typing.Iterable[tuple[str, _ValidatorCallback]] + ] = methodcaller("items"), ): """ Create a new validator class. @@ -186,7 +194,7 @@ def create( @attr.s class Validator: - VALIDATORS = dict(validators) + VALIDATORS: dict[str, _ValidatorCallback] = dict(validators) META_SCHEMA = dict(meta_schema) TYPE_CHECKER = type_checker FORMAT_CHECKER = format_checker_arg @@ -339,7 +347,7 @@ def is_valid(self, instance, _schema=None): if version is not None: safe = version.title().replace(" ", "").replace("-", "") Validator.__name__ = Validator.__qualname__ = f"{safe}Validator" - Validator = validates(version)(Validator) + Validator = validates(version)(Validator) # type: ignore[misc] return Validator