Skip to content

Chained references are not resolved correctly #292

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

Closed
st31ny opened this issue Aug 22, 2024 · 0 comments · Fixed by #300
Closed

Chained references are not resolved correctly #292

st31ny opened this issue Aug 22, 2024 · 0 comments · Fixed by #300
Labels

Comments

@st31ny
Copy link

st31ny commented Aug 22, 2024

Describe the bug
I only recently tried to switch to version 0.5.5 (from 0.4.6) and noticed that apparently relative $refs in a referenced document are not resolved correctly anymore with the new referencing lib.

So, when I reference a definition d1 within a a schema schema-a from a schema-b, and d1 references another definition d2 within the very same schema-a using a local JSON pointer, the resolution of d2 fails as it searches in schema-b. With version 0.4.6 this exact use case worked using the legacy RefResolver.

Example Schema and code

import python_jsonschema_objects as pjs
import referencing

SCHEMA_A = {
        '$id': 'schema-a',
        '$schema': 'http://json-schema.org/draft-04/schema#',
        'title': "Schema A",
        'definitions': {
                'myint': {
                        'type': 'integer',
                        'minimum': 42
                    },
                'myintarray': {
                        'type': 'array',
                        'items': {
                                '$ref': '#/definitions/myint', # using 'schema-a#/definitions/myint' would work
                            },
                    },
                'myintref': {
                        '$ref': '#/definitions/myint', # using 'schema-a#/definitions/myint' would work
                    },
            },
        'type': 'object',
        'properties': {
                'theint': {
                        '$ref': '#/definitions/myint', # using 'schema-a#/definitions/myint' would work
                    },
            },
    }

SCHEMA_B = {
        '$id': 'schema-b',
        '$schema': 'http://json-schema.org/draft-04/schema#',
        'title': "Schema B",
        'type': 'object',
        'definitions': {
            'myintref': {
                    '$ref': 'schema-a#/definitions/myintref',
                },
        },
        'properties': {
                # all three properties cause the failure
                'obja': {
                        '$ref': 'schema-a',
                    },
                'theintarray': {
                        '$ref': 'schema-a#/definitions/myintarray',
                    },
                'theintref': {
                        '$ref': '#/definitions/myintref',
                    },
            },
    }

registry = referencing.Registry().with_resources([
        ('schema-a', referencing.Resource.from_contents(SCHEMA_A)),
        ('schema-b', referencing.Resource.from_contents(SCHEMA_B)),
    ])

# works fine
builder_a = pjs.ObjectBuilder(SCHEMA_A, registry=registry)
namespace_a = builder_a.build_classes(named_only=False)

# fails
builder_b = pjs.ObjectBuilder(SCHEMA_B, registry=registry)
namespace_b = builder_b.build_classes(named_only=False) # referencing.exceptions.PointerToNowhere: '/definitions/myint' does not exist within SCHEMA_B

b = namespace_b.SchemaB()
b.obja = {'theint': 42}
b.theintarray = [42, 43, 44]
b.theintref = 42
print(b.for_json())

The exception throws by build_classes() actually references SCHEMA_B, although the given pointer /definitions/myint is only ever used in SCHEMA_A.

Expected behavior
I would expect that local JSON pointers are resolved within the current document not within any referencing document.

Workaround
Make all $refs absolute.

Fix
I believe that there needs to be a new ClassBuilder instance with an adapted resolver when recursing in ClassBuilder.resolve_type(). My quick and dirty patch seems to work and the above code prints {'obja': {'theint': 42}, 'theintarray': [42, 43, 44], 'theintref': 42}:

--- a/python_jsonschema_objects/classbuilder.py
+++ b/python_jsonschema_objects/classbuilder.py
@@ -492,7 +492,8 @@
                 )
             )
             resolved = self.resolver.lookup(ref)
-            self.resolved[uri] = self.construct(uri, resolved.contents, (ProtocolBase,))
+            sub_cb = ClassBuilder(referencing._core.Resolver(base_uri=uri.split('#')[0], registry=self.resolver._registry))
+            self.resolved[uri] = sub_cb.construct(uri, resolved.contents, (ProtocolBase,))
             return self.resolved[uri]
 
     def construct(self, uri, *args, **kw):
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant