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

How to apply custom validator to schemas that specify $schema? #1133

Closed
Vidminas opened this issue Jul 18, 2023 · 1 comment
Closed

How to apply custom validator to schemas that specify $schema? #1133

Vidminas opened this issue Jul 18, 2023 · 1 comment

Comments

@Vidminas
Copy link

This seems related to f79bad5, but I'm facing the issue in jsonschema v4.18.3.

While making a custom validator that would allow relative minimum/maximum constraints, I found it was not applying when validating against a schema that includes another full schema. This is a trimmed version of the validator:

from jsonschema import validators
from jsonschema.validators import Draft202012Validator
from jsonschema.exceptions import ValidationError

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

    def set_minvars(validator, properties, instance, schema):
        for property, subschema in properties.items():
            if property not in instance:
                continue
            if "minVariable" in subschema:
                if subschema["minVariable"] not in instance:
                    yield ValidationError(
                        f"Field {subschema['minVariable']!r} referenced in minVariable constraint not found!"
                    )
                elif instance[property] < instance[subschema["minVariable"]]:
                    yield ValidationError(
                        f"{property!r}={instance[property]} is less than the minimum {subschema['minVariable']!r}={instance[subschema['minVariable']]}!"
                    )

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

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

MinVarValidator = extend_with_variables(Draft202012Validator)

When it's used to validate a single schema, this works, even when it contains refs, for example:

schema = {
    "type": "object",
    "allOf": [
        { "$ref": "#/$defs/min" },
        { "$ref": "#/$defs/max" },
    ],
    "required": ["fridge_min_temp", "fridge_max_temp"],
    "$defs": {
        "min": { "properties": { "fridge_min_temp": { "type": "integer", "minimum": -20, "maximum": 5 } } },
        "max": { "properties": { "fridge_max_temp": { "type": "integer", "minVariable": "fridge_min_temp", "maximum": 5 } } },
    }
}
MinVarValidator(schema).validate({
    "fridge_min_temp": 5,
    "fridge_max_temp": 4,
})

produces:

ValidationError: 'fridge_max_temp'=4 is less than the minimum 'fridge_min_temp'=5!

Failed validating 'properties' in schema['allOf'][1]:
    {'properties': {'fridge_max_temp': {'maximum': 5,
                                        'minVariable': 'fridge_min_temp',
                                        'type': 'integer'}}}

On instance:
    {'fridge_max_temp': 4, 'fridge_min_temp': 5}

But if I add the $schema keyword to the subschema containing 'minVariable' then it passes validation, like this:

schema = {
    "type": "object",
    "allOf": [
        { "$ref": "#/$defs/min" },
        { "$ref": "#/$defs/max" },
    ],
    "required": ["fridge_min_temp", "fridge_max_temp"],
    "$defs": {
        "min": { "properties": { "fridge_min_temp": { "type": "integer", "minimum": -20, "maximum": 5 } } },
        "max": { 
            "$schema": "https://json-schema.org/draft/2020-12/schema",
            "properties": { "fridge_max_temp": { "type": "integer", "minVariable": "fridge_min_temp", "maximum": 5 } }
        },
    }
}
MinVarValidator(schema).validate({
    "fridge_min_temp": 5,
    "fridge_max_temp": 4,
})

Looking deeper with a debugger, it seems that the ref is resolved correctly, but the $schema makes it choose the Draft202012Validator's properties validator for the referenced schema, instead of continuing to use my custom one, here:
https://github.com/python-jsonschema/jsonschema/blob/main/jsonschema/validators.py#L358C51-L358C51

This toy example may not make much sense, but it's usual practice to specify both $id and $schema for schemas defined in separate files. Since my custom validator extends Draft202012Validator, I would expect it to apply to all schemas that declare the Draft202012 standard. Is this a bug or intended behaviour? If it's intended, is there a way to specify which validator to use for referenced schemas?

@Julian
Copy link
Member

Julian commented Jul 18, 2023

Hey! Thanks.

This looks like a duplicate of #994 (so going to close and ask you to have a look there and leave a comment if you'd like).

It's essentially intended behavior to be honest -- when you say "$schema": "https://json-schema.org/draft/2020-12/schema" you are saying "my schema is a draft 2020-12 schema, use those semantics", and indeed in 2020-12, there are no such keywords.

The right thing to do as I mentioned there is to use your own new URI representing your own new dialect (even though it's extending draft 2020-12), and then use that as your $schema value.

Though as you see I've left the issue open in case some other alternative comes up for solving this sort of use case where people seem to want to redefine what it means to be draft 2020-12 (or whatever dialect).

@Julian Julian closed this as not planned Won't fix, can't repro, duplicate, stale Jul 18, 2023
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

No branches or pull requests

2 participants