def test_children_have_their_errors_dicts_built(self): e1, e2 = ( exceptions.ValidationError("1", validator="foo", path=["bar", 0]), exceptions.ValidationError("2", validator="quux", path=["bar", 0]), ) tree = exceptions.ErrorTree([e1, e2]) self.assertEqual(tree["bar"][0].errors, {"foo": e1, "quux": e2})
def test_short_paths_are_better_matches(self): shallow = exceptions.ValidationError("Oh no!", path=["baz"]) deep = exceptions.ValidationError("Oh yes!", path=["foo", "bar"]) match = max([shallow, deep], key=exceptions.relevance) self.assertIs(match, shallow) match = max([deep, shallow], key=exceptions.relevance) self.assertIs(match, shallow)
def test_it_creates_a_child_tree_for_each_nested_path(self): errors = [ exceptions.ValidationError("a bar message", path=["bar"]), exceptions.ValidationError("a bar -> 0 message", path=["bar", 0]), ] tree = exceptions.ErrorTree(errors) self.assertIn(0, tree["bar"]) self.assertNotIn(1, tree["bar"])
def test_weak_validators_are_lower_priority(self): weak = exceptions.ValidationError("Oh no!", path=[], validator="a") normal = exceptions.ValidationError("Oh yes!", path=[], validator="b") best_match = exceptions.by_relevance(weak="a") match = max([weak, normal], key=best_match) self.assertIs(match, normal) match = max([normal, weak], key=best_match) self.assertIs(match, normal)
def test_multiple_errors_with_instance(self): e1, e2 = ( exceptions.ValidationError("1", validator="foo", path=["bar", "bar2"], instance="i1"), exceptions.ValidationError("2", validator="quux", path=["foobar", 2], instance="i2"), ) exceptions.ErrorTree([e1, e2])
def test_strong_validators_are_higher_priority(self): weak = exceptions.ValidationError("Oh no!", path=[], validator="a") normal = exceptions.ValidationError("Oh yes!", path=[], validator="b") strong = exceptions.ValidationError("Oh fine!", path=[], validator="c") best_match = exceptions.by_relevance(weak="a", strong="c") match = max([weak, normal, strong], key=best_match) self.assertIs(match, strong) match = max([strong, normal, weak], key=best_match) self.assertIs(match, strong)
def test_regression_multiple_errors_with_instance(self): e1, e2 = ( exceptions.ValidationError("1", validator="foo", path=["bar", "bar2"], instance="i1"), exceptions.ValidationError("2", validator="quux", path=["foobar", 2], instance="i2"), ) # Will raise an exception if the bug is still there. exceptions.ErrorTree([e1, e2])
def test_repr(self): e1, e2 = ( exceptions.ValidationError( "1", validator="foo", path=["bar", "bar2"], instance="i1"), exceptions.ValidationError( "2", validator="quux", path=["foobar", 2], instance="i2"), ) tree = exceptions.ErrorTree([e1, e2]) self.assertEqual(repr(tree), "<ErrorTree (2 total errors)>")
def test_global_errors_are_even_better_matches(self): shallow = exceptions.ValidationError("Oh no!", path=[]) deep = exceptions.ValidationError("Oh yes!", path=["foo"]) errors = sorted([shallow, deep], key=exceptions.relevance) self.assertEqual( [list(error.path) for error in errors], [["foo"], []], ) errors = sorted([deep, shallow], key=exceptions.relevance) self.assertEqual( [list(error.path) for error in errors], [["foo"], []], )
def properties(validator, properties, instance, schema): if not validator.is_type(instance, "object"): return # for managing defaults in the schema for property, subschema in properties.items(): if "default" in subschema: instance.setdefault(property, subschema["default"]) # for handling additional properties found in user spec for property, value in instance.items(): if property in properties: for error in validator.descend( value, properties[property], path=properties[property], schema_path=properties[property], ): yield error else: error = "Additional properties are not allowed : %r" % ( property) yield exceptions.ValidationError(error)
def test_validate(self): # set up expected = "No exception" side_effect = [ expected, exceptions.SchemaError("SchemaError"), exceptions.ValidationError("ValidationError") ] returned_defaults = Mock() validator_with_defaults = Mock(return_value=returned_defaults) validate = Mock(side_effect=side_effect) returned_defaults.validate = validate self.validator.schema = "schema" self.validator._extend_with_default = \ Mock(return_value=validator_with_defaults) cfg = "cfg" # test positive case result = self.validator.validate(cfg) self.validator._extend_with_default.assert_called_once_with( Draft4Validator) assert result == expected, expected validate.assert_called_once_with(cfg) validator_with_defaults.assert_called_once_with(self.validator.schema) # test negative cases expected_exceptions = [F5CcclError, F5CcclError] while expected_exceptions: expected = expected_exceptions.pop(0) with pytest.raises(expected): self.validator.validate(cfg)
def test_unset_error(self): error = exceptions.ValidationError("message") self.assertEqual(str(error), "message") kwargs = { "validator": "type", "validator_value": "string", "instance": 5, "schema": {"type": "string"}, } # Just the message should show if any of the attributes are unset for attr in kwargs: k = dict(kwargs) del k[attr] error = exceptions.ValidationError("message", **k) self.assertEqual(str(error), "message")
def _soft_validate_additional_properties(validator, aP, instance, schema): """This validator function is used for legacy v2 compatible mode in v2.1. This will skip all the addtional properties checking but keep check the 'patternProperties'. 'patternProperties' is used for metadata API. """ if not validator.is_type(instance, "object"): return properties = schema.get("properties", {}) patterns = "|".join(schema.get("patternProperties", {})) extra_properties = set() for prop in instance: if prop not in properties: if patterns: if not re.search(patterns, prop): extra_properties.add(prop) else: extra_properties.add(prop) if not extra_properties: return if patterns: error = "Additional properties are not allowed (%s %s unexpected)" if len(extra_properties) == 1: verb = "was" else: verb = "were" yield jsonschema_exc.ValidationError( error % (", ".join(repr(extra) for extra in extra_properties), verb)) else: for prop in extra_properties: del instance[prop]
def __call__(self, **op_kwargs): try: return super(OSLCallableOperation, self).__call__( **op_kwargs).response().result except (SwaggerMappingError, jsonschema.exceptions.ValidationError) as e: exc_type, exc_value, exc_traceback = sys.exc_info() LOG.exception( 'Exception happens during request or response parsing. ' 'Please check schema is valid and server version corresponds ' 'to the declared') raise six.reraise( exceptions.ValidationError, exceptions.ValidationError(e), exc_traceback) except Exception as e: exc_type, exc_value, exc_traceback = sys.exc_info() LOG.exception( 'Exception happens while getting response from server. ' 'This could be part of the normal flow ' 'like 404 for non existing objects') new_exception = exceptions.UnexpectedResponse(e, e) raise six.reraise( exceptions.UnexpectedResponse, new_exception, exc_traceback)
def test_it_knows_how_many_total_errors_it_contains(self): # FIXME: https://github.com/Julian/jsonschema/issues/442 errors = [ exceptions.ValidationError("Something", validator=i) for i in range(8) ] tree = exceptions.ErrorTree(errors) self.assertEqual(tree.total_errors, 8)
def to_internal_value(self, data): """Verbatim copy of the original drf `to_internal_value()` method. This method replicates the implementation found on `restframework.serializers.Serializer.to_internal_value` method because the dynamic-rest package (which we are using, and which overrides the base implementation) adds some custom stuff on top of it which depends on the input data containing the instance's `id` property, which we are not requiring. A HarvestableResource's `id` is an implementation detail that is not exposed publicly. We rely on the instance's `unique_identifier` instead. """ if not isinstance(data, typing.Mapping): message = self.error_messages['invalid'].format( datatype=type(data).__name__ ) raise exceptions.ValidationError({ api_settings.NON_FIELD_ERRORS_KEY: [message] }, code='invalid') ret = collections.OrderedDict() errors = collections.OrderedDict() fields = self._writable_fields for field in fields: validate_method = getattr(self, f"validate_{field.field_name}", None) primitive_value = field.get_value(data) try: validated_value = field.run_validation(primitive_value) if validate_method is not None: validated_value = validate_method(validated_value) except exceptions.ValidationError as exc: errors[field.field_name] = exc.detail except DjangoValidationError as exc: errors[field.field_name] = get_error_detail(exc) except SkipField: pass else: set_value(ret, field.source_attrs, validated_value) if errors: raise exceptions.ValidationError(errors) return ret
def test_it_knows_how_many_total_errors_it_contains(self): # FIXME: #442 errors = [ exceptions.ValidationError("Something", validator=i) for i in range(8) ] tree = exceptions.ErrorTree(errors) self.assertEqual(tree.total_errors, 8)
def iter_errors(self, instance, _schema=None): if _schema is None: _schema = self.schema if _schema is True: return elif _schema is False: yield exceptions.ValidationError( "False schema does not allow %r" % (instance, ), validator=None, validator_value=None, instance=instance, schema=_schema, ) return scope = id_of(_schema) if scope: self.resolver.push_scope(scope) try: ref = _schema.get(u"$ref") if ref is not None: validators = [(u"$ref", ref)] else: validators = iteritems(_schema) for k, v in validators: validator = self.VALIDATORS.get(k) if validator is None: continue if PY36 and inspect.isasyncgenfunction(validator): bp = AsyncValidationBreakpoint( coroutine=validator, value=v, validator=k, validator_value=v, instance=instance, schema=_schema, ) yield bp errors = bp.errors else: errors = validator(self, v, instance, _schema) or () for error in errors: # set details if not already set by the called fn error._set( validator=k, validator_value=v, instance=instance, schema=_schema, ) if k != u"$ref": error.schema_path.appendleft(k) yield error finally: if scope: self.resolver.pop_scope()
def test_main_with_mock_invalid_source(self, mock_parse_arguments, mock_setup_logger, mock_Source): mock_parse_arguments.return_value = \ argparse.Namespace(debug=False, source=sentinel.source) mock_Source.return_value.compile.side_effect = \ jsonschema_exceptions.ValidationError(sentinel.message) with self.assertRaises(SystemExit): cli.main()
def make_error(self, **kwargs): defaults = dict( message=u"hello", validator=u"type", validator_value=u"string", instance=5, schema={u"type": u"string"}, ) defaults.update(kwargs) return exceptions.ValidationError(**defaults)
def _soft_validate_additional_properties(validator, additional_properties_value, instance, schema): """This validator function is used for legacy v2 compatible mode in v2.1. This will skip all the additional properties checking but keep check the 'patternProperties'. 'patternProperties' is used for metadata API. If there are not any properties on the instance that are not specified in the schema, this will return without any effect. If there are any such extra properties, they will be handled as follows: - if the validator passed to the method is not of type "object", this method will return without any effect. - if the 'additional_properties_value' parameter is True, this method will return without any effect. - if the schema has an additionalProperties value of True, the extra properties on the instance will not be touched. - if the schema has an additionalProperties value of False and there aren't patternProperties specified, the extra properties will be stripped from the instance. - if the schema has an additionalProperties value of False and there are patternProperties specified, the extra properties will not be touched and raise validation error if pattern doesn't match. """ if (not validator.is_type(instance, "object") or additional_properties_value): return properties = schema.get("properties", {}) patterns = "|".join(schema.get("patternProperties", {})) extra_properties = set() for prop in instance: if prop not in properties: if patterns: if not re.search(patterns, prop): extra_properties.add(prop) else: extra_properties.add(prop) if not extra_properties: return if patterns: error = "Additional properties are not allowed (%s %s unexpected)" if len(extra_properties) == 1: verb = "was" else: verb = "were" yield jsonschema_exc.ValidationError( error % (", ".join(repr(extra) for extra in extra_properties), verb)) else: for prop in extra_properties: del instance[prop]
def test_if_its_in_the_tree_anyhow_it_does_not_raise_an_error(self): """ If a validator is dumb (like :validator:`required` in draft 3) and refers to a path that isn't in the instance, the tree still properly returns a subtree for that path. """ error = exceptions.ValidationError( "a message", validator="foo", instance={}, path=["foo"], ) tree = exceptions.ErrorTree([error]) self.assertIsInstance(tree["foo"], exceptions.ErrorTree)
def iter_errors(self, instance, _schema=None): if _schema is not None: warnings.warn( ("Passing a schema to Validator.iter_errors " "is deprecated and will be removed in a future " "release. Call validator.evolve(schema=new_schema)." "iter_errors(...) instead."), DeprecationWarning, stacklevel=2, ) else: _schema = self.schema if _schema is True: return elif _schema is False: yield exceptions.ValidationError( f"False schema does not allow {instance!r}", validator=None, validator_value=None, instance=instance, schema=_schema, ) return scope = id_of(_schema) if scope: self.resolver.push_scope(scope) try: for k, v in applicable_validators(_schema): validator = self.VALIDATORS.get(k) if validator is None: continue errors = validator(self, v, instance, _schema) or () for error in errors: # set details if not already set by the called fn error._set( validator=k, validator_value=v, instance=instance, schema=_schema, ) if k not in {"if", "$ref"}: error.schema_path.appendleft(k) yield error finally: if scope: self.resolver.pop_scope()
def iter_errors(self, instance, _schema=None): if _schema is None: _schema = self.schema if _schema is True: return elif _schema is False: yield exceptions.ValidationError( "False schema does not allow %r" % (instance, ), validator=None, validator_value=None, instance=instance, schema=_schema, ) return scope = id_of(_schema) if scope: self.resolver.push_scope(scope) try: ref = _schema.get(u"$ref") if ref is not None: validators = [(u"$ref", ref)] else: validators = iteritems(_schema) for k, v in validators: validator = self.VALIDATORS.get(k) if validator is None: continue errors = validator(self, v, instance, _schema) or () for error in errors: # set details if not already set by the called fn error._set( validator=k, validator_value=v, instance=instance, schema=_schema, ) if k not in {u"if", u"$ref"}: error.schema_path.appendleft(k) yield error finally: if scope: self.resolver.pop_scope()
def test_str_works_with_instances_having_overriden_eq_operator(self): """ Check for https://github.com/Julian/jsonschema/issues/164 which rendered exceptions unusable when a `ValidationError` involved instances with an `__eq__` method that returned truthy values. """ instance = mock.MagicMock() error = exceptions.ValidationError( "a message", validator="foo", instance=instance, validator_value="some", schema="schema", ) str(error) self.assertFalse(instance.__eq__.called)
def validate(self, loaded_user_configuration: Any) -> str: """Perform validation on the configuration against supported schemas. :param loaded_user_configuration: A Python object containing user config. :returns: The first configuration schema version that passes validation. :raises: :class:`jsonschema.exceptions.ValidationError` """ validation_exception = exceptions.ValidationError("No schema found!") for version, schema in self.schemas.items(): try: resolver = RefResolver("file://" + str(self._schema_folder), None) validate(loaded_user_configuration, schema, resolver=resolver) return version except exceptions.ValidationError as last_exception: validation_exception = last_exception raise validation_exception
def test_str_works_with_instances_having_overriden_eq_operator(self): """ Check for https://github.com/Julian/jsonschema/issues/164 which rendered exceptions unusable when a `ValidationError` involved instances with an `__eq__` method that returned truthy values. """ class DontEQMeBro(object): def __eq__(this, other): # pragma: no cover self.fail("Don't!") def __ne__(this, other): # pragma: no cover self.fail("Don't!") instance = DontEQMeBro() error = exceptions.ValidationError( "a message", validator="foo", instance=instance, validator_value="some", schema="schema", ) self.assertIn(repr(instance), str(error))
def test_repr(self): self.assertEqual( repr(exceptions.ValidationError(message="Hello!")), "<ValidationError: %r>" % "Hello!", )
def test_it_does_not_contain_subtrees_that_are_not_in_the_instance(self): error = exceptions.ValidationError("123", validator="foo", instance=[]) tree = exceptions.ErrorTree([error]) with self.assertRaises(IndexError): tree[0]
def test_validators_that_failed_appear_in_errors_dict(self): error = exceptions.ValidationError("a message", validator="foo") tree = exceptions.ErrorTree([error]) self.assertEqual(tree.errors, {"foo": error})