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

Add validate hook #1310

Closed
wants to merge 1 commit into from
Closed

Conversation

wodesuck
Copy link

@wodesuck wodesuck commented Oct 27, 2024

Run user-defined hooks after validate.

One usage for me is clearing default keys when validate error in oneOf/anyOf. For example, if we use extend_with_default from docs to validate oneOf schema with default, such as following code:

from jsonschema import Draft202012Validator, validators


def extend_with_default(validator_class):
    validate_properties = validator_class.VALIDATORS["properties"]

    def set_defaults(validator, properties, instance, schema):
        for property, subschema in properties.items():
            if "default" in subschema:
                instance.setdefault(property, subschema["default"])

        for error in validate_properties(
            validator, properties, instance, schema,
        ):
            yield error

    return validators.extend(
        validator_class, {"properties" : set_defaults},
    )


DefaultValidatingValidator = extend_with_default(Draft202012Validator)

obj = {'type': 'second_type'}
schema = {
    'oneOf': [
        {
            'properties': {
                'type': {'const': 'first_type'},
                'foo': {'default': 'bar'},
            },
            'additionalProperties': False,
        },
        {
            'properties': {
                'type': {'const': 'second_type'},
                'baz': {'default': 'qux'},
            },
            'additionalProperties': False,
        },
    ],
}

DefaultValidatingValidator(schema).validate(obj)

We will get an error like this:

jsonschema.exceptions.ValidationError: {'type': 'second_type', 'foo': 'bar', 'baz': 'qux'} is not valid under any of the given schemas

Failed validating 'oneOf' in schema:
    {'oneOf': [{'properties': {'type': {'const': 'first_type'},
                               'foo': {'default': 'bar'}},
                'additionalProperties': False},
               {'properties': {'type': {'const': 'second_type'},
                               'baz': {'default': 'qux'}},
                'additionalProperties': False}]}

On instance:
    {'type': 'second_type', 'foo': 'bar', 'baz': 'qux'}

That is because the "foo" was added when validate "first_type", making the schema of "second_type" invalid. With validate_hooks, we could add a hook to delete "foo" when validate error on "first_type". For example:

def extend_with_default(validator_class):
    validate_properties = validator_class.VALIDATORS["properties"]
    default_keys = {}

    def set_defaults(validator, properties, instance, schema):
        dkeys = []
        for property, subschema in properties.items():
            if "default" in subschema and property not in instance:
                instance[property] = subschema["default"]
                dkeys.append(property)
        if dkeys:
            default_keys[(id(instance), id(schema))] = dkeys

        for error in validate_properties(
            validator, properties, instance, schema,
        ):
            yield error

    def validate_hook(is_valid, instance, schema):
        dkeys = default_keys.pop((id(instance), id(schema)), [])
        if not is_valid:
            for keys in dkeys:
                del instance[keys]

    return validators.extend(
        validator_class, {"properties" : set_defaults},
        validate_hooks = [validate_hook],
    )

📚 Documentation preview 📚: https://python-jsonschema--1310.org.readthedocs.build/en/1310/

@Julian
Copy link
Member

Julian commented Oct 27, 2024

Hi there. Thanks for your contribution.
It's good to discuss these sorts of changes ahead of time.
I wouldn't want to touch any of this kind of functionality until we support JSON Schema annotations, and possibly until we rewrite the validator interface to support them.
I also wouldn't want to do this to specifically support the default code, as that was entirely a POC to satisfy simple needs from people looking to do so, we'd need multiple use cases for this functionality I think.
(This also would need tests).
Appreciate your contribution though and definitely happy to discuss somewhere.

@Julian Julian closed this Oct 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants