Skip to content
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

Fix incorrect schema dict for arrays, rename Schema.schema to jsonschema_dict #30

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,49 @@ bases of Person::
.. autoclass:: wysdom.ReadsYAML
:members:


Internals
=========

Schema objects
--------------

.. autoclass:: wysdom.Schema
:members:

Base schemas
............

The following schemas define simple atomic schemas
(defined in the subpackage `wysdom.base_schema`):

=============== ==================================================================
Name Description
=============== ==================================================================
Schema abstract base class
SchemaType abstract base class for any schema with the "type" directive
SchemaAnything any valid JSON will be accepted
SchemaConst a string constant
SchemaNone a null value
SchemaPrimitive a primitive variable
=============== ==================================================================

Object schemas
..............

The following schemas define complex schemas which reference other schemas
(defined in the subpackage `wysdom.object_schema`):

=============== ==================================================================
Name Description
=============== ==================================================================
SchemaAnyOf Any of the permitted schemas supplied
SchemaArray An array (corresponding to a Python list)
SchemaObject An object with named properties
SchemaDict An object with dynamic properties (corresponding to a Python dict)
=============== ==================================================================


Indices and tables
==================

Expand Down
45 changes: 45 additions & 0 deletions features/dict.feature
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,50 @@ Feature: Test JSON DOM objects
}
example = dict_module.Person(example_dict_input)
example_dict_output = example.to_builtin()
expected_schema = {
"additionalProperties": False,
"properties": {
"first_name": {"type": "string"},
"last_name": {"type": "string"},
"current_address": {
"additionalProperties": False,
"properties": {
"city": {"type": "string"},
"first_line": {"type": "string"},
"postal_code": {"type": "integer"},
"second_line": {"type": "string"}
},
"type": "object"
},
"previous_addresses": {
"array": {
"items": {
"additionalProperties": False,
"properties": {
"city": {"type": "string"},
"first_line": {"type": "string"},
"postal_code": {"type": "integer"},
"second_line": {"type": "string"}
},
"type": "object"
}
}
},
"vehicles": {
"properties": {},
"additionalProperties": {
"properties": {
"color": {"type": "string"},
"description": {"type": "string"}
},
"additionalProperties": False,
"type": "object"
},
"type": "object"
}
},
"type": "object"
}
"""
Then the following statements are true:
"""
Expand Down Expand Up @@ -63,6 +107,7 @@ Feature: Test JSON DOM objects
document(example["vehicles"]["eabf04"]) is example
key(example["vehicles"]["eabf04"]) == "eabf04"
schema(example).is_valid(example_dict_input)
schema(example).jsonschema_dict == expected_schema
example_dict_output == example_dict_input
"""

Expand Down
42 changes: 37 additions & 5 deletions wysdom/base_schema/Schema.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,55 @@
from abc import ABC, abstractmethod
from typing import Any, Dict, Tuple

from ..exceptions import ValidationError
from jsonschema.validators import validator_for


class Schema(ABC):
"""
Abstract base class for JSON schemas. Objects of type `Schema` can be used to generate
jsonschema-compatible dictionaries, and to validate potential input data against those
schemas.

Objects of type `Schema` are also callable, and when called will create DOM objects or
primitive Python object containing the data that is supplied to them.
"""

@abstractmethod
def __call__(
self,
value: Any,
dom_info: Tuple = None
) -> Any:
return value
"""
Return either a DOM object or primitive Python object containing the data
supplied in `value`, if `value` is a valid instance of this schema.

:param value: The raw value for which to create an object
:param dom_info: An optional tuple containing DOM information for the object, if relevant
:return: A DOM object or primitive Python object containing the data in `value`
"""
if self.is_valid(value):
return value
else:
raise ValidationError(
f"The supplied value does not conform to this schema: {value}"
)

@property
@abstractmethod
def schema(self) -> Dict[str, Any]:
return {}
def jsonschema_dict(self) -> Dict[str, Any]:
"""
Compile this schema as a jsonschema-compatible dictionary.

:return: A jsonschema-compatible dictionary
"""
pass

def is_valid(self, value: Any) -> bool:
return validator_for(self.schema)(self.schema).is_valid(value)
"""
Determine whether a given object conforms to this schema.

:param value: An object to test for validity against this schema
:return: True if the object is valid, otherwise False
"""
return validator_for(self.jsonschema_dict)(self.jsonschema_dict).is_valid(value)
9 changes: 1 addition & 8 deletions wysdom/base_schema/SchemaAnything.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,6 @@

class SchemaAnything(Schema):

def __call__(
self,
value: Any,
dom_info: Tuple = None
) -> Any:
return value

@property
def schema(self) -> Dict[str, Any]:
def jsonschema_dict(self) -> Dict[str, Any]:
return {}
11 changes: 1 addition & 10 deletions wysdom/base_schema/SchemaConst.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,8 @@ def __init__(
) -> None:
self.value = value

def __call__(
self,
value: str,
dom_info: Tuple = None
) -> Any:
if value != self.value:
raise ValueError(f"Value can only be '{self.value}'.")
return self.value

@property
def schema(self) -> Dict[str, Any]:
def jsonschema_dict(self) -> Dict[str, Any]:
return {
"const": self.value
}
9 changes: 0 additions & 9 deletions wysdom/base_schema/SchemaNone.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,5 @@


class SchemaNone(SchemaType):

type_name: str = 'null'

def __call__(
self,
value: None,
dom_info: Tuple = None
) -> Any:
if value is not None:
raise ValueError('Value can only be None.')
return None
2 changes: 1 addition & 1 deletion wysdom/base_schema/SchemaPrimitive.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def __call__(
value: Any,
dom_info: Tuple = None
) -> Any:
return self.python_type(value)
return super().__call__(self.python_type(value))

@property
def type_name(self) -> str:
Expand Down
2 changes: 1 addition & 1 deletion wysdom/base_schema/SchemaType.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def type_name(self) -> str:
pass

@property
def schema(self) -> Dict[str, Any]:
def jsonschema_dict(self) -> Dict[str, Any]:
return {"type": self.type_name}


4 changes: 2 additions & 2 deletions wysdom/object_schema/SchemaAnyOf.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ def __call__(
return valid_schemas[0](value, dom_info)

@property
def schema(self) -> Dict[str, Any]:
def jsonschema_dict(self) -> Dict[str, Any]:
return {
'anyOf': [
allowed_schema.schema
allowed_schema.jsonschema_dict
for allowed_schema in self.allowed_schemas
]
}
4 changes: 2 additions & 2 deletions wysdom/object_schema/SchemaArray.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ def __call__(
)

@property
def schema(self) -> Dict[str, Any]:
def jsonschema_dict(self) -> Dict[str, Any]:
return {
"array": {
"items": self.items
"items": self.items.jsonschema_dict
}
}
8 changes: 4 additions & 4 deletions wysdom/object_schema/SchemaObject.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ def __call__(
return self.object_type(value, dom_info)

@property
def schema(self) -> Dict[str, Any]:
def jsonschema_dict(self) -> Dict[str, Any]:
return {
**super().schema,
**super().jsonschema_dict,
'properties': {
k: v.schema
k: v.jsonschema_dict
for k, v in self.properties.items()
},
"additionalProperties": (
self.additional_properties.schema
self.additional_properties.jsonschema_dict
if isinstance(self.additional_properties, Schema)
else self.additional_properties
)
Expand Down