From 4c69f4f0b07a167016b3494005381f183cec10a6 Mon Sep 17 00:00:00 2001 From: Christoph Zwerschke Date: Sat, 16 Apr 2022 00:53:04 +0200 Subject: [PATCH] Switch to GraphQL-core 3.2 Replicates graphql/graphql-relay-js@d4c533fa0ebe866abe655262bf01999a1e2c984c --- README.md | 137 +++++++++--------- poetry.lock | 8 +- pyproject.toml | 2 +- setup.py | 4 +- src/graphql_relay/__init__.py | 9 -- .../connection/array_connection.py | 61 -------- .../connection/arrayconnection.py | 7 - src/graphql_relay/connection/connection.py | 34 +---- src/graphql_relay/mutation/mutation.py | 16 +- tests/connection/test_array_connection.py | 47 ------ tests/connection/test_connection.py | 2 +- tests/mutation/test_mutation.py | 21 ++- tests/node/test_global.py | 16 +- tests/node/test_node.py | 8 +- tests/node/test_node_async.py | 2 +- tests/node/test_plural.py | 2 +- tests/star_wars_schema.py | 4 +- tox.ini | 8 +- 18 files changed, 122 insertions(+), 266 deletions(-) diff --git a/README.md b/README.md index 78c2b50..5a76f63 100644 --- a/README.md +++ b/README.md @@ -63,47 +63,53 @@ from graphql_relay import connection_definitions ### Connections Helper functions are provided for both building the GraphQL types -for connections and for implementing the `resolver` method for fields +for connections and for implementing the `resolve` method for fields returning those types. - `connection_args` returns the arguments that fields should provide when -they return a connection type. +they return a connection type that supports bidirectional pagination. + - `forward_connection_args` returns the arguments that fields should provide when +they return a connection type that only supports forward pagination. + - `backward_connection_args` returns the arguments that fields should provide when +they return a connection type that only supports backward pagination. - `connection_definitions` returns a `connection_type` and its associated `edgeType`, given a name and a node type. - `connection_from_array` is a helper method that takes an array and the arguments from `connection_args`, does pagination and filtering, and returns -an object in the shape expected by a `connection_type`'s `resolver` function. +an object in the shape expected by a `connection_type`'s `resolve` function. - `cursor_for_object_in_connection` is a helper method that takes an array and a member object, and returns a cursor for use in the mutation payload. + - `offset_to_cursor` takes the index of a member object in an array + and returns an opaque cursor for use in the mutation payload. + - `cursor_to_offset` takes an opaque cursor (created with `offset_to_cursor`) +and returns the corresponding array index. An example usage of these methods from the [test schema](tests/star_wars_schema.py): ```python -ship_edge, ship_connection = connection_definitions('Ship', shipType) +ship_edge, ship_connection = connection_definitions(ship_type, "Ship") -factionType = GraphQLObjectType( - name='Faction', - description='A faction in the Star Wars saga', +faction_type = GraphQLObjectType( + name="Faction", + description="A faction in the Star Wars saga", fields=lambda: { - 'id': global_id_field('Faction'), - 'name': GraphQLField( - GraphQLString, - description='The name of the faction.', - ), - 'ships': GraphQLField( + "id": global_id_field("Faction"), + "name": GraphQLField(GraphQLString, description="The name of the faction."), + "ships": GraphQLField( ship_connection, - description='The ships used by the faction.', + description="The ships used by the faction.", args=connection_args, resolve=lambda faction, _info, **args: connection_from_array( - [getShip(ship) for ship in faction.ships], args), - ) + [get_ship(ship) for ship in faction.ships], args + ), + ), }, - interfaces=[node_interface] + interfaces=[node_interface], ) ``` This shows adding a `ships` field to the `Faction` object that is a connection. -It uses `connection_definitions('Ship', shipType)` to create the connection +It uses `connection_definitions(ship_type, "Ship")` to create the connection type, adds `connection_args` as arguments on this function, and then implements the resolver function by passing the array of ships and the arguments to `connection_from_array`. @@ -131,40 +137,49 @@ An example usage of these methods from the [test schema](tests/star_wars_schema. ```python def get_node(global_id, _info): type_, id_ = from_global_id(global_id) - if type_ == 'Faction': - return getFaction(id_) - elif type_ == 'Ship': - return getShip(id_) - else: - return None + if type_ == "Faction": + return get_faction(id_) + if type_ == "Ship": + return get_ship(id_) + return None # pragma: no cover def get_node_type(obj, _info, _type): if isinstance(obj, Faction): - return factionType - else: - return shipType + return faction_type.name + return ship_type.name -node_interface, node_field = node_definitions(get_node, get_node_type) +node_interface, node_field = node_definitions(get_node, get_node_type)[:2] -factionType = GraphQLObjectType( - name= 'Faction', - description= 'A faction in the Star Wars saga', - fields= lambda: { - 'id': global_id_field('Faction'), +faction_type = GraphQLObjectType( + name="Faction", + description="A faction in the Star Wars saga", + fields=lambda: { + "id": global_id_field("Faction"), + "name": GraphQLField(GraphQLString, description="The name of the faction."), + "ships": GraphQLField( + ship_connection, + description="The ships used by the faction.", + args=connection_args, + resolve=lambda faction, _info, **args: connection_from_array( + [get_ship(ship) for ship in faction.ships], args + ), + ), }, - interfaces= [node_interface] + interfaces=[node_interface], ) -queryType = GraphQLObjectType( - name= 'Query', - fields= lambda: { - 'node': node_field - } +query_type = GraphQLObjectType( + name="Query", + fields=lambda: { + "rebels": GraphQLField(faction_type, resolve=lambda _obj, _info: get_rebels()), + "empire": GraphQLField(faction_type, resolve=lambda _obj, _info: get_empire()), + "node": node_field, + }, ) ``` This uses `node_definitions` to construct the `Node` interface and the `node` -field; it uses `from_global_id` to resolve the IDs passed in in the implementation +field; it uses `from_global_id` to resolve the IDs passed in the implementation of the function mapping ID to object. It then uses the `global_id_field` method to create the `id` field on `Faction`, which also ensures implements the `node_interface`. Finally, it adds the `node` field to the query type, using the @@ -184,43 +199,35 @@ An example usage of these methods from the [test schema](tests/star_wars_schema. ```python class IntroduceShipMutation: + def __init__(self, shipId, factionId, clientMutationId=None): self.shipId = shipId self.factionId = factionId self.clientMutationId = clientMutationId -def mutate_and_get_payload(_info, shipName, factionId): - newShip = createShip(shipName, factionId) - return IntroduceShipMutation(shipId=newShip.id, factionId=factionId) +def mutate_and_get_payload(_info, shipName, factionId, **_input): + new_ship = create_ship(shipName, factionId) + return IntroduceShipMutation(shipId=new_ship.id, factionId=factionId) -shipMutation = mutation_with_client_mutation_id( - 'IntroduceShip', +ship_mutation = mutation_with_client_mutation_id( + "IntroduceShip", input_fields={ - 'shipName': GraphQLInputField( - GraphQLNonNull(GraphQLString) - ), - 'factionId': GraphQLInputField( - GraphQLNonNull(GraphQLID) - ) + "shipName": GraphQLInputField(GraphQLNonNull(GraphQLString)), + "factionId": GraphQLInputField(GraphQLNonNull(GraphQLID)), }, output_fields={ - 'ship': GraphQLField( - shipType, - resolve=lambda payload, _info: getShip(payload.shipId) + "ship": GraphQLField( + ship_type, resolve=lambda payload, _info: get_ship(payload.shipId) + ), + "faction": GraphQLField( + faction_type, resolve=lambda payload, _info: get_faction(payload.factionId) ), - 'faction': GraphQLField( - factionType, - resolve=lambda payload, _info: getFaction(payload.factionId) - ) }, - mutate_and_get_payload=mutate_and_get_payload + mutate_and_get_payload=mutate_and_get_payload, ) -mutationType = GraphQLObjectType( - 'Mutation', - fields=lambda: { - 'introduceShip': shipMutation - } +mutation_type = GraphQLObjectType( + "Mutation", fields=lambda: {"introduceShip": ship_mutation} ) ``` @@ -268,5 +275,5 @@ Python versions and perform all additional source code checks. You can also restrict tox to an individual environment, like this: ```sh -poetry run tox -e py37 +poetry run tox -e py39 ``` diff --git a/poetry.lock b/poetry.lock index 5cefee3..5070bcf 100644 --- a/poetry.lock +++ b/poetry.lock @@ -197,7 +197,7 @@ pyflakes = ">=2.4.0,<2.5.0" [[package]] name = "graphql-core" -version = "3.1.7" +version = "3.2.1" description = "GraphQL implementation for Python, a port of GraphQL.js, the JavaScript reference implementation for GraphQL." category = "main" optional = false @@ -554,7 +554,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [metadata] lock-version = "1.1" python-versions = "^3.6" -content-hash = "aa8abc87c2bc028507d17096cdc43d6908a21efc0930a43f72c1e2d1535f2015" +content-hash = "a697c403fc34533863e70972dfabd69751fc6a8acece291d4b40d9d631312cc0" [metadata.files] appdirs = [ @@ -681,8 +681,8 @@ flake8 = [ {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, ] graphql-core = [ - {file = "graphql-core-3.1.7.tar.gz", hash = "sha256:62ec192150ccecd9a18cfb79e3e72eb7d1fd68fb594ef19c40099b6deec8ef0c"}, - {file = "graphql_core-3.1.7-py3-none-any.whl", hash = "sha256:9b460f60320be01c7f3b1766cf3e406933003008055079b9d983b8f3988f4400"}, + {file = "graphql-core-3.2.1.tar.gz", hash = "sha256:9d1bf141427b7d54be944587c8349df791ce60ade2e3cccaf9c56368c133c201"}, + {file = "graphql_core-3.2.1-py3-none-any.whl", hash = "sha256:f83c658e4968998eed1923a2e3e3eddd347e005ac0315fbb7ca4d70ea9156323"}, ] importlib-metadata = [ {file = "importlib_metadata-4.2.0-py3-none-any.whl", hash = "sha256:057e92c15bc8d9e8109738a48db0ccb31b4d9d5cfbee5a8670879a30be66304b"}, diff --git a/pyproject.toml b/pyproject.toml index 1dab273..6b5183d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,7 +42,7 @@ packages = [ [tool.poetry.dependencies] python = "^3.6" -graphql-core = "~3.1" +graphql-core = "~3.2" typing-extensions = { version = "^4.1", python = "<3.8" } [tool.poetry.dev-dependencies] diff --git a/setup.py b/setup.py index f01e962..2fc5171 100644 --- a/setup.py +++ b/setup.py @@ -32,8 +32,8 @@ "Programming Language :: Python :: Implementation :: PyPy", ], install_requires=[ - "graphql-core>=3.1,<3.2", - "typing-extensions>=4,<5; python_version < '3.8'", + "graphql-core>=3.2,<3.3", + "typing-extensions>=4.1,<5; python_version < '3.8'", ], python_requires=">=3.6,<4", packages=find_packages("src"), diff --git a/src/graphql_relay/__init__.py b/src/graphql_relay/__init__.py index 53d13ae..bab0672 100644 --- a/src/graphql_relay/__init__.py +++ b/src/graphql_relay/__init__.py @@ -38,7 +38,6 @@ # Helper for creating mutations with client mutation IDs from .mutation.mutation import ( mutation_with_client_mutation_id, - resolve_maybe_thunk, MutationFn, MutationFnWithoutArgs, NullResult, @@ -53,13 +52,6 @@ # Utilities for creating global IDs in systems that don't have them from .node.node import from_global_id, global_id_field, to_global_id, ResolvedGlobalId -# Deprecated functions from older graphql-relay-py versions -# noinspection PyProtectedMember,PyUnresolvedReferences,PyDeprecation -from .connection.array_connection import ( # noqa: F401 - connection_from_list, - connection_from_list_slice, -) - __version__ = version __version_info__ = version_info __version_js__ = version_js @@ -99,7 +91,6 @@ "page_info_type", "plural_identifying_root_field", "ResolvedGlobalId", - "resolve_maybe_thunk", "SizedSliceable", "to_global_id", "version", diff --git a/src/graphql_relay/connection/array_connection.py b/src/graphql_relay/connection/array_connection.py index ddc3c09..f25cdce 100644 --- a/src/graphql_relay/connection/array_connection.py +++ b/src/graphql_relay/connection/array_connection.py @@ -1,4 +1,3 @@ -import warnings from typing import Any, Iterator, Optional, Sequence try: @@ -71,33 +70,6 @@ def connection_from_array( ) -def connection_from_list( - data: Sequence, - args: Optional[ConnectionArguments] = None, - 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 - is too narrow a type and there is no other really appropriate type name. - """ - warnings.warn( - "connection_from_list() has been deprecated." - " Please use connection_from_array() instead.", - DeprecationWarning, - stacklevel=2, - ) - return connection_from_array_slice( - data, - args, - connection_type=connection_type, - edge_type=edge_type, - page_info_type=pageinfo_type, - ) - - def connection_from_array_slice( array_slice: SizedSliceable, args: Optional[ConnectionArguments] = None, @@ -183,39 +155,6 @@ def connection_from_array_slice( ) -def connection_from_list_slice( - list_slice: Sequence, - args: Optional[ConnectionArguments] = None, - connection_type: ConnectionConstructor = Connection, - edge_type: EdgeConstructor = Edge, - pageinfo_type: PageInfoConstructor = PageInfo, - slice_start: int = 0, - list_length: int = 0, - list_slice_length: Optional[int] = None, -) -> ConnectionType: - """Deprecated alias for connection_from_array_slice. - - We're now using the JavaScript terminology in Python as well, since list - is too narrow a type and there is no other really appropriate type name. - """ - warnings.warn( - "connection_from_list_slice() has been deprecated." - " Please use connection_from_array_slice() instead.", - DeprecationWarning, - stacklevel=2, - ) - return connection_from_array_slice( - list_slice, - args, - slice_start=slice_start, - array_length=list_length, - array_slice_length=list_slice_length, - connection_type=connection_type, - edge_type=edge_type, - page_info_type=pageinfo_type, - ) - - PREFIX = "arrayconnection:" diff --git a/src/graphql_relay/connection/arrayconnection.py b/src/graphql_relay/connection/arrayconnection.py index cee3404..efae32e 100644 --- a/src/graphql_relay/connection/arrayconnection.py +++ b/src/graphql_relay/connection/arrayconnection.py @@ -11,13 +11,6 @@ SizedSliceable, ) -# Deprecated functions from older graphql-relay-py versions -# noinspection PyProtectedMember,PyUnresolvedReferences,PyDeprecation -from .array_connection import ( # noqa: F401 - connection_from_list, - connection_from_list_slice, -) - warnings.warn( "The 'arrayconnection' module is deprecated. " "Functions should be imported from the top-level package instead.", diff --git a/src/graphql_relay/connection/connection.py b/src/graphql_relay/connection/connection.py index f8c031b..2058baa 100644 --- a/src/graphql_relay/connection/connection.py +++ b/src/graphql_relay/connection/connection.py @@ -2,37 +2,21 @@ from graphql import ( get_named_type, + resolve_thunk, GraphQLArgument, GraphQLArgumentMap, GraphQLBoolean, GraphQLField, - GraphQLFieldMap, GraphQLFieldResolver, GraphQLInt, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLString, - Thunk, + ThunkMapping, ) -try: - from graphql import GraphQLNamedOutputType -except ImportError: # GraphQL < 3.1.7 - from graphql import ( - GraphQLEnumType, - GraphQLInterfaceType, - GraphQLScalarType, - GraphQLUnionType, - ) - - GraphQLNamedOutputType = Union[ # type: ignore - GraphQLScalarType, - GraphQLObjectType, - GraphQLInterfaceType, - GraphQLUnionType, - GraphQLEnumType, - ] +from graphql import GraphQLNamedOutputType try: from typing import Protocol @@ -97,10 +81,6 @@ class GraphQLConnectionDefinitions(NamedTuple): connection_type: GraphQLObjectType -def resolve_maybe_thunk(thing_or_thunk: Thunk) -> Any: - return thing_or_thunk() if callable(thing_or_thunk) else thing_or_thunk - - """A type alias for cursors in this implementation.""" ConnectionCursor = str @@ -122,8 +102,8 @@ def connection_definitions( name: Optional[str] = None, resolve_node: Optional[GraphQLFieldResolver] = None, resolve_cursor: Optional[GraphQLFieldResolver] = None, - edge_fields: Optional[Thunk[GraphQLFieldMap]] = None, - connection_fields: Optional[Thunk[GraphQLFieldMap]] = None, + edge_fields: Optional[ThunkMapping[GraphQLField]] = None, + connection_fields: Optional[ThunkMapping[GraphQLField]] = None, ) -> GraphQLConnectionDefinitions: """Return GraphQLObjectTypes for a connection with the given name. @@ -145,7 +125,7 @@ def connection_definitions( resolve=resolve_cursor, description="A cursor for use in pagination", ), - **resolve_maybe_thunk(edge_fields or {}), + **resolve_thunk(edge_fields or {}), }, ) @@ -160,7 +140,7 @@ def connection_definitions( "edges": GraphQLField( GraphQLList(edge_type), description="A list of edges." ), - **resolve_maybe_thunk(connection_fields or {}), + **resolve_thunk(connection_fields or {}), }, ) diff --git a/src/graphql_relay/mutation/mutation.py b/src/graphql_relay/mutation/mutation.py index f316743..f927bec 100644 --- a/src/graphql_relay/mutation/mutation.py +++ b/src/graphql_relay/mutation/mutation.py @@ -3,6 +3,7 @@ from typing import Any, Callable, Dict, Optional from graphql import ( + resolve_thunk, GraphQLArgument, GraphQLField, GraphQLFieldMap, @@ -13,13 +14,12 @@ GraphQLObjectType, GraphQLResolveInfo, GraphQLString, - Thunk, + ThunkMapping, ) from graphql.pyutils import AwaitableOrValue __all__ = [ "mutation_with_client_mutation_id", - "resolve_maybe_thunk", "MutationFn", "MutationFnWithoutArgs", "NullResult", @@ -34,10 +34,6 @@ MutationFn = Callable[..., AwaitableOrValue[Any]] -def resolve_maybe_thunk(thing_or_thunk: Thunk) -> Any: - return thing_or_thunk() if callable(thing_or_thunk) else thing_or_thunk - - class NullResult: def __init__(self, clientMutationId: Optional[str] = None) -> None: self.clientMutationId = clientMutationId @@ -45,8 +41,8 @@ def __init__(self, clientMutationId: Optional[str] = None) -> None: def mutation_with_client_mutation_id( name: str, - input_fields: Thunk[GraphQLInputFieldMap], - output_fields: Thunk[GraphQLFieldMap], + input_fields: ThunkMapping[GraphQLInputField], + output_fields: ThunkMapping[GraphQLField], mutate_and_get_payload: MutationFn, description: Optional[str] = None, deprecation_reason: Optional[str] = None, @@ -69,13 +65,13 @@ def mutation_with_client_mutation_id( def augmented_input_fields() -> GraphQLInputFieldMap: return dict( - resolve_maybe_thunk(input_fields), + resolve_thunk(input_fields), clientMutationId=GraphQLInputField(GraphQLString), ) def augmented_output_fields() -> GraphQLFieldMap: return dict( - resolve_maybe_thunk(output_fields), + resolve_thunk(output_fields), clientMutationId=GraphQLField(GraphQLString), ) diff --git a/tests/connection/test_array_connection.py b/tests/connection/test_array_connection.py index 098b469..a6cb7a0 100644 --- a/tests/connection/test_array_connection.py +++ b/tests/connection/test_array_connection.py @@ -12,10 +12,6 @@ PageInfo, ) -# noinspection PyProtectedMember -from graphql_relay import connection_from_list, connection_from_list_slice - - array_abcde = ["A", "B", "C", "D", "E"] cursor_a = "YXJyYXljb25uZWN0aW9uOjA=" @@ -454,26 +450,6 @@ def __init__( assert page_info.hasPreviousPage is False assert page_info.hasNextPage is False - def provides_deprecated_connection_from_list(): - with deprecated_call(): - # noinspection PyDeprecation - c = connection_from_list( - array_abcde[:1], - args={}, - connection_type=Connection, - edge_type=Edge, - pageinfo_type=PageInfo, - ) - assert c == Connection( - edges=[edge_a], - pageInfo=PageInfo( - startCursor=cursor_a, - endCursor=cursor_a, - hasPreviousPage=False, - hasNextPage=False, - ), - ) - def describe_connection_from_array_slice(): def warns_for_deprecated_import(): @@ -785,26 +761,3 @@ def __init__( assert page_info.endCursor == cursor_a assert page_info.hasPreviousPage is False assert page_info.hasNextPage is False - - def provides_deprecated_connection_from_list_slice(): - with deprecated_call(): - # noinspection PyDeprecation - c = connection_from_list_slice( - array_abcde[:1], - args={}, - connection_type=Connection, - edge_type=Edge, - pageinfo_type=PageInfo, - slice_start=0, - list_length=1, - list_slice_length=1, - ) - assert c == Connection( - edges=[edge_a], - pageInfo=PageInfo( - startCursor=cursor_a, - endCursor=cursor_a, - hasPreviousPage=False, - hasNextPage=False, - ), - ) diff --git a/tests/connection/test_connection.py b/tests/connection/test_connection.py index 13f4596..025cb2d 100644 --- a/tests/connection/test_connection.py +++ b/tests/connection/test_connection.py @@ -181,7 +181,7 @@ def works_with_backward_connection_args(): ) def generates_correct_types(): - assert print_schema(schema).rstrip() == dedent( + assert print_schema(schema) == dedent( ''' type Query { user: User diff --git a/tests/mutation/test_mutation.py b/tests/mutation/test_mutation.py index 0118a21..41792cb 100644 --- a/tests/mutation/test_mutation.py +++ b/tests/mutation/test_mutation.py @@ -4,6 +4,7 @@ graphql, graphql_sync, print_schema, + print_type, GraphQLField, GraphQLFieldMap, GraphQLInputField, @@ -131,7 +132,7 @@ async def supports_async_mutations(): ) def can_access_root_value(): - some_mutation = mutation_with_client_mutation_id( + some_mutation = mutation_with_client_mutation_id( # pragma: no cover "SomeMutation", {}, {"result": GraphQLField(GraphQLInt)}, @@ -139,18 +140,14 @@ def can_access_root_value(): info.root_value, clientMutationId ), ) - schema = wrap_in_schema({"someMutation": some_mutation}) - source = """ - mutation { - someMutation(input: {clientMutationId: "abc"}) { - result - clientMutationId - } + + wrapper_type = GraphQLObjectType("WrapperType", {"someMutation": some_mutation}) + assert print_type(wrapper_type) == dedent( + """ + type WrapperType { + someMutation(input: SomeMutationInput!): SomeMutationPayload } """ - assert graphql_sync(schema, source, root_value=1) == ( - {"someMutation": {"result": 1, "clientMutationId": "abc"}}, - None, ) def supports_mutations_returning_null(): @@ -301,7 +298,7 @@ def generates_correct_types(): schema = wrap_in_schema({"someMutation": some_mutation}) - assert print_schema(schema).rstrip() == dedent( + assert print_schema(schema) == dedent( ''' type Query { dummy: Int diff --git a/tests/node/test_global.py b/tests/node/test_global.py index 06d26b8..bbf6b3a 100644 --- a/tests/node/test_global.py +++ b/tests/node/test_global.py @@ -68,14 +68,14 @@ def get_node(global_id: str, info: GraphQLResolveInfo) -> Any: def get_node_type( obj: Any, info: GraphQLResolveInfo, _type: Any - ) -> Optional[GraphQLObjectType]: + ) -> Optional[str]: assert info.schema is schema if "name" in obj: - return user_type + return user_type.name if "photo_id" in obj: - return photo_type + return photo_type.name if "text" in obj: - return post_type + return post_type.name return None # pragma: no cover else: @@ -93,14 +93,14 @@ def get_node(global_id: str, info: GraphQLResolveInfo) -> Any: def get_node_type( obj: Any, info: GraphQLResolveInfo, _type: Any - ) -> Optional[GraphQLObjectType]: + ) -> Optional[str]: assert info.schema is schema if isinstance(obj, User): - return user_type + return user_type.name if isinstance(obj, Photo): - return photo_type + return photo_type.name if isinstance(obj, Post): - return post_type + return post_type.name return None # pragma: no cover node_interface, node_field = node_definitions(get_node, get_node_type)[:2] diff --git a/tests/node/test_node.py b/tests/node/test_node.py index a481ec4..bec195f 100644 --- a/tests/node/test_node.py +++ b/tests/node/test_node.py @@ -47,12 +47,12 @@ def get_node(id_: str, info: GraphQLResolveInfo) -> Optional[Union[User, Photo]] def get_node_type( obj: Union[User, Photo], info: GraphQLResolveInfo, _type: Any -) -> Optional[GraphQLObjectType]: +) -> Optional[str]: assert info.schema is schema if obj in user_data: - return user_type + return user_type.name if obj in photo_data: - return photo_type + return photo_type.name return None # pragma: no cover @@ -193,7 +193,7 @@ def returns_nulls_for_bad_ids(): ) def generates_correct_types(): - assert print_schema(schema).rstrip() == dedent( + assert print_schema(schema) == dedent( ''' """An object with an ID""" interface Node { diff --git a/tests/node/test_node_async.py b/tests/node/test_node_async.py index 7f14ff0..5c5c571 100644 --- a/tests/node/test_node_async.py +++ b/tests/node/test_node_async.py @@ -26,7 +26,7 @@ class User(NamedTuple): node_interface, node_field = node_definitions( lambda id_, _info: next(filter(lambda obj: obj.id == id_, user_data), None), - lambda _obj, _info, _type: user_type, + lambda _obj, _info, _type: user_type.name, )[:2] diff --git a/tests/node/test_plural.py b/tests/node/test_plural.py index 1336321..f16c098 100644 --- a/tests/node/test_plural.py +++ b/tests/node/test_plural.py @@ -87,7 +87,7 @@ def allows_fetching(): ) def generates_correct_types(): - assert print_schema(schema).rstrip() == dedent( + assert print_schema(schema) == dedent( ''' type Query { """Map from a username to the user""" diff --git a/tests/star_wars_schema.py b/tests/star_wars_schema.py index 6b5945a..73b9aaa 100644 --- a/tests/star_wars_schema.py +++ b/tests/star_wars_schema.py @@ -110,8 +110,8 @@ def get_node(global_id, _info): def get_node_type(obj, _info, _type): if isinstance(obj, Faction): - return faction_type - return ship_type + return faction_type.name + return ship_type.name node_interface, node_field = node_definitions(get_node, get_node_type)[:2] diff --git a/tox.ini b/tox.ini index 5d22cb5..07d445a 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py3{6,7,8,9,10}, black, flake8, mypy, manifest, core310 +envlist = py3{6,7,8,9,10}, black, flake8, mypy, manifest, core320 isolated_build = true [gh-actions] @@ -36,10 +36,10 @@ deps = check-manifest>=0.48,<1 commands = check-manifest -v -[testenv:core310] +[testenv:core320] basepython = python3.9 deps = - graphql-core>=3.1,<3.2 + graphql-core==3.2.0 pytest>=6.2,<7 pytest-asyncio>=0.16,<1 pytest-describe>=2,<3 @@ -52,6 +52,6 @@ deps = pytest-asyncio>=0.16,<1 pytest-cov>=3,<4 pytest-describe>=2,<3 - py36,py37: typing-extensions>=4,<5 + py36,py37: typing-extensions>=4.1,<5 commands = pytest tests {posargs: --cov-report=term-missing --cov=graphql_relay --cov=tests --cov-fail-under=100}