hypothesis-jsonschema icon indicating copy to clipboard operation
hypothesis-jsonschema copied to clipboard

Error resolving sub-schema property with type object or array

Open aciba90 opened this issue 3 years ago • 3 comments

The following jsonschema provokes hypothesis-jsonschema to fail:

from hypothesis import given
from hypothesis_jsonschema import from_schema

schema = {
    "$schema": "http://json-schema.org/draft-04/schema#",
    "$defs": {
        "def_foo": {
            "properties": {
                "foo": {"type": "string"}
            },
            "additionalProperties": False,
        },
        "def_bar": {
            "type": "object",
            "properties": {
                "bar": {
                    "type": ["object", "array"],
                    "$ref": "#/$defs/def_foo",
                    "items": {
                        "type": "object",
                        "$ref": "#/$defs/def_foo",
                    },
                },
            },
            
        },
    },
    "allOf": [
        {"$ref": "#/$defs/def_bar"},
    ],
}


@given(from_schema(schema))
def test_dummy(instance):
    assert True

Exception:

self = <hypothesis_jsonschema._resolve.LocalResolver object at 0x7f2a1058ce10>
document = {'additionalProperties': {'not': {}}, 'items': {'$ref': '#/$defs/def_foo', 'type': 'object'}, 'maxProperties': 1, 'properties': {'foo': {'type': 'string'}}, ...}
fragment = '$defs/def_foo'

    def resolve_fragment(self, document, fragment):
        """
        Resolve a ``fragment`` within the referenced ``document``.
    
        Arguments:
    
            document:
    
                The referent document
    
            fragment (str):
    
                a URI fragment to resolve within it
        """
    
        fragment = fragment.lstrip(u"/")
        parts = unquote(fragment).split(u"/") if fragment else []
    
        for part in parts:
            part = part.replace(u"~1", u"/").replace(u"~0", u"~")
    
            if isinstance(document, Sequence):
                # Array indexes should be turned into integers
                try:
                    part = int(part)
                except ValueError:
                    pass
            try:
                document = document[part]
            except (TypeError, LookupError):
                raise exceptions.RefResolutionError(
>                   "Unresolvable JSON pointer: %r" % fragment
                )
E               jsonschema.exceptions.RefResolutionError: Unresolvable JSON pointer: '$defs/def_foo'

./venv/bin/python3.6/site-packages/jsonschema/validators.py:814: RefResolutionError

Environment:

  • Python version: 3.10.4
  • hypothesis==6.47.0
  • hypothesis-jsonschema==0.22.0
  • jsonschema==4.6.0

aciba90 avatar Jun 09 '22 10:06 aciba90

hypothesis-jsonschema doesn't support JSON Schema drafts newer than Draft 7. I assume that it is the case, as $ref is used along with other keywords in $defs/bar/properties/pbar

Stranger6667 avatar Jun 17 '22 07:06 Stranger6667

hypothesis-jsonschema doesn't support JSON Schema drafts newer than Draft 7. I assume that it is the case, as $ref is used along with other keywords in $defs/bar/properties/pbar

It is a Draft 4 schema. We can see that hypothesis-jsonschema supports references with the following example:

import jsonschema
from hypothesis_jsonschema import from_schema

schema = {
    "$schema": "http://json-schema.org/draft-04/schema#",
    "$defs": {
        "foo": {
            "type": "object",
            "properties": {"a": {"type": "string"}},
            "additionalProperties": False
        },
    },
    "allOf": [
        {"$ref": "#/$defs/foo"},
    ],
}

jsonschema.validate({"a": "asdf"}, schema)
strategy = from_schema(schema)
print(strategy.example())

Executing the previous example outputs:

{'a': '\U0005c9f5<À+'}

aciba90 avatar Jun 22 '22 10:06 aciba90

I have updated the description with a schema containing the Draft version. I have realized that that schema seems to be not 100% correct as:

jsonschema.validate({"bar": {"foo": "asdf"}}, schema)  # valid
jsonschema.validate({"bar": [{"foo": "asdf"}, {"foo": "asdf"}]}, schema)  # valid
jsonschema.validate({"bar": ["0", 1]}, schema) # invalid

The last validation should raise, but it doesn't.

This could mean that is schema is not fully correct, but jsonschema does not raise a SchemaError, therefore it is treating it as valid.

From a hypothesis-jsonschema point of view, I still think the LocalResolver gets confused because:

We define an object and an array together In $defs.def_bar.properties.bar and the fragments are not correctly resolved in order.

aciba90 avatar Jun 22 '22 10:06 aciba90