From 0a80913994d4b991f86fee4e769e108f1f82215f Mon Sep 17 00:00:00 2001 From: Leszek Hanusz Date: Thu, 6 Mar 2025 11:10:20 +0100 Subject: [PATCH 1/2] Retrofit introspection commits from master branch Using gql version of the get_introspection_query method (#523) Adding the input_value_deprecation argument to get_introspection_query_ast (#524) Fix test for introspection type recursion level change in graphql-core v3.3.0a7 (#521) --- gql/client.py | 19 ++-- gql/utilities/get_introspection_query_ast.py | 20 +++- gql/utilities/node_tree.py | 5 +- tests/starwars/test_dsl.py | 112 +++++++++++-------- 4 files changed, 98 insertions(+), 58 deletions(-) diff --git a/gql/client.py b/gql/client.py index a79d4b72..4800fb2d 100644 --- a/gql/client.py +++ b/gql/client.py @@ -29,7 +29,6 @@ GraphQLSchema, IntrospectionQuery, build_ast_schema, - get_introspection_query, parse, validate, ) @@ -39,7 +38,7 @@ from .transport.exceptions import TransportClosed, TransportQueryError from .transport.local_schema import LocalSchemaTransport from .transport.transport import Transport -from .utilities import build_client_schema +from .utilities import build_client_schema, get_introspection_query_ast from .utilities import parse_result as parse_result_fn from .utilities import serialize_variable_values from .utils import str_first_element @@ -98,8 +97,8 @@ def __init__( :param transport: The provided :ref:`transport `. :param fetch_schema_from_transport: Boolean to indicate that if we want to fetch the schema from the transport using an introspection query. - :param introspection_args: arguments passed to the get_introspection_query - method of graphql-core. + :param introspection_args: arguments passed to the + :meth:`gql.utilities.get_introspection_query_ast` method. :param execute_timeout: The maximum time in seconds for the execution of a request before a TimeoutError is raised. Only used for async transports. Passing None results in waiting forever for a response. @@ -1289,8 +1288,10 @@ def fetch_schema(self) -> None: Don't use this function and instead set the fetch_schema_from_transport attribute to True""" - introspection_query = get_introspection_query(**self.client.introspection_args) - execution_result = self.transport.execute(parse(introspection_query)) + introspection_query = get_introspection_query_ast( + **self.client.introspection_args + ) + execution_result = self.transport.execute(introspection_query) self.client._build_schema_from_introspection(execution_result) @@ -1657,8 +1658,10 @@ async def fetch_schema(self) -> None: Don't use this function and instead set the fetch_schema_from_transport attribute to True""" - introspection_query = get_introspection_query(**self.client.introspection_args) - execution_result = await self.transport.execute(parse(introspection_query)) + introspection_query = get_introspection_query_ast( + **self.client.introspection_args + ) + execution_result = await self.transport.execute(introspection_query) self.client._build_schema_from_introspection(execution_result) diff --git a/gql/utilities/get_introspection_query_ast.py b/gql/utilities/get_introspection_query_ast.py index d35a2a75..975ccc83 100644 --- a/gql/utilities/get_introspection_query_ast.py +++ b/gql/utilities/get_introspection_query_ast.py @@ -10,6 +10,7 @@ def get_introspection_query_ast( specified_by_url: bool = False, directive_is_repeatable: bool = False, schema_description: bool = False, + input_value_deprecation: bool = False, type_recursion_level: int = 7, ) -> DocumentNode: """Get a query for introspection as a document using the DSL module. @@ -43,13 +44,20 @@ def get_introspection_query_ast( directives = ds.__Schema.directives.select(ds.__Directive.name) + deprecated_expand = {} + + if input_value_deprecation: + deprecated_expand = { + "includeDeprecated": True, + } + if descriptions: directives.select(ds.__Directive.description) if directive_is_repeatable: directives.select(ds.__Directive.isRepeatable) directives.select( ds.__Directive.locations, - ds.__Directive.args.select(fragment_InputValue), + ds.__Directive.args(**deprecated_expand).select(fragment_InputValue), ) schema.select(directives) @@ -69,7 +77,7 @@ def get_introspection_query_ast( fields.select(ds.__Field.description) fields.select( - ds.__Field.args.select(fragment_InputValue), + ds.__Field.args(**deprecated_expand).select(fragment_InputValue), ds.__Field.type.select(fragment_TypeRef), ds.__Field.isDeprecated, ds.__Field.deprecationReason, @@ -89,7 +97,7 @@ def get_introspection_query_ast( fragment_FullType.select( fields, - ds.__Type.inputFields.select(fragment_InputValue), + ds.__Type.inputFields(**deprecated_expand).select(fragment_InputValue), ds.__Type.interfaces.select(fragment_TypeRef), enum_values, ds.__Type.possibleTypes.select(fragment_TypeRef), @@ -105,6 +113,12 @@ def get_introspection_query_ast( ds.__InputValue.defaultValue, ) + if input_value_deprecation: + fragment_InputValue.select( + ds.__InputValue.isDeprecated, + ds.__InputValue.deprecationReason, + ) + fragment_TypeRef.select( ds.__Type.kind, ds.__Type.name, diff --git a/gql/utilities/node_tree.py b/gql/utilities/node_tree.py index c307d937..4313188e 100644 --- a/gql/utilities/node_tree.py +++ b/gql/utilities/node_tree.py @@ -19,7 +19,7 @@ def _node_tree_recursive( results.append(" " * indent + f"{type(obj).__name__}") try: - keys = obj.keys + keys = sorted(obj.keys) except AttributeError: # If the object has no keys attribute, print its repr and return. results.append(" " * (indent + 1) + repr(obj)) @@ -70,6 +70,9 @@ def node_tree( Useful to debug deep DocumentNode instances created by gql or dsl_gql. + NOTE: from gql version 3.6.0b4 the elements of each node are sorted to ignore + small changes in graphql-core + WARNING: the output of this method is not guaranteed and may change without notice. """ diff --git a/tests/starwars/test_dsl.py b/tests/starwars/test_dsl.py index 2aadf92f..5cd051ba 100644 --- a/tests/starwars/test_dsl.py +++ b/tests/starwars/test_dsl.py @@ -984,18 +984,36 @@ def test_get_introspection_query_ast(option): specified_by_url=option, directive_is_repeatable=option, schema_description=option, + input_value_deprecation=option, ) dsl_introspection_query = get_introspection_query_ast( descriptions=option, specified_by_url=option, directive_is_repeatable=option, schema_description=option, + input_value_deprecation=option, ) - assert print_ast(gql(introspection_query)) == print_ast(dsl_introspection_query) - assert node_tree(dsl_introspection_query) == node_tree( - gql(print_ast(dsl_introspection_query)) - ) + try: + assert print_ast(gql(introspection_query)) == print_ast(dsl_introspection_query) + assert node_tree(dsl_introspection_query) == node_tree( + gql(print_ast(dsl_introspection_query)) + ) + except AssertionError: + + # From graphql-core version 3.3.0a7, there is two more type recursion levels + dsl_introspection_query = get_introspection_query_ast( + descriptions=option, + specified_by_url=option, + directive_is_repeatable=option, + schema_description=option, + input_value_deprecation=option, + type_recursion_level=9, + ) + assert print_ast(gql(introspection_query)) == print_ast(dsl_introspection_query) + assert node_tree(dsl_introspection_query) == node_tree( + gql(print_ast(dsl_introspection_query)) + ) def test_typename_aliased(ds): @@ -1028,11 +1046,10 @@ def test_node_tree_with_loc(ds): node_tree_result = """ DocumentNode - loc: - Location - definitions: OperationDefinitionNode + directives: + empty tuple loc: Location @@ -1043,10 +1060,8 @@ def test_node_tree_with_loc(ds): value: 'GetHeroName' - directives: - empty tuple - variable_definitions: - empty tuple + operation: + selection_set: SelectionSetNode loc: @@ -1054,13 +1069,15 @@ def test_node_tree_with_loc(ds): selections: FieldNode + alias: + None + arguments: + empty tuple + directives: + empty tuple loc: Location - directives: - empty tuple - alias: - None name: NameNode loc: @@ -1068,8 +1085,6 @@ def test_node_tree_with_loc(ds): value: 'hero' - arguments: - empty tuple nullability_assertion: None selection_set: @@ -1079,13 +1094,15 @@ def test_node_tree_with_loc(ds): selections: FieldNode + alias: + None + arguments: + empty tuple + directives: + empty tuple loc: Location - directives: - empty tuple - alias: - None name: NameNode loc: @@ -1093,23 +1110,23 @@ def test_node_tree_with_loc(ds): value: 'name' - arguments: - empty tuple nullability_assertion: None selection_set: None - operation: - + variable_definitions: + empty tuple + loc: + Location + """.strip() node_tree_result_stable = """ DocumentNode - loc: - Location - definitions: OperationDefinitionNode + directives: + empty tuple loc: Location @@ -1120,10 +1137,8 @@ def test_node_tree_with_loc(ds): value: 'GetHeroName' - directives: - empty tuple - variable_definitions: - empty tuple + operation: + selection_set: SelectionSetNode loc: @@ -1131,13 +1146,15 @@ def test_node_tree_with_loc(ds): selections: FieldNode + alias: + None + arguments: + empty tuple + directives: + empty tuple loc: Location - directives: - empty tuple - alias: - None name: NameNode loc: @@ -1145,8 +1162,6 @@ def test_node_tree_with_loc(ds): value: 'hero' - arguments: - empty tuple selection_set: SelectionSetNode loc: @@ -1154,13 +1169,15 @@ def test_node_tree_with_loc(ds): selections: FieldNode + alias: + None + arguments: + empty tuple + directives: + empty tuple loc: Location - directives: - empty tuple - alias: - None name: NameNode loc: @@ -1168,14 +1185,17 @@ def test_node_tree_with_loc(ds): value: 'name' - arguments: - empty tuple selection_set: None - operation: - + variable_definitions: + empty tuple + loc: + Location + """.strip() + print(node_tree(document, ignore_loc=False)) + try: assert node_tree(document, ignore_loc=False) == node_tree_result except AssertionError: From afc01d59f33a6ff46a3457f76fbaab75b9023d03 Mon Sep 17 00:00:00 2001 From: Leszek Hanusz Date: Thu, 6 Mar 2025 11:17:45 +0100 Subject: [PATCH 2/2] Bump graphql-core to <3.2.5 Allow to use graphql-core 3.2.4 which works with Python 3.14 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 66cf2d01..f34b2e35 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ from setuptools import setup, find_packages install_requires = [ - "graphql-core>=3.2,<3.2.4", + "graphql-core>=3.2,<3.2.5", "yarl>=1.6,<2.0", "backoff>=1.11.1,<3.0", "anyio>=3.0,<5",