22import collections .abc
33import datetime
44import decimal
5- import inspect
65import enum
6+ import inspect
77import sys
88import typing as T
99import uuid
10- from bson import ObjectId
1110
12- from graphene import (
13- UUID ,
14- Boolean ,
15- Enum ,
16- Field ,
17- Float ,
18- InputField ,
19- Int ,
20- List ,
21- String ,
22- Union ,
23- JSONString ,
24- ID
25- )
2611import graphene
12+ from bson import ObjectId
13+ from graphene import (Boolean , Enum , Field , Float , ID , InputField , Int , JSONString , List , String , UUID , Union )
2714from graphene .types .base import BaseType
2815from graphene .types .datetime import Date , DateTime , Time
2916from pydantic import BaseModel
30- from pydantic .fields import ModelField
31- from pydantic . typing import evaluate_forwardref
17+ from pydantic .fields import FieldInfo
18+ from pydantic_core import PydanticUndefined
3219
3320from .registry import Registry
3421from .util import construct_union_class_name
3522
36- from pydantic import fields
37-
3823GRAPHENE2 = graphene .VERSION [0 ] < 3
3924
40- SHAPE_SINGLETON = (fields .SHAPE_SINGLETON ,)
41- SHAPE_SEQUENTIAL = (
42- fields .SHAPE_LIST ,
43- fields .SHAPE_TUPLE ,
44- fields .SHAPE_TUPLE_ELLIPSIS ,
45- fields .SHAPE_SEQUENCE ,
46- fields .SHAPE_SET ,
47- )
48-
49- if hasattr (fields , "SHAPE_DICT" ):
50- SHAPE_MAPPING = T .cast (
51- T .Tuple , (fields .SHAPE_MAPPING , fields .SHAPE_DICT , fields .SHAPE_DEFAULTDICT )
52- )
53- else :
54- SHAPE_MAPPING = T .cast (T .Tuple , (fields .SHAPE_MAPPING ,))
55-
56-
5725try :
5826 from graphene .types .decimal import Decimal as GrapheneDecimal
5927
6230 # graphene 2.1.5+ is required for Decimals
6331 DECIMAL_SUPPORTED = False
6432
65-
6633NONE_TYPE = None .__class__ # need to do this because mypy complains about type(None)
6734
6835
@@ -83,82 +50,89 @@ def _get_field(root, _info):
8350
8451
8552def convert_pydantic_input_field (
86- field : ModelField ,
87- registry : Registry ,
88- parent_type : T .Type = None ,
89- model : T .Type [BaseModel ] = None ,
90- ** field_kwargs ,
53+ field : FieldInfo ,
54+ registry : Registry ,
55+ parent_type : T .Type = None ,
56+ model : T .Type [BaseModel ] = None ,
57+ ** field_kwargs ,
9158) -> InputField :
9259 """
9360 Convert a Pydantic model field into a Graphene type field that we can add
9461 to the generated Graphene data model type.
9562 """
96- declared_type = getattr (field , "type_ " , None )
63+ declared_type = getattr (field , "annotation " , None )
9764 field_kwargs .setdefault (
9865 "type" if GRAPHENE2 else "type_" ,
9966 convert_pydantic_type (
10067 declared_type , field , registry , parent_type = parent_type , model = model
10168 ),
10269 )
103- field_kwargs .setdefault ("required" , field .required )
104- field_kwargs .setdefault ("default_value" , field .default )
70+ field_kwargs .setdefault ("required" , field .is_required () )
71+ field_kwargs .setdefault ("default_value" , None if PydanticUndefined else field .default )
10572 # TODO: find a better way to get a field's description. Some ideas include:
10673 # - hunt down the description from the field's schema, or the schema
10774 # from the field's base model
10875 # - maybe even (Sphinx-style) parse attribute documentation
109- field_kwargs .setdefault ("description" , field .field_info . description )
76+ field_kwargs .setdefault ("description" , field .description )
11077
11178 return InputField (** field_kwargs )
11279
11380
11481def convert_pydantic_field (
115- field : ModelField ,
116- registry : Registry ,
117- parent_type : T .Type = None ,
118- model : T .Type [BaseModel ] = None ,
119- ** field_kwargs ,
82+ name : str ,
83+ field : FieldInfo ,
84+ registry : Registry ,
85+ parent_type : T .Type = None ,
86+ model : T .Type [BaseModel ] = None ,
87+ ** field_kwargs ,
12088) -> Field :
12189 """
12290 Convert a Pydantic model field into a Graphene type field that we can add
12391 to the generated Graphene data model type.
12492 """
125- declared_type = getattr (field , "type_ " , None )
93+ declared_type = getattr (field , "annotation " , None )
12694 field_kwargs .setdefault (
12795 "type" if GRAPHENE2 else "type_" ,
12896 convert_pydantic_type (
12997 declared_type , field , registry , parent_type = parent_type , model = model
13098 ),
13199 )
132- field_kwargs .setdefault ("required" , not field .allow_none )
133- field_kwargs .setdefault ("default_value" , field .default )
134- if field .has_alias :
100+ field_kwargs .setdefault (
101+ "required" ,
102+ field .is_required () or (
103+ type (field .default ) is not PydanticUndefined and
104+ getattr (declared_type , '_name' , '' ) != 'Optional'
105+ )
106+ )
107+ field_kwargs .setdefault ("default_value" , None if type (field .default ) is PydanticUndefined else field .default )
108+ if field .alias :
135109 field_kwargs .setdefault ("name" , field .alias )
136110 # TODO: find a better way to get a field's description. Some ideas include:
137111 # - hunt down the description from the field's schema, or the schema
138112 # from the field's base model
139113 # - maybe even (Sphinx-style) parse attribute documentation
140- field_kwargs .setdefault ("description" , field .field_info . description )
114+ field_kwargs .setdefault ("description" , field .description )
141115
142116 # Handle Graphene 2 and 3
143117 field_type = field_kwargs .pop ("type" , field_kwargs .pop ("type_" , None ))
144118 if field_type is None :
145119 raise ValueError ("No field type could be determined." )
146120
147- resolver_function = getattr (parent_type , "resolve_" + field . name , None )
121+ resolver_function = getattr (parent_type , "resolve_" + name , None )
148122 if resolver_function and callable (resolver_function ):
149123 field_resolver = resolver_function
150124 else :
151- field_resolver = get_attr_resolver (field . name )
125+ field_resolver = get_attr_resolver (name )
152126
153127 return Field (field_type , resolver = field_resolver , ** field_kwargs )
154128
155129
156130def convert_pydantic_type (
157- type_ : T .Type ,
158- field : ModelField ,
159- registry : Registry ,
160- parent_type : T .Type = None ,
161- model : T .Type [BaseModel ] = None ,
131+ type_ : T .Type ,
132+ field : FieldInfo ,
133+ registry : Registry ,
134+ parent_type : T .Type = None ,
135+ model : T .Type [BaseModel ] = None ,
162136) -> BaseType : # noqa: C901
163137 """
164138 Convert a Pydantic type to a Graphene Field type, including not just the
@@ -168,21 +142,23 @@ def convert_pydantic_type(
168142 graphene_type = find_graphene_type (
169143 type_ , field , registry , parent_type = parent_type , model = model
170144 )
171- if field .shape in SHAPE_SINGLETON :
172- return graphene_type
173- elif field . shape in SHAPE_SEQUENTIAL :
174- # TODO: _should_ Sets remain here?
145+ field_type = getattr ( field .annotation , '__origin__' , None )
146+
147+ # TODO: _should_ Sets remain here?
148+ if field_type in [ list , set , tuple ]: # SHAPE_SEQUENTIAL
175149 return List (graphene_type )
176- elif field . shape in SHAPE_MAPPING :
150+ if field_type == map : # SHAPE_MAPPING
177151 raise ConversionError ("Don't know how to handle mappings in Graphene." )
178152
153+ return graphene_type
154+
179155
180156def find_graphene_type (
181- type_ : T .Type ,
182- field : ModelField ,
183- registry : Registry ,
184- parent_type : T .Type = None ,
185- model : T .Type [BaseModel ] = None ,
157+ type_ : T .Type ,
158+ field : FieldInfo ,
159+ registry : Registry ,
160+ parent_type : T .Type = None ,
161+ model : T .Type [BaseModel ] = None ,
186162) -> BaseType : # noqa: C901
187163 """
188164 Map a native Python type to a Graphene-supported Field type, where possible,
@@ -216,8 +192,8 @@ def find_graphene_type(
216192 elif registry and registry .get_type_for_model (type_ ):
217193 return registry .get_type_for_model (type_ )
218194 elif registry and (
219- isinstance (type_ , BaseModel )
220- or (inspect .isclass (type_ ) and issubclass (type_ , BaseModel ))
195+ isinstance (type_ , BaseModel )
196+ or (inspect .isclass (type_ ) and issubclass (type_ , BaseModel ))
221197 ):
222198 # If it's a Pydantic model that hasn't yet been wrapped with a ObjectType,
223199 # we can put a placeholder in and request that `resolve_placeholders()`
@@ -238,12 +214,12 @@ def find_graphene_type(
238214 if not sibling :
239215 raise ConversionError (
240216 "Don't know how to convert the Pydantic field "
241- f"{ field !r} ({ field .type_ } ), could not resolve "
217+ f"{ field !r} ({ field .annotation } ), could not resolve "
242218 "the forward reference. Did you call `resolve_placeholders()`? "
243219 "See the README for more on forward references."
244220 )
245221 module_ns = sys .modules [sibling .__module__ ].__dict__
246- resolved = evaluate_forwardref (type_ , module_ns , None )
222+ resolved = T . cast ( T . Any , type_ ). _evaluate (type_ , module_ns , None )
247223 # TODO: make this behavior optional. maybe this is a place for the TypeOptions to play a role?
248224 if registry :
249225 registry .add_placeholder_for_model (resolved )
@@ -277,11 +253,11 @@ def find_graphene_type(
277253
278254
279255def convert_generic_python_type (
280- type_ : T .Type ,
281- field : ModelField ,
282- registry : Registry ,
283- parent_type : T .Type = None ,
284- model : T .Type [BaseModel ] = None ,
256+ type_ : T .Type ,
257+ field : FieldInfo ,
258+ registry : Registry ,
259+ parent_type : T .Type = None ,
260+ model : T .Type [BaseModel ] = None ,
285261) -> BaseType : # noqa: C901
286262 """
287263 Convert annotated Python generic types into the most appropriate Graphene
@@ -303,13 +279,13 @@ def convert_generic_python_type(
303279 type_ , field , registry , parent_type = parent_type , model = model
304280 )
305281 elif origin in (
306- T .Tuple ,
307- T .List ,
308- T .Set ,
309- T .Collection ,
310- T .Iterable ,
311- list ,
312- set ,
282+ T .Tuple ,
283+ T .List ,
284+ T .Set ,
285+ T .Collection ,
286+ T .Iterable ,
287+ list ,
288+ set ,
313289 ) or issubclass (origin , collections .abc .Sequence ):
314290 # TODO: find a better way of divining that the origin is sequence-like
315291 inner_types = getattr (type_ , "__args__" , [])
@@ -326,19 +302,19 @@ def convert_generic_python_type(
326302 )
327303 )
328304 elif origin in (T .Dict , T .Mapping , collections .OrderedDict , dict ) or issubclass (
329- origin , collections .abc .Mapping
305+ origin , collections .abc .Mapping
330306 ):
331- raise ConversionError ("Don't know how to handle mappings in Graphene" )
307+ raise ConversionError ("Don't know how to handle mappings in Graphene. " )
332308 else :
333309 raise ConversionError (f"Don't know how to handle { type_ } (generic: { origin } )" )
334310
335311
336312def convert_union_type (
337- type_ : T .Type ,
338- field : ModelField ,
339- registry : Registry ,
340- parent_type : T .Type = None ,
341- model : T .Type [BaseModel ] = None ,
313+ type_ : T .Type ,
314+ field : FieldInfo ,
315+ registry : Registry ,
316+ parent_type : T .Type = None ,
317+ model : T .Type [BaseModel ] = None ,
342318):
343319 """
344320 Convert an annotated Python Union type into a Graphene Union.
@@ -367,11 +343,11 @@ def convert_union_type(
367343
368344
369345def convert_literal_type (
370- type_ : T .Type ,
371- field : ModelField ,
372- registry : Registry ,
373- parent_type : T .Type = None ,
374- model : T .Type [BaseModel ] = None ,
346+ type_ : T .Type ,
347+ field : FieldInfo ,
348+ registry : Registry ,
349+ parent_type : T .Type = None ,
350+ model : T .Type [BaseModel ] = None ,
375351):
376352 """
377353 Convert an annotated Python Literal type into a Graphene Scalar or Union of Scalars.
0 commit comments