From 51853dd444539a7a74ceeeef1f08c7337ba2237d Mon Sep 17 00:00:00 2001 From: Cory Dolphin Date: Tue, 20 Oct 2020 21:09:10 -0600 Subject: [PATCH 1/3] Convert all FrozenList type usages to Sequence --- src/graphql/execution/execute.py | 4 +- src/graphql/execution/values.py | 8 +-- src/graphql/language/ast.py | 72 +++++++++---------- src/graphql/pyutils/undefined.py | 3 +- src/graphql/type/definition.py | 15 ++-- src/graphql/type/directives.py | 6 +- src/graphql/type/introspection.py | 7 +- src/graphql/type/scalars.py | 4 +- src/graphql/type/schema.py | 9 +-- .../utilities/find_breaking_changes.py | 4 +- .../utilities/lexicographic_sort_schema.py | 6 +- src/graphql/validation/specified_rules.py | 24 +++---- tests/language/test_visitor.py | 5 +- 13 files changed, 86 insertions(+), 81 deletions(-) diff --git a/src/graphql/execution/execute.py b/src/graphql/execution/execute.py index 0c821fe9..a15c4f82 100644 --- a/src/graphql/execution/execute.py +++ b/src/graphql/execution/execute.py @@ -968,9 +968,9 @@ async def collect_and_execute_subfields_async() -> Dict[str, Any]: raise invalid_return_type_error( return_type, result, field_nodes ) - return self.collect_and_execute_subfields( + return self.collect_and_execute_subfields( # type: ignore return_type, field_nodes, path, result - ) # type: ignore + ) return collect_and_execute_subfields_async() diff --git a/src/graphql/execution/values.py b/src/graphql/execution/values.py index 50e182d7..848cc862 100644 --- a/src/graphql/execution/values.py +++ b/src/graphql/execution/values.py @@ -1,4 +1,4 @@ -from typing import Any, Callable, Dict, List, Optional, Union, cast +from typing import Any, Callable, Dict, List, Optional, Union, cast, Sequence from ..error import GraphQLError from ..language import ( @@ -16,7 +16,7 @@ VariableNode, print_ast, ) -from ..pyutils import inspect, print_path_list, FrozenList, Undefined +from ..pyutils import inspect, print_path_list, Undefined from ..type import ( GraphQLDirective, GraphQLField, @@ -37,7 +37,7 @@ def get_variable_values( schema: GraphQLSchema, - var_def_nodes: FrozenList[VariableDefinitionNode], + var_def_nodes: Sequence[VariableDefinitionNode], inputs: Dict[str, Any], max_errors: Optional[int] = None, ) -> CoercedVariableValues: @@ -71,7 +71,7 @@ def on_error(error: GraphQLError) -> None: def coerce_variable_values( schema: GraphQLSchema, - var_def_nodes: FrozenList[VariableDefinitionNode], + var_def_nodes: Sequence[VariableDefinitionNode], inputs: Dict[str, Any], on_error: Callable[[GraphQLError], None], ) -> Dict[str, Any]: diff --git a/src/graphql/language/ast.py b/src/graphql/language/ast.py index 188cfe6a..2db128fb 100644 --- a/src/graphql/language/ast.py +++ b/src/graphql/language/ast.py @@ -1,6 +1,6 @@ from copy import copy, deepcopy from enum import Enum -from typing import Any, Dict, List, Optional, Union +from typing import Any, Dict, List, Optional, Union, Sequence from .source import Source from .token_kind import TokenKind @@ -292,7 +292,7 @@ class NameNode(Node): class DocumentNode(Node): __slots__ = ("definitions",) - definitions: FrozenList["DefinitionNode"] + definitions: Sequence["DefinitionNode"] class DefinitionNode(Node): @@ -303,8 +303,8 @@ class ExecutableDefinitionNode(DefinitionNode): __slots__ = "name", "directives", "variable_definitions", "selection_set" name: Optional[NameNode] - directives: FrozenList["DirectiveNode"] - variable_definitions: FrozenList["VariableDefinitionNode"] + directives: Sequence["DirectiveNode"] + variable_definitions: Sequence["VariableDefinitionNode"] selection_set: "SelectionSetNode" @@ -320,19 +320,19 @@ class VariableDefinitionNode(Node): variable: "VariableNode" type: "TypeNode" default_value: Optional["ValueNode"] - directives: FrozenList["DirectiveNode"] + directives: Sequence["DirectiveNode"] class SelectionSetNode(Node): __slots__ = ("selections",) - selections: FrozenList["SelectionNode"] + selections: Sequence["SelectionNode"] class SelectionNode(Node): __slots__ = ("directives",) - directives: FrozenList["DirectiveNode"] + directives: Sequence["DirectiveNode"] class FieldNode(SelectionNode): @@ -340,7 +340,7 @@ class FieldNode(SelectionNode): alias: Optional[NameNode] name: NameNode - arguments: FrozenList["ArgumentNode"] + arguments: Sequence["ArgumentNode"] selection_set: Optional[SelectionSetNode] @@ -425,13 +425,13 @@ class EnumValueNode(ValueNode): class ListValueNode(ValueNode): __slots__ = ("values",) - values: FrozenList[ValueNode] + values: Sequence[ValueNode] class ObjectValueNode(ValueNode): __slots__ = ("fields",) - fields: FrozenList["ObjectFieldNode"] + fields: Sequence["ObjectFieldNode"] class ObjectFieldNode(Node): @@ -448,7 +448,7 @@ class DirectiveNode(Node): __slots__ = "name", "arguments" name: NameNode - arguments: FrozenList[ArgumentNode] + arguments: Sequence[ArgumentNode] # Type Reference @@ -487,8 +487,8 @@ class SchemaDefinitionNode(TypeSystemDefinitionNode): __slots__ = "description", "directives", "operation_types" description: Optional[StringValueNode] - directives: FrozenList[DirectiveNode] - operation_types: FrozenList["OperationTypeDefinitionNode"] + directives: Sequence[DirectiveNode] + operation_types: Sequence["OperationTypeDefinitionNode"] class OperationTypeDefinitionNode(Node): @@ -506,7 +506,7 @@ class TypeDefinitionNode(TypeSystemDefinitionNode): description: Optional[StringValueNode] name: NameNode - directives: FrozenList[DirectiveNode] + directives: Sequence[DirectiveNode] class ScalarTypeDefinitionNode(TypeDefinitionNode): @@ -516,8 +516,8 @@ class ScalarTypeDefinitionNode(TypeDefinitionNode): class ObjectTypeDefinitionNode(TypeDefinitionNode): __slots__ = "interfaces", "fields" - interfaces: FrozenList[NamedTypeNode] - fields: FrozenList["FieldDefinitionNode"] + interfaces: Sequence[NamedTypeNode] + fields: Sequence["FieldDefinitionNode"] class FieldDefinitionNode(DefinitionNode): @@ -525,8 +525,8 @@ class FieldDefinitionNode(DefinitionNode): description: Optional[StringValueNode] name: NameNode - directives: FrozenList[DirectiveNode] - arguments: FrozenList["InputValueDefinitionNode"] + directives: Sequence[DirectiveNode] + arguments: Sequence["InputValueDefinitionNode"] type: TypeNode @@ -535,7 +535,7 @@ class InputValueDefinitionNode(DefinitionNode): description: Optional[StringValueNode] name: NameNode - directives: FrozenList[DirectiveNode] + directives: Sequence[DirectiveNode] type: TypeNode default_value: Optional[ValueNode] @@ -543,20 +543,20 @@ class InputValueDefinitionNode(DefinitionNode): class InterfaceTypeDefinitionNode(TypeDefinitionNode): __slots__ = "fields", "interfaces" - fields: FrozenList["FieldDefinitionNode"] - interfaces: FrozenList[NamedTypeNode] + fields: Sequence["FieldDefinitionNode"] + interfaces: Sequence[NamedTypeNode] class UnionTypeDefinitionNode(TypeDefinitionNode): __slots__ = ("types",) - types: FrozenList[NamedTypeNode] + types: Sequence[NamedTypeNode] class EnumTypeDefinitionNode(TypeDefinitionNode): __slots__ = ("values",) - values: FrozenList["EnumValueDefinitionNode"] + values: Sequence["EnumValueDefinitionNode"] class EnumValueDefinitionNode(TypeDefinitionNode): @@ -566,7 +566,7 @@ class EnumValueDefinitionNode(TypeDefinitionNode): class InputObjectTypeDefinitionNode(TypeDefinitionNode): __slots__ = ("fields",) - fields: FrozenList[InputValueDefinitionNode] + fields: Sequence[InputValueDefinitionNode] # Directive Definitions @@ -577,9 +577,9 @@ class DirectiveDefinitionNode(TypeSystemDefinitionNode): description: Optional[StringValueNode] name: NameNode - arguments: FrozenList[InputValueDefinitionNode] + arguments: Sequence[InputValueDefinitionNode] repeatable: bool - locations: FrozenList[NameNode] + locations: Sequence[NameNode] # Type System Extensions @@ -588,8 +588,8 @@ class DirectiveDefinitionNode(TypeSystemDefinitionNode): class SchemaExtensionNode(Node): __slots__ = "directives", "operation_types" - directives: FrozenList[DirectiveNode] - operation_types: FrozenList[OperationTypeDefinitionNode] + directives: Sequence[DirectiveNode] + operation_types: Sequence[OperationTypeDefinitionNode] # Type Extensions @@ -599,7 +599,7 @@ class TypeExtensionNode(TypeSystemDefinitionNode): __slots__ = "name", "directives" name: NameNode - directives: FrozenList[DirectiveNode] + directives: Sequence[DirectiveNode] TypeSystemExtensionNode = Union[SchemaExtensionNode, TypeExtensionNode] @@ -612,30 +612,30 @@ class ScalarTypeExtensionNode(TypeExtensionNode): class ObjectTypeExtensionNode(TypeExtensionNode): __slots__ = "interfaces", "fields" - interfaces: FrozenList[NamedTypeNode] - fields: FrozenList[FieldDefinitionNode] + interfaces: Sequence[NamedTypeNode] + fields: Sequence[FieldDefinitionNode] class InterfaceTypeExtensionNode(TypeExtensionNode): __slots__ = "interfaces", "fields" - interfaces: FrozenList[NamedTypeNode] - fields: FrozenList[FieldDefinitionNode] + interfaces: Sequence[NamedTypeNode] + fields: Sequence[FieldDefinitionNode] class UnionTypeExtensionNode(TypeExtensionNode): __slots__ = ("types",) - types: FrozenList[NamedTypeNode] + types: Sequence[NamedTypeNode] class EnumTypeExtensionNode(TypeExtensionNode): __slots__ = ("values",) - values: FrozenList[EnumValueDefinitionNode] + values: Sequence[EnumValueDefinitionNode] class InputObjectTypeExtensionNode(TypeExtensionNode): __slots__ = ("fields",) - fields: FrozenList[InputValueDefinitionNode] + fields: Sequence[InputValueDefinitionNode] diff --git a/src/graphql/pyutils/undefined.py b/src/graphql/pyutils/undefined.py index 73dc5314..e6e53142 100644 --- a/src/graphql/pyutils/undefined.py +++ b/src/graphql/pyutils/undefined.py @@ -26,8 +26,7 @@ def __ne__(self, other: Any) -> bool: # Used to indicate undefined or invalid values (like "undefined" in JavaScript): Undefined = UndefinedType() - -Undefined.__doc__ = """Symbol for undefined values +"""Symbol for undefined values This singleton object is used to describe undefined or invalid values. It can be used in places where you would use ``undefined`` in GraphQL.js. diff --git a/src/graphql/type/definition.py b/src/graphql/type/definition.py index 95b0a244..e5b5a162 100644 --- a/src/graphql/type/definition.py +++ b/src/graphql/type/definition.py @@ -14,6 +14,7 @@ Union, cast, overload, + Sequence, ) from ..error import GraphQLError @@ -196,7 +197,7 @@ class GraphQLNamedType(GraphQLType): description: Optional[str] extensions: Optional[Dict[str, Any]] ast_node: Optional[TypeDefinitionNode] - extension_ast_nodes: Optional[FrozenList[TypeExtensionNode]] + extension_ast_nodes: Optional[Sequence[TypeExtensionNode]] def __init__( self, @@ -320,7 +321,7 @@ def serialize_odd(value): specified_by_url: Optional[str] ast_node: Optional[ScalarTypeDefinitionNode] - extension_ast_nodes: Optional[FrozenList[ScalarTypeExtensionNode]] + extension_ast_nodes: Optional[Sequence[ScalarTypeExtensionNode]] def __init__( self, @@ -690,7 +691,7 @@ class GraphQLObjectType(GraphQLNamedType): is_type_of: Optional[GraphQLIsTypeOfFn] ast_node: Optional[ObjectTypeDefinitionNode] - extension_ast_nodes: Optional[FrozenList[ObjectTypeExtensionNode]] + extension_ast_nodes: Optional[Sequence[ObjectTypeExtensionNode]] def __init__( self, @@ -810,7 +811,7 @@ class GraphQLInterfaceType(GraphQLNamedType): resolve_type: Optional[GraphQLTypeResolver] ast_node: Optional[InterfaceTypeDefinitionNode] - extension_ast_nodes: Optional[FrozenList[InterfaceTypeExtensionNode]] + extension_ast_nodes: Optional[Sequence[InterfaceTypeExtensionNode]] def __init__( self, @@ -933,7 +934,7 @@ def resolve_type(obj, _info, _type): resolve_type: Optional[GraphQLTypeResolver] ast_node: Optional[UnionTypeDefinitionNode] - extension_ast_nodes: Optional[FrozenList[UnionTypeExtensionNode]] + extension_ast_nodes: Optional[Sequence[UnionTypeExtensionNode]] def __init__( self, @@ -1039,7 +1040,7 @@ class RGBEnum(enum.Enum): values: GraphQLEnumValueMap ast_node: Optional[EnumTypeDefinitionNode] - extension_ast_nodes: Optional[FrozenList[EnumTypeExtensionNode]] + extension_ast_nodes: Optional[Sequence[EnumTypeExtensionNode]] def __init__( self, @@ -1267,7 +1268,7 @@ class GeoPoint(GraphQLInputObjectType): """ ast_node: Optional[InputObjectTypeDefinitionNode] - extension_ast_nodes: Optional[FrozenList[InputObjectTypeExtensionNode]] + extension_ast_nodes: Optional[Sequence[InputObjectTypeExtensionNode]] def __init__( self, diff --git a/src/graphql/type/directives.py b/src/graphql/type/directives.py index 794aa4be..6fe26462 100644 --- a/src/graphql/type/directives.py +++ b/src/graphql/type/directives.py @@ -1,4 +1,4 @@ -from typing import Any, Collection, Dict, List, Optional, cast +from typing import Any, Collection, Dict, List, Optional, cast, Sequence from ..language import ast, DirectiveLocation from ..pyutils import inspect, is_description, FrozenList @@ -211,7 +211,7 @@ def assert_directive(directive: Any) -> GraphQLDirective: ) -specified_directives: FrozenList[GraphQLDirective] = FrozenList( +specified_directives: Sequence[GraphQLDirective] = FrozenList( [ GraphQLIncludeDirective, GraphQLSkipDirective, @@ -219,7 +219,7 @@ def assert_directive(directive: Any) -> GraphQLDirective: GraphQLSpecifiedByDirective, ] ) -specified_directives.__doc__ = """The full list of specified directives.""" +"""The full list of specified directives.""" def is_specified_directive(directive: GraphQLDirective) -> bool: diff --git a/src/graphql/type/introspection.py b/src/graphql/type/introspection.py index 6262b595..5c840cb2 100644 --- a/src/graphql/type/introspection.py +++ b/src/graphql/type/introspection.py @@ -1,4 +1,5 @@ from enum import Enum +from typing import Mapping, Union from .definition import ( GraphQLArgument, @@ -506,7 +507,9 @@ class TypeKind(Enum): # Since double underscore names are subject to name mangling in Python, # the introspection classes are best imported via this dictionary: -introspection_types = FrozenDict( +introspection_types: Mapping[ + str, Union[GraphQLNamedType, GraphQLObjectType] +] = FrozenDict( { "__Schema": __Schema, "__Directive": __Directive, @@ -518,7 +521,7 @@ class TypeKind(Enum): "__TypeKind": __TypeKind, } ) -introspection_types.__doc__ = """A dictionary containing all introspection types.""" +"""A dictionary containing all introspection types.""" def is_introspection_type(type_: GraphQLNamedType) -> bool: diff --git a/src/graphql/type/scalars.py b/src/graphql/type/scalars.py index fc3c13de..a22fd853 100644 --- a/src/graphql/type/scalars.py +++ b/src/graphql/type/scalars.py @@ -1,5 +1,5 @@ from math import isfinite -from typing import Any +from typing import Any, Mapping from ..error import GraphQLError from ..pyutils import inspect, is_finite, is_integer, FrozenDict @@ -275,7 +275,7 @@ def parse_id_literal(value_node: ValueNode, _variables: Any = None) -> str: ) -specified_scalar_types: FrozenDict[str, GraphQLScalarType] = FrozenDict( +specified_scalar_types: Mapping[str, GraphQLScalarType] = FrozenDict( { type_.name: type_ for type_ in ( diff --git a/src/graphql/type/schema.py b/src/graphql/type/schema.py index 06f73bfc..c26ff604 100644 --- a/src/graphql/type/schema.py +++ b/src/graphql/type/schema.py @@ -8,6 +8,7 @@ Set, Union, cast, + Sequence, ) from ..error import GraphQLError @@ -93,11 +94,11 @@ class GraphQLSchema: mutation_type: Optional[GraphQLObjectType] subscription_type: Optional[GraphQLObjectType] type_map: TypeMap - directives: FrozenList[GraphQLDirective] + directives: Sequence[GraphQLDirective] description: Optional[str] extensions: Optional[Dict[str, Any]] ast_node: Optional[ast.SchemaDefinitionNode] - extension_ast_nodes: Optional[FrozenList[ast.SchemaExtensionNode]] + extension_ast_nodes: Optional[Sequence[ast.SchemaExtensionNode]] _implementations_map: Dict[str, InterfaceImplementations] _sub_type_map: Dict[str, Set[str]] @@ -171,7 +172,7 @@ def __init__( self.extensions = extensions self.ast_node = ast_node self.extension_ast_nodes = ( - cast(FrozenList[ast.SchemaExtensionNode], extension_ast_nodes) + cast(Sequence[ast.SchemaExtensionNode], extension_ast_nodes) if extension_ast_nodes else None ) @@ -183,7 +184,7 @@ def __init__( self.directives = ( specified_directives if directives is None - else cast(FrozenList[GraphQLDirective], directives) + else cast(Sequence[GraphQLDirective], directives) ) # To preserve order of user-provided types, we add first to add them to diff --git a/src/graphql/utilities/find_breaking_changes.py b/src/graphql/utilities/find_breaking_changes.py index 19a649da..7807e28b 100644 --- a/src/graphql/utilities/find_breaking_changes.py +++ b/src/graphql/utilities/find_breaking_changes.py @@ -1,6 +1,6 @@ from enum import Enum from operator import attrgetter -from typing import Any, Dict, List, NamedTuple, Union, cast +from typing import Any, Dict, List, NamedTuple, Union, cast, Sequence from ..language import print_ast, visit, ObjectValueNode, Visitor from ..pyutils import inspect, FrozenList, Undefined @@ -580,7 +580,7 @@ class ListDiff(NamedTuple): persisted: List -def list_diff(old_list: List, new_list: List) -> ListDiff: +def list_diff(old_list: Sequence, new_list: Sequence) -> ListDiff: """Get differences between two lists of named items.""" added = [] persisted = [] diff --git a/src/graphql/utilities/lexicographic_sort_schema.py b/src/graphql/utilities/lexicographic_sort_schema.py index 4cb49f1d..51982a58 100644 --- a/src/graphql/utilities/lexicographic_sort_schema.py +++ b/src/graphql/utilities/lexicographic_sort_schema.py @@ -1,7 +1,7 @@ -from typing import Dict, List, Optional, Tuple, Union, cast +from typing import Dict, List, Optional, Tuple, Union, cast, Sequence from ..language import DirectiveLocation -from ..pyutils import inspect, FrozenList +from ..pyutils import inspect from ..type import ( GraphQLArgument, GraphQLDirective, @@ -97,7 +97,7 @@ def sort_input_fields( for name, field in sorted(fields_map.items()) } - def sort_types(arr: FrozenList[GraphQLNamedType]) -> List[GraphQLNamedType]: + def sort_types(arr: Sequence[GraphQLNamedType]) -> List[GraphQLNamedType]: return [ replace_named_type(type_) for type_ in sorted(arr, key=sort_by_name_key) ] diff --git a/src/graphql/validation/specified_rules.py b/src/graphql/validation/specified_rules.py index dd0c2633..6df8c1c0 100644 --- a/src/graphql/validation/specified_rules.py +++ b/src/graphql/validation/specified_rules.py @@ -1,4 +1,4 @@ -from typing import Type +from typing import Type, Sequence from ..pyutils import FrozenList @@ -101,7 +101,7 @@ # The order of the rules in this list has been adjusted to lead to the # most clear output when encountering multiple validation errors. -specified_rules: FrozenList[Type[ASTValidationRule]] = FrozenList( +specified_rules: Sequence[Type[ASTValidationRule]] = FrozenList( [ ExecutableDefinitionsRule, UniqueOperationNamesRule, @@ -131,14 +131,14 @@ UniqueInputFieldNamesRule, ] ) -specified_rules.__doc__ = """\ - This list includes all validation rules defined by the GraphQL spec. +""" +This list includes all validation rules defined by the GraphQL spec. - The order of the rules in this list has been adjusted to lead to the - most clear output when encountering multiple validation errors. - """ +The order of the rules in this list has been adjusted to lead to the +most clear output when encountering multiple validation errors. +""" -specified_sdl_rules: FrozenList[Type[ASTValidationRule]] = FrozenList( +specified_sdl_rules: Sequence[Type[ASTValidationRule]] = FrozenList( [ LoneSchemaDefinitionRule, UniqueOperationTypesRule, @@ -156,8 +156,8 @@ ProvidedRequiredArgumentsOnDirectivesRule, ] ) -specified_sdl_rules.__doc__ = """\ - This list includes all rules for validating SDL. +""" +This list includes all rules for validating SDL. - For internal use only. - """ +For internal use only. +""" diff --git a/tests/language/test_visitor.py b/tests/language/test_visitor.py index 0fd72230..ecbf339d 100644 --- a/tests/language/test_visitor.py +++ b/tests/language/test_visitor.py @@ -982,13 +982,14 @@ class CustomFieldNode(SelectionNode): custom_selection_set = cast(FieldNode, custom_ast.definitions[0]).selection_set assert custom_selection_set is not None - custom_selection_set.selections = custom_selection_set.selections + [ + custom_selection_set.selections = [ + *custom_selection_set.selections, CustomFieldNode( name=NameNode(value="b"), selection_set=SelectionSetNode( selections=CustomFieldNode(name=NameNode(value="c")) ), - ) + ), ] def does_not_traverse_unknown_node_kinds(): From 85e6ecc95dda3327964a5f49aeaddc591d10c386 Mon Sep 17 00:00:00 2001 From: Cory Dolphin Date: Wed, 21 Oct 2020 11:45:21 -0600 Subject: [PATCH 2/3] Convert FrozenList to use Tuple as base class for immutability --- docs/conf.py | 185 ++++++++------- src/graphql/error/graphql_error.py | 4 +- src/graphql/language/visitor.py | 13 +- src/graphql/pyutils/frozen_list.py | 57 +---- src/graphql/utilities/extend_schema.py | 21 +- tests/language/test_parser.py | 56 ++--- tests/language/test_schema_parser.py | 250 ++++++++++---------- tests/language/test_visitor.py | 6 +- tests/pyutils/test_frozen_list.py | 113 --------- tests/type/test_definition.py | 16 +- tests/type/test_schema.py | 8 +- tests/utilities/test_build_ast_schema.py | 6 +- tests/utilities/test_build_client_schema.py | 2 +- 13 files changed, 297 insertions(+), 440 deletions(-) delete mode 100644 tests/pyutils/test_frozen_list.py diff --git a/docs/conf.py b/docs/conf.py index 7695f807..2cc70234 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -30,29 +30,29 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', + "sphinx.ext.autodoc", ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. # # source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = 'GraphQL-core 3' -copyright = '2020, Christoph Zwerschke' -author = 'Christoph Zwerschke' +project = "GraphQL-core 3" +copyright = "2020, Christoph Zwerschke" +author = "Christoph Zwerschke" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -61,7 +61,7 @@ # The short X.Y version. # version = '3.1' # The full version, including alpha/beta/rc tags. -version = release = '3.1.2' +version = release = "3.1.2" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -82,16 +82,16 @@ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] # AutoDoc configuration autoclass_content = "class" autodoc_default_options = { - 'members': True, - 'inherited-members': True, - 'special-members': '__init__', - 'undoc-members': True, - 'show-inheritance': True + "members": True, + "inherited-members": True, + "special-members": "__init__", + "undoc-members": True, + "show-inheritance": True, } autosummary_generate = True @@ -100,29 +100,37 @@ # qualified form, but the documentation has the shorter form. # We need to give autodoc a little help in this cases. graphql_modules = { - 'error': ['graphql_error'], - 'execution': ['execute', 'middleware'], - 'language': ['ast', 'directive_locations', 'location', - 'source', 'token_kind', 'visitor'], - 'pyutils': ['event_emitter', 'frozen_list', 'path'], - 'subscription': [], - 'type': ['definition', 'directives', 'schema'], - 'utilities': ['find_breaking_changes', 'type_info'], - 'validation': ['rules', 'validation_context']} + "error": ["graphql_error"], + "execution": ["execute", "middleware"], + "language": [ + "ast", + "directive_locations", + "location", + "source", + "token_kind", + "visitor", + ], + "pyutils": ["event_emitter", "frozen_list", "path"], + "subscription": [], + "type": ["definition", "directives", "schema"], + "utilities": ["find_breaking_changes", "type_info"], + "validation": ["rules", "validation_context"], +} # GraphQL-core classes that autodoc sometimes cannot find # (e.g. where specified as string in type hints). # We need to give autodoc a little help in this cases, too: graphql_classes = { - 'GraphQLAbstractType': 'type', - 'GraphQLObjectType': 'type', - 'Node': 'language', - 'Source': 'language', - 'SourceLocation': 'language' + "GraphQLAbstractType": "type", + "GraphQLObjectType": "type", + "Node": "language", + "Source": "language", + "SourceLocation": "language", } # ignore the following undocumented or internal references: -ignore_references = set(''' +ignore_references = set( + """ GNT GT T enum.Enum asyncio.events.AbstractEventLoop @@ -130,72 +138,74 @@ graphql.validation.validation_context.VariableUsage graphql.validation.rules.known_argument_names.KnownArgumentNamesOnDirectivesRule graphql.validation.rules.provided_required_arguments.ProvidedRequiredArgumentsOnDirectivesRule -'''.split()) +""".split() +) ignore_references.update(__builtins__.keys()) def on_missing_reference(app, env, node, contnode): """Fix or skip any missing references.""" - if node.get('refdomain') != 'py': + if node.get("refdomain") != "py": return None - target = node.get('reftarget') + target = node.get("reftarget") if not target: return None if target in ignore_references: return contnode - typ = node.get('reftype') - if typ != 'class': + typ = node.get("reftype") + if typ != "class": return None - if '.' in target: # maybe too specific - base_module, target = target.split('.', 1) - if base_module == 'typing': + if "." in target: # maybe too specific + base_module, target = target.split(".", 1) + if base_module == "typing": return contnode - if base_module == 'graphql': - if '.' not in target: + if base_module == "graphql": + if "." not in target: return None - base_module, target = target.split('.', 1) - if '.' not in target: + base_module, target = target.split(".", 1) + if "." not in target: return None sub_modules = graphql_modules.get(base_module) if not sub_modules: return - sub_module = target.split('.', 1)[0] + sub_module = target.split(".", 1)[0] if sub_module not in sub_modules: return None - target = 'graphql.' + base_module + '.' + target.rsplit('.', 1)[-1] + target = "graphql." + base_module + "." + target.rsplit(".", 1)[-1] else: # maybe not specific enough base_module = graphql_classes.get(target) if not base_module: return None - target = 'graphql.' + base_module + '.' + target + target = "graphql." + base_module + "." + target # replace target - if contnode.__class__.__name__ == 'Text': + if contnode.__class__.__name__ == "Text": contnode = contnode.__class__(target) - elif contnode.__class__.__name__ == 'literal': + elif contnode.__class__.__name__ == "literal": if len(contnode.children) != 1: return None textnode = contnode.children[0] contnode.children[0] = textnode.__class__(target) else: return None - node['reftarget'] = target - fromdoc = node.get('refdoc') + node["reftarget"] = target + fromdoc = node.get("refdoc") if not fromdoc: - doc_module = node.get('py:module') + doc_module = node.get("py:module") if doc_module: - if doc_module.startswith('graphql.'): - doc_module = doc_module.split('.', 1)[-1] - if doc_module not in graphql_modules and doc_module != 'graphql': + if doc_module.startswith("graphql."): + doc_module = doc_module.split(".", 1)[-1] + if doc_module not in graphql_modules and doc_module != "graphql": doc_module = None - fromdoc = 'modules/' + (doc_module or base_module) + fromdoc = "modules/" + (doc_module or base_module) # try resolving again with replaced target - return env.domains['py'].resolve_xref( - env, fromdoc, app.builder, typ, target, node, contnode) + return env.domains["py"].resolve_xref( + env, fromdoc, app.builder, typ, target, node, contnode + ) def setup(app): - app.connect('missing-reference', on_missing_reference) + app.connect("missing-reference", on_missing_reference) # be nitpicky (handle all possible problems in on_missing_reference) @@ -222,7 +232,7 @@ def setup(app): # show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] @@ -238,15 +248,13 @@ def setup(app): # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'sphinx_rtd_theme' +html_theme = "sphinx_rtd_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # -html_theme_options = { - 'navigation_depth': 5 -} +html_theme_options = {"navigation_depth": 5} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] @@ -354,34 +362,36 @@ def setup(app): # html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = 'GraphQL-core-3-doc' +htmlhelp_basename = "GraphQL-core-3-doc" # -- Options for LaTeX output --------------------------------------------- latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'GraphQL-core-3.tex', 'GraphQL-core 3 Documentation', - 'Christoph Zwerschke', 'manual'), + ( + master_doc, + "GraphQL-core-3.tex", + "GraphQL-core 3 Documentation", + "Christoph Zwerschke", + "manual", + ), ] # The name of an image file (relative to this directory) to place at the top of @@ -415,10 +425,7 @@ def setup(app): # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'graphql-core', 'GraphQL-core 3 Documentation', - [author], 1) -] +man_pages = [(master_doc, "graphql-core", "GraphQL-core 3 Documentation", [author], 1)] # If true, show URL addresses after external links. # @@ -431,9 +438,15 @@ def setup(app): # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'GraphQL-core', 'GraphQL-core 3 Documentation', - author, 'GraphQL-core 3', 'One line description of project.', - 'Miscellaneous'), + ( + master_doc, + "GraphQL-core", + "GraphQL-core 3 Documentation", + author, + "GraphQL-core 3", + "One line description of project.", + "Miscellaneous", + ), ] # Documents to append as an appendix to all manuals. diff --git a/src/graphql/error/graphql_error.py b/src/graphql/error/graphql_error.py index 1e60320f..0c5bdb2b 100644 --- a/src/graphql/error/graphql_error.py +++ b/src/graphql/error/graphql_error.py @@ -1,6 +1,8 @@ from sys import exc_info from typing import Any, Collection, Dict, List, Optional, Union, TYPE_CHECKING +from ..pyutils.is_collection import is_collection + if TYPE_CHECKING: from ..language.ast import Node # noqa: F401 from ..language.location import SourceLocation # noqa: F401 @@ -92,7 +94,7 @@ def __init__( ) -> None: super().__init__(message) self.message = message - if nodes and not isinstance(nodes, list): + if nodes and not is_collection(nodes): nodes = [nodes] # type: ignore self.nodes = nodes or None # type: ignore self.source = source diff --git a/src/graphql/language/visitor.py b/src/graphql/language/visitor.py index 2378e27b..8ef6f004 100644 --- a/src/graphql/language/visitor.py +++ b/src/graphql/language/visitor.py @@ -12,7 +12,7 @@ Union, ) -from ..pyutils import inspect, snake_to_camel +from ..pyutils import inspect, snake_to_camel, is_collection from . import ast from .ast import Node @@ -244,7 +244,7 @@ def visit( if visitor_keys is None: visitor_keys = QUERY_DOCUMENT_KEYS stack: Any = None - in_array = isinstance(root, list) + in_array = is_collection(root) keys: Tuple[Node, ...] = (root,) idx = -1 edits: List[Any] = [] @@ -267,7 +267,7 @@ def visit( parent = ancestors_pop() if ancestors else None if is_edited: if in_array: - node = node[:] + node = list(node) # Mutable copy else: node = copy(node) edit_offset = 0 @@ -282,7 +282,8 @@ def visit( node[edit_key] = edit_value else: setattr(node, edit_key, edit_value) - + if is_edited and in_array: + node = tuple(node) # Immutable copy idx = stack.idx keys = stack.keys edits = stack.edits @@ -304,7 +305,7 @@ def visit( if parent: path_append(key) - if isinstance(node, list): + if is_collection(node): result = None else: if not isinstance(node, Node): @@ -340,7 +341,7 @@ def visit( path_pop() else: stack = Stack(in_array, idx, keys, edits, stack) - in_array = isinstance(node, list) + in_array = is_collection(node) keys = node if in_array else visitor_keys.get(node.kind, ()) idx = -1 edits = [] diff --git a/src/graphql/pyutils/frozen_list.py b/src/graphql/pyutils/frozen_list.py index e1bd7ebc..cf60ef54 100644 --- a/src/graphql/pyutils/frozen_list.py +++ b/src/graphql/pyutils/frozen_list.py @@ -1,5 +1,5 @@ from copy import deepcopy -from typing import List, TypeVar +from typing import List, TypeVar, Tuple from .frozen_error import FrozenError @@ -9,58 +9,5 @@ T = TypeVar("T", covariant=True) -class FrozenList(List[T]): +class FrozenList(Tuple[T, ...]): """List that can only be read, but not changed.""" - - def __delitem__(self, key): - raise FrozenError - - def __setitem__(self, key, value): - raise FrozenError - - def __add__(self, value): - if isinstance(value, tuple): - value = list(value) - return list.__add__(self, value) - - def __iadd__(self, value): - raise FrozenError - - def __mul__(self, value): - return list.__mul__(self, value) - - def __imul__(self, value): - raise FrozenError - - def __hash__(self): - return hash(tuple(self)) - - def __copy__(self): - return FrozenList(self) - - def __deepcopy__(self, memo): - return FrozenList(deepcopy(value, memo) for value in self) - - def append(self, x): - raise FrozenError - - def extend(self, iterable): - raise FrozenError - - def insert(self, i, x): - raise FrozenError - - def remove(self, x): - raise FrozenError - - def pop(self, i=None): - raise FrozenError - - def clear(self): - raise FrozenError - - def sort(self, *, key=None, reverse=False): - raise FrozenError - - def reverse(self): - raise FrozenError diff --git a/src/graphql/utilities/extend_schema.py b/src/graphql/utilities/extend_schema.py index 71cf6309..1638823e 100644 --- a/src/graphql/utilities/extend_schema.py +++ b/src/graphql/utilities/extend_schema.py @@ -253,7 +253,8 @@ def extend_input_object_type( }, **build_input_field_map(extensions), }, - "extension_ast_nodes": kwargs["extension_ast_nodes"] + extensions, + "extension_ast_nodes": kwargs["extension_ast_nodes"] + + tuple(extensions), } ) @@ -265,7 +266,8 @@ def extend_enum_type(type_: GraphQLEnumType) -> GraphQLEnumType: **{ **kwargs, "values": {**kwargs["values"], **build_enum_value_map(extensions)}, - "extension_ast_nodes": kwargs["extension_ast_nodes"] + extensions, + "extension_ast_nodes": kwargs["extension_ast_nodes"] + + tuple(extensions), } ) @@ -281,7 +283,8 @@ def extend_scalar_type(type_: GraphQLScalarType) -> GraphQLScalarType: **{ **kwargs, "specified_by_url": specified_by_url, - "extension_ast_nodes": kwargs["extension_ast_nodes"] + extensions, + "extension_ast_nodes": kwargs["extension_ast_nodes"] + + tuple(extensions), } ) @@ -305,7 +308,8 @@ def extend_object_type(type_: GraphQLObjectType) -> GraphQLObjectType: }, **build_field_map(extensions), }, - "extension_ast_nodes": kwargs["extension_ast_nodes"] + extensions, + "extension_ast_nodes": kwargs["extension_ast_nodes"] + + tuple(extensions), } ) @@ -329,7 +333,8 @@ def extend_interface_type(type_: GraphQLInterfaceType) -> GraphQLInterfaceType: }, **build_field_map(extensions), }, - "extension_ast_nodes": kwargs["extension_ast_nodes"] + extensions, + "extension_ast_nodes": kwargs["extension_ast_nodes"] + + tuple(extensions), } ) @@ -345,7 +350,8 @@ def extend_union_type(type_: GraphQLUnionType) -> GraphQLUnionType: for member_type in kwargs["types"] ] + build_union_types(extensions), - "extension_ast_nodes": kwargs["extension_ast_nodes"] + extensions, + "extension_ast_nodes": kwargs["extension_ast_nodes"] + + tuple(extensions), } ) @@ -662,7 +668,8 @@ def build_type(ast_node: TypeDefinitionNode) -> GraphQLNamedType: else None, "extensions": None, "ast_node": schema_def or schema_kwargs["ast_node"], - "extension_ast_nodes": schema_kwargs["extension_ast_nodes"] + schema_extensions, + "extension_ast_nodes": schema_kwargs["extension_ast_nodes"] + + tuple(schema_extensions), "assume_valid": assume_valid, } diff --git a/tests/language/test_parser.py b/tests/language/test_parser.py index 573ef7c1..cab9aabb 100644 --- a/tests/language/test_parser.py +++ b/tests/language/test_parser.py @@ -27,7 +27,7 @@ parse_value, Source, ) -from graphql.pyutils import inspect +from graphql.pyutils import inspect, is_collection from ..fixtures import kitchen_sink_query # noqa: F401 from ..utils import dedent @@ -133,14 +133,14 @@ def parses_multi_byte_characters(): """ ) definitions = doc.definitions - assert isinstance(definitions, list) + assert is_collection(definitions) assert len(definitions) == 1 selection_set = cast(OperationDefinitionNode, definitions[0]).selection_set selections = selection_set.selections - assert isinstance(selections, list) + assert is_collection(selections) assert len(selections) == 1 arguments = cast(FieldNode, selections[0]).arguments - assert isinstance(arguments, list) + assert is_collection(arguments) assert len(arguments) == 1 value = arguments[0].value assert isinstance(value, StringValueNode) @@ -227,20 +227,20 @@ def creates_ast(): assert isinstance(doc, DocumentNode) assert doc.loc == (0, 41) definitions = doc.definitions - assert isinstance(definitions, list) + assert is_collection(definitions) assert len(definitions) == 1 definition = cast(OperationDefinitionNode, definitions[0]) assert isinstance(definition, DefinitionNode) assert definition.loc == (0, 40) assert definition.operation == OperationType.QUERY assert definition.name is None - assert definition.variable_definitions == [] - assert definition.directives == [] + assert definition.variable_definitions == () + assert definition.directives == () selection_set = definition.selection_set assert isinstance(selection_set, SelectionSetNode) assert selection_set.loc == (0, 40) selections = selection_set.selections - assert isinstance(selections, list) + assert is_collection(selections) assert len(selections) == 1 field = selections[0] assert isinstance(field, FieldNode) @@ -251,7 +251,7 @@ def creates_ast(): assert name.loc == (4, 8) assert name.value == "node" arguments = field.arguments - assert isinstance(arguments, list) + assert is_collection(arguments) assert len(arguments) == 1 argument = arguments[0] assert isinstance(argument, ArgumentNode) @@ -265,11 +265,11 @@ def creates_ast(): assert value.loc == (13, 14) assert value.value == "4" assert argument.loc == (9, 14) - assert field.directives == [] + assert field.directives == () selection_set = field.selection_set # type: ignore assert isinstance(selection_set, SelectionSetNode) selections = selection_set.selections - assert isinstance(selections, list) + assert is_collection(selections) assert len(selections) == 2 field = selections[0] assert isinstance(field, FieldNode) @@ -279,8 +279,8 @@ def creates_ast(): assert isinstance(name, NameNode) assert name.loc == (22, 24) assert name.value == "id" - assert field.arguments == [] - assert field.directives == [] + assert field.arguments == () + assert field.directives == () assert field.selection_set is None field = selections[0] assert isinstance(field, FieldNode) @@ -290,8 +290,8 @@ def creates_ast(): assert isinstance(name, NameNode) assert name.loc == (22, 24) assert name.value == "id" - assert field.arguments == [] - assert field.directives == [] + assert field.arguments == () + assert field.directives == () assert field.selection_set is None field = selections[1] assert isinstance(field, FieldNode) @@ -301,8 +301,8 @@ def creates_ast(): assert isinstance(name, NameNode) assert name.loc == (30, 34) assert name.value == "name" - assert field.arguments == [] - assert field.directives == [] + assert field.arguments == () + assert field.directives == () assert field.selection_set is None def creates_ast_from_nameless_query_without_variables(): @@ -320,20 +320,20 @@ def creates_ast_from_nameless_query_without_variables(): assert isinstance(doc, DocumentNode) assert doc.loc == (0, 30) definitions = doc.definitions - assert isinstance(definitions, list) + assert is_collection(definitions) assert len(definitions) == 1 definition = definitions[0] assert isinstance(definition, OperationDefinitionNode) assert definition.loc == (0, 29) assert definition.operation == OperationType.QUERY assert definition.name is None - assert definition.variable_definitions == [] - assert definition.directives == [] + assert definition.variable_definitions == () + assert definition.directives == () selection_set = definition.selection_set assert isinstance(selection_set, SelectionSetNode) assert selection_set.loc == (6, 29) selections = selection_set.selections - assert isinstance(selections, list) + assert is_collection(selections) assert len(selections) == 1 field = selections[0] assert isinstance(field, FieldNode) @@ -343,13 +343,13 @@ def creates_ast_from_nameless_query_without_variables(): assert isinstance(name, NameNode) assert name.loc == (10, 14) assert name.value == "node" - assert field.arguments == [] - assert field.directives == [] + assert field.arguments == () + assert field.directives == () selection_set = field.selection_set # type: ignore assert isinstance(selection_set, SelectionSetNode) assert selection_set.loc == (15, 27) selections = selection_set.selections - assert isinstance(selections, list) + assert is_collection(selections) assert len(selections) == 1 field = selections[0] assert isinstance(field, FieldNode) @@ -359,8 +359,8 @@ def creates_ast_from_nameless_query_without_variables(): assert isinstance(name, NameNode) assert name.loc == (21, 23) assert name.value == "id" - assert field.arguments == [] - assert field.directives == [] + assert field.arguments == () + assert field.directives == () assert field.selection_set is None def allows_parsing_without_source_location_information(): @@ -430,7 +430,7 @@ def parses_list_values(): assert isinstance(result, ListValueNode) assert result.loc == (0, 11) values = result.values - assert isinstance(values, list) + assert is_collection(values) assert len(values) == 2 value = values[0] assert isinstance(value, IntValueNode) @@ -446,7 +446,7 @@ def parses_block_strings(): assert isinstance(result, ListValueNode) assert result.loc == (0, 20) values = result.values - assert isinstance(values, list) + assert is_collection(values) assert len(values) == 2 value = values[0] assert isinstance(value, StringValueNode) diff --git a/tests/language/test_schema_parser.py b/tests/language/test_schema_parser.py index cacfa657..6e7ae844 100644 --- a/tests/language/test_schema_parser.py +++ b/tests/language/test_schema_parser.py @@ -30,6 +30,7 @@ UnionTypeDefinitionNode, parse, ) +from graphql.pyutils import is_collection, FrozenList from ..fixtures import kitchen_sink_sdl # noqa: F401 @@ -48,7 +49,7 @@ def assert_definitions(body, loc, num=1): assert isinstance(doc, DocumentNode) assert doc.loc == loc definitions = doc.definitions - assert isinstance(definitions, list) + assert is_collection(definitions) assert len(definitions) == num return definitions[0] if num == 1 else definitions @@ -131,13 +132,13 @@ def simple_type(): assert isinstance(definition, ObjectTypeDefinitionNode) assert definition.name == name_node("Hello", (6, 11)) assert definition.description is None - assert definition.interfaces == [] - assert definition.directives == [] - assert definition.fields == [ + assert definition.interfaces == () + assert definition.directives == () + assert definition.fields == ( field_node( name_node("world", (16, 21)), type_node("String", (23, 29)), (16, 29) - ) - ] + ), + ) assert definition.loc == (1, 31) def parses_type_with_description_string(): @@ -200,13 +201,13 @@ def simple_extension(): extension = assert_definitions(body, (0, 39)) assert isinstance(extension, ObjectTypeExtensionNode) assert extension.name == name_node("Hello", (13, 18)) - assert extension.interfaces == [] - assert extension.directives == [] - assert extension.fields == [ + assert extension.interfaces == () + assert extension.directives == () + assert extension.fields == ( field_node( name_node("world", (23, 28)), type_node("String", (30, 36)), (23, 36) - ) - ] + ), + ) assert extension.loc == (1, 38) def object_extension_without_fields(): @@ -214,9 +215,9 @@ def object_extension_without_fields(): extension = assert_definitions(body, (0, 37)) assert isinstance(extension, ObjectTypeExtensionNode) assert extension.name == name_node("Hello", (12, 17)) - assert extension.interfaces == [type_node("Greeting", (29, 37))] - assert extension.directives == [] - assert extension.fields == [] + assert extension.interfaces == tuple([type_node("Greeting", (29, 37))]) + assert extension.directives == () + assert extension.fields == () assert extension.loc == (0, 37) def interface_extension_without_fields(): @@ -224,9 +225,9 @@ def interface_extension_without_fields(): extension = assert_definitions(body, (0, 42)) assert isinstance(extension, InterfaceTypeExtensionNode) assert extension.name == name_node("Hello", (17, 22)) - assert extension.interfaces == [type_node("Greeting", (34, 42))] - assert extension.directives == [] - assert extension.fields == [] + assert extension.interfaces == (type_node("Greeting", (34, 42)),) + assert extension.directives == () + assert extension.fields == () assert extension.loc == (0, 42) def object_extension_without_fields_followed_by_extension(): @@ -238,16 +239,16 @@ def object_extension_without_fields_followed_by_extension(): extension = extensions[0] assert isinstance(extension, ObjectTypeExtensionNode) assert extension.name == name_node("Hello", (19, 24)) - assert extension.interfaces == [type_node("Greeting", (36, 44))] - assert extension.directives == [] - assert extension.fields == [] + assert extension.interfaces == tuple([type_node("Greeting", (36, 44))]) + assert extension.directives == () + assert extension.fields == () assert extension.loc == (7, 44) extension = extensions[1] assert isinstance(extension, ObjectTypeExtensionNode) assert extension.name == name_node("Hello", (64, 69)) - assert extension.interfaces == [type_node("SecondGreeting", (81, 95))] - assert extension.directives == [] - assert extension.fields == [] + assert extension.interfaces == tuple([type_node("SecondGreeting", (81, 95))]) + assert extension.directives == () + assert extension.fields == () assert extension.loc == (52, 95) def extension_without_anything_throws(): @@ -267,16 +268,16 @@ def interface_extension_without_fields_followed_by_extension(): extension = extensions[0] assert isinstance(extension, InterfaceTypeExtensionNode) assert extension.name == name_node("Hello", (24, 29)) - assert extension.interfaces == [type_node("Greeting", (41, 49))] - assert extension.directives == [] - assert extension.fields == [] + assert extension.interfaces == tuple([type_node("Greeting", (41, 49))]) + assert extension.directives == () + assert extension.fields == () assert extension.loc == (7, 49) extension = extensions[1] assert isinstance(extension, InterfaceTypeExtensionNode) assert extension.name == name_node("Hello", (74, 79)) - assert extension.interfaces == [type_node("SecondGreeting", (91, 105))] - assert extension.directives == [] - assert extension.fields == [] + assert extension.interfaces == tuple([type_node("SecondGreeting", (91, 105))]) + assert extension.directives == () + assert extension.fields == () assert extension.loc == (57, 105) def object_extension_do_not_include_descriptions(): @@ -325,7 +326,7 @@ def schema_extension(): doc = parse(body) assert isinstance(doc, DocumentNode) assert doc.loc == (0, 75) - assert doc.definitions == [ + assert doc.definitions == ( schema_extension_node( [], [ @@ -336,21 +337,21 @@ def schema_extension(): ) ], (13, 75), - ) - ] + ), + ) def schema_extension_with_only_directives(): body = "extend schema @directive" doc = parse(body) assert isinstance(doc, DocumentNode) assert doc.loc == (0, 24) - assert doc.definitions == [ + assert doc.definitions == ( schema_extension_node( [directive_node(name_node("directive", (15, 24)), [], (14, 24))], [], (0, 24), - ) - ] + ), + ) def schema_extension_without_anything_throws(): assert_syntax_error("extend schema", "Unexpected .", (1, 14)) @@ -372,15 +373,15 @@ def simple_non_null_type(): assert isinstance(definition, ObjectTypeDefinitionNode) assert definition.name == name_node("Hello", (6, 11)) assert definition.description is None - assert definition.interfaces == [] - assert definition.directives == [] - assert definition.fields == [ + assert definition.interfaces == () + assert definition.directives == () + assert definition.fields == ( field_node( name_node("world", (16, 21)), non_null_type(type_node("String", (23, 29)), (23, 30)), (16, 30), - ) - ] + ), + ) assert definition.loc == (1, 32) def simple_interface_inheriting_interface(): @@ -389,13 +390,13 @@ def simple_interface_inheriting_interface(): assert isinstance(definition, InterfaceTypeDefinitionNode) assert definition.name == name_node("Hello", (10, 15)) assert definition.description is None - assert definition.interfaces == [type_node("World", (27, 32))] - assert definition.directives == [] - assert definition.fields == [ + assert definition.interfaces == tuple([type_node("World", (27, 32))]) + assert definition.directives == () + assert definition.fields == ( field_node( name_node("field", (35, 40)), type_node("String", (42, 48)), (35, 48) - ) - ] + ), + ) assert definition.loc == (0, 50) def simple_type_inheriting_interface(): @@ -404,13 +405,13 @@ def simple_type_inheriting_interface(): assert isinstance(definition, ObjectTypeDefinitionNode) assert definition.name == name_node("Hello", (5, 10)) assert definition.description is None - assert definition.interfaces == [type_node("World", (22, 27))] - assert definition.directives == [] - assert definition.fields == [ + assert definition.interfaces == tuple([type_node("World", (22, 27))]) + assert definition.directives == () + assert definition.fields == ( field_node( name_node("field", (30, 35)), type_node("String", (37, 43)), (30, 43) - ) - ] + ), + ) assert definition.loc == (0, 45) def simple_type_inheriting_multiple_interfaces(): @@ -419,16 +420,16 @@ def simple_type_inheriting_multiple_interfaces(): assert isinstance(definition, ObjectTypeDefinitionNode) assert definition.name == name_node("Hello", (5, 10)) assert definition.description is None - assert definition.interfaces == [ + assert definition.interfaces == ( type_node("Wo", (22, 24)), type_node("rld", (27, 30)), - ] - assert definition.directives == [] - assert definition.fields == [ + ) + assert definition.directives == () + assert definition.fields == ( field_node( name_node("field", (33, 38)), type_node("String", (40, 46)), (33, 46) - ) - ] + ), + ) assert definition.loc == (0, 48) def simple_interface_inheriting_multiple_interfaces(): @@ -437,16 +438,16 @@ def simple_interface_inheriting_multiple_interfaces(): assert isinstance(definition, InterfaceTypeDefinitionNode) assert definition.name == name_node("Hello", (10, 15)) assert definition.description is None - assert definition.interfaces == [ + assert definition.interfaces == ( type_node("Wo", (27, 29)), type_node("rld", (32, 35)), - ] - assert definition.directives == [] - assert definition.fields == [ + ) + assert definition.directives == () + assert definition.fields == ( field_node( name_node("field", (38, 43)), type_node("String", (45, 51)), (38, 51) - ) - ] + ), + ) assert definition.loc == (0, 53) def simple_type_inheriting_multiple_interfaces_with_leading_ampersand(): @@ -455,16 +456,16 @@ def simple_type_inheriting_multiple_interfaces_with_leading_ampersand(): assert isinstance(definition, ObjectTypeDefinitionNode) assert definition.name == name_node("Hello", (5, 10)) assert definition.description is None - assert definition.interfaces == [ + assert definition.interfaces == ( type_node("Wo", (24, 26)), type_node("rld", (29, 32)), - ] - assert definition.directives == [] - assert definition.fields == [ + ) + assert definition.directives == () + assert definition.fields == ( field_node( name_node("field", (35, 40)), type_node("String", (42, 48)), (35, 48) - ) - ] + ), + ) assert definition.loc == (0, 50) def simple_interface_inheriting_multiple_interfaces_with_leading_ampersand(): @@ -473,16 +474,16 @@ def simple_interface_inheriting_multiple_interfaces_with_leading_ampersand(): assert isinstance(definition, InterfaceTypeDefinitionNode) assert definition.name == name_node("Hello", (10, 15)) assert definition.description is None - assert definition.interfaces == [ + assert definition.interfaces == ( type_node("Wo", (29, 31)), type_node("rld", (34, 37)), - ] - assert definition.directives == [] - assert definition.fields == [ + ) + assert definition.directives == () + assert definition.fields == ( field_node( name_node("field", (40, 45)), type_node("String", (47, 53)), (40, 53) - ) - ] + ), + ) assert definition.loc == (0, 55) def single_value_enum(): @@ -491,8 +492,8 @@ def single_value_enum(): assert isinstance(definition, EnumTypeDefinitionNode) assert definition.name == name_node("Hello", (5, 10)) assert definition.description is None - assert definition.directives == [] - assert definition.values == [enum_value_node("WORLD", (13, 18))] + assert definition.directives == () + assert definition.values == tuple([enum_value_node("WORLD", (13, 18))]) assert definition.loc == (0, 20) def double_value_enum(): @@ -501,11 +502,11 @@ def double_value_enum(): assert isinstance(definition, EnumTypeDefinitionNode) assert definition.name == name_node("Hello", (5, 10)) assert definition.description is None - assert definition.directives == [] - assert definition.values == [ + assert definition.directives == () + assert definition.values == ( enum_value_node("WO", (13, 15)), enum_value_node("RLD", (17, 20)), - ] + ) assert definition.loc == (0, 22) def simple_interface(): @@ -520,13 +521,13 @@ def simple_interface(): assert isinstance(definition, InterfaceTypeDefinitionNode) assert definition.name == name_node("Hello", (11, 16)) assert definition.description is None - assert definition.interfaces == [] - assert definition.directives == [] - assert definition.fields == [ + assert definition.interfaces == () + assert definition.directives == () + assert definition.fields == ( field_node( name_node("world", (21, 26)), type_node("String", (28, 34)), (21, 34) - ) - ] + ), + ) assert definition.loc == (1, 36) def simple_field_with_arg(): @@ -541,9 +542,9 @@ def simple_field_with_arg(): assert isinstance(definition, ObjectTypeDefinitionNode) assert definition.name == name_node("Hello", (6, 11)) assert definition.description is None - assert definition.interfaces == [] - assert definition.directives == [] - assert definition.fields == [ + assert definition.interfaces == () + assert definition.directives == () + assert definition.fields == ( field_node_with_args( name_node("world", (16, 21)), type_node("String", (38, 44)), @@ -556,8 +557,8 @@ def simple_field_with_arg(): ) ], (16, 44), - ) - ] + ), + ) assert definition.loc == (1, 46) def simple_field_with_arg_with_default_value(): @@ -572,9 +573,9 @@ def simple_field_with_arg_with_default_value(): assert isinstance(definition, ObjectTypeDefinitionNode) assert definition.name == name_node("Hello", (6, 11)) assert definition.description is None - assert definition.interfaces == [] - assert definition.directives == [] - assert definition.fields == [ + assert definition.interfaces == () + assert definition.directives == () + assert definition.fields == ( field_node_with_args( name_node("world", (16, 21)), type_node("String", (45, 51)), @@ -587,8 +588,8 @@ def simple_field_with_arg_with_default_value(): ) ], (16, 51), - ) - ] + ), + ) assert definition.loc == (1, 53) def simple_field_with_list_arg(): @@ -603,9 +604,9 @@ def simple_field_with_list_arg(): assert isinstance(definition, ObjectTypeDefinitionNode) assert definition.name == name_node("Hello", (6, 11)) assert definition.description is None - assert definition.interfaces == [] - assert definition.directives == [] - assert definition.fields == [ + assert definition.interfaces == () + assert definition.directives == () + assert definition.fields == ( field_node_with_args( name_node("world", (16, 21)), type_node("String", (41, 47)), @@ -618,8 +619,8 @@ def simple_field_with_list_arg(): ) ], (16, 47), - ) - ] + ), + ) assert definition.loc == (1, 49) def simple_field_with_two_args(): @@ -634,9 +635,9 @@ def simple_field_with_two_args(): assert isinstance(definition, ObjectTypeDefinitionNode) assert definition.name == name_node("Hello", (6, 11)) assert definition.description is None - assert definition.interfaces == [] - assert definition.directives == [] - assert definition.fields == [ + assert definition.interfaces == () + assert definition.directives == () + assert definition.fields == ( field_node_with_args( name_node("world", (16, 21)), type_node("String", (53, 59)), @@ -655,8 +656,8 @@ def simple_field_with_two_args(): ), ], (16, 59), - ) - ] + ), + ) assert definition.loc == (1, 61) def simple_union(): @@ -665,8 +666,8 @@ def simple_union(): assert isinstance(definition, UnionTypeDefinitionNode) assert definition.name == name_node("Hello", (6, 11)) assert definition.description is None - assert definition.directives == [] - assert definition.types == [type_node("World", (14, 19))] + assert definition.directives == () + assert definition.types == tuple([type_node("World", (14, 19))]) assert definition.loc == (0, 19) def union_with_two_types(): @@ -675,11 +676,11 @@ def union_with_two_types(): assert isinstance(definition, UnionTypeDefinitionNode) assert definition.name == name_node("Hello", (6, 11)) assert definition.description is None - assert definition.directives == [] - assert definition.types == [ + assert definition.directives == () + assert definition.types == ( type_node("Wo", (14, 16)), type_node("Rld", (19, 22)), - ] + ) assert definition.loc == (0, 22) def union_with_two_types_and_leading_pipe(): @@ -687,11 +688,11 @@ def union_with_two_types_and_leading_pipe(): definition = assert_definitions(body, (0, 24)) assert isinstance(definition, UnionTypeDefinitionNode) assert definition.name == name_node("Hello", (6, 11)) - assert definition.directives == [] - assert definition.types == [ + assert definition.directives == () + assert definition.types == ( type_node("Wo", (16, 18)), type_node("Rld", (21, 24)), - ] + ) assert definition.loc == (0, 24) def union_fails_with_no_types(): @@ -718,7 +719,7 @@ def scalar(): assert isinstance(definition, ScalarTypeDefinitionNode) assert definition.name == name_node("Hello", (7, 12)) assert definition.description is None - assert definition.directives == [] + assert definition.directives == () assert definition.loc == (0, 12) def simple_input_object(): @@ -727,15 +728,15 @@ def simple_input_object(): assert isinstance(definition, InputObjectTypeDefinitionNode) assert definition.name == name_node("Hello", (7, 12)) assert definition.description is None - assert definition.directives == [] - assert definition.fields == [ + assert definition.directives == () + assert definition.fields == ( input_value_node( name_node("world", (17, 22)), type_node("String", (24, 30)), None, (17, 30), - ) - ] + ), + ) assert definition.loc == (1, 32) def simple_input_object_with_args_should_fail(): @@ -751,12 +752,11 @@ def directive_definition(): assert isinstance(definition, DirectiveDefinitionNode) assert definition.name == name_node("foo", (11, 14)) assert definition.description is None - assert definition.arguments == [] + assert definition.arguments == () assert definition.repeatable is False - assert definition.locations == [ - name_node("OBJECT", (18, 24)), - name_node("INTERFACE", (27, 36)), - ] + assert definition.locations == tuple( + [name_node("OBJECT", (18, 24)), name_node("INTERFACE", (27, 36)),] + ) def repeatable_directive_definition(): body = "directive @foo repeatable on OBJECT | INTERFACE" @@ -764,12 +764,12 @@ def repeatable_directive_definition(): assert isinstance(definition, DirectiveDefinitionNode) assert definition.name == name_node("foo", (11, 14)) assert definition.description is None - assert definition.arguments == [] + assert definition.arguments == () assert definition.repeatable is True - assert definition.locations == [ + assert definition.locations == ( name_node("OBJECT", (29, 35)), name_node("INTERFACE", (38, 47)), - ] + ) def directive_with_incorrect_locations(): assert_syntax_error( diff --git a/tests/language/test_visitor.py b/tests/language/test_visitor.py index ecbf339d..2a5326a9 100644 --- a/tests/language/test_visitor.py +++ b/tests/language/test_visitor.py @@ -19,7 +19,7 @@ Visitor, ) from graphql.language.visitor import QUERY_DOCUMENT_KEYS -from graphql.pyutils import FrozenList +from graphql.pyutils import FrozenList, is_collection from ..fixtures import kitchen_sink_query # noqa: F401 @@ -39,7 +39,7 @@ def check_visitor_fn_args(ast, node, key, parent, path, ancestors, is_edited=Fal assert isinstance(key, (int, str)) if isinstance(key, int): - assert isinstance(parent, list) + assert is_collection(parent) assert 0 <= key <= len(parent) else: assert isinstance(parent, Node) @@ -59,7 +59,7 @@ def check_visitor_fn_args(ast, node, key, parent, path, ancestors, is_edited=Fal k = path[i] assert isinstance(k, (int, str)) if isinstance(k, int): - assert isinstance(current_node, list) + assert is_collection(current_node) assert 0 <= k <= len(current_node) current_node = current_node[k] else: diff --git a/tests/pyutils/test_frozen_list.py b/tests/pyutils/test_frozen_list.py deleted file mode 100644 index a7ad69f0..00000000 --- a/tests/pyutils/test_frozen_list.py +++ /dev/null @@ -1,113 +0,0 @@ -from copy import copy, deepcopy - -from pytest import raises # type: ignore - -from graphql.pyutils import FrozenError, FrozenList - - -def describe_frozen_list(): - def can_read(): - fl = FrozenList([1, 2, 3]) - assert fl == [1, 2, 3] - assert list(i for i in fl) == fl - assert fl.copy() == fl - assert 2 in fl - assert 4 not in fl - assert fl + [4, 5] == [1, 2, 3, 4, 5] - assert [4, 5] + fl == [4, 5, 1, 2, 3] - assert fl * 2 == [1, 2, 3, 1, 2, 3] - assert 2 * fl == [1, 2, 3, 1, 2, 3] - assert fl[1] == 2 - with raises(IndexError): - fl[3] - assert fl[1:4] == [2, 3] - assert fl[::2] == [1, 3] - assert len(fl) == 3 - assert min(fl) == 1 - assert max(fl) == 3 - assert sum(fl) == 6 - assert fl.index(2) == 1 - with raises(ValueError): - fl.index(4) - assert fl.count(2) == 1 - assert fl.count(4) == 0 - assert list(reversed(fl)) == [3, 2, 1] - assert sorted(fl) == [1, 2, 3] - - def cannot_write(): - fl = FrozenList([1, 2, 3]) - with raises(FrozenError): - fl[1] = 4 - with raises(FrozenError): - fl[1:4] = [4] - with raises(FrozenError): - del fl[1] - with raises(FrozenError): - del fl[1:4] - with raises(FrozenError): - fl[1::2] = [4] - with raises(FrozenError): - del fl[::2] - with raises(FrozenError): - fl.append(4) - with raises(FrozenError): - fl.clear() - with raises(FrozenError): - fl.extend([4]) - with raises(FrozenError): - fl += [4] - with raises(FrozenError): - fl *= 2 - with raises(FrozenError): - fl.insert(1, 4) - with raises(FrozenError): - fl.pop() - with raises(FrozenError): - fl.remove(2) - with raises(FrozenError): - fl.sort() - with raises(FrozenError): - fl.reverse() - assert fl == [1, 2, 3] - - def can_add_rol(): - fl1 = FrozenList([1, 2]) - rol2 = FrozenList([3, 4]) - assert fl1 + rol2 == [1, 2, 3, 4] - - def can_add_tuple(): - fl = FrozenList([1, 2]) - assert fl + (3, 4) == [1, 2, 3, 4] - - def can_hash(): - fl1 = FrozenList([1, 2]) - fl2 = FrozenList([1, 2]) - assert fl2 == fl1 - assert fl2 is not fl1 - assert hash(fl2) == hash(fl1) - fl3 = FrozenList([1, 3]) - assert fl3 != fl1 - assert hash(fl3) != hash(fl1) - - def can_copy(): - fl1 = FrozenList([1, 2]) - fl2 = copy(fl1) - assert isinstance(fl2, FrozenList) - assert fl2 == fl1 - assert hash(fl2) == hash(fl1) - assert fl2 is not fl1 - - def can_deep_copy(): - fl11 = FrozenList([1, 2]) - fl12 = FrozenList([2, 1]) - fl1 = FrozenList([fl11, fl12]) - fl2 = deepcopy(fl1) - assert isinstance(fl2, FrozenList) - assert fl2 == fl1 - assert hash(fl2) == hash(fl1) - assert isinstance(fl2[0], FrozenList) - assert isinstance(fl2[1], FrozenList) - assert fl2[0] == fl1[0] - assert fl2[0] is not fl1[0] - assert fl2[1] == fl1[1] - assert fl2[1] is not fl1[1] diff --git a/tests/type/test_definition.py b/tests/type/test_definition.py index c0c784a8..9bf0c756 100644 --- a/tests/type/test_definition.py +++ b/tests/type/test_definition.py @@ -75,7 +75,7 @@ def defines_a_scalar_type(): "parse_literal": None, "extensions": None, "ast_node": None, - "extension_ast_nodes": [], + "extension_ast_nodes": (), } def accepts_a_scalar_type_defining_serialize(): @@ -143,7 +143,7 @@ def use_parse_value_for_parsing_literals_if_parse_literal_omitted(): def accepts_a_scalar_type_with_ast_node_and_extension_ast_nodes(): ast_node = ScalarTypeDefinitionNode() - extension_ast_nodes = [ScalarTypeExtensionNode()] + extension_ast_nodes = (ScalarTypeExtensionNode(),) scalar = GraphQLScalarType( "SomeScalar", ast_node=ast_node, extension_ast_nodes=extension_ast_nodes ) @@ -497,7 +497,7 @@ def accepts_a_lambda_as_an_object_field_resolver(): def accepts_an_object_type_with_ast_node_and_extension_ast_nodes(): ast_node = ObjectTypeDefinitionNode() - extension_ast_nodes = [ObjectTypeExtensionNode()] + extension_ast_nodes = (ObjectTypeExtensionNode(),) object_type = GraphQLObjectType( "SomeObject", {"f": GraphQLField(ScalarType)}, @@ -706,7 +706,7 @@ def defines_an_interface_type(): "resolve_type": None, "extensions": None, "ast_node": None, - "extension_ast_nodes": [], + "extension_ast_nodes": (), } def accepts_an_interface_type_defining_resolve_type(): @@ -778,7 +778,7 @@ def interfaces(): def accepts_an_interface_type_with_ast_node_and_extension_ast_nodes(): ast_node = InterfaceTypeDefinitionNode() - extension_ast_nodes = [InterfaceTypeExtensionNode()] + extension_ast_nodes = FrozenList([InterfaceTypeExtensionNode()]) interface_type = GraphQLInterfaceType( "SomeInterface", {"f": GraphQLField(ScalarType)}, @@ -931,7 +931,7 @@ def accepts_a_union_type_without_types(): def accepts_a_union_type_with_ast_node_and_extension_ast_nodes(): ast_node = UnionTypeDefinitionNode() - extension_ast_nodes = [UnionTypeExtensionNode()] + extension_ast_nodes = (UnionTypeExtensionNode(),) union_type = GraphQLUnionType( "SomeUnion", [ObjectType], @@ -1184,7 +1184,7 @@ def parses_an_enum(): def accepts_an_enum_type_with_ast_node_and_extension_ast_nodes(): ast_node = EnumTypeDefinitionNode() - extension_ast_nodes = [EnumTypeExtensionNode()] + extension_ast_nodes = (EnumTypeExtensionNode(),) enum_type = GraphQLEnumType( "SomeEnum", {}, # type: ignore @@ -1377,7 +1377,7 @@ def provides_default_out_type_if_omitted(): def accepts_an_input_object_type_with_ast_node_and_extension_ast_nodes(): ast_node = InputObjectTypeDefinitionNode() - extension_ast_nodes = [InputObjectTypeExtensionNode()] + extension_ast_nodes = (InputObjectTypeExtensionNode(),) input_obj_type = GraphQLInputObjectType( "SomeInputObject", {}, diff --git a/tests/type/test_schema.py b/tests/type/test_schema.py index 5c6245ed..0f156346 100644 --- a/tests/type/test_schema.py +++ b/tests/type/test_schema.py @@ -100,7 +100,7 @@ def define_sample_schema(): kwargs = schema.to_kwargs() types = kwargs.pop("types") - assert types == list(schema.type_map.values()) + assert types == tuple(schema.type_map.values()) assert kwargs == { "query": BlogQuery, "mutation": BlogMutation, @@ -109,7 +109,7 @@ def define_sample_schema(): "description": "Sample schema", "extensions": None, "ast_node": None, - "extension_ast_nodes": [], + "extension_ast_nodes": (), "assume_valid": False, } @@ -159,7 +159,7 @@ def define_sample_schema(): ) def freezes_the_specified_directives(): - directives = [GraphQLDirective("SomeDirective", [])] + directives = (GraphQLDirective("SomeDirective", []),) schema = GraphQLSchema(directives=directives) assert isinstance(schema.directives, FrozenList) assert schema.directives == directives @@ -395,7 +395,7 @@ def configures_the_schema_to_have_no_errors(): def describe_ast_nodes(): def accepts_a_scalar_type_with_ast_node_and_extension_ast_nodes(): ast_node = SchemaDefinitionNode() - extension_ast_nodes = [SchemaExtensionNode()] + extension_ast_nodes = (SchemaExtensionNode(),) schema = GraphQLSchema( GraphQLObjectType("Query", {}), ast_node=ast_node, diff --git a/tests/utilities/test_build_ast_schema.py b/tests/utilities/test_build_ast_schema.py index 1bdc62ad..f2f9f43f 100644 --- a/tests/utilities/test_build_ast_schema.py +++ b/tests/utilities/test_build_ast_schema.py @@ -312,7 +312,7 @@ def empty_interface(): definition = parse(sdl).definitions[0] assert isinstance(definition, InterfaceTypeDefinitionNode) - assert definition.interfaces == [] + assert definition.interfaces == () assert cycle_sdl(sdl) == sdl @@ -1130,7 +1130,7 @@ def correctly_assign_ast_nodes(): test_scalar = assert_scalar_type(schema.get_type("TestScalar")) test_directive = assert_directive(schema.get_directive("test")) - assert [ + assert ( schema.ast_node, query.ast_node, test_input.ast_node, @@ -1140,7 +1140,7 @@ def correctly_assign_ast_nodes(): test_type.ast_node, test_scalar.ast_node, test_directive.ast_node, - ] == ast.definitions + ) == ast.definitions test_field = query.fields["testField"] assert print_ast_node(test_field) == ( diff --git a/tests/utilities/test_build_client_schema.py b/tests/utilities/test_build_client_schema.py index 0efe16c8..f2900cdc 100644 --- a/tests/utilities/test_build_client_schema.py +++ b/tests/utilities/test_build_client_schema.py @@ -490,7 +490,7 @@ def builds_a_schema_without_directives(): client_schema = build_client_schema(introspection) assert schema.directives - assert client_schema.directives == [] + assert client_schema.directives == () assert print_schema(client_schema) == sdl def builds_a_schema_aware_of_deprecation(): From 805b53e82d75e96e683f75b77760679a807e8455 Mon Sep 17 00:00:00 2001 From: Cory Dolphin Date: Mon, 2 Nov 2020 08:39:32 -0700 Subject: [PATCH 3/3] Fix lint errors --- docs/conf.py | 185 +++++++++++++-------------- src/graphql/pyutils/frozen_list.py | 5 +- tests/language/test_schema_parser.py | 7 +- tests/language/test_visitor.py | 2 +- tests/type/test_definition.py | 22 +++- tests/type/test_schema.py | 10 +- 6 files changed, 116 insertions(+), 115 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 2cc70234..7695f807 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -30,29 +30,29 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - "sphinx.ext.autodoc", + 'sphinx.ext.autodoc', ] # Add any paths that contain templates here, relative to this directory. -templates_path = ["_templates"] +templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] -source_suffix = ".rst" +source_suffix = '.rst' # The encoding of source files. # # source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = "index" +master_doc = 'index' # General information about the project. -project = "GraphQL-core 3" -copyright = "2020, Christoph Zwerschke" -author = "Christoph Zwerschke" +project = 'GraphQL-core 3' +copyright = '2020, Christoph Zwerschke' +author = 'Christoph Zwerschke' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -61,7 +61,7 @@ # The short X.Y version. # version = '3.1' # The full version, including alpha/beta/rc tags. -version = release = "3.1.2" +version = release = '3.1.2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -82,16 +82,16 @@ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # AutoDoc configuration autoclass_content = "class" autodoc_default_options = { - "members": True, - "inherited-members": True, - "special-members": "__init__", - "undoc-members": True, - "show-inheritance": True, + 'members': True, + 'inherited-members': True, + 'special-members': '__init__', + 'undoc-members': True, + 'show-inheritance': True } autosummary_generate = True @@ -100,37 +100,29 @@ # qualified form, but the documentation has the shorter form. # We need to give autodoc a little help in this cases. graphql_modules = { - "error": ["graphql_error"], - "execution": ["execute", "middleware"], - "language": [ - "ast", - "directive_locations", - "location", - "source", - "token_kind", - "visitor", - ], - "pyutils": ["event_emitter", "frozen_list", "path"], - "subscription": [], - "type": ["definition", "directives", "schema"], - "utilities": ["find_breaking_changes", "type_info"], - "validation": ["rules", "validation_context"], -} + 'error': ['graphql_error'], + 'execution': ['execute', 'middleware'], + 'language': ['ast', 'directive_locations', 'location', + 'source', 'token_kind', 'visitor'], + 'pyutils': ['event_emitter', 'frozen_list', 'path'], + 'subscription': [], + 'type': ['definition', 'directives', 'schema'], + 'utilities': ['find_breaking_changes', 'type_info'], + 'validation': ['rules', 'validation_context']} # GraphQL-core classes that autodoc sometimes cannot find # (e.g. where specified as string in type hints). # We need to give autodoc a little help in this cases, too: graphql_classes = { - "GraphQLAbstractType": "type", - "GraphQLObjectType": "type", - "Node": "language", - "Source": "language", - "SourceLocation": "language", + 'GraphQLAbstractType': 'type', + 'GraphQLObjectType': 'type', + 'Node': 'language', + 'Source': 'language', + 'SourceLocation': 'language' } # ignore the following undocumented or internal references: -ignore_references = set( - """ +ignore_references = set(''' GNT GT T enum.Enum asyncio.events.AbstractEventLoop @@ -138,74 +130,72 @@ graphql.validation.validation_context.VariableUsage graphql.validation.rules.known_argument_names.KnownArgumentNamesOnDirectivesRule graphql.validation.rules.provided_required_arguments.ProvidedRequiredArgumentsOnDirectivesRule -""".split() -) +'''.split()) ignore_references.update(__builtins__.keys()) def on_missing_reference(app, env, node, contnode): """Fix or skip any missing references.""" - if node.get("refdomain") != "py": + if node.get('refdomain') != 'py': return None - target = node.get("reftarget") + target = node.get('reftarget') if not target: return None if target in ignore_references: return contnode - typ = node.get("reftype") - if typ != "class": + typ = node.get('reftype') + if typ != 'class': return None - if "." in target: # maybe too specific - base_module, target = target.split(".", 1) - if base_module == "typing": + if '.' in target: # maybe too specific + base_module, target = target.split('.', 1) + if base_module == 'typing': return contnode - if base_module == "graphql": - if "." not in target: + if base_module == 'graphql': + if '.' not in target: return None - base_module, target = target.split(".", 1) - if "." not in target: + base_module, target = target.split('.', 1) + if '.' not in target: return None sub_modules = graphql_modules.get(base_module) if not sub_modules: return - sub_module = target.split(".", 1)[0] + sub_module = target.split('.', 1)[0] if sub_module not in sub_modules: return None - target = "graphql." + base_module + "." + target.rsplit(".", 1)[-1] + target = 'graphql.' + base_module + '.' + target.rsplit('.', 1)[-1] else: # maybe not specific enough base_module = graphql_classes.get(target) if not base_module: return None - target = "graphql." + base_module + "." + target + target = 'graphql.' + base_module + '.' + target # replace target - if contnode.__class__.__name__ == "Text": + if contnode.__class__.__name__ == 'Text': contnode = contnode.__class__(target) - elif contnode.__class__.__name__ == "literal": + elif contnode.__class__.__name__ == 'literal': if len(contnode.children) != 1: return None textnode = contnode.children[0] contnode.children[0] = textnode.__class__(target) else: return None - node["reftarget"] = target - fromdoc = node.get("refdoc") + node['reftarget'] = target + fromdoc = node.get('refdoc') if not fromdoc: - doc_module = node.get("py:module") + doc_module = node.get('py:module') if doc_module: - if doc_module.startswith("graphql."): - doc_module = doc_module.split(".", 1)[-1] - if doc_module not in graphql_modules and doc_module != "graphql": + if doc_module.startswith('graphql.'): + doc_module = doc_module.split('.', 1)[-1] + if doc_module not in graphql_modules and doc_module != 'graphql': doc_module = None - fromdoc = "modules/" + (doc_module or base_module) + fromdoc = 'modules/' + (doc_module or base_module) # try resolving again with replaced target - return env.domains["py"].resolve_xref( - env, fromdoc, app.builder, typ, target, node, contnode - ) + return env.domains['py'].resolve_xref( + env, fromdoc, app.builder, typ, target, node, contnode) def setup(app): - app.connect("missing-reference", on_missing_reference) + app.connect('missing-reference', on_missing_reference) # be nitpicky (handle all possible problems in on_missing_reference) @@ -232,7 +222,7 @@ def setup(app): # show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = "sphinx" +pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] @@ -248,13 +238,15 @@ def setup(app): # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = "sphinx_rtd_theme" +html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # -html_theme_options = {"navigation_depth": 5} +html_theme_options = { + 'navigation_depth': 5 +} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] @@ -362,36 +354,34 @@ def setup(app): # html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = "GraphQL-core-3-doc" +htmlhelp_basename = 'GraphQL-core-3-doc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ( - master_doc, - "GraphQL-core-3.tex", - "GraphQL-core 3 Documentation", - "Christoph Zwerschke", - "manual", - ), + (master_doc, 'GraphQL-core-3.tex', 'GraphQL-core 3 Documentation', + 'Christoph Zwerschke', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -425,7 +415,10 @@ def setup(app): # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [(master_doc, "graphql-core", "GraphQL-core 3 Documentation", [author], 1)] +man_pages = [ + (master_doc, 'graphql-core', 'GraphQL-core 3 Documentation', + [author], 1) +] # If true, show URL addresses after external links. # @@ -438,15 +431,9 @@ def setup(app): # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ( - master_doc, - "GraphQL-core", - "GraphQL-core 3 Documentation", - author, - "GraphQL-core 3", - "One line description of project.", - "Miscellaneous", - ), + (master_doc, 'GraphQL-core', 'GraphQL-core 3 Documentation', + author, 'GraphQL-core 3', 'One line description of project.', + 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. diff --git a/src/graphql/pyutils/frozen_list.py b/src/graphql/pyutils/frozen_list.py index cf60ef54..f73406a6 100644 --- a/src/graphql/pyutils/frozen_list.py +++ b/src/graphql/pyutils/frozen_list.py @@ -1,7 +1,4 @@ -from copy import deepcopy -from typing import List, TypeVar, Tuple - -from .frozen_error import FrozenError +from typing import TypeVar, Tuple __all__ = ["FrozenList"] diff --git a/tests/language/test_schema_parser.py b/tests/language/test_schema_parser.py index 6e7ae844..53c47695 100644 --- a/tests/language/test_schema_parser.py +++ b/tests/language/test_schema_parser.py @@ -30,7 +30,7 @@ UnionTypeDefinitionNode, parse, ) -from graphql.pyutils import is_collection, FrozenList +from graphql.pyutils import is_collection from ..fixtures import kitchen_sink_sdl # noqa: F401 @@ -754,8 +754,9 @@ def directive_definition(): assert definition.description is None assert definition.arguments == () assert definition.repeatable is False - assert definition.locations == tuple( - [name_node("OBJECT", (18, 24)), name_node("INTERFACE", (27, 36)),] + assert definition.locations == ( + name_node("OBJECT", (18, 24)), + name_node("INTERFACE", (27, 36)), ) def repeatable_directive_definition(): diff --git a/tests/language/test_visitor.py b/tests/language/test_visitor.py index 2a5326a9..16a08d0b 100644 --- a/tests/language/test_visitor.py +++ b/tests/language/test_visitor.py @@ -1,6 +1,6 @@ from copy import copy from functools import partial -from typing import cast, Dict, List, Optional, Tuple +from typing import cast, Dict, List, Optional, Tuple, Sequence from pytest import mark, raises # type: ignore diff --git a/tests/type/test_definition.py b/tests/type/test_definition.py index 9bf0c756..a5c5c018 100644 --- a/tests/type/test_definition.py +++ b/tests/type/test_definition.py @@ -1,5 +1,5 @@ from math import isnan, nan -from typing import cast +from typing import cast, Sequence from pytest import mark, raises # type: ignore @@ -143,7 +143,9 @@ def use_parse_value_for_parsing_literals_if_parse_literal_omitted(): def accepts_a_scalar_type_with_ast_node_and_extension_ast_nodes(): ast_node = ScalarTypeDefinitionNode() - extension_ast_nodes = (ScalarTypeExtensionNode(),) + extension_ast_nodes: Sequence[ScalarTypeExtensionNode] = ( + ScalarTypeExtensionNode(), + ) scalar = GraphQLScalarType( "SomeScalar", ast_node=ast_node, extension_ast_nodes=extension_ast_nodes ) @@ -497,7 +499,9 @@ def accepts_a_lambda_as_an_object_field_resolver(): def accepts_an_object_type_with_ast_node_and_extension_ast_nodes(): ast_node = ObjectTypeDefinitionNode() - extension_ast_nodes = (ObjectTypeExtensionNode(),) + extension_ast_nodes: Sequence[ObjectTypeExtensionNode] = ( + ObjectTypeExtensionNode(), + ) object_type = GraphQLObjectType( "SomeObject", {"f": GraphQLField(ScalarType)}, @@ -931,7 +935,9 @@ def accepts_a_union_type_without_types(): def accepts_a_union_type_with_ast_node_and_extension_ast_nodes(): ast_node = UnionTypeDefinitionNode() - extension_ast_nodes = (UnionTypeExtensionNode(),) + extension_ast_nodes: Sequence[UnionTypeExtensionNode] = ( + UnionTypeExtensionNode(), + ) union_type = GraphQLUnionType( "SomeUnion", [ObjectType], @@ -1184,7 +1190,9 @@ def parses_an_enum(): def accepts_an_enum_type_with_ast_node_and_extension_ast_nodes(): ast_node = EnumTypeDefinitionNode() - extension_ast_nodes = (EnumTypeExtensionNode(),) + extension_ast_nodes: Sequence[EnumTypeExtensionNode] = ( + EnumTypeExtensionNode(), + ) enum_type = GraphQLEnumType( "SomeEnum", {}, # type: ignore @@ -1377,7 +1385,9 @@ def provides_default_out_type_if_omitted(): def accepts_an_input_object_type_with_ast_node_and_extension_ast_nodes(): ast_node = InputObjectTypeDefinitionNode() - extension_ast_nodes = (InputObjectTypeExtensionNode(),) + extension_ast_nodes: Sequence[InputObjectTypeExtensionNode] = ( + InputObjectTypeExtensionNode(), + ) input_obj_type = GraphQLInputObjectType( "SomeInputObject", {}, diff --git a/tests/type/test_schema.py b/tests/type/test_schema.py index 0f156346..6a6b8d28 100644 --- a/tests/type/test_schema.py +++ b/tests/type/test_schema.py @@ -1,3 +1,5 @@ +from typing import Sequence + from pytest import raises # type: ignore from graphql.language import ( @@ -159,7 +161,9 @@ def define_sample_schema(): ) def freezes_the_specified_directives(): - directives = (GraphQLDirective("SomeDirective", []),) + directives: Sequence[GraphQLDirective] = ( + GraphQLDirective("SomeDirective", []), + ) schema = GraphQLSchema(directives=directives) assert isinstance(schema.directives, FrozenList) assert schema.directives == directives @@ -395,7 +399,9 @@ def configures_the_schema_to_have_no_errors(): def describe_ast_nodes(): def accepts_a_scalar_type_with_ast_node_and_extension_ast_nodes(): ast_node = SchemaDefinitionNode() - extension_ast_nodes = (SchemaExtensionNode(),) + extension_ast_nodes: Sequence[SchemaExtensionNode] = ( + SchemaExtensionNode(), + ) schema = GraphQLSchema( GraphQLObjectType("Query", {}), ast_node=ast_node,