Last active
October 29, 2020 22:05
-
-
Save edvardm/c6f6689aee493710f8389c28bc0ae3d6 to your computer and use it in GitHub Desktop.
Show power of PyContracts through mutually recursive typedef
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
from contracts import contract, new_contract | |
import pytest | |
from contracts.interface import ContractNotRespected | |
import json | |
# There are probably bugs, but this should check that given | |
# argument is a valid object which can be encoded as json. | |
# | |
# It assumes the following Python structure, if type hints | |
# could be recursive: | |
# | |
# JsonObj = Union[JsonDict, JsonList] | |
# JsonDict = Union[EmptyDict, Dict[str, JsonValue]] | |
# JsonList = Union[EmptyList, List[JsonValue], Tuple[JsonValue, ...]] | |
# JsonValue = Union[str, int, float, bool, None, JsonObj] | |
def scalar(obj): | |
return obj == None or isinstance(obj, (int, float, str, bool)) | |
# note: mutually recursive with valid_json_root() | |
def json_value(obj): | |
return scalar(obj) or json_obj(obj) | |
def json_obj(obj): | |
if not isinstance(obj, (list, dict, tuple)): | |
return False | |
if isinstance(obj, (list, tuple)): | |
return all(json_value(x) for x in obj) | |
return all(isinstance(key, str) and json_value(val) for key, val in obj.items()) | |
new_contract("json_obj", lambda x: json_obj(x)) | |
# This is how it can be then used: single, clean decorator should now | |
# accept only objects which can be encoded as json | |
@contract(obj="json_obj") | |
def encode(obj): | |
json.dumps(obj) | |
def describe_valid_cases(): | |
def describe_simple(): | |
def empty_object(): | |
encode(dict()) | |
def empty_list(): | |
encode([]) | |
def simple_list(): | |
encode([1]) | |
def simple_dict(): | |
encode({"a": 1}) | |
def describe_nested(): | |
probes = ( | |
[1, {"a": 2}], | |
[1, {"a": [2]}], | |
[1, None, [{"a": 1}]], | |
[1, (1, 2)], # encodes equal to [1, [1, 2]] | |
) | |
@pytest.mark.parametrize("probe", probes) | |
def lists(probe): | |
encode(probe) | |
probes = ({"a": [1]}, {"a": {"b": 1}}, {"a": {"b": [1, {"c": []}]}}) | |
@pytest.mark.parametrize("probe", probes) | |
def dicts(probe): | |
encode(probe) | |
def describe_invalid(): | |
probes = (1, None, True, {1: "a"}) | |
@pytest.mark.parametrize("probe", probes) | |
def invalid(probe): | |
with pytest.raises(ContractNotRespected): | |
encode(probe) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment