Skip to content

fix: prevent async iterable detection for sync execution in gql-core 3.3#4267

Merged
patrick91 merged 8 commits intomainfrom
fix/gql-core-33-sync-async-iterable
Mar 8, 2026
Merged

fix: prevent async iterable detection for sync execution in gql-core 3.3#4267
patrick91 merged 8 commits intomainfrom
fix/gql-core-33-sync-async-iterable

Conversation

@bellini666
Copy link
Copy Markdown
Member

@bellini666 bellini666 commented Feb 24, 2026

Summary

graphql-core 3.3 added an is_async_iterable parameter to execute(). When not provided, it defaults to a check that returns True for any object with __aiter__ — including Django QuerySets.

This causes execute() to return a coroutine instead of an ExecutionResult during sync execution, which then crashes in execute_sync() with:

RuntimeError: There is no current event loop in thread 'MainThread'.

Fix: When execute_sync() calls execute(), pass is_async_iterable=lambda _x: False to prevent graphql-core 3.3 from treating sync iterables as async. This is gated behind the IS_GQL_33 version flag so it only applies to 3.3+, and a runtime version check ensures users on older 3.3 alphas (before is_async_iterable existed) get a clear error directing them to upgrade to >= 3.3.0a12.

Test plan

  • strawberry-django CI with gql-core 3.3 matrix (249 tests) — currently all failing, should pass with this fix
  • gql-core 3.2 matrix unaffected (parameter not passed)
  • Added regression test verifying execute_sync works with resolvers returning objects that have __aiter__

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Feb 24, 2026

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Adds a guarded is_async_iterable override for sync execution paths so graphql-core 3.3 does not mis-detect sync values (like Django QuerySets) as async iterables, wiring it through existing custom context kwargs only when supported.

File-Level Changes

Change Details Files
Guard graphql-core 3.3 execute() sync calls from async-iterable auto-detection by wiring an is_async_iterable override through custom context kwargs when supported.
  • Introduce a feature-detection flag that inspects execute() to determine if the is_async_iterable parameter is available in graphql-core 3.3.
  • Extend _get_custom_context_kwargs to accept a sync flag and, when running sync and the parameter exists, inject is_async_iterable=lambda _x: False into the kwargs.
  • Update execute_sync to call _get_custom_context_kwargs with sync=True so sync execution passes the override while leaving async and graphql-core 3.2 behavior unchanged.
strawberry/schema/schema.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • Consider extracting the lambda _x: False into a named module-level function (e.g. _never_async_iterable) to avoid recreating the lambda on each call and to make the intent clearer for future readers.
  • The new sync flag on _get_custom_context_kwargs is a bit indirect in what it controls; renaming it to something like force_sync_iterables (or similar) would better communicate that it specifically toggles the is_async_iterable behavior rather than generic sync/async execution.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider extracting the `lambda _x: False` into a named module-level function (e.g. `_never_async_iterable`) to avoid recreating the lambda on each call and to make the intent clearer for future readers.
- The new `sync` flag on `_get_custom_context_kwargs` is a bit indirect in what it controls; renaming it to something like `force_sync_iterables` (or similar) would better communicate that it specifically toggles the `is_async_iterable` behavior rather than generic sync/async execution.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@botberry
Copy link
Copy Markdown
Member

botberry commented Feb 24, 2026

Thanks for adding the RELEASE.md file!

Here's a preview of the changelog:


Fix sync execution crash with graphql-core 3.3 where execute_sync() would return a coroutine
instead of an ExecutionResult, causing RuntimeError: There is no current event loop,
because graphql-core 3.3's is_async_iterable default treats objects with __aiter__
(like Django QuerySets) as async iterables.

Now passes is_async_iterable=lambda _x: False during sync execution to prevent this.

Note: graphql-core >= 3.3.0a12 is now the minimum required version for the 3.3.x series.

Here's the tweet text:

🆕 Release (next) is out! Thanks to @_bellini666 for the PR 👏

Get it here 👉 https://strawberry.rocks/release/(next)

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Feb 24, 2026

Greptile Summary

Fixes a critical bug where execute_sync() would crash with graphql-core 3.3 when working with Django QuerySets. The issue occurs because graphql-core 3.3 introduced an is_async_iterable parameter that defaults to checking for __aiter__, which Django QuerySets have despite being synchronous.

The fix adds runtime detection using inspect.signature() to check if the execute() function accepts the is_async_iterable parameter, then passes lambda _x: False during synchronous execution to prevent async iterable detection. This approach is backwards-compatible with graphql-core 3.2 where the parameter doesn't exist.

Key changes:

  • Added _EXECUTE_HAS_IS_ASYNC_ITERABLE flag combining version check and runtime signature inspection
  • Modified _get_custom_context_kwargs() to accept a sync parameter and conditionally add is_async_iterable=lambda _x: False
  • Updated execute_sync() to pass sync=True when calling _get_custom_context_kwargs()
  • Async execution path (execute()) remains unchanged, passing sync=False by default

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The fix is well-designed with proper version and feature detection, backwards compatible with graphql-core 3.2, and addresses a critical bug affecting Django integrations. The implementation is minimal, focused, and uses defensive programming with runtime signature inspection
  • No files require special attention

Important Files Changed

Filename Overview
strawberry/schema/schema.py Added runtime detection for is_async_iterable parameter in graphql-core 3.3 and passes lambda _x: False during sync execution to prevent Django QuerySets from being treated as async iterables

Last reviewed commit: e6ca453

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 file reviewed, no comments

Edit Code Review Agent Settings | Greptile

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Feb 24, 2026

Merging this PR will not alter performance

✅ 31 untouched benchmarks


Comparing fix/gql-core-33-sync-async-iterable (be7936e) with main (625d838)

Open in CodSpeed

Comment thread strawberry/schema/schema.py Outdated
@botberry
Copy link
Copy Markdown
Member

botberry commented Mar 7, 2026

Apollo Federation Subgraph Compatibility Results

Federation 1 Support Federation 2 Support
_service🟢
@key (single)🟢
@key (multi)🟢
@key (composite)🟢
repeatable @key🟢
@requires🟢
@provides🟢
federated tracing🔲
@link🟢
@shareable🟢
@tag🟢
@override🟢
@inaccessible🟢
@composeDirective🟢
@interfaceObject🟢

Learn more:

@bellini666 bellini666 force-pushed the fix/gql-core-33-sync-async-iterable branch from 018a569 to c0cabe7 Compare March 7, 2026 11:13
@bellini666 bellini666 requested a review from patrick91 March 7, 2026 11:14
@bellini666
Copy link
Copy Markdown
Member Author

bellini666 commented Mar 7, 2026

@patrick91 should be ready now

edit: not really, some tests are failing. Will check why =P

edit2: there was another "breaking" change, fixed now

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR is a multi-version release (v0.308.0 through v0.308.3) bundling several fixes and one new feature. The primary changes address graphql-core 3.3 compatibility (preventing sync execution from incorrectly returning coroutines), Python 3.14 pydantic v1 warning suppression, ApolloTracingExtension crash fixes for invalid queries, lazy type resolution improvements for generic wrappers with from __future__ import annotations, and a new Annotated syntax for field definitions.

Changes:

  • Adds is_async_iterable=lambda _x: False to execute_sync() for graphql-core 3.3, preventing objects with __aiter__ (like Django QuerySets) from being treated as async iterables, plus a minimum version guard requiring gql-core >= 3.3.0a12.
  • Introduces support for Annotated[type, strawberry.field(...)] field definition syntax with proper error handling for duplicate strawberry.fields, and extends the AST namespace resolver to handle lazy types inside generic subscripts.
  • Fixes ApolloTracingExtension crashes on invalid queries by initializing timing attributes in __init__ and wrapping lifecycle hooks with try/finally, plus suppresses pydantic v1 warnings on Python 3.14+.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
strawberry/utils/__init__.py Adds minimum version guard for graphql-core 3.3 (>= 3.3.0a12)
strawberry/schema/schema.py Passes is_async_iterable=lambda _x: False for sync execution in gql-core 3.3
strawberry/extensions/tracing/apollo.py Initializes timing attributes in __init__, wraps hooks with try/finally
strawberry/utils/typing.py Adds already_resolved guard for lazy types + generic subscript fallback
strawberry/types/type_resolver.py Adds _get_field_from_annotated() for Annotated field extraction
strawberry/exceptions/multiple_strawberry_fields.py New MultipleStrawberryFieldsError exception class
strawberry/exceptions/__init__.py Exports the new exception
strawberry/experimental/pydantic/_compat.py Skips pydantic v1 imports on Python 3.14+
tests/types/test_object_types.py Comprehensive tests for Annotated field definitions
tests/types/test_lazy_types_future_annotations.py Tests for TYPE_CHECKING guard with lazy types
tests/schema/extensions/test_apollo.py Regression tests for invalid queries with tracing
tests/experimental/pydantic/schema/test_1_and_2.py Python 3.14 pydantic warning test
docs/guides/custom-extensions.md Fixes info type in resolve example to GraphQLResolveInfo
docs/errors/multiple-strawberry-fields.md Error documentation for new exception
CHANGELOG.md Changelog entries for v0.308.0–v0.308.3
pyproject.toml Version bump to 0.308.3
poetry.lock FastAPI dependency update
.pre-commit-config.yaml Ruff pre-commit hook update to v0.15.4
.github/workflows/test.yml GitHub Actions version bumps
.github/workflows/e2e-tests.yml upload-artifact action version bump

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread strawberry/utils/__init__.py
@bellini666 bellini666 force-pushed the fix/gql-core-33-sync-async-iterable branch from 67122b6 to 390f827 Compare March 7, 2026 11:18
graphql-core 3.3.0a12 requires tuples instead of lists for AST node
collection fields (e.g. ObjectValueNode.fields). Convert the list
comprehension in ast_from_leaf_type to a tuple.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread strawberry/schema/schema.py
Comment thread strawberry/schema/schema.py
@bellini666 bellini666 force-pushed the fix/gql-core-33-sync-async-iterable branch from 4b120c5 to be7936e Compare March 7, 2026 12:09
@patrick91 patrick91 merged commit 91a58d7 into main Mar 8, 2026
85 checks passed
@patrick91 patrick91 deleted the fix/gql-core-33-sync-async-iterable branch March 8, 2026 13:58
mikeldking added a commit to Arize-ai/phoenix that referenced this pull request Mar 31, 2026
- Add upstream tracking comment to Strawberry printer patch (strawberry-graphql/strawberry#4267)
- Clarify patch docstring
- Add sed post-processing to normalize {  } → {} in exported schema
mikeldking added a commit to Arize-ai/phoenix that referenced this pull request Mar 31, 2026
…r patch

- Upgrade strawberry-graphql from 0.287.3 to 0.312.2 across all pins
- Remove _patch_strawberry_schema_printer() monkey-patch (fixed upstream in strawberry-graphql/strawberry#4267)
- Fix deprecated union syntax in Secret.py
- Remove test_schema.py (tested the now-removed patch)
mikeldking added a commit to Arize-ai/phoenix that referenced this pull request Apr 1, 2026
- Add upstream tracking comment to Strawberry printer patch (strawberry-graphql/strawberry#4267)
- Clarify patch docstring
- Add sed post-processing to normalize {  } → {} in exported schema
mikeldking added a commit to Arize-ai/phoenix that referenced this pull request Apr 1, 2026
* chore: unblock graphql defer

* chore: add tracking comment and normalize schema.graphql spacing

- Add upstream tracking comment to Strawberry printer patch (strawberry-graphql/strawberry#4267)
- Clarify patch docstring
- Add sed post-processing to normalize {  } → {} in exported schema

* chore: revert sed post-processing, rebuild schema

* chore: remove test_schema.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants