From fabf714e13f99dbeddca8536e6cbc452e3b7e136 Mon Sep 17 00:00:00 2001 From: Mark Edwards Date: Mon, 18 May 2020 12:14:09 +0000 Subject: [PATCH] Define protocols for connection type overrides --- pyproject.toml | 1 + setup.py | 5 +- .../connection/arrayconnection.py | 36 +++++++------ .../connection/connectiontypes.py | 50 +++++++++++++++++++ 4 files changed, 75 insertions(+), 17 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6feb370..7165508 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,7 @@ packages = [ [tool.poetry.dependencies] python = "^3.6" graphql-core = "^3.0" +typing-extensions = { version = "^3.6.2", python = "<3.8" } [tool.poetry.dev-dependencies] pytest = "^5.3" diff --git a/setup.py b/setup.py index c8a96fe..3af0102 100644 --- a/setup.py +++ b/setup.py @@ -29,7 +29,10 @@ "Programming Language :: Python :: 3.8", "Programming Language :: Python :: Implementation :: PyPy", ], - install_requires=["graphql-core>=3.0.0"], + install_requires=[ + "graphql-core>=3.0.0", + "typing-extensions>=3.6.2,<4; python_version < '3.8'", + ], python_requires=">=3.6,<4", packages=find_packages("src"), package_dir={"": "src"}, diff --git a/src/graphql_relay/connection/arrayconnection.py b/src/graphql_relay/connection/arrayconnection.py index ae8c935..e30bc1b 100644 --- a/src/graphql_relay/connection/arrayconnection.py +++ b/src/graphql_relay/connection/arrayconnection.py @@ -6,9 +6,13 @@ from .connectiontypes import ( Connection, ConnectionArguments, + ConnectionConstructor, ConnectionCursor, + ConnectionType, Edge, + EdgeConstructor, PageInfo, + PageInfoConstructor, ) __all__ = [ @@ -24,10 +28,10 @@ def connection_from_array( data: Sequence, args: ConnectionArguments = None, - connection_type: Any = Connection, - edge_type: Any = Edge, - page_info_type: Any = PageInfo, -) -> Connection: + connection_type: ConnectionConstructor = Connection, + edge_type: EdgeConstructor = Edge, + page_info_type: PageInfoConstructor = PageInfo, +) -> ConnectionType: """Create a connection object from a sequence of objects. Note that different from its JavaScript counterpart which expects an array, @@ -54,10 +58,10 @@ def connection_from_array( def connection_from_list( data: Sequence, args: ConnectionArguments = None, - connection_type: Any = Connection, - edge_type: Any = Edge, - pageinfo_type: Any = PageInfo, -) -> Connection: + connection_type: ConnectionConstructor = Connection, + edge_type: EdgeConstructor = Edge, + pageinfo_type: PageInfoConstructor = PageInfo, +) -> ConnectionType: """Deprecated alias for connection_from_array. We're now using the JavaScript terminology in Python as well, since list @@ -84,10 +88,10 @@ def connection_from_array_slice( slice_start: int = 0, array_length: int = None, array_slice_length: int = None, - connection_type: Any = Connection, - edge_type: Any = Edge, - page_info_type: Any = PageInfo, -) -> Connection: + connection_type: ConnectionConstructor = Connection, + edge_type: EdgeConstructor = Edge, + page_info_type: PageInfoConstructor = PageInfo, +) -> ConnectionType: """Create a connection object from a slice of the result set. Note that different from its JavaScript counterpart which expects an array, @@ -162,13 +166,13 @@ def connection_from_array_slice( def connection_from_list_slice( list_slice: Sequence, args: ConnectionArguments = None, - connection_type: Any = Connection, - edge_type: Any = Edge, - pageinfo_type: Any = PageInfo, + connection_type: ConnectionConstructor = Connection, + edge_type: EdgeConstructor = Edge, + pageinfo_type: PageInfoConstructor = PageInfo, slice_start=0, list_length=0, list_slice_length=None, -) -> Connection: +) -> ConnectionType: """Deprecated alias for connection_from_array_slice. We're now using the JavaScript terminology in Python as well, since list diff --git a/src/graphql_relay/connection/connectiontypes.py b/src/graphql_relay/connection/connectiontypes.py index 60d02d6..a052ece 100644 --- a/src/graphql_relay/connection/connectiontypes.py +++ b/src/graphql_relay/connection/connectiontypes.py @@ -1,4 +1,8 @@ from typing import Any, Dict, List, NamedTuple, Optional +try: + from typing import Protocol +except ImportError: + from typing_extensions import Protocol # type: ignore __all__ = ["Connection", "ConnectionArguments", "ConnectionCursor", "Edge", "PageInfo"] @@ -7,6 +11,25 @@ ConnectionCursor = str +class PageInfoType(Protocol): + @property + def startCursor(self) -> Optional[ConnectionCursor]: ... + def endCursor(self) -> Optional[ConnectionCursor]: ... + def hasPreviousPage(self) -> Optional[bool]: ... + def hasNextPage(self) -> Optional[bool]: ... + + +class PageInfoConstructor(Protocol): + def __call__( + self, + *, + startCursor: Optional[ConnectionCursor], + endCursor: Optional[ConnectionCursor], + hasPreviousPage: Optional[bool], + hasNextPage: Optional[bool], + ) -> PageInfoType: ... + + class PageInfo(NamedTuple): """A type designed to be exposed as `PageInfo` over GraphQL.""" @@ -16,6 +39,17 @@ class PageInfo(NamedTuple): hasNextPage: Optional[bool] +class EdgeType(Protocol): + @property + def node(self) -> Any: ... + @property + def cursor(self) -> ConnectionCursor: ... + + +class EdgeConstructor(Protocol): + def __call__(self, *, node: Any, cursor: ConnectionCursor) -> EdgeType: ... + + class Edge(NamedTuple): """A type designed to be exposed as a `Edge` over GraphQL.""" @@ -23,6 +57,22 @@ class Edge(NamedTuple): cursor: ConnectionCursor +class ConnectionType(Protocol): + @property + def edges(self): List[EdgeType]: ... + @property + def pageInfo(self): PageInfoType: ... + + +class ConnectionConstructor(Protocol): + def __call__( + self, + *, + edges: List[EdgeType], + pageInfo: PageInfoType, + ) -> ConnectionType: ... + + class Connection(NamedTuple): """A type designed to be exposed as a `Connection` over GraphQL."""