Spaces:
Running
Running
# The contents of this file are automatically written by | |
# tools/generate_schema_wrapper.py. Do not modify directly. | |
import copy | |
import io | |
import json | |
import jsonschema | |
import pickle | |
import pytest | |
import numpy as np | |
from ..schemapi import ( | |
UndefinedType, | |
SchemaBase, | |
Undefined, | |
_FromDict, | |
SchemaValidationError, | |
) | |
# Make tests inherit from _TestSchema, so that when we test from_dict it won't | |
# try to use SchemaBase objects defined elsewhere as wrappers. | |
class _TestSchema(SchemaBase): | |
def _default_wrapper_classes(cls): | |
return _TestSchema.__subclasses__() | |
class MySchema(_TestSchema): | |
_schema = { | |
"definitions": { | |
"StringMapping": { | |
"type": "object", | |
"additionalProperties": {"type": "string"}, | |
}, | |
"StringArray": {"type": "array", "items": {"type": "string"}}, | |
}, | |
"properties": { | |
"a": {"$ref": "#/definitions/StringMapping"}, | |
"a2": {"type": "object", "additionalProperties": {"type": "number"}}, | |
"b": {"$ref": "#/definitions/StringArray"}, | |
"b2": {"type": "array", "items": {"type": "number"}}, | |
"c": {"type": ["string", "number"]}, | |
"d": { | |
"anyOf": [ | |
{"$ref": "#/definitions/StringMapping"}, | |
{"$ref": "#/definitions/StringArray"}, | |
] | |
}, | |
"e": {"items": [{"type": "string"}, {"type": "string"}]}, | |
}, | |
} | |
class StringMapping(_TestSchema): | |
_schema = {"$ref": "#/definitions/StringMapping"} | |
_rootschema = MySchema._schema | |
class StringArray(_TestSchema): | |
_schema = {"$ref": "#/definitions/StringArray"} | |
_rootschema = MySchema._schema | |
class Derived(_TestSchema): | |
_schema = { | |
"definitions": { | |
"Foo": {"type": "object", "properties": {"d": {"type": "string"}}}, | |
"Bar": {"type": "string", "enum": ["A", "B"]}, | |
}, | |
"type": "object", | |
"additionalProperties": False, | |
"properties": { | |
"a": {"type": "integer"}, | |
"b": {"type": "string"}, | |
"c": {"$ref": "#/definitions/Foo"}, | |
}, | |
} | |
class Foo(_TestSchema): | |
_schema = {"$ref": "#/definitions/Foo"} | |
_rootschema = Derived._schema | |
class Bar(_TestSchema): | |
_schema = {"$ref": "#/definitions/Bar"} | |
_rootschema = Derived._schema | |
class SimpleUnion(_TestSchema): | |
_schema = {"anyOf": [{"type": "integer"}, {"type": "string"}]} | |
class DefinitionUnion(_TestSchema): | |
_schema = {"anyOf": [{"$ref": "#/definitions/Foo"}, {"$ref": "#/definitions/Bar"}]} | |
_rootschema = Derived._schema | |
class SimpleArray(_TestSchema): | |
_schema = { | |
"type": "array", | |
"items": {"anyOf": [{"type": "integer"}, {"type": "string"}]}, | |
} | |
class InvalidProperties(_TestSchema): | |
_schema = { | |
"type": "object", | |
"properties": {"for": {}, "as": {}, "vega-lite": {}, "$schema": {}}, | |
} | |
def test_construct_multifaceted_schema(): | |
dct = { | |
"a": {"foo": "bar"}, | |
"a2": {"foo": 42}, | |
"b": ["a", "b", "c"], | |
"b2": [1, 2, 3], | |
"c": 42, | |
"d": ["x", "y", "z"], | |
"e": ["a", "b"], | |
} | |
myschema = MySchema.from_dict(dct) | |
assert myschema.to_dict() == dct | |
myschema2 = MySchema(**dct) | |
assert myschema2.to_dict() == dct | |
assert isinstance(myschema.a, StringMapping) | |
assert isinstance(myschema.a2, dict) | |
assert isinstance(myschema.b, StringArray) | |
assert isinstance(myschema.b2, list) | |
assert isinstance(myschema.d, StringArray) | |
def test_schema_cases(): | |
assert Derived(a=4, b="yo").to_dict() == {"a": 4, "b": "yo"} | |
assert Derived(a=4, c={"d": "hey"}).to_dict() == {"a": 4, "c": {"d": "hey"}} | |
assert Derived(a=4, b="5", c=Foo(d="val")).to_dict() == { | |
"a": 4, | |
"b": "5", | |
"c": {"d": "val"}, | |
} | |
assert Foo(d="hello", f=4).to_dict() == {"d": "hello", "f": 4} | |
assert Derived().to_dict() == {} | |
assert Foo().to_dict() == {} | |
with pytest.raises(jsonschema.ValidationError): | |
# a needs to be an integer | |
Derived(a="yo").to_dict() | |
with pytest.raises(jsonschema.ValidationError): | |
# Foo.d needs to be a string | |
Derived(c=Foo(4)).to_dict() | |
with pytest.raises(jsonschema.ValidationError): | |
# no additional properties allowed | |
Derived(foo="bar").to_dict() | |
def test_round_trip(): | |
D = {"a": 4, "b": "yo"} | |
assert Derived.from_dict(D).to_dict() == D | |
D = {"a": 4, "c": {"d": "hey"}} | |
assert Derived.from_dict(D).to_dict() == D | |
D = {"a": 4, "b": "5", "c": {"d": "val"}} | |
assert Derived.from_dict(D).to_dict() == D | |
D = {"d": "hello", "f": 4} | |
assert Foo.from_dict(D).to_dict() == D | |
def test_from_dict(): | |
D = {"a": 4, "b": "5", "c": {"d": "val"}} | |
obj = Derived.from_dict(D) | |
assert obj.a == 4 | |
assert obj.b == "5" | |
assert isinstance(obj.c, Foo) | |
def test_simple_type(): | |
assert SimpleUnion(4).to_dict() == 4 | |
def test_simple_array(): | |
assert SimpleArray([4, 5, "six"]).to_dict() == [4, 5, "six"] | |
assert SimpleArray.from_dict(list("abc")).to_dict() == list("abc") | |
def test_definition_union(): | |
obj = DefinitionUnion.from_dict("A") | |
assert isinstance(obj, Bar) | |
assert obj.to_dict() == "A" | |
obj = DefinitionUnion.from_dict("B") | |
assert isinstance(obj, Bar) | |
assert obj.to_dict() == "B" | |
obj = DefinitionUnion.from_dict({"d": "yo"}) | |
assert isinstance(obj, Foo) | |
assert obj.to_dict() == {"d": "yo"} | |
def test_invalid_properties(): | |
dct = {"for": 2, "as": 3, "vega-lite": 4, "$schema": 5} | |
invalid = InvalidProperties.from_dict(dct) | |
assert invalid["for"] == 2 | |
assert invalid["as"] == 3 | |
assert invalid["vega-lite"] == 4 | |
assert invalid["$schema"] == 5 | |
assert invalid.to_dict() == dct | |
def test_undefined_singleton(): | |
assert Undefined is UndefinedType() | |
def dct(): | |
return { | |
"a": {"foo": "bar"}, | |
"a2": {"foo": 42}, | |
"b": ["a", "b", "c"], | |
"b2": [1, 2, 3], | |
"c": 42, | |
"d": ["x", "y", "z"], | |
} | |
def test_copy_method(dct): | |
myschema = MySchema.from_dict(dct) | |
# Make sure copy is deep | |
copy = myschema.copy(deep=True) | |
copy["a"]["foo"] = "new value" | |
copy["b"] = ["A", "B", "C"] | |
copy["c"] = 164 | |
assert myschema.to_dict() == dct | |
# If we ignore a value, changing the copy changes the original | |
copy = myschema.copy(deep=True, ignore=["a"]) | |
copy["a"]["foo"] = "new value" | |
copy["b"] = ["A", "B", "C"] | |
copy["c"] = 164 | |
mydct = myschema.to_dict() | |
assert mydct["a"]["foo"] == "new value" | |
assert mydct["b"][0] == dct["b"][0] | |
assert mydct["c"] == dct["c"] | |
# If copy is not deep, then changing copy below top level changes original | |
copy = myschema.copy(deep=False) | |
copy["a"]["foo"] = "baz" | |
copy["b"] = ["A", "B", "C"] | |
copy["c"] = 164 | |
mydct = myschema.to_dict() | |
assert mydct["a"]["foo"] == "baz" | |
assert mydct["b"] == dct["b"] | |
assert mydct["c"] == dct["c"] | |
def test_copy_module(dct): | |
myschema = MySchema.from_dict(dct) | |
cp = copy.deepcopy(myschema) | |
cp["a"]["foo"] = "new value" | |
cp["b"] = ["A", "B", "C"] | |
cp["c"] = 164 | |
assert myschema.to_dict() == dct | |
def test_attribute_error(): | |
m = MySchema() | |
with pytest.raises(AttributeError) as err: | |
m.invalid_attribute | |
assert str(err.value) == ( | |
"'MySchema' object has no attribute " "'invalid_attribute'" | |
) | |
def test_to_from_json(dct): | |
json_str = MySchema.from_dict(dct).to_json() | |
new_dct = MySchema.from_json(json_str).to_dict() | |
assert new_dct == dct | |
def test_to_from_pickle(dct): | |
myschema = MySchema.from_dict(dct) | |
output = io.BytesIO() | |
pickle.dump(myschema, output) | |
output.seek(0) | |
myschema_new = pickle.load(output) | |
assert myschema_new.to_dict() == dct | |
def test_class_with_no_schema(): | |
class BadSchema(SchemaBase): | |
pass | |
with pytest.raises(ValueError) as err: | |
BadSchema(4) | |
assert str(err.value).startswith("Cannot instantiate object") | |
def test_hash_schema(use_json): | |
classes = _TestSchema._default_wrapper_classes() | |
for cls in classes: | |
hsh1 = _FromDict.hash_schema(cls._schema, use_json=use_json) | |
hsh2 = _FromDict.hash_schema(cls._schema, use_json=use_json) | |
assert hsh1 == hsh2 | |
assert hash(hsh1) == hash(hsh2) | |
def test_schema_validation_error(): | |
try: | |
MySchema(a={"foo": 4}) | |
the_err = None | |
except jsonschema.ValidationError as err: | |
the_err = err | |
assert isinstance(the_err, SchemaValidationError) | |
message = str(the_err) | |
assert message.startswith("Invalid specification") | |
assert "test_schemapi.MySchema->a" in message | |
assert "validating {!r}".format(the_err.validator) in message | |
assert the_err.message in message | |
def test_serialize_numpy_types(): | |
m = MySchema( | |
a={"date": np.datetime64("2019-01-01")}, | |
a2={"int64": np.int64(1), "float64": np.float64(2)}, | |
b2=np.arange(4), | |
) | |
out = m.to_json() | |
dct = json.loads(out) | |
assert dct == { | |
"a": {"date": "2019-01-01T00:00:00"}, | |
"a2": {"int64": 1, "float64": 2}, | |
"b2": [0, 1, 2, 3], | |
} | |