Skip to content

Commit 2241527

Browse files
committed
Add custom is_awaitable to test perf
1 parent abbe4c2 commit 2241527

File tree

3 files changed

+84
-0
lines changed

3 files changed

+84
-0
lines changed

strawberry/execution/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""Execution utilities for Strawberry GraphQL."""
2+
3+
from .is_awaitable import optimized_is_awaitable
4+
5+
__all__ = ["optimized_is_awaitable"]
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
"""Optimized is_awaitable implementation for GraphQL execution.
2+
3+
This module provides a highly optimized is_awaitable function that adds a fast path
4+
for common synchronous types, significantly improving performance when dealing with
5+
large result sets containing primitive values.
6+
"""
7+
8+
from __future__ import annotations
9+
10+
import inspect
11+
from types import CoroutineType, GeneratorType
12+
from typing import Any
13+
14+
__all__ = ["optimized_is_awaitable"]
15+
16+
CO_ITERABLE_COROUTINE = inspect.CO_ITERABLE_COROUTINE
17+
18+
# Common synchronous types that are never awaitable
19+
# Using a frozenset for O(1) lookup
20+
_NON_AWAITABLE_TYPES: frozenset[type] = frozenset(
21+
{
22+
type(None),
23+
bool,
24+
int,
25+
float,
26+
str,
27+
bytes,
28+
bytearray,
29+
list,
30+
tuple,
31+
dict,
32+
set,
33+
frozenset,
34+
}
35+
)
36+
37+
38+
def optimized_is_awaitable(value: Any) -> bool:
39+
"""Return true if object can be passed to an ``await`` expression.
40+
41+
This is an optimized version of graphql-core's is_awaitable that adds a fast path
42+
for common synchronous types. For large result sets containing mostly primitive
43+
values (ints, strings, lists, etc.), this can provide significant performance
44+
improvements.
45+
46+
Performance characteristics:
47+
- Fast path for primitives: O(1) type lookup
48+
- Falls back to standard checks for other types
49+
- Avoids expensive isinstance and hasattr calls for common types
50+
51+
Args:
52+
value: The value to check
53+
54+
Returns:
55+
True if the value is awaitable, False otherwise
56+
"""
57+
# Fast path: check if the type is a known non-awaitable type
58+
# This single check replaces 3 checks (isinstance, isinstance, hasattr)
59+
# for the most common case
60+
value_type = type(value)
61+
if value_type in _NON_AWAITABLE_TYPES:
62+
return False
63+
64+
# For other types, use the standard graphql-core logic
65+
# This handles coroutines, generators, and custom awaitable objects
66+
return (
67+
# check for coroutine objects
68+
isinstance(value, CoroutineType)
69+
# check for old-style generator based coroutine objects
70+
or (
71+
isinstance(value, GeneratorType)
72+
and bool(value.gi_code.co_flags & CO_ITERABLE_COROUTINE)
73+
)
74+
# check for other awaitables (e.g. futures)
75+
or hasattr(value, "__await__")
76+
)

strawberry/schema/schema.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
from strawberry import relay
4040
from strawberry.annotation import StrawberryAnnotation
4141
from strawberry.exceptions import MissingQueryError
42+
from strawberry.execution import optimized_is_awaitable
4243
from strawberry.extensions import SchemaExtension
4344
from strawberry.extensions.directives import (
4445
DirectivesExtension,
@@ -616,6 +617,7 @@ async def execute(
616617
operation_name=execution_context.operation_name,
617618
context_value=execution_context.context,
618619
execution_context_class=self.execution_context_class,
620+
is_awaitable=optimized_is_awaitable,
619621
**custom_context_kwargs,
620622
)
621623
)
@@ -751,6 +753,7 @@ def execute_sync(
751753
operation_name=execution_context.operation_name,
752754
context_value=execution_context.context,
753755
execution_context_class=self.execution_context_class,
756+
is_awaitable=optimized_is_awaitable,
754757
**custom_context_kwargs,
755758
)
756759

0 commit comments

Comments
 (0)