Skip to content

Commit f7686b2

Browse files
authored
Merge pull request #42 from necaris/fix-self-referencing
Fix self-referencing case
2 parents a59aa31 + abe3741 commit f7686b2

File tree

2 files changed

+50
-23
lines changed

2 files changed

+50
-23
lines changed

graphene_pydantic/converters.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import collections.abc
33
import datetime
44
import decimal
5+
import inspect
56
import enum
67
import sys
78
import typing as T
@@ -199,7 +200,10 @@ def find_graphene_type(
199200
return List
200201
elif registry and registry.get_type_for_model(type_):
201202
return registry.get_type_for_model(type_)
202-
elif registry and isinstance(type_, BaseModel):
203+
elif registry and (
204+
isinstance(type_, BaseModel)
205+
or (inspect.isclass(type_) and issubclass(type_, BaseModel))
206+
):
203207
# If it's a Pydantic model that hasn't yet been wrapped with a ObjectType,
204208
# we can put a placeholder in and request that `resolve_placeholders()`
205209
# be called to update it.
@@ -262,15 +266,19 @@ def convert_generic_python_type(
262266
return convert_union_type(
263267
type_, field, registry, parent_type=parent_type, model=model
264268
)
265-
elif origin in (
266-
T.Tuple,
267-
T.List,
268-
T.Set,
269-
T.Collection,
270-
T.Iterable,
271-
list,
272-
set,
273-
) or issubclass(origin, collections.abc.Sequence):
269+
elif (
270+
origin
271+
in (
272+
T.Tuple,
273+
T.List,
274+
T.Set,
275+
T.Collection,
276+
T.Iterable,
277+
list,
278+
set,
279+
)
280+
or issubclass(origin, collections.abc.Sequence)
281+
):
274282
# TODO: find a better way of divining that the origin is sequence-like
275283
inner_types = getattr(type_, "__args__", [])
276284
if not inner_types: # pragma: no cover # this really should be impossible

tests/test_converters.py

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import graphene_pydantic.converters as converters
1414
from graphene_pydantic.converters import ConversionError, convert_pydantic_field
1515
from graphene_pydantic.objecttype import PydanticObjectType
16-
from graphene_pydantic.registry import get_global_registry
16+
from graphene_pydantic.registry import get_global_registry, Placeholder
1717

1818

1919
def _get_field_from_spec(name, type_spec_or_default):
@@ -141,15 +141,34 @@ class Meta:
141141
assert field.type == GraphFoo
142142

143143

144-
def test_unknown():
145-
with pytest.raises(ConversionError) as exc:
146-
_convert_field_from_spec("attr", (create_model("Model", size=int), None))
147-
assert "Don't know how to convert" in exc.value.args[0]
148-
if pydantic.version.VERSION < "1.0":
149-
assert "Field(attr type=Model default=None)" in exc.value.args[0]
150-
else:
151-
# this worked at least as of 1.1
152-
assert (
153-
"ModelField(name='attr', type=Optional[Model], required=False, default=None)"
154-
in exc.value.args[0]
155-
)
144+
def test_unresolved_placeholders():
145+
# no errors should be raised here -- instead a placeholder is created
146+
field = _convert_field_from_spec("attr", (create_model("Model", size=int), None))
147+
assert any(
148+
isinstance(x, Placeholder)
149+
for x in get_global_registry(PydanticObjectType)._registry.values()
150+
)
151+
# this is a runtime error waiting to happen, but what can we do about it?
152+
assert field.type is None
153+
154+
155+
def test_self_referencing():
156+
class NodeModel(BaseModel):
157+
id: int
158+
name: str
159+
# nodes: Union['NodeModel', None]
160+
nodes: T.Optional["NodeModel"]
161+
162+
NodeModel.update_forward_refs()
163+
164+
class NodeModelSchema(PydanticObjectType):
165+
class Meta: # noqa: too-few-public-methods
166+
model = NodeModel
167+
168+
@classmethod
169+
def is_type_of(cls, root, info):
170+
return isinstance(root, (cls, NodeModel))
171+
172+
NodeModelSchema.resolve_placeholders()
173+
174+
assert NodeModelSchema._meta.model is NodeModel

0 commit comments

Comments
 (0)