Skip to content

Lazy schema references #68

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

Merged
merged 1 commit into from
Jul 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions openapi_core/schema/properties/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@

class PropertiesGenerator(object):

def __init__(self, dereferencer):
def __init__(self, dereferencer, schemas_registry):
self.dereferencer = dereferencer
self.schemas_registry = schemas_registry

def generate(self, properties):
for property_name, schema_spec in iteritems(properties):
schema = self._create_schema(schema_spec)
yield property_name, schema

def _create_schema(self, schema_spec):
from openapi_core.schema.schemas.factories import SchemaFactory
return SchemaFactory(self.dereferencer).create(schema_spec)
schema, _ = self.schemas_registry.get_or_create(schema_spec)
return schema
2 changes: 1 addition & 1 deletion openapi_core/schema/schemas/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def create(self, schema_spec):
@property
@lru_cache()
def properties_generator(self):
return PropertiesGenerator(self.dereferencer)
return PropertiesGenerator(self.dereferencer, self)

def _create_items(self, items_spec):
return self.create(items_spec)
18 changes: 14 additions & 4 deletions openapi_core/schema/schemas/registries.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
"""OpenAPI core schemas registries module"""
import logging

from lazy_object_proxy import Proxy

from openapi_core.schema.schemas.factories import SchemaFactory
from openapi_core.schema.schemas.util import dicthash

log = logging.getLogger(__name__)

Expand All @@ -13,10 +16,17 @@ def __init__(self, dereferencer):
self._schemas = {}

def get_or_create(self, schema_spec):
schema_hash = dicthash(schema_spec)
schema_deref = self.dereferencer.dereference(schema_spec)
model = schema_deref.get('x-model', None)

if model and model in self._schemas:
return self._schemas[model], False
if schema_hash in self._schemas:
return self._schemas[schema_hash], False

if '$ref' in schema_spec:
schema = Proxy(lambda: self.create(schema_deref))
else:
schema = self.create(schema_deref)

self._schemas[schema_hash] = schema

return self.create(schema_deref), True
return schema, True
5 changes: 5 additions & 0 deletions openapi_core/schema/schemas/util.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
"""OpenAPI core schemas util module"""
from distutils.util import strtobool
from json import dumps


def forcebool(val):
if isinstance(val, str):
val = strtobool(val)

return bool(val)


def dicthash(d):
return hash(dumps(d, sort_keys=True))
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
openapi-spec-validator
six
yarl<1.2.0
lazy-object-proxy
2 changes: 2 additions & 0 deletions tests/integration/data/v3.0/petstore.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,8 @@ components:
properties:
rootCause:
type: string
suberror:
$ref: "#/components/schemas/ExtendedError"
additionalProperties:
type: string
responses:
Expand Down
49 changes: 49 additions & 0 deletions tests/unit/schema/test_schemas_registry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import pytest

from jsonschema.validators import RefResolver
from openapi_spec_validator.validators import Dereferencer
from openapi_spec_validator import default_handlers

from openapi_core.schema.schemas.registries import SchemaRegistry


class TestSchemaRegistryGetOrCreate(object):

@pytest.fixture
def schema_dict(self):
return {
'type': 'object',
'properties': {
'message': {
'type': 'string',
},
'suberror': {
'$ref': '#/components/schemas/Error',
},
},
}

@pytest.fixture
def spec_dict(self, schema_dict):
return {
'components': {
'schemas': {
'Error': schema_dict,
},
},
}

@pytest.fixture
def dereferencer(self, spec_dict):
spec_resolver = RefResolver('', spec_dict, handlers=default_handlers)
return Dereferencer(spec_resolver)

@pytest.fixture
def schemas_registry(self, dereferencer):
return SchemaRegistry(dereferencer)

def test_recursion(self, schemas_registry, schema_dict):
schema, _ = schemas_registry.get_or_create(schema_dict)

assert schema.properties['suberror'] ==\
schema.properties['suberror'].properties['suberror']