[BUG]: recursive schema definition not work
Describe the bug A schema with recursive definition raised error
To Reproduce Steps to reproduce the behavior:
- Using sample schema as follow with recursive structure, allowed by spec:
"definitions": { "models.Equipment": { "title": "Equipment", "type": "object", "properties": { "Features": { "type": "array", "items": { "$ref": "#/definitions/models.Feature" } }, "Id": { "type": "integer", "format": "int64" }, "IdType": { "type": "string" }, "Name": { "type": "string" }, "Price": { "type": "integer", "format": "int32" } } }, "models.Feature": { "title": "Feature", "type": "object", "properties": { "Equipments": { "type": "array", "items": { "$ref": "#/definitions/models.Equipment" } }, "Id": { "type": "integer", "format": "int64" }, "IdFeature": { "$ref": "#/definitions/models.Feature" }, "Name": { "type": "string" } } } }
Expected behavior
Schema must be accepted, but there's error:
openapi_parser.errors.ParserError: OpenAPI file parsing error: Recursion reached limit of 1 trying to resolve "file:///Users/nguyenh/Downloads/recursive_schema.json#/definitions/models.Feature"! file:///Users/nguyenh/Downloads/recursive_schema.json#/definitions/models.Feature file:///Users/nguyenh/Downloads/recursive_schema.json#/definitions/models.Equipment file:///Users/nguyenh/Downloads/recursive_schema.json#/definitions/models.Feature
System details (please complete the following information):
- OS: [e.g. Windows 11, macOS 12.4, etc]
- OpenAPI / Swagger version [e.g. OpenAPI 2]
- Python version [e.g. 12]
Hey @thaichat04, thanks for opening this! This error comes from the prance module that is used for initial file parsing, I will check if the issue still persists in newer versions. If so, I will open an issue there, otherwise I will try to publish a new version soon.
Thanks @manchenkoff, we are already on latest version of prance lib:
Requirement already satisfied: prance in /usr/local/lib/python3.12/site-packages (23.6.21.0)
Thanks for confirmation @thaichat04, I see that there are some issues previously created in prance, I'll try to take a look if there is a solution or config to handle this. But most probably this recursion has to be stopped before reaching the limit somehow.
Yes, but this one is still open from oct. 23: https://github.com/RonnyPfannschmidt/prance/issues/158
Yes, but this one is skill open from oct. 23: https://github.com/RonnyPfannschmidt/prance/issues/158
Oh, then we'll probably need to wait for that. Not sure that I'll be able to replace the resolver sooner.
We can have a workaround like this:
class OpenAPIResolver:
_resolver: prance.ResolvingParser
@staticmethod
def recursion_limit_handler(limit, parsed_url, recursions=()):
"""Ignore recursive."""
path = []
for rc in recursions:
path.append("{}#/{}".format(rc[0], "/".join(rc[1])))
path = "\n".join(path)
printf(path)
def __init__(self, uri: Optional[str], spec_string: Optional[str] = None) -> None:
self._resolver = prance.ResolvingParser(
uri,
spec_string=spec_string,
backend=OPENAPI_SPEC_VALIDATOR,
strict=False,
lazy=True,
recursion_limit_handler = OpenAPIResolver.recursion_limit_handler,
)
@thaichat04 do you mind opening a PR with your suggestion and a test to make sure that the produced schema is still valid to use in the project? Looks like I won't be able to test it sooner than in a couple of weeks 😞
Just adding my findings here as well.
I'm working with the Semantic Kernel plugin for openapi in my use case.
I used:
parser = ResolvingParser(spec_string=openapi_spec, resolve_types = resolver.RESOLVE_FILES, strict=False, recursion_limit=10)
parsed_spec = parser.specification
and passed this to:
kernel.add_plugin_from_openapi(
plugin_name=plugin_name,
openapi_parsed_spec=parsed_spec,
execution_settings=OpenAPIFunctionExecutionParameters(
# Determines whether payload parameter names are augmented with namespaces.
# Namespaces prevent naming conflicts by adding the parent parameter name
# as a prefix, separated by dots
auth_callback=my_auth_callback,
enable_payload_namespacing=True
)
)
This resolved the issue for me, with the Azure OpenAPI specs.
@adamhockemeyer did the circular dependency got resolved for the Azure OpenAPI specs with semantic kernel with Prance resolver package ?
@adamhockemeyer can you explain exactly what you did? I have the same parsing error: Recursion reached limit of 1 trying to resolve...
@adamhockemeyer can you explain exactly what you did? I have the same parsing error: Recursion reached limit of 1 trying to resolve...
Hi, did you try the example I posted above - it is exactly what I tried.
Yes @adamhockemeyer , I tried I am also getting recursion error because I have definition which is referencing itself and prance is unable to resolve it. Can you paste ur openapi spec here
Yes @adamhockemeyer , I tried I am also getting recursion error because I have definition which is referencing itself and prance is unable to resolve it. Can you paste ur openapi spec here
@adamhockemeyer can you add the imports and the full code? from where ResolvingParser , resolver.RESOLVE_FILES came from?
save_openapi_spec = "weather.json"
from prance import ResolvingParser
from prance.util import resolver
# try:
# parser = ResolvingParser(save_openapi_spec)
# print(parser)
# except Exception as e:
# print(e)
# parser = ResolvingParser(spec_string=save_openapi_spec, resolve_types = resolver.RESOLVE_FILES, strict=False, recursion_limit=10)
# parsed_spec = parser.specification
url = "https://raw.githubusercontent.com/Azure/azure-rest-api-specs/refs/heads/main/specification/maps/data-plane/Microsoft.Maps/Weather/preview/1.0/weather.json"
# import requests
# resp = requests.get(url)
# if resp.status_code == 200:
# parser = ResolvingParser(spec_string=resp.json(), resolve_types = resolver.RESOLVE_FILES, strict=False, recursion_limit=10)
# parsed_spec = parser.specification
parser = ResolvingParser(url=url, resolve_types = resolver.RESOLVE_HTTP, strict=False, recursion_limit=10)
parsed_spec = parser.specification
print(parsed_spec)
@adamhockemeyer "I've tried passing the URL, filename, spec JSON, and spec text in all scenarios, but I am consistently encountering a recursion error. Can you help me identify what I might be missing?"
With Filename
With URL