Skip to content

Commit f9b002c

Browse files
committed
Add type tags to GraphQL types for fast dispatch in executor
Add class-level type tags to all GraphQL type classes to avoid isinstance() overhead in the execution hot paths. This enables direct attribute access for type checking which is ~1.6x faster than isinstance chains. Changes: - definition.py: Add GraphQLTypeKind enum with bit flags - definition.py: Add _kind and _is_* class vars to all type classes - execute.py: Use return_type._is_* directly in complete_value Performance: ~5% faster execution on nested queries.
1 parent d588e2b commit f9b002c

2 files changed

Lines changed: 77 additions & 7 deletions

File tree

src/graphql/execution/execute.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -703,7 +703,7 @@ def handle_field_error(
703703

704704
# If the field type is non-nullable, then it is resolved without any protection
705705
# from errors, however it still properly locates the error.
706-
if is_non_null_type(return_type):
706+
if return_type._is_non_null_type:
707707
raise error
708708

709709
# Otherwise, error protection is applied, logging the error and resolving a
@@ -752,7 +752,8 @@ def complete_value(
752752

753753
# If field type is NonNull, complete for inner type, and throw field error if
754754
# result is null.
755-
if is_non_null_type(return_type):
755+
# Use type tags for fast dispatch (avoids isinstance overhead)
756+
if return_type._is_non_null_type:
756757
completed = self.complete_value(
757758
return_type.of_type,
758759
field_group,
@@ -775,7 +776,7 @@ def complete_value(
775776
return GraphQLWrappedResult(None)
776777

777778
# If field type is List, complete each item in the list with inner type
778-
if is_list_type(return_type):
779+
if return_type._is_list_type:
779780
return self.complete_list_value(
780781
return_type,
781782
field_group,
@@ -788,12 +789,12 @@ def complete_value(
788789

789790
# If field type is a leaf type, Scalar or Enum, serialize to a valid value,
790791
# returning null if serialization is not possible.
791-
if is_leaf_type(return_type):
792+
if return_type._is_leaf_type:
792793
return GraphQLWrappedResult(self.complete_leaf_value(return_type, result))
793794

794795
# If field type is an abstract type, Interface or Union, determine the runtime
795796
# Object type and complete for that type.
796-
if is_abstract_type(return_type):
797+
if return_type._is_abstract_type:
797798
return self.complete_abstract_value(
798799
return_type,
799800
field_group,
@@ -805,7 +806,7 @@ def complete_value(
805806
)
806807

807808
# If field type is Object, execute and complete all sub-selections.
808-
if is_object_type(return_type):
809+
if return_type._is_object_type:
809810
return self.complete_object_value(
810811
return_type,
811812
field_group,

src/graphql/type/definition.py

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464

6565
__all__ = [
6666
"GraphQLAbstractType",
67+
"GraphQLTypeKind",
6768
"GraphQLArgument",
6869
"GraphQLArgumentKwargs",
6970
"GraphQLArgumentMap",
@@ -157,13 +158,47 @@
157158
]
158159

159160

161+
class GraphQLTypeKind:
162+
"""Type kind constants for fast dispatch (avoids isinstance overhead)."""
163+
164+
# Bit flags for efficient type category checks
165+
NON_NULL = 1
166+
LIST = 2
167+
SCALAR = 4
168+
ENUM = 8
169+
OBJECT = 16
170+
INTERFACE = 32
171+
UNION = 64
172+
INPUT_OBJECT = 128
173+
174+
# Composite categories (precomputed bitmasks)
175+
LEAF = SCALAR | ENUM
176+
COMPOSITE = OBJECT | INTERFACE | UNION
177+
ABSTRACT = INTERFACE | UNION
178+
INPUT = SCALAR | ENUM | INPUT_OBJECT
179+
OUTPUT = SCALAR | ENUM | OBJECT | INTERFACE | UNION
180+
181+
160182
class GraphQLType:
161183
"""Base class for all GraphQL types"""
162184

163185
# Note: We don't use slots for GraphQLType objects because memory considerations
164186
# are not really important for the schema definition, and it would make caching
165187
# properties slower or more complicated.
166188

189+
# Type kind for fast dispatch (set by subclasses)
190+
_kind: ClassVar[int] = 0
191+
192+
# Type tags for fast type checking (avoids isinstance overhead in hot paths)
193+
_is_non_null_type: ClassVar[bool] = False
194+
_is_list_type: ClassVar[bool] = False
195+
_is_leaf_type: ClassVar[bool] = False
196+
_is_composite_type: ClassVar[bool] = False
197+
_is_abstract_type: ClassVar[bool] = False
198+
_is_object_type: ClassVar[bool] = False
199+
_is_input_type: ClassVar[bool] = False
200+
_is_output_type: ClassVar[bool] = False
201+
167202

168203
# There are predicates for each kind of GraphQL type.
169204

@@ -349,6 +384,11 @@ def serialize_odd(value: Any) -> int:
349384
350385
"""
351386

387+
_kind: ClassVar[int] = GraphQLTypeKind.SCALAR
388+
_is_leaf_type: ClassVar[bool] = True
389+
_is_input_type: ClassVar[bool] = True
390+
_is_output_type: ClassVar[bool] = True
391+
352392
specified_by_url: str | None
353393
ast_node: ScalarTypeDefinitionNode | None
354394
extension_ast_nodes: tuple[ScalarTypeExtensionNode, ...]
@@ -737,6 +777,11 @@ class GraphQLObjectType(GraphQLNamedType):
737777
738778
"""
739779

780+
_kind: ClassVar[int] = GraphQLTypeKind.OBJECT
781+
_is_composite_type: ClassVar[bool] = True
782+
_is_object_type: ClassVar[bool] = True
783+
_is_output_type: ClassVar[bool] = True
784+
740785
is_type_of: GraphQLIsTypeOfFn | None
741786
ast_node: ObjectTypeDefinitionNode | None
742787
extension_ast_nodes: tuple[ObjectTypeExtensionNode, ...]
@@ -842,6 +887,11 @@ class GraphQLInterfaceType(GraphQLNamedType):
842887
})
843888
"""
844889

890+
_kind: ClassVar[int] = GraphQLTypeKind.INTERFACE
891+
_is_composite_type: ClassVar[bool] = True
892+
_is_abstract_type: ClassVar[bool] = True
893+
_is_output_type: ClassVar[bool] = True
894+
845895
resolve_type: GraphQLTypeResolver | None
846896
ast_node: InterfaceTypeDefinitionNode | None
847897
extension_ast_nodes: tuple[InterfaceTypeExtensionNode, ...]
@@ -949,6 +999,11 @@ def resolve_type(obj, _info, _type):
949999
PetType = GraphQLUnionType('Pet', [DogType, CatType], resolve_type)
9501000
"""
9511001

1002+
_kind: ClassVar[int] = GraphQLTypeKind.UNION
1003+
_is_composite_type: ClassVar[bool] = True
1004+
_is_abstract_type: ClassVar[bool] = True
1005+
_is_output_type: ClassVar[bool] = True
1006+
9521007
resolve_type: GraphQLTypeResolver | None
9531008
ast_node: UnionTypeDefinitionNode | None
9541009
extension_ast_nodes: tuple[UnionTypeExtensionNode, ...]
@@ -1058,6 +1113,11 @@ class RGBEnum(enum.Enum):
10581113
be used as its internal value when the value is serialized.
10591114
"""
10601115

1116+
_kind: ClassVar[int] = GraphQLTypeKind.ENUM
1117+
_is_leaf_type: ClassVar[bool] = True
1118+
_is_input_type: ClassVar[bool] = True
1119+
_is_output_type: ClassVar[bool] = True
1120+
10611121
values: GraphQLEnumValueMap
10621122
ast_node: EnumTypeDefinitionNode | None
10631123
extension_ast_nodes: tuple[EnumTypeExtensionNode, ...]
@@ -1305,6 +1365,9 @@ class GeoPoint(GraphQLInputObjectType):
13051365
converted to other types by specifying an ``out_type`` function or class.
13061366
"""
13071367

1368+
_kind: ClassVar[int] = GraphQLTypeKind.INPUT_OBJECT
1369+
_is_input_type: ClassVar[bool] = True
1370+
13081371
ast_node: InputObjectTypeDefinitionNode | None
13091372
extension_ast_nodes: tuple[InputObjectTypeExtensionNode, ...]
13101373
is_one_of: bool
@@ -1481,6 +1544,9 @@ def fields(self):
14811544
}
14821545
"""
14831546

1547+
_kind: ClassVar[int] = GraphQLTypeKind.LIST
1548+
_is_list_type: ClassVar[bool] = True
1549+
14841550
def __init__(self, type_: GT_co) -> None:
14851551
super().__init__(type_=type_)
14861552

@@ -1490,7 +1556,7 @@ def __str__(self) -> str:
14901556

14911557
def is_list_type(type_: Any) -> TypeGuard[GraphQLList]:
14921558
"""Check whether this is a GraphQL list type."""
1493-
return isinstance(type_, GraphQLList)
1559+
return getattr(type_, "_is_list_type", False)
14941560

14951561

14961562
def assert_list_type(type_: Any) -> GraphQLList:
@@ -1525,6 +1591,9 @@ class RowType(GraphQLObjectType):
15251591
Note: the enforcement of non-nullability occurs within the executor.
15261592
"""
15271593

1594+
_kind: ClassVar[int] = GraphQLTypeKind.NON_NULL
1595+
_is_non_null_type: ClassVar[bool] = True
1596+
15281597
def __init__(self, type_: GNT_co) -> None:
15291598
super().__init__(type_=type_)
15301599

0 commit comments

Comments
 (0)