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

Infinite recursion when "$ref" at root. #92

Open
NfNitLoop opened this issue Jun 2, 2023 · 4 comments
Open

Infinite recursion when "$ref" at root. #92

NfNitLoop opened this issue Jun 2, 2023 · 4 comments
Assignees
Labels
bug Something isn't working

Comments

@NfNitLoop
Copy link

NfNitLoop commented Jun 2, 2023

Describe the bug
When the root type of a schema is a $ref, statham has infinite recursion.

Steps to Reproduce

Working schema:

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    }
  },
  "required": [
    "name"
  ],
  "additionalProperties": false,
  "definitions": {},
  "$schema": "http://json-schema.org/draft-07/schema#"
}

Schema that breaks statham:

{
  "$ref": "#/definitions/Example",
  "definitions": {
    "Example": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string"
        }
      },
      "required": [
        "name"
      ],
      "additionalProperties": false
    }
  },
  "$schema": "http://json-schema.org/draft-07/schema#"
}

Expected behaviour
I expect both above schemas to output identical/similar models.

Actual behaviour
stack overflow:

Traceback (most recent call last):
  File "/opt/homebrew/bin/statham", line 8, in <module>
    sys.exit(entry_point())
             ^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/statham/__main__.py", line 105, in entry_point
    output.write(main(uri))  # pragma: no cover
                 ^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/statham/__main__.py", line 94, in main
    RefDict.from_uri(input_uri), context_labeller=title_labeller()
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/json_ref_dict/ref_dict.py", line 46, in from_uri
    uri, value = _resolve_uri(uri)
                 ^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/json_ref_dict/ref_dict.py", line 11, in _resolve_uri
    return resolve_uri_to_urivalue_pair(uri)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/json_ref_dict/ref_pointer.py", line 183, in resolve_uri_to_urivalue_pair
    remote_uri, value = pointer.resolve_with_uri(document)
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/json_ref_dict/ref_pointer.py", line 98, in resolve_with_uri
    remote_uri, remote = self.resolve_remote_with_uri(doc, -1)
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/json_ref_dict/ref_pointer.py", line 58, in resolve_remote_with_uri
    resolved_remote_uri, value = resolve_uri_to_urivalue_pair(remote_uri)
                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/json_ref_dict/ref_pointer.py", line 183, in resolve_uri_to_urivalue_pair

[ … ]

  File "/opt/homebrew/lib/python3.11/site-packages/json_ref_dict/ref_pointer.py", line 98, in resolve_with_uri
    remote_uri, remote = self.resolve_remote_with_uri(doc, -1)
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/json_ref_dict/ref_pointer.py", line 51, in resolve_remote_with_uri
    isinstance(doc, abc.Mapping) and isinstance(doc.get("$ref"), str)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen abc>", line 119, in __instancecheck__
RecursionError: maximum recursion depth exceeded in comparison

System information

  • OS: MacOS Ventura 13.3.1a (arm64)
  • Python version: 3.11.3
  • Installed python libraries: (output of pip freeze)
> pip freeze | grep statham
statham-schema==0.14.0

Workaround

I guess I won't use a "$ref" at my root.

Though the schema generator I'm using (https://github.com/StefanTerdell/zod-to-json-schema) does this by default.

@NfNitLoop NfNitLoop added the bug Something isn't working label Jun 2, 2023
@NfNitLoop
Copy link
Author

I also tried just pointing directly to the definition I wanted to generate instead of relying on the "$ref" at the root:

But I got an error:

> statham --input example.json#/definitions/Example
Traceback (most recent call last):
  File "/opt/homebrew/bin/statham", line 8, in <module>
    sys.exit(entry_point())
             ^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/statham/__main__.py", line 105, in entry_point
    output.write(main(uri))  # pragma: no cover
                 ^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/statham/__main__.py", line 94, in main
    RefDict.from_uri(input_uri), context_labeller=title_labeller()
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/json_ref_dict/ref_dict.py", line 46, in from_uri
    uri, value = _resolve_uri(uri)
                 ^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/json_ref_dict/ref_dict.py", line 11, in _resolve_uri
    return resolve_uri_to_urivalue_pair(uri)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/json_ref_dict/ref_pointer.py", line 183, in resolve_uri_to_urivalue_pair
    remote_uri, value = pointer.resolve_with_uri(document)
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/json_ref_dict/ref_pointer.py", line 98, in resolve_with_uri
    remote_uri, remote = self.resolve_remote_with_uri(doc, -1)
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/json_ref_dict/ref_pointer.py", line 54, in resolve_remote_with_uri
    remote_uri = self.uri.relative(doc["$ref"]).get(
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/json_ref_dict/uri.py", line 65, in relative
    raise ReferenceParseError(
json_ref_dict.exceptions.ReferenceParseError: Reference: '#/definitions/Example' from context 'example.json#/definitions/Example' is self-referential. Cannot resolve.```

@jacksmith15
Copy link
Owner

jacksmith15 commented Jun 2, 2023

A root-level $ref doesn't entirely sense from my reading of the spec. From the JSON Reference spec:

Any members other than "$ref" in a JSON Reference object SHALL be ignored.

The definitions key is immediately discarded, and the ref target references itself, causing a recursive loop.

@NfNitLoop
Copy link
Author

Just to give some context:

Statham is the first JSONSchema tool I've encountered that doesn't handle a root $ref. Admittedly, I'm not usually working with JSONSchemas. I've just been investigating options over the last couple days for a use case that came up.

Things that supported it:

The main advantage that I see is that the root type gets a name, instead of relying on its file name to name it. Plus all your schemas are defined in the same "definitions" block instead of having them in two different places. 🤷

@NfNitLoop
Copy link
Author

Also, I only figured that out after some testing on my part. I very nearly abandoned investigating statham earlier and just assumed the tool was broken. If you don't want to support root "$ref", it might be a good idea to check for root "$ref" and give an error that it's not supported. My initial perception was that statham might've been broken/unmaintained.

Sorry for being verbose. I also maintain some OSS projects and I prefer users report issues rather than not. Trying to return the favor. Thanks for making this tool available! 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants