From 353e1f971cc60b777359a09eff990fe52a7c0249 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 23 Oct 2023 20:57:55 +0100 Subject: [PATCH] [pre-commit.ci] pre-commit autoupdate (#3015) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Patrick Arminio --- .pre-commit-config.yaml | 10 +- CHANGELOG.md | 36 +- docs/_test.md | 2 + docs/guides/pagination/connections.md | 378 +++++++++--------- docs/guides/pagination/cursor-based.md | 358 +++++++---------- strawberry/channels/handlers/base.py | 4 +- strawberry/channels/testing.py | 12 +- strawberry/cli/commands/upgrade/__init__.py | 2 +- .../cli/commands/upgrade/_fake_progress.py | 2 +- strawberry/enum.py | 2 - strawberry/federation/schema.py | 2 +- strawberry/field_extensions/input_mutation.py | 3 - strawberry/relay/fields.py | 3 - strawberry/relay/types.py | 1 - .../experimental/pydantic/test_conversion.py | 1 - tests/fastapi/test_context.py | 2 +- tests/http/conftest.py | 2 +- tests/pyright/utils.py | 4 +- tests/relay/schema.py | 7 +- tests/relay/schema_future_annotations.py | 7 +- tests/schema/test_lazy/test_lazy_generic.py | 2 +- tests/websockets/conftest.py | 2 +- 22 files changed, 383 insertions(+), 459 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 029459b0b8..6b4568ce7e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,12 +1,12 @@ repos: - repo: https://github.com/psf/black - rev: 23.7.0 + rev: 23.10.0 hooks: - id: black exclude: ^(tests/\w+/snapshots/|tests/python_312/) - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.280 + rev: v0.1.1 hooks: - id: ruff exclude: ^tests/\w+/snapshots/ @@ -18,13 +18,13 @@ repos: exclude: (CHANGELOG|TWEET).md - repo: https://github.com/pre-commit/mirrors-prettier - rev: v3.0.0 + rev: v3.0.3 hooks: - id: prettier files: '^docs/.*\.mdx?$' - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: trailing-whitespace - id: check-merge-conflict @@ -35,7 +35,7 @@ repos: args: ['--branch', 'main'] - repo: https://github.com/adamchainz/blacken-docs - rev: 1.15.0 + rev: 1.16.0 hooks: - id: blacken-docs args: [--skip-errors] diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a744a5b09..ea4c1c463e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -774,31 +774,34 @@ An example of migrating existing code is given below: # Existing code @strawberry.type class MyDataType: - name: str + name: str + @strawberry.type class Subscription: - @strawberry.subscription - async def my_data_subscription( - self, info: Info, groups: list[str] - ) -> AsyncGenerator[MyDataType | None, None]: - yield None - async for message in info.context["ws"].channel_listen("my_data", groups=groups): - yield MyDataType(name=message["payload"]) + @strawberry.subscription + async def my_data_subscription( + self, info: Info, groups: list[str] + ) -> AsyncGenerator[MyDataType | None, None]: + yield None + async for message in info.context["ws"].channel_listen( + "my_data", groups=groups + ): + yield MyDataType(name=message["payload"]) ``` ```py # New code @strawberry.type class Subscription: - @strawberry.subscription - async def my_data_subscription( - self, info: Info, groups: list[str] - ) -> AsyncGenerator[MyDataType | None, None]: - async with info.context["ws"].listen_to_channel("my_data", groups=groups) as cm: - yield None - async for message in cm: - yield MyDataType(name=message["payload"]) + @strawberry.subscription + async def my_data_subscription( + self, info: Info, groups: list[str] + ) -> AsyncGenerator[MyDataType | None, None]: + async with info.context["ws"].listen_to_channel("my_data", groups=groups) as cm: + yield None + async for message in cm: + yield MyDataType(name=message["payload"]) ``` Contributed by [Moritz Ulmer](https://github.com/moritz89) via [PR #2856](https://github.com/strawberry-graphql/strawberry/pull/2856/) @@ -1417,6 +1420,7 @@ class Point: x: float y: float + class GetPointsResult: circle_points: List[Point] square_points: List[Point] diff --git a/docs/_test.md b/docs/_test.md index 3aec4a1871..506d3759a6 100644 --- a/docs/_test.md +++ b/docs/_test.md @@ -16,6 +16,7 @@ Code blocks now support: ```python highlight=strawberry,str import strawberry + @strawberry.type class X: name: str @@ -26,6 +27,7 @@ class X: ```python lines=1-4 import strawberry + @strawberry.type class X: name: str diff --git a/docs/guides/pagination/connections.md b/docs/guides/pagination/connections.md index d6f526ca53..3ea01492fa 100644 --- a/docs/guides/pagination/connections.md +++ b/docs/guides/pagination/connections.md @@ -86,13 +86,12 @@ GenericType = TypeVar("GenericType") @strawberry.type class Connection(Generic[GenericType]): page_info: "PageInfo" = strawberry.field( - description="Information to aid in pagination." + description="Information to aid in pagination." ) edges: list["Edge[GenericType]"] = strawberry.field( - description="A list of edges in this connection." + description="A list of edges in this connection." ) - ``` Connections must have atleast two fields: `edges` and `page_info`. @@ -114,32 +113,31 @@ GenericType = TypeVar("GenericType") @strawberry.type class Connection(Generic[GenericType]): page_info: "PageInfo" = strawberry.field( - description="Information to aid in pagination." + description="Information to aid in pagination." ) edges: list["Edge[GenericType]"] = strawberry.field( - description="A list of edges in this connection." + description="A list of edges in this connection." ) @strawberry.type class PageInfo: has_next_page: bool = strawberry.field( - description="When paginating forwards, are there more items?" + description="When paginating forwards, are there more items?" ) has_previous_page: bool = strawberry.field( - description="When paginating backwards, are there more items?" + description="When paginating backwards, are there more items?" ) start_cursor: Optional[str] = strawberry.field( - description="When paginating backwards, the cursor to continue." + description="When paginating backwards, the cursor to continue." ) end_cursor: Optional[str] = strawberry.field( - description="When paginating forwards, the cursor to continue." + description="When paginating forwards, the cursor to continue." ) - ``` You can read more about the `PageInfo` type at: @@ -166,43 +164,38 @@ GenericType = TypeVar("GenericType") @strawberry.type class Connection(Generic[GenericType]): page_info: "PageInfo" = strawberry.field( - description="Information to aid in pagination." + description="Information to aid in pagination." ) edges: list["Edge[GenericType]"] = strawberry.field( - description="A list of edges in this connection." + description="A list of edges in this connection." ) @strawberry.type class PageInfo: has_next_page: bool = strawberry.field( - description="When paginating forwards, are there more items?" + description="When paginating forwards, are there more items?" ) has_previous_page: bool = strawberry.field( - description="When paginating backwards, are there more items?" + description="When paginating backwards, are there more items?" ) start_cursor: Optional[str] = strawberry.field( - description="When paginating backwards, the cursor to continue." + description="When paginating backwards, the cursor to continue." ) end_cursor: Optional[str] = strawberry.field( - description="When paginating forwards, the cursor to continue." + description="When paginating forwards, the cursor to continue." ) @strawberry.type class Edge(Generic[GenericType]): - node: GenericType = strawberry.field( - description="The item at the end of the edge." - ) - - cursor: str = strawberry.field( - description="A cursor for use in pagination." - ) + node: GenericType = strawberry.field(description="The item at the end of the edge.") + cursor: str = strawberry.field(description="A cursor for use in pagination.") ``` EdgeTypes must have atleast two fields - `cursor` and `node`. @@ -219,30 +212,30 @@ from typing import Generic, TypeVar import strawberry user_data = [ - { - "id": 1, - "name": "Norman Osborn", - "occupation": "Founder, Oscorp Industries", - "age": 42 - }, - { - "id": 2, - "name": "Peter Parker", - "occupation": "Freelance Photographer, The Daily Bugle", - "age": 20 - }, - { - "id": 3, - "name": "Harold Osborn", - "occupation": "President, Oscorp Industries", - "age": 19 - }, - { - "id": 4, - "name": "Eddie Brock", - "occupation": "Journalist, The Eddie Brock Report", - "age": 20 - } + { + "id": 1, + "name": "Norman Osborn", + "occupation": "Founder, Oscorp Industries", + "age": 42, + }, + { + "id": 2, + "name": "Peter Parker", + "occupation": "Freelance Photographer, The Daily Bugle", + "age": 20, + }, + { + "id": 3, + "name": "Harold Osborn", + "occupation": "President, Oscorp Industries", + "age": 19, + }, + { + "id": 4, + "name": "Eddie Brock", + "occupation": "Journalist, The Eddie Brock Report", + "age": 20, + }, ] @@ -252,43 +245,38 @@ GenericType = TypeVar("GenericType") @strawberry.type class Connection(Generic[GenericType]): page_info: "PageInfo" = strawberry.field( - description="Information to aid in pagination." + description="Information to aid in pagination." ) edges: list["Edge[GenericType]"] = strawberry.field( - description="A list of edges in this connection." + description="A list of edges in this connection." ) @strawberry.type class PageInfo: has_next_page: bool = strawberry.field( - description="When paginating forwards, are there more items?" + description="When paginating forwards, are there more items?" ) has_previous_page: bool = strawberry.field( - description="When paginating backwards, are there more items?" + description="When paginating backwards, are there more items?" ) start_cursor: Optional[str] = strawberry.field( - description="When paginating backwards, the cursor to continue." + description="When paginating backwards, the cursor to continue." ) end_cursor: Optional[str] = strawberry.field( - description="When paginating forwards, the cursor to continue." + description="When paginating forwards, the cursor to continue." ) @strawberry.type class Edge(Generic[GenericType]): - node: GenericType = strawberry.field( - description="The item at the end of the edge." - ) - - cursor: str = strawberry.field( - description="A cursor for use in pagination." - ) + node: GenericType = strawberry.field(description="The item at the end of the edge.") + cursor: str = strawberry.field(description="A cursor for use in pagination.") ``` Now is a good time to think of what we could use as a cursor for our dataset. Our cursor @@ -314,53 +302,54 @@ from typing import Generic, TypeVar import strawberry user_data = [ - { - "id": 1, - "name": "Norman Osborn", - "occupation": "Founder, Oscorp Industries", - "age": 42 - }, - { - "id": 2, - "name": "Peter Parker", - "occupation": "Freelance Photographer, The Daily Bugle", - "age": 20 - }, - { - "id": 3, - "name": "Harold Osborn", - "occupation": "President, Oscorp Industries", - "age": 19 - }, - { - "id": 4, - "name": "Eddie Brock", - "occupation": "Journalist, The Eddie Brock Report", - "age": 20 - } + { + "id": 1, + "name": "Norman Osborn", + "occupation": "Founder, Oscorp Industries", + "age": 42, + }, + { + "id": 2, + "name": "Peter Parker", + "occupation": "Freelance Photographer, The Daily Bugle", + "age": 20, + }, + { + "id": 3, + "name": "Harold Osborn", + "occupation": "President, Oscorp Industries", + "age": 19, + }, + { + "id": 4, + "name": "Eddie Brock", + "occupation": "Journalist, The Eddie Brock Report", + "age": 20, + }, ] + def encode_user_cursor(id: int) -> str: - """ - Encodes the given user ID into a cursor. + """ + Encodes the given user ID into a cursor. - :param id: The user ID to encode. + :param id: The user ID to encode. - :return: The encoded cursor. - """ - return b64encode(f"user:{id}".encode("ascii")).decode("ascii") + :return: The encoded cursor. + """ + return b64encode(f"user:{id}".encode("ascii")).decode("ascii") def decode_user_cursor(cursor: str) -> int: - """ - Decodes the user ID from the given cursor. + """ + Decodes the user ID from the given cursor. - :param cursor: The cursor to decode. + :param cursor: The cursor to decode. - :return: The decoded user ID. - """ - cursor_data = b64decode(cursor.encode("ascii")).decode("ascii") - return int(cursor_data.split(":")[1]) + :return: The decoded user ID. + """ + cursor_data = b64decode(cursor.encode("ascii")).decode("ascii") + return int(cursor_data.split(":")[1]) GenericType = TypeVar("GenericType") @@ -369,43 +358,38 @@ GenericType = TypeVar("GenericType") @strawberry.type class Connection(Generic[GenericType]): page_info: "PageInfo" = strawberry.field( - description="Information to aid in pagination." + description="Information to aid in pagination." ) edges: list["Edge[GenericType]"] = strawberry.field( - description="A list of edges in this connection." + description="A list of edges in this connection." ) @strawberry.type class PageInfo: has_next_page: bool = strawberry.field( - description="When paginating forwards, are there more items?" + description="When paginating forwards, are there more items?" ) has_previous_page: bool = strawberry.field( - description="When paginating backwards, are there more items?" + description="When paginating backwards, are there more items?" ) start_cursor: Optional[str] = strawberry.field( - description="When paginating backwards, the cursor to continue." + description="When paginating backwards, the cursor to continue." ) end_cursor: Optional[str] = strawberry.field( - description="When paginating forwards, the cursor to continue." + description="When paginating forwards, the cursor to continue." ) @strawberry.type class Edge(Generic[GenericType]): - node: GenericType = strawberry.field( - description="The item at the end of the edge." - ) - - cursor: str = strawberry.field( - description="A cursor for use in pagination." - ) + node: GenericType = strawberry.field(description="The item at the end of the edge.") + cursor: str = strawberry.field(description="A cursor for use in pagination.") ``` Let us define a `get_users` field which returns a connection of users, as well as an `UserType`. @@ -420,53 +404,54 @@ from typing import List, Optional, Generic, TypeVar import strawberry user_data = [ - { - "id": 1, - "name": "Norman Osborn", - "occupation": "Founder, Oscorp Industries", - "age": 42 - }, - { - "id": 2, - "name": "Peter Parker", - "occupation": "Freelance Photographer, The Daily Bugle", - "age": 20 - }, - { - "id": 3, - "name": "Harold Osborn", - "occupation": "President, Oscorp Industries", - "age": 19 - }, - { - "id": 4, - "name": "Eddie Brock", - "occupation": "Journalist, The Eddie Brock Report", - "age": 20 - } + { + "id": 1, + "name": "Norman Osborn", + "occupation": "Founder, Oscorp Industries", + "age": 42, + }, + { + "id": 2, + "name": "Peter Parker", + "occupation": "Freelance Photographer, The Daily Bugle", + "age": 20, + }, + { + "id": 3, + "name": "Harold Osborn", + "occupation": "President, Oscorp Industries", + "age": 19, + }, + { + "id": 4, + "name": "Eddie Brock", + "occupation": "Journalist, The Eddie Brock Report", + "age": 20, + }, ] + def encode_user_cursor(id: int) -> str: - """ - Encodes the given user ID into a cursor. + """ + Encodes the given user ID into a cursor. - :param id: The user ID to encode. + :param id: The user ID to encode. - :return: The encoded cursor. - """ - return b64encode(f"user:{id}".encode("ascii")).decode("ascii") + :return: The encoded cursor. + """ + return b64encode(f"user:{id}".encode("ascii")).decode("ascii") def decode_user_cursor(cursor: str) -> int: - """ - Decodes the user ID from the given cursor. + """ + Decodes the user ID from the given cursor. - :param cursor: The cursor to decode. + :param cursor: The cursor to decode. - :return: The decoded user ID. - """ - cursor_data = b64decode(cursor.encode("ascii")).decode("ascii") - return int(cursor_data.split(":")[1]) + :return: The decoded user ID. + """ + cursor_data = b64decode(cursor.encode("ascii")).decode("ascii") + return int(cursor_data.split(":")[1]) GenericType = TypeVar("GenericType") @@ -475,87 +460,80 @@ GenericType = TypeVar("GenericType") @strawberry.type class Connection(Generic[GenericType]): page_info: "PageInfo" = strawberry.field( - description="Information to aid in pagination." + description="Information to aid in pagination." ) edges: list["Edge[GenericType]"] = strawberry.field( - description="A list of edges in this connection." + description="A list of edges in this connection." ) @strawberry.type class PageInfo: has_next_page: bool = strawberry.field( - description="When paginating forwards, are there more items?" + description="When paginating forwards, are there more items?" ) has_previous_page: bool = strawberry.field( - description="When paginating backwards, are there more items?" + description="When paginating backwards, are there more items?" ) start_cursor: Optional[str] = strawberry.field( - description="When paginating backwards, the cursor to continue." + description="When paginating backwards, the cursor to continue." ) end_cursor: Optional[str] = strawberry.field( - description="When paginating forwards, the cursor to continue." + description="When paginating forwards, the cursor to continue." ) @strawberry.type class Edge(Generic[GenericType]): - node: GenericType = strawberry.field( - description="The item at the end of the edge." - ) + node: GenericType = strawberry.field(description="The item at the end of the edge.") + + cursor: str = strawberry.field(description="A cursor for use in pagination.") - cursor: str = strawberry.field( - description="A cursor for use in pagination." - ) @strawberry.type class User: - name: str = strawberry.field( - description="The name of the user." - ) + name: str = strawberry.field(description="The name of the user.") - occupation: str = strawberry.field( - description="The occupation of the user." - ) + occupation: str = strawberry.field(description="The occupation of the user.") - age: int = strawberry.field( - description="The age of the user." - ) + age: int = strawberry.field(description="The age of the user.") @strawberry.type class Query: @strawberry.field(description="Get a list of users.") - def get_users(self, first: int = 2, after: Optional[str] = None) -> Connection[User]: + def get_users( + self, first: int = 2, after: Optional[str] = None + ) -> Connection[User]: if after is not None: - # decode the user ID from the given cursor. - user_id = decode_user_cursor(cursor=after) + # decode the user ID from the given cursor. + user_id = decode_user_cursor(cursor=after) else: - # no cursor was given (this happens usually when the - # client sends a query for the first time). - user_id = 0 + # no cursor was given (this happens usually when the + # client sends a query for the first time). + user_id = 0 # filter the user data, going through the next set of results. filtered_data = map(lambda user: user.id > user_id, user_data) # slice the relevant user data (Here, we also slice an # additional user instance, to prepare the next cursor). - sliced_users = filtered_data[after:first+1] + sliced_users = filtered_data[after : first + 1] if len(sliced_users) > first: - # calculate the client's next cursor. - last_user = sliced_users.pop(-1) - next_cursor = encode_user_cursor(id=last_user.id) - has_next_page = True + # calculate the client's next cursor. + last_user = sliced_users.pop(-1) + next_cursor = encode_user_cursor(id=last_user.id) + has_next_page = True else: - # We have reached the last page, and - # don't have the next cursor. - next_cursor = None - has_next_page = False + # We have reached the last page, and + # don't have the next cursor. + next_cursor = None + has_next_page = False # We know that we have items in the # previous page window if the initial user ID @@ -564,28 +542,28 @@ class Query: # build user edges. edges = [ - Edge( - node=cast(UserType, user), - cursor=encode_user_cursor(id=user.id), - ) - for user in sliced_users + Edge( + node=cast(UserType, user), + cursor=encode_user_cursor(id=user.id), + ) + for user in sliced_users ] if edges: - # we have atleast one edge. Get the cursor - # of the first edge we have. - start_cursor = edges[0].cursor + # we have atleast one edge. Get the cursor + # of the first edge we have. + start_cursor = edges[0].cursor else: - # We have no edges to work with. - start_cursor = None + # We have no edges to work with. + start_cursor = None if len(edges) > 1: - # We have atleast 2 edges. Get the cursor - # of the last edge we have. - end_cursor = edges[-1].cursor + # We have atleast 2 edges. Get the cursor + # of the last edge we have. + end_cursor = edges[-1].cursor else: - # We don't have enough edges to work with. - end_cursor = None + # We don't have enough edges to work with. + end_cursor = None return Connection( edges=edges, @@ -594,11 +572,11 @@ class Query: has_previous_page=has_previous_page, start_cursor=start_cursor, end_cursor=end_cursor, - ) + ), ) -schema = strawberry.Schema(query=Query) +schema = strawberry.Schema(query=Query) ``` you can start the debug server with the following command: diff --git a/docs/guides/pagination/cursor-based.md b/docs/guides/pagination/cursor-based.md index 9a131ed39b..43ebf07e2c 100644 --- a/docs/guides/pagination/cursor-based.md +++ b/docs/guides/pagination/cursor-based.md @@ -65,31 +65,18 @@ import strawberry @strawberry.type class User: - id: str = strawberry.field( - description="ID of the user." - ) - - name: str = strawberry.field( - description="The name of the user." - ) + id: str = strawberry.field(description="ID of the user.") + name: str = strawberry.field(description="The name of the user.") - occupation: str = strawberry.field( - description="The occupation of the user." - ) - + occupation: str = strawberry.field(description="The occupation of the user.") - age: int = strawberry.field( - description="The age of the user." - ) + age: int = strawberry.field(description="The age of the user.") @staticmethod def from_row(row: Dict[str, Any]) -> "User": return User( - id=row['id'], - name=row['name'], - occupation=row['occupation'], - age=row['age'] + id=row["id"], name=row["name"], occupation=row["occupation"], age=row["age"] ) @@ -102,13 +89,9 @@ class PageMeta: @strawberry.type class UserResponse: - users: List[User] = strawberry.field( - description="The list of users." - ) + users: List[User] = strawberry.field(description="The list of users.") - page_meta: PageMeta = strawberry.field( - description="Metadata to aid in pagination." - ) + page_meta: PageMeta = strawberry.field(description="Metadata to aid in pagination.") @strawberry.type @@ -117,8 +100,8 @@ class Query: def get_users(self) -> UserResponse: ... -schema = strawberry.Schema(query=Query) +schema = strawberry.Schema(query=Query) ``` For simplicity's sake, our dataset is going to be an in-memory list. @@ -131,60 +114,47 @@ from typing import List, Optional, Dict, Any, cast import strawberry user_data = [ - { - "id": 1, - "name": "Norman Osborn", - "occupation": "Founder, Oscorp Industries", - "age": 42 - }, - { - "id": 2, - "name": "Peter Parker", - "occupation": "Freelance Photographer, The Daily Bugle", - "age": 20 - }, - { - "id": 3, - "name": "Harold Osborn", - "occupation": "President, Oscorp Industries", - "age": 19 - }, - { - "id": 4, - "name": "Eddie Brock", - "occupation": "Journalist, The Eddie Brock Report", - "age": 20 - } + { + "id": 1, + "name": "Norman Osborn", + "occupation": "Founder, Oscorp Industries", + "age": 42, + }, + { + "id": 2, + "name": "Peter Parker", + "occupation": "Freelance Photographer, The Daily Bugle", + "age": 20, + }, + { + "id": 3, + "name": "Harold Osborn", + "occupation": "President, Oscorp Industries", + "age": 19, + }, + { + "id": 4, + "name": "Eddie Brock", + "occupation": "Journalist, The Eddie Brock Report", + "age": 20, + }, ] @strawberry.type class User: - id: str = strawberry.field( - description="ID of the user." - ) - - name: str = strawberry.field( - description="The name of the user." - ) + id: str = strawberry.field(description="ID of the user.") + name: str = strawberry.field(description="The name of the user.") - occupation: str = strawberry.field( - description="The occupation of the user." - ) - + occupation: str = strawberry.field(description="The occupation of the user.") - age: int = strawberry.field( - description="The age of the user." - ) + age: int = strawberry.field(description="The age of the user.") @staticmethod def from_row(row: Dict[str, Any]) -> "User": return User( - id=row['id'], - name=row['name'], - occupation=row['occupation'], - age=row['age'] + id=row["id"], name=row["name"], occupation=row["occupation"], age=row["age"] ) @@ -197,13 +167,9 @@ class PageMeta: @strawberry.type class UserResponse: - users: List[User] = strawberry.field( - description="The list of users." - ) + users: List[User] = strawberry.field(description="The list of users.") - page_meta: PageMeta = strawberry.field( - description="Metadata to aid in pagination." - ) + page_meta: PageMeta = strawberry.field(description="Metadata to aid in pagination.") @strawberry.type @@ -212,8 +178,8 @@ class Query: def get_users(self) -> UserResponse: ... -schema = strawberry.Schema(query=Query) +schema = strawberry.Schema(query=Query) ``` Now is a good time to think of what we could use as a cursor for our dataset. Our cursor needs to be an opaque value, @@ -237,80 +203,70 @@ from typing import List, Optional, Dict, Any, cast import strawberry user_data = [ - { - "id": 1, - "name": "Norman Osborn", - "occupation": "Founder, Oscorp Industries", - "age": 42 - }, - { - "id": 2, - "name": "Peter Parker", - "occupation": "Freelance Photographer, The Daily Bugle", - "age": 20 - }, - { - "id": 3, - "name": "Harold Osborn", - "occupation": "President, Oscorp Industries", - "age": 19 - }, - { - "id": 4, - "name": "Eddie Brock", - "occupation": "Journalist, The Eddie Brock Report", - "age": 20 - } + { + "id": 1, + "name": "Norman Osborn", + "occupation": "Founder, Oscorp Industries", + "age": 42, + }, + { + "id": 2, + "name": "Peter Parker", + "occupation": "Freelance Photographer, The Daily Bugle", + "age": 20, + }, + { + "id": 3, + "name": "Harold Osborn", + "occupation": "President, Oscorp Industries", + "age": 19, + }, + { + "id": 4, + "name": "Eddie Brock", + "occupation": "Journalist, The Eddie Brock Report", + "age": 20, + }, ] + def encode_user_cursor(id: int) -> str: - """ - Encodes the given user ID into a cursor. + """ + Encodes the given user ID into a cursor. - :param id: The user ID to encode. + :param id: The user ID to encode. - :return: The encoded cursor. - """ - return b64encode(f"user:{id}".encode("ascii")).decode("ascii") + :return: The encoded cursor. + """ + return b64encode(f"user:{id}".encode("ascii")).decode("ascii") def decode_user_cursor(cursor: str) -> int: - """ - Decodes the user ID from the given cursor. + """ + Decodes the user ID from the given cursor. - :param cursor: The cursor to decode. + :param cursor: The cursor to decode. - :return: The decoded user ID. - """ - cursor_data = b64decode(cursor.encode("ascii")).decode("ascii") - return int(cursor_data.split(":")[1]) + :return: The decoded user ID. + """ + cursor_data = b64decode(cursor.encode("ascii")).decode("ascii") + return int(cursor_data.split(":")[1]) @strawberry.type class User: - id: str = strawberry.field( - description="ID of the user." - ) + id: str = strawberry.field(description="ID of the user.") - name: str = strawberry.field( - description="The name of the user." - ) + name: str = strawberry.field(description="The name of the user.") - occupation: str = strawberry.field( - description="The occupation of the user." - ) + occupation: str = strawberry.field(description="The occupation of the user.") - age: int = strawberry.field( - description="The age of the user." - ) + age: int = strawberry.field(description="The age of the user.") @staticmethod def from_row(row: Dict[str, Any]) -> "User": return User( - id=row['id'], - name=row['name'], - occupation=row['occupation'], - age=row['age'] + id=row["id"], name=row["name"], occupation=row["occupation"], age=row["age"] ) @@ -323,13 +279,9 @@ class PageMeta: @strawberry.type class UserResponse: - users: List[User] = strawberry.field( - description="The list of users." - ) + users: List[User] = strawberry.field(description="The list of users.") - page_meta: PageMeta = strawberry.field( - description="Metadata to aid in pagination." - ) + page_meta: PageMeta = strawberry.field(description="Metadata to aid in pagination.") @strawberry.type @@ -338,8 +290,8 @@ class Query: def get_users(self) -> UserResponse: ... -schema = strawberry.Schema(query=Query) +schema = strawberry.Schema(query=Query) ``` We're going to use the dataset we defined in our `get_users` field resolver. @@ -357,81 +309,70 @@ from typing import List, Optional, Dict, Any, cast import strawberry user_data = [ - { - "id": 1, - "name": "Norman Osborn", - "occupation": "Founder, Oscorp Industries", - "age": 42 - }, - { - "id": 2, - "name": "Peter Parker", - "occupation": "Freelance Photographer, The Daily Bugle", - "age": 20 - }, - { - "id": 3, - "name": "Harold Osborn", - "occupation": "President, Oscorp Industries", - "age": 19 - }, - { - "id": 4, - "name": "Eddie Brock", - "occupation": "Journalist, The Eddie Brock Report", - "age": 20 - } + { + "id": 1, + "name": "Norman Osborn", + "occupation": "Founder, Oscorp Industries", + "age": 42, + }, + { + "id": 2, + "name": "Peter Parker", + "occupation": "Freelance Photographer, The Daily Bugle", + "age": 20, + }, + { + "id": 3, + "name": "Harold Osborn", + "occupation": "President, Oscorp Industries", + "age": 19, + }, + { + "id": 4, + "name": "Eddie Brock", + "occupation": "Journalist, The Eddie Brock Report", + "age": 20, + }, ] + def encode_user_cursor(id: int) -> str: - """ - Encodes the given user ID into a cursor. + """ + Encodes the given user ID into a cursor. - :param id: The user ID to encode. + :param id: The user ID to encode. - :return: The encoded cursor. - """ - return b64encode(f"user:{id}".encode("ascii")).decode("ascii") + :return: The encoded cursor. + """ + return b64encode(f"user:{id}".encode("ascii")).decode("ascii") def decode_user_cursor(cursor: str) -> int: - """ - Decodes the user ID from the given cursor. + """ + Decodes the user ID from the given cursor. - :param cursor: The cursor to decode. + :param cursor: The cursor to decode. - :return: The decoded user ID. - """ - cursor_data = b64decode(cursor.encode("ascii")).decode("ascii") - return int(cursor_data.split(":")[1]) + :return: The decoded user ID. + """ + cursor_data = b64decode(cursor.encode("ascii")).decode("ascii") + return int(cursor_data.split(":")[1]) @strawberry.type class User: + id: str = strawberry.field(description="ID of the user.") - id: str = strawberry.field( - description="ID of the user." - ) + name: str = strawberry.field(description="The name of the user.") - name: str = strawberry.field( - description="The name of the user." - ) - - occupation: str = strawberry.field( - description="The occupation of the user." - ) + occupation: str = strawberry.field(description="The occupation of the user.") - age: int = strawberry.field( - description="The age of the user." - ) + age: int = strawberry.field(description="The age of the user.") @staticmethod def from_row(row: Dict[str, Any]) -> "User": return User( - id=row['id'], - name=row['name'], - occupation=row['occupation'], - age=row['age'] + id=row["id"], name=row["name"], occupation=row["occupation"], age=row["age"] ) @@ -444,13 +385,9 @@ class PageMeta: @strawberry.type class UserResponse: - users: List[User] = strawberry.field( - description="The list of users." - ) + users: List[User] = strawberry.field(description="The list of users.") - page_meta: PageMeta = strawberry.field( - description="Metadata to aid in pagination." - ) + page_meta: PageMeta = strawberry.field(description="Metadata to aid in pagination.") @strawberry.type @@ -458,40 +395,37 @@ class Query: @strawberry.field(description="Get a list of users.") def get_users(self, limit: int, cursor: Optional[str] = None) -> UserResponse: if cursor is not None: - # decode the user ID from the given cursor. - user_id = decode_user_cursor(cursor=cursor) + # decode the user ID from the given cursor. + user_id = decode_user_cursor(cursor=cursor) else: - # no cursor was given (this happens usually when the - # client sends a query for the first time). - user_id = 0 + # no cursor was given (this happens usually when the + # client sends a query for the first time). + user_id = 0 # filter the user data, going through the next set of results. - filtered_data = [user for user in user_data if user['id'] >= user_id] + filtered_data = [user for user in user_data if user["id"] >= user_id] # slice the relevant user data (Here, we also slice an # additional user instance, to prepare the next cursor). - sliced_users = filtered_data[:limit+1] + sliced_users = filtered_data[: limit + 1] if len(sliced_users) > limit: - # calculate the client's next cursor. - last_user = sliced_users.pop(-1) - next_cursor = encode_user_cursor(id=last_user['id']) + # calculate the client's next cursor. + last_user = sliced_users.pop(-1) + next_cursor = encode_user_cursor(id=last_user["id"]) else: - # We have reached the last page, and - # don't have the next cursor. - next_cursor = None + # We have reached the last page, and + # don't have the next cursor. + next_cursor = None sliced_users = [User.from_row(x) for x in sliced_users] return UserResponse( - users=sliced_users, - page_meta=PageMeta( - next_cursor=next_cursor - ) + users=sliced_users, page_meta=PageMeta(next_cursor=next_cursor) ) -schema = strawberry.Schema(query=Query) +schema = strawberry.Schema(query=Query) ``` diff --git a/strawberry/channels/handlers/base.py b/strawberry/channels/handlers/base.py index d28a3e5b1e..5d837d16da 100644 --- a/strawberry/channels/handlers/base.py +++ b/strawberry/channels/handlers/base.py @@ -138,7 +138,7 @@ async def channel_listen( awaitable = asyncio.wait_for(awaitable, timeout) try: yield await awaitable - except asyncio.TimeoutError: # noqa: PERF203 + except asyncio.TimeoutError: # TODO: shall we add log here and maybe in the suppress below? return finally: @@ -215,7 +215,7 @@ async def _listen_to_channel_generator( awaitable = asyncio.wait_for(awaitable, timeout) try: yield await awaitable - except asyncio.TimeoutError: # noqa: PERF203 + except asyncio.TimeoutError: # TODO: shall we add log here and maybe in the suppress below? return diff --git a/strawberry/channels/testing.py b/strawberry/channels/testing.py index 5d2bf8efd6..570842a965 100644 --- a/strawberry/channels/testing.py +++ b/strawberry/channels/testing.py @@ -34,6 +34,9 @@ from strawberry.types import ExecutionResult if TYPE_CHECKING: + from types import TracebackType + from typing_extensions import Self + from asgiref.typing import ASGIApplication @@ -80,11 +83,16 @@ def __init__( subprotocols.append(protocol) super().__init__(application, path, headers, subprotocols=subprotocols) - async def __aenter__(self) -> GraphQLWebsocketCommunicator: + async def __aenter__(self) -> Self: await self.gql_init() return self - async def __aexit__(self, exc_type: Type, exc_val: Any, exc_tb: Any) -> None: + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: await self.disconnect() async def gql_init(self) -> None: diff --git a/strawberry/cli/commands/upgrade/__init__.py b/strawberry/cli/commands/upgrade/__init__.py index 710ee5736d..5ea4761e23 100644 --- a/strawberry/cli/commands/upgrade/__init__.py +++ b/strawberry/cli/commands/upgrade/__init__.py @@ -57,7 +57,7 @@ def upgrade( for path in paths: if path.is_dir(): glob_path = str(path / "**/*.py") - files.extend(glob.glob(glob_path, recursive=True)) + files.extend(glob.glob(glob_path, recursive=True)) # noqa: PTH207 else: files.append(str(path)) diff --git a/strawberry/cli/commands/upgrade/_fake_progress.py b/strawberry/cli/commands/upgrade/_fake_progress.py index 40d92da5a0..6aacc36179 100644 --- a/strawberry/cli/commands/upgrade/_fake_progress.py +++ b/strawberry/cli/commands/upgrade/_fake_progress.py @@ -17,5 +17,5 @@ def add_task(self, *args: Any, **kwargs: Any) -> TaskID: def __enter__(self) -> "FakeProgress": return self - def __exit__(self, *args: Any, **kwargs: Any) -> None: + def __exit__(self, *args: object, **kwargs: Any) -> None: pass diff --git a/strawberry/enum.py b/strawberry/enum.py index e4c1857de1..ad761bb567 100644 --- a/strawberry/enum.py +++ b/strawberry/enum.py @@ -90,8 +90,6 @@ def _process_enum( if not name: name = cls.__name__ - description = description - values = [] for item in cls: # type: ignore item_value = item.value diff --git a/strawberry/federation/schema.py b/strawberry/federation/schema.py index c7b10e4e2a..a0a29ca4ad 100644 --- a/strawberry/federation/schema.py +++ b/strawberry/federation/schema.py @@ -209,7 +209,7 @@ def entities_resolver( try: result = get_result() - except Exception as e: # noqa: PERF203 + except Exception as e: result = GraphQLError( f"Unable to resolve reference for {definition.origin}", original_error=e, diff --git a/strawberry/field_extensions/input_mutation.py b/strawberry/field_extensions/input_mutation.py index 1d70ea332c..568515cfe7 100644 --- a/strawberry/field_extensions/input_mutation.py +++ b/strawberry/field_extensions/input_mutation.py @@ -4,7 +4,6 @@ TYPE_CHECKING, Any, Dict, - TypeVar, ) import strawberry @@ -21,8 +20,6 @@ if TYPE_CHECKING: from strawberry.types.info import Info -_T = TypeVar("_T") - class InputMutationExtension(FieldExtension): def apply(self, field: StrawberryField) -> None: diff --git a/strawberry/relay/fields.py b/strawberry/relay/fields.py index 7866e5fba2..c5bb1d21d5 100644 --- a/strawberry/relay/fields.py +++ b/strawberry/relay/fields.py @@ -21,7 +21,6 @@ Sequence, Tuple, Type, - TypeVar, Union, cast, overload, @@ -53,8 +52,6 @@ from strawberry.permission import BasePermission from strawberry.types.info import Info -_T = TypeVar("_T") - class NodeExtension(FieldExtension): def apply(self, field: StrawberryField) -> None: diff --git a/strawberry/relay/types.py b/strawberry/relay/types.py index 0890701dc0..2db9242a4c 100644 --- a/strawberry/relay/types.py +++ b/strawberry/relay/types.py @@ -45,7 +45,6 @@ from strawberry.utils.await_maybe import AwaitableOrValue _T = TypeVar("_T") -_R = TypeVar("_R") NodeIterableType: TypeAlias = Union[ Iterator[_T], diff --git a/tests/experimental/pydantic/test_conversion.py b/tests/experimental/pydantic/test_conversion.py index d11b2eab45..ec9ba5495f 100644 --- a/tests/experimental/pydantic/test_conversion.py +++ b/tests/experimental/pydantic/test_conversion.py @@ -881,7 +881,6 @@ def factory_func(): field = _get_field(mutable_default) created_factory = get_default_factory_for_field(field) - created_factory = created_factory # should return a factory that copies the default parameter assert created_factory() == mutable_default diff --git a/tests/fastapi/test_context.py b/tests/fastapi/test_context.py index 7b668e0096..ffd7ad64f9 100644 --- a/tests/fastapi/test_context.py +++ b/tests/fastapi/test_context.py @@ -115,7 +115,7 @@ class Query: @strawberry.field def abc(self, info: Info[Any, None]) -> str: assert info.context.get("request") is not None - assert "connection_params" not in info.context.keys() + assert "connection_params" not in info.context assert info.context.get("strawberry") == "rocks" return "abc" diff --git a/tests/http/conftest.py b/tests/http/conftest.py index 81d99115f3..487a1328ea 100644 --- a/tests/http/conftest.py +++ b/tests/http/conftest.py @@ -30,7 +30,7 @@ def _get_http_client_classes() -> Generator[Any, None, None]: importlib.import_module(f".{module}", package="tests.http.clients"), client, ) - except ImportError: # noqa: PERF203 + except ImportError: client_class = None yield pytest.param( diff --git a/tests/pyright/utils.py b/tests/pyright/utils.py index 59c62ea001..7e7bb17e42 100644 --- a/tests/pyright/utils.py +++ b/tests/pyright/utils.py @@ -39,7 +39,9 @@ def run_pyright(code: str, strict: bool = True) -> List[Result]: with tempfile.NamedTemporaryFile("w", suffix=".py", delete=False) as f: f.write(code) - process_result = subprocess.run(["pyright", f.name], stdout=subprocess.PIPE) + process_result = subprocess.run( + ["pyright", f.name], stdout=subprocess.PIPE, check=False + ) os.unlink(f.name) # noqa: PTH108 diff --git a/tests/relay/schema.py b/tests/relay/schema.py index a7263a7dce..01abc1b0ac 100644 --- a/tests/relay/schema.py +++ b/tests/relay/schema.py @@ -1,5 +1,4 @@ import dataclasses -from collections import namedtuple from typing import ( Any, AsyncGenerator, @@ -9,6 +8,7 @@ Iterable, Iterator, List, + NamedTuple, Optional, cast, ) @@ -155,7 +155,10 @@ def resolve_connection( } -FruitAlike = namedtuple("FruitAlike", ["id", "name", "color"]) +class FruitAlike(NamedTuple): + id: int + name: str + color: str @strawberry.type diff --git a/tests/relay/schema_future_annotations.py b/tests/relay/schema_future_annotations.py index 19cb09c6f2..3c8e847cc3 100644 --- a/tests/relay/schema_future_annotations.py +++ b/tests/relay/schema_future_annotations.py @@ -1,7 +1,6 @@ from __future__ import annotations import dataclasses -from collections import namedtuple from typing import ( Any, AsyncGenerator, @@ -11,6 +10,7 @@ Iterable, Iterator, List, + NamedTuple, Optional, cast, ) @@ -157,7 +157,10 @@ def resolve_connection( } -FruitAlike = namedtuple("FruitAlike", ["id", "name", "color"]) +class FruitAlike(NamedTuple): + id: int + name: str + color: str @strawberry.type diff --git a/tests/schema/test_lazy/test_lazy_generic.py b/tests/schema/test_lazy/test_lazy_generic.py index b115747cc5..3bc8102d2b 100644 --- a/tests/schema/test_lazy/test_lazy_generic.py +++ b/tests/schema/test_lazy/test_lazy_generic.py @@ -107,8 +107,8 @@ def test_lazy_types_loaded_from_same_module(commands: Sequence[str]): args=[*commands], env=os.environ, capture_output=True, + check=True, ) - result.check_returncode() expected = """\ type Query { diff --git a/tests/websockets/conftest.py b/tests/websockets/conftest.py index 4960c5bde7..00cfa6ac14 100644 --- a/tests/websockets/conftest.py +++ b/tests/websockets/conftest.py @@ -18,7 +18,7 @@ def _get_http_client_classes() -> Generator[Any, None, None]: client_class = getattr( importlib.import_module(f"tests.http.clients.{module}"), client ) - except ImportError: # noqa: PERF203 + except ImportError: client_class = None yield pytest.param(