Skip to content

Use lia instead of our own adapters - #3967

Merged
patrick91 merged 6 commits into
mainfrom
feature/lia
Aug 5, 2025
Merged

Use lia instead of our own adapters#3967
patrick91 merged 6 commits into
mainfrom
feature/lia

Conversation

@patrick91

@patrick91 patrick91 commented Aug 4, 2025

Copy link
Copy Markdown
Member

Summary by Sourcery

Adopt lia as the external source for HTTP adapter implementations and exceptions by removing all custom adapter code, update imports and multipart logic accordingly, add ChannelsRequestAdapter enhancements, and bump dependencies with release documentation

New Features:

  • Expose url and cookies properties on ChannelsRequestAdapter

Enhancements:

  • Replace custom HTTP adapter classes across various framework integrations with implementations from the lia library
  • Remove internal Async/Sync HTTP adapter base classes and HTTPException in favor of lia’s versions
  • Update multipart parsing logic to use consolidated form_data.form and form_data.files fields
  • Switch ASGI and FastAPI GraphQL routers to use StarletteRequestAdapter instead of the removed ASGIRequestAdapter

Build:

  • Add lia-web (>=0.2.1) to project dependencies

Documentation:

  • Add RELEASE.md with patch release notes

@sourcery-ai

sourcery-ai Bot commented Aug 4, 2025

Copy link
Copy Markdown
Contributor

Reviewer's Guide

This PR replaces all custom HTTP request adapters across frameworks with the external lia library, re-exports lia’s HTTPException for streamlined error handling, updates ASGI and FastAPI adapter defaults, enhances ChannelsRequestAdapter, fixes multipart parsing logic, and adds lia-web as a new dependency.

File-Level Changes

Change Details Files
Delegate framework-specific HTTP adapters to lia
  • Removed custom adapter classes for Flask, Django, Sanic, Chalice, AioHTTP, Litestar, ASGI, and Quart
  • Imported and used lia’s adapter classes in view and controller modules
  • Updated ASGI and FastAPI routers to use StarletteRequestAdapter
strawberry/flask/views.py
strawberry/django/views.py
strawberry/sanic/views.py
strawberry/chalice/views.py
strawberry/aiohttp/views.py
strawberry/litestar/controller.py
strawberry/asgi/__init__.py
strawberry/quart/views.py
strawberry/fastapi/router.py
strawberry/http/async_base_view.py
strawberry/http/sync_base_view.py
Streamline exception handling by re-exporting lia’s HTTPException
  • Imported HTTPException from lia and re-exported it
  • Removed internal HTTPException class and updated all exports
  • Replaced framework modules’ HTTPException imports with lia’s version
strawberry/http/exceptions.py
strawberry/flask/views.py
strawberry/sanic/views.py
strawberry/chalice/views.py
Enhance ChannelsRequestAdapter
  • Added url and cookies properties
  • Updated post_data and files to use form_data attributes
  • Implemented get_form_data returning FormData
strawberry/channels/handlers/http_handler.py
Fix multipart parsing logic in AsyncBaseHTTPView
  • Switched to using form_data.form and form_data.files directly
  • Adjusted replace_placeholders_with_files call to new variables
strawberry/http/async_base_view.py
Add lia-web as a project dependency
  • Added lia-web (>=0.2.1) to project dependencies
pyproject.toml

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

@botberry

botberry commented Aug 4, 2025

Copy link
Copy Markdown
Member

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:

@codecov

codecov Bot commented Aug 4, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 69.09091% with 17 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.31%. Comparing base (1bbe8db) to head (23344ad).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3967      +/-   ##
==========================================
- Coverage   94.40%   94.31%   -0.10%     
==========================================
  Files         528      528              
  Lines       34371    34154     -217     
  Branches     1803     1804       +1     
==========================================
- Hits        32449    32213     -236     
- Misses       1630     1648      +18     
- Partials      292      293       +1     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@codspeed-hq

codspeed-hq Bot commented Aug 4, 2025

Copy link
Copy Markdown

CodSpeed Performance Report

Merging #3967 will not alter performance

Comparing feature/lia (23344ad) with main (1bbe8db)

Summary

✅ 26 untouched benchmarks

@patrick91
patrick91 force-pushed the feature/lia branch 3 times, most recently from 535fa8e to da7181f Compare August 5, 2025 07:54
@patrick91
patrick91 force-pushed the feature/lia branch 3 times, most recently from 787ce38 to 9a8f7aa Compare August 5, 2025 21:52
@patrick91
patrick91 marked this pull request as ready for review August 5, 2025 22:04
@botberry

botberry commented Aug 5, 2025

Copy link
Copy Markdown
Member

Thanks for adding the RELEASE.md file!

Here's a preview of the changelog:


This release removes some internal code in favour of using an external dependency,
this will help us with maintaining the codebase in the future 😊

Here's the tweet text:

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

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

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey @patrick91 - I've reviewed your changes - here's some feedback:

  • Import StarletteRequestAdapter from lia in the ASGI and FastAPI router modules to match the updated request_adapter_class and prevent NameError.
  • Split out the ChannelsRequestAdapter enhancements (url and cookies parsing) into a separate pull request, as these are orthogonal to the lia adapter refactor.
  • Verify that SyncHTTPRequestAdapter and AsyncHTTPRequestAdapter are properly aliased and re-exported from lia to maintain backward compatibility across existing code paths.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Import StarletteRequestAdapter from lia in the ASGI and FastAPI router modules to match the updated request_adapter_class and prevent NameError.
- Split out the ChannelsRequestAdapter enhancements (url and cookies parsing) into a separate pull request, as these are orthogonal to the lia adapter refactor.
- Verify that SyncHTTPRequestAdapter and AsyncHTTPRequestAdapter are properly aliased and re-exported from lia to maintain backward compatibility across existing code paths.

## Individual Comments

### Comment 1
<location> `strawberry/channels/handlers/http_handler.py:129` </location>
<code_context>
-    def __init__(self, request: web.Request) -> None:
-        self.request = request
-
-    @property
-    def query_params(self) -> QueryParams:
-        return self.request.query.copy()  # type: ignore[attr-defined]
</code_context>

<issue_to_address>
New url and cookies properties may not be consistently available across all request types.

These properties rely on certain fields and header formats that may not always be present, which could cause runtime errors. Please add error handling or validation to ensure robustness.
</issue_to_address>

### Comment 2
<location> `strawberry/channels/handlers/http_handler.py:173` </location>
<code_context>
-        return self.request.form_data["files"]
+        return self.request.form_data.files
+
+    def get_form_data(self) -> FormData:
+        return self.request.form_data


</code_context>

<issue_to_address>
get_form_data is defined as a synchronous method, which may be inconsistent with async expectations.

If other adapters use async for get_form_data, consider updating this method to async for consistency, or verify that all usages support both sync and async implementations.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
    def get_form_data(self) -> FormData:
        return self.request.form_data
=======
    async def get_form_data(self) -> FormData:
        return self.request.form_data
>>>>>>> REPLACE

</suggested_fix>

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.

Comment on lines +129 to +138
@property
def url(self) -> str:
scheme = self.request.consumer.scope["scheme"]
host = self.headers.get("host", "localhost")
path = self.request.consumer.scope["path"]
query_string = self.request.consumer.scope["query_string"]
url = f"{scheme}://{host}{path}"
if query_string:
url += f"?{query_string.decode()}"
return url

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion (bug_risk): New url and cookies properties may not be consistently available across all request types.

These properties rely on certain fields and header formats that may not always be present, which could cause runtime errors. Please add error handling or validation to ensure robustness.

Comment on lines +173 to +174
def get_form_data(self) -> FormData:
return self.request.form_data

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion: get_form_data is defined as a synchronous method, which may be inconsistent with async expectations.

If other adapters use async for get_form_data, consider updating this method to async for consistency, or verify that all usages support both sync and async implementations.

Suggested change
def get_form_data(self) -> FormData:
return self.request.form_data
async def get_form_data(self) -> FormData:
return self.request.form_data

Comment on lines +131 to +138
scheme = self.request.consumer.scope["scheme"]
host = self.headers.get("host", "localhost")
path = self.request.consumer.scope["path"]
query_string = self.request.consumer.scope["query_string"]
url = f"{scheme}://{host}{path}"
if query_string:
url += f"?{query_string.decode()}"
return url

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

issue (code-quality): We've found these issues:

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Greptile Summary

This PR refactors Strawberry GraphQL's HTTP adapter infrastructure by replacing custom framework-specific adapters with standardized implementations from the external lia-web library. The change affects all major web framework integrations including Flask, Django, FastAPI, Sanic, Chalice, AioHTTP, Quart, Litestar, and ASGI.

The refactoring removes approximately 400+ lines of custom adapter code across multiple files and consolidates this functionality into the lia-web dependency (>=0.2.1). Each framework previously maintained its own HTTP request adapter class that handled query parameters, request bodies, form data, headers, and HTTP methods. These have been replaced with unified adapters from lia that provide the same interface but with standardized implementations.

Key architectural changes include:

  • Dependency Management: Adds lia-web (>=0.2.1) as a core dependency in pyproject.toml
  • Exception Handling: Migrates from custom HTTPException to lia's implementation across all view classes
  • Adapter Standardization: Replaces framework-specific adapters (e.g., FlaskHTTPRequestAdapter, DjangoHTTPRequestAdapter) with lia equivalents
  • Form Data Handling: Updates multipart form data access patterns from dictionary-style (form_data['form']) to attribute-style (form_data.form) to match lia's FormData structure
  • Backward Compatibility: Maintains existing public APIs by preserving class names and interfaces while delegating implementation to lia

The WebSocket adapters remain custom implementations since they contain GraphQL-specific subscription logic that's not covered by lia's HTTP abstractions. This refactoring allows Strawberry to focus on GraphQL-specific functionality rather than maintaining HTTP protocol handling details across multiple web frameworks.

Confidence score: 4/5

  • This PR is generally safe to merge but requires thorough testing of framework integrations
  • Score reflects the large scope of changes across critical HTTP handling code, though the external library approach reduces maintenance burden
  • Pay close attention to form data handling changes and ensure lia-web dependency compatibility across all supported framework versions

16 files reviewed, 1 comment

Edit Code Review Bot Settings | Greptile

Comment thread pyproject.toml
"typing-extensions>=4.5.0",
"python-dateutil~=2.7",
"packaging>=23",
"lia-web (>=0.2.1)",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

style: The dependency syntax uses parentheses instead of standard quotes. While this works, the standard format would be 'lia-web>=0.2.1' for consistency with other dependencies.

@patrick91
patrick91 merged commit 6e5b67c into main Aug 5, 2025
114 of 116 checks passed
@patrick91
patrick91 deleted the feature/lia branch August 5, 2025 22:11
@ryanpetrello

ryanpetrello commented Sep 5, 2025

Copy link
Copy Markdown
Contributor

👋 could this be notated as a breaking interface change in Strawberry's changelog?

https://strawberry.rocks/docs/breaking-changes/

My project has a custom subclass of strawberry.flask.views.GraphQLView which provides some conditional logic that sometimes raises HTTP-level errors i.e., raise HTTPException(401, 'Unauthorized').

I ran into the following import error while upgrading, and didn't see mention of this anywhere in Strawberry's changelog (ultimately, I discovered the problem by looking at Strawberry's commit history and discovering this PR).

  File "/builds/my/example/project/views.py", line 7, in <module>
    from strawberry.http.exceptions import HTTPException
ImportError: cannot import name 'HTTPException' from 'strawberry.http.exceptions' (/app/venv/lib/python3.12/site-packages/strawberry/http/exceptions.py)

Obviously this isn't a huge deal, but it might be nice to spell out this change for others who might encounter it in the future.

@patrick91

Copy link
Copy Markdown
Member Author

@ryanpetrello sorry about that! would be ok with making a PR? otherwise I'll try over the weekend 😊

@ryanpetrello

ryanpetrello commented Sep 5, 2025

Copy link
Copy Markdown
Contributor

Hey, it happens 🤷 !

Overall, Strawberry has excellent documentation and ya'll are really great about communicating breaking changes (and this is a fairly minor one).

If I wanted to open an MR, would I just add a version-specific update here?

https://github.com/strawberry-graphql/strawberry/tree/main/docs/breaking-changes

Similar to this?

edb8348

@ryanpetrello

Copy link
Copy Markdown
Contributor

Here you go @patrick91 #3989

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants