Skip to content

Commit 3ab55e4

Browse files
committed
Lazy schema references
1 parent db37be8 commit 3ab55e4

File tree

7 files changed

+76
-8
lines changed

7 files changed

+76
-8
lines changed

openapi_core/schema/properties/generators.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44

55
class PropertiesGenerator(object):
66

7-
def __init__(self, dereferencer):
7+
def __init__(self, dereferencer, schemas_registry):
88
self.dereferencer = dereferencer
9+
self.schemas_registry = schemas_registry
910

1011
def generate(self, properties):
1112
for property_name, schema_spec in iteritems(properties):
1213
schema = self._create_schema(schema_spec)
1314
yield property_name, schema
1415

1516
def _create_schema(self, schema_spec):
16-
from openapi_core.schema.schemas.factories import SchemaFactory
17-
return SchemaFactory(self.dereferencer).create(schema_spec)
17+
schema, _ = self.schemas_registry.get_or_create(schema_spec)
18+
return schema

openapi_core/schema/schemas/factories.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def create(self, schema_spec):
6161
@property
6262
@lru_cache()
6363
def properties_generator(self):
64-
return PropertiesGenerator(self.dereferencer)
64+
return PropertiesGenerator(self.dereferencer, self)
6565

6666
def _create_items(self, items_spec):
6767
return self.create(items_spec)
Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
"""OpenAPI core schemas registries module"""
22
import logging
33

4+
from lazy_object_proxy import Proxy
5+
46
from openapi_core.schema.schemas.factories import SchemaFactory
7+
from openapi_core.schema.schemas.util import dicthash
58

69
log = logging.getLogger(__name__)
710

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

1518
def get_or_create(self, schema_spec):
19+
schema_hash = dicthash(schema_spec)
1620
schema_deref = self.dereferencer.dereference(schema_spec)
17-
model = schema_deref.get('x-model', None)
1821

19-
if model and model in self._schemas:
20-
return self._schemas[model], False
22+
if schema_hash in self._schemas:
23+
return self._schemas[schema_hash], False
24+
25+
if '$ref' in schema_spec:
26+
schema = Proxy(lambda: self.create(schema_deref))
27+
else:
28+
schema = self.create(schema_deref)
29+
30+
self._schemas[schema_hash] = schema
2131

22-
return self.create(schema_deref), True
32+
return schema, True

openapi_core/schema/schemas/util.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
"""OpenAPI core schemas util module"""
22
from distutils.util import strtobool
3+
from json import dumps
34

45

56
def forcebool(val):
67
if isinstance(val, str):
78
val = strtobool(val)
89

910
return bool(val)
11+
12+
13+
def dicthash(d):
14+
return hash(dumps(d, sort_keys=True))

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
openapi-spec-validator
22
six
33
yarl<1.2.0
4+
lazy-object-proxy

tests/integration/data/v3.0/petstore.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,8 @@ components:
298298
properties:
299299
rootCause:
300300
type: string
301+
suberror:
302+
$ref: "#/components/schemas/ExtendedError"
301303
additionalProperties:
302304
type: string
303305
responses:
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import pytest
2+
3+
from jsonschema.validators import RefResolver
4+
from openapi_spec_validator.validators import Dereferencer
5+
from openapi_spec_validator import default_handlers
6+
7+
from openapi_core.schema.schemas.registries import SchemaRegistry
8+
9+
10+
class TestSchemaRegistryGetOrCreate(object):
11+
12+
@pytest.fixture
13+
def schema_dict(self):
14+
return {
15+
'type': 'object',
16+
'properties': {
17+
'message': {
18+
'type': 'string',
19+
},
20+
'suberror': {
21+
'$ref': '#/components/schemas/Error',
22+
},
23+
},
24+
}
25+
26+
@pytest.fixture
27+
def spec_dict(self, schema_dict):
28+
return {
29+
'components': {
30+
'schemas': {
31+
'Error': schema_dict,
32+
},
33+
},
34+
}
35+
36+
@pytest.fixture
37+
def dereferencer(self, spec_dict):
38+
spec_resolver = RefResolver('', spec_dict, handlers=default_handlers)
39+
return Dereferencer(spec_resolver)
40+
41+
@pytest.fixture
42+
def schemas_registry(self, dereferencer):
43+
return SchemaRegistry(dereferencer)
44+
45+
def test_recursion(self, schemas_registry, schema_dict):
46+
schema, _ = schemas_registry.get_or_create(schema_dict)
47+
48+
assert schema.properties['suberror'] ==\
49+
schema.properties['suberror'].properties['suberror']

0 commit comments

Comments
 (0)