def test_coerce(): underlying_spec = in_range(1, 2) s = coerce(int, underlying_spec) check_spec(s, 1) check_spec(s, "1", expected_conform=1) # TODO: problem contains underlying_spec. Not sure yet if this is the right behaviour check_spec(s, 2, [Problem(path(), 2, underlying_spec, "not between 1 and 2")]) check_spec(s, "2", [Problem(path(), 2, underlying_spec, "not between 1 and 2")]) check_spec(s, "one", [Problem(path(), "one", s, "could not coerce 'one' (str) using coercer: int because:\n" "invalid literal for int() with base 10: 'one'")]) spec_using_a_class_as_a_coercer = coerce(CoercingClass(), underlying_spec) check_spec(spec_using_a_class_as_a_coercer, "one", [Problem(path(), "one", spec_using_a_class_as_a_coercer, "could not coerce 'one' (str) using coercer: CoercingClass because:\n" "invalid literal for int() with base 10: 'one'")]) spec_using_a_lambda_as_a_coercer = coerce(lambda x: int(x), underlying_spec) check_spec(spec_using_a_lambda_as_a_coercer, "one", [Problem(path(), "one", spec_using_a_lambda_as_a_coercer, "could not coerce 'one' (str) using coercer: <lambda> because:\n" "invalid literal for int() with base 10: 'one'")])
def test_even(): s = even() check_spec(s, 2) check_spec(s, 3, [Problem(path(), 3, s, "not an even number")]) check_spec(s, "", [Problem(path(), "", s, "not an even number")])
def test_is_none(): s = is_none() check_spec(s, None) check_spec(s, "", [Problem(path(), "", s, "not None")]) check_spec(s, [], [Problem(path(), [], s, "not None")])
def test_greater_than(): s = gt(2) check_spec(s, 3) check_spec(s, 2, [Problem(path(), 2, s, "not greater than 2")]) check_spec(s, 1, [Problem(path(), 1, s, "not greater than 2")])
def test_less_than(): s = lt(2) check_spec(s, 1) check_spec(s, 2, [Problem(path(), 2, s, "not less than 2")]) check_spec(s, 3, [Problem(path(), 3, s, "not less than 2")])
def test_odd(): s = odd() check_spec(s, 3) check_spec(s, 4, [Problem(path(), 4, s, "not an odd number")]) check_spec(s, "", [Problem(path(), "", s, "not an odd number")])
def test_in_range(): s = in_range(2, 4) check_spec(s, 2) check_spec(s, 3) check_spec(s, 1, [Problem(path(), 1, s, "not between 2 and 4")]) check_spec(s, 4, [Problem(path(), 4, s, "not between 2 and 4")])
def test_greater_than_or_equal_to(): s = gte(2) check_spec(s, 3) check_spec(s, 2) check_spec(s, 1, [Problem(path(), 1, s, "not greater than or equal to 2")]) check_spec(s, 0, [Problem(path(), 0, s, "not greater than or equal to 2")])
def test_less_than_or_equal_to(): s = lte(2) check_spec(s, 1) check_spec(s, 2) check_spec(s, 3, [Problem(path(), 3, s, "not less than or equal to 2")]) check_spec(s, 4, [Problem(path(), 4, s, "not less than or equal to 2")])
def test_is_in_over_lists(): s = is_in(["a", "b"]) check_spec(s, "a") check_spec(s, "b") check_spec(s, "c", [Problem(path(), "c", specize(s), "not in ['a', 'b']")])
def test_is_in_over_dicts(): s = is_in({"a": 1, "b": 2}) check_spec(s, "a") check_spec(s, "b") check_spec(s, "c", [Problem(path(), "c", specize(s), "not in ['a', 'b']")])
def test_sets(): s = {"a", "b"} check_spec(s, "a") check_spec(s, "b") check_spec(s, "c", [Problem(path(), "c", specize(s), "not in ['a', 'b']")])
def test_is_instance(): s = is_instance(int) check_spec(s, 1) check_spec(s, "", [Problem(path(), "", s, "expected an int but got a str")]) assert is_instance(int) == is_instance(int)
def explain(self, p: Path, x: object) -> List[Problem]: # noinspection PyBroadException try: c = self._coercer(x) except Exception as e: return [Problem(p, x, self, self._explain_coercion_failure(x, e))] else: return super().explain(p, c)
def test_assert(): s = specize(int) assert_spec(s, 1) try: assert_spec(s, "one") assert False, "Expected exception" except SpecError as e: error = e assert error.explanation == Explanation.with_problems(Problem(path(), "one", s, "expected an int but got a str"))
def test_coll_of(): item_spec = specize(int) s = coll_of(item_spec) check_spec(s, [1]) check_spec(s, [1, 2]) check_spec(s, (1, 2)) try: assert_spec(s, ["one", 2, "three"]) assert False, "Expected exception" except SpecError as e: error = e assert error.explanation == Explanation.with_problems( Problem(path(0), "one", item_spec, "expected an int but got a str"), Problem(path(2), "three", item_spec, "expected an int but got a str")) try: assert_spec(s, 1) assert False, "Expected exception" except SpecError as e: error = e assert error.explanation == Explanation.with_problems(Problem(path(), 1, s, "not iterable"))
def test_dict_example_treats_values_as_equal_to_spec(): expected_value = UUID('80b71e04-9862-462b-ac0c-0c34dc272c7b') s = dict_example({'k': expected_value}) check_spec(s, {'k': expected_value}) wrong_value = UUID('a5bef1a0-d139-49d3-91ff-79a69aa39759') check_spec(s, {'k': wrong_value}, [ Problem( path('k'), wrong_value, equal_to(expected_value), "expected 80b71e04-9862-462b-ac0c-0c34dc272c7b (UUID) but got a5bef1a0-d139-49d3-91ff-79a69aa39759 (UUID)" ) ])
def explain(self, p: Path, x: object) -> List[Problem]: if not isinstance(x, Mapping): return [Problem(p, x, self, "not a Mapping")] problems = [] for typevar, names in self._typevar_to_attr_names.items(): first_name_found = next((name for name in names if name in x), None) implied_type = type( x[first_name_found]) if first_name_found else None s = self._spec_generator(implied_type) for name in names: value = x[name] ps = s.explain(p, value) problems.extend(ps) return problems
def explain(self, p: Path, x: object) -> List[Problem]: if not _acceptably_dict_like(x): return [Problem(p, x, self, "not a dictionary {}".format(type(x)))] problems = [] for k, s in self._key_to_spec.items(): if k not in x: problems.append("Missing {}".format(k)) continue value = x[k] explanation_path = p + path(k) subspec_problems = s.explain(explanation_path, value) if subspec_problems: problems.extend(subspec_problems) return problems
def explain(self, p: Path, x: object) -> List[Problem]: return [Problem(p, x, self, "this spec will always fail")]
def test_equal_to(): s = equal_to(1) check_spec(s, 1) check_spec(s, 2, [Problem(path(), 2, s, "expected 1 (int) but got 2 (int)")])
def explain(self, p: Path, x: object) -> List[Problem]: if x in self._coll: return [] else: return [Problem(p, x, self, "not {}".format(self.describe()))]
def test_types_as_specs(): s = int check_spec(s, 1) check_spec(s, "", [Problem(path(), "", is_instance(s), "expected an int but got a str")])