diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index ac4c67a..35b8b17 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -7,10 +7,10 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python 3.9 - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.9 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index fc16674..85f6b23 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -10,10 +10,10 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python 3.9 - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.9 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 09278c1..c1395c6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,10 +11,10 @@ jobs: python: ['3.6', '3.7', '3.8', '3.9', '3.10', 'pypy3'] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} diff --git a/src/graphql_relay/__init__.py b/src/graphql_relay/__init__.py index bab0672..6572019 100644 --- a/src/graphql_relay/__init__.py +++ b/src/graphql_relay/__init__.py @@ -22,6 +22,7 @@ PageInfo, PageInfoConstructor, PageInfoType, + Traversable, ) # Helpers for creating connections from arrays @@ -92,6 +93,7 @@ "plural_identifying_root_field", "ResolvedGlobalId", "SizedSliceable", + "Traversable", "to_global_id", "version", "version_info", diff --git a/src/graphql_relay/connection/array_connection.py b/src/graphql_relay/connection/array_connection.py index f25cdce..cae8c24 100644 --- a/src/graphql_relay/connection/array_connection.py +++ b/src/graphql_relay/connection/array_connection.py @@ -1,9 +1,10 @@ -from typing import Any, Iterator, Optional, Sequence +import sys +from typing import Any, Iterator, Optional, overload, Sequence -try: - from typing import Protocol -except ImportError: # Python < 3.8 - from typing_extensions import Protocol # type: ignore +if sys.version_info >= (3, 8): + from typing import Protocol # pragma: no cover +else: + from typing_extensions import Protocol # pragma: no cover from ..utils.base64 import base64, unbase64 from .connection import ( @@ -11,7 +12,6 @@ ConnectionArguments, ConnectionConstructor, ConnectionCursor, - ConnectionType, Edge, EdgeConstructor, PageInfo, @@ -33,20 +33,54 @@ class SizedSliceable(Protocol): def __getitem__(self, index: slice) -> Any: ... - def __iter__(self) -> Iterator: + def __iter__(self) -> Iterator[Any]: ... def __len__(self) -> int: ... +@overload +def connection_from_array( + data: SizedSliceable, + args: Optional[ConnectionArguments] = None, + *, + edge_type: EdgeConstructor = Edge, + page_info_type: PageInfoConstructor = PageInfo, +) -> Connection: + ... + + +@overload +def connection_from_array( + data: SizedSliceable, + args: Optional[ConnectionArguments], + connection_type: ConnectionConstructor, + edge_type: EdgeConstructor = Edge, + page_info_type: PageInfoConstructor = PageInfo, +) -> Any: + ... + + +@overload +def connection_from_array( + data: SizedSliceable, + args: Optional[ConnectionArguments] = None, + *, + connection_type: ConnectionConstructor, + edge_type: EdgeConstructor = Edge, + page_info_type: PageInfoConstructor = PageInfo, +) -> Any: + ... + + def connection_from_array( data: SizedSliceable, args: Optional[ConnectionArguments] = None, connection_type: ConnectionConstructor = Connection, edge_type: EdgeConstructor = Edge, page_info_type: PageInfoConstructor = PageInfo, -) -> ConnectionType: +) -> Any: """Create a connection object from a sequence of objects. Note that different from its JavaScript counterpart which expects an array, @@ -70,6 +104,49 @@ def connection_from_array( ) +@overload +def connection_from_array_slice( + array_slice: SizedSliceable, + args: Optional[ConnectionArguments] = None, + slice_start: int = 0, + array_length: Optional[int] = None, + array_slice_length: Optional[int] = None, + *, + edge_type: EdgeConstructor = Edge, + page_info_type: PageInfoConstructor = PageInfo, +) -> Connection: + ... + + +@overload +def connection_from_array_slice( + array_slice: SizedSliceable, + args: Optional[ConnectionArguments], + slice_start: int, + array_length: Optional[int], + array_slice_length: Optional[int], + connection_type: ConnectionConstructor, + edge_type: EdgeConstructor = Edge, + page_info_type: PageInfoConstructor = PageInfo, +) -> Any: + ... + + +@overload +def connection_from_array_slice( + array_slice: SizedSliceable, + args: Optional[ConnectionArguments] = None, + slice_start: int = 0, + array_length: Optional[int] = None, + array_slice_length: Optional[int] = None, + *, + connection_type: ConnectionConstructor, + edge_type: EdgeConstructor = Edge, + page_info_type: PageInfoConstructor = PageInfo, +) -> Any: + ... + + def connection_from_array_slice( array_slice: SizedSliceable, args: Optional[ConnectionArguments] = None, @@ -79,7 +156,7 @@ def connection_from_array_slice( connection_type: ConnectionConstructor = Connection, edge_type: EdgeConstructor = Edge, page_info_type: PageInfoConstructor = PageInfo, -) -> ConnectionType: +) -> Any: """Create a connection object from a slice of the result set. Note that different from its JavaScript counterpart which expects an array, diff --git a/src/graphql_relay/connection/connection.py b/src/graphql_relay/connection/connection.py index 2058baa..9eeca0a 100644 --- a/src/graphql_relay/connection/connection.py +++ b/src/graphql_relay/connection/connection.py @@ -1,3 +1,4 @@ +import sys from typing import Any, Dict, List, NamedTuple, Optional, Union from graphql import ( @@ -18,10 +19,10 @@ from graphql import GraphQLNamedOutputType -try: - from typing import Protocol -except ImportError: # Python < 3.8 - from typing_extensions import Protocol # type: ignore +if sys.version_info >= (3, 8): + from typing import Protocol # pragma: no cover +else: + from typing_extensions import Protocol # pragma: no cover __all__ = [ "backward_connection_args", @@ -41,6 +42,7 @@ "PageInfo", "PageInfoConstructor", "PageInfoType", + "Traversable", ] @@ -152,12 +154,15 @@ class PageInfoType(Protocol): def startCursor(self) -> Optional[ConnectionCursor]: ... + @property def endCursor(self) -> Optional[ConnectionCursor]: ... + @property def hasPreviousPage(self) -> bool: ... + @property def hasNextPage(self) -> bool: ... @@ -170,7 +175,7 @@ def __call__( endCursor: Optional[ConnectionCursor], hasPreviousPage: bool, hasNextPage: bool, - ) -> PageInfoType: + ) -> Any: ... @@ -183,18 +188,20 @@ class PageInfo(NamedTuple): hasNextPage: bool -class EdgeType(Protocol): +class Traversable(Protocol): @property - def node(self) -> Any: + def cursor(self) -> ConnectionCursor: ... + +class EdgeType(Traversable, Protocol): @property - def cursor(self) -> ConnectionCursor: + def node(self) -> Any: ... class EdgeConstructor(Protocol): - def __call__(self, *, node: Any, cursor: ConnectionCursor) -> EdgeType: + def __call__(self, *, node: Any, cursor: ConnectionCursor) -> Traversable: ... @@ -219,9 +226,9 @@ class ConnectionConstructor(Protocol): def __call__( self, *, - edges: List[EdgeType], - pageInfo: PageInfoType, - ) -> ConnectionType: + edges: List[Any], + pageInfo: Any, + ) -> Any: ... diff --git a/src/graphql_relay/mutation/mutation.py b/src/graphql_relay/mutation/mutation.py index f927bec..eedd779 100644 --- a/src/graphql_relay/mutation/mutation.py +++ b/src/graphql_relay/mutation/mutation.py @@ -80,7 +80,6 @@ def augmented_output_fields() -> GraphQLFieldMap: input_type = GraphQLInputObjectType(name + "Input", fields=augmented_input_fields) if iscoroutinefunction(mutate_and_get_payload): - # noinspection PyShadowingBuiltins async def resolve(_root: Any, info: GraphQLResolveInfo, input: Dict) -> Any: payload = await mutate_and_get_payload(info, **input) @@ -94,7 +93,6 @@ async def resolve(_root: Any, info: GraphQLResolveInfo, input: Dict) -> Any: return payload else: - # noinspection PyShadowingBuiltins def resolve( # type: ignore _root: Any, info: GraphQLResolveInfo, input: Dict diff --git a/src/graphql_relay/node/node.py b/src/graphql_relay/node/node.py index ad062a5..619ae90 100644 --- a/src/graphql_relay/node/node.py +++ b/src/graphql_relay/node/node.py @@ -24,7 +24,6 @@ class GraphQLNodeDefinitions(NamedTuple): - node_interface: GraphQLInterfaceType node_field: GraphQLField nodes_field: GraphQLField @@ -83,7 +82,6 @@ def node_definitions( class ResolvedGlobalId(NamedTuple): - type: str id: str diff --git a/tests/mutation/test_mutation.py b/tests/mutation/test_mutation.py index 41792cb..bcee981 100644 --- a/tests/mutation/test_mutation.py +++ b/tests/mutation/test_mutation.py @@ -19,7 +19,6 @@ class Result: - # noinspection PyPep8Naming def __init__(self, result, clientMutationId=None): self.clientMutationId = clientMutationId diff --git a/tests/star_wars_schema.py b/tests/star_wars_schema.py index 73b9aaa..6a4925a 100644 --- a/tests/star_wars_schema.py +++ b/tests/star_wars_schema.py @@ -212,7 +212,6 @@ def get_node_type(obj, _info, _type): class IntroduceShipMutation: - # noinspection PyPep8Naming def __init__(self, shipId, factionId, clientMutationId=None): self.shipId = shipId