Skip to content

Commit

Permalink
Use FrozenLists in AST nodes, fix hash (#45)
Browse files Browse the repository at this point in the history
Partially replicates graphql/graphql-js@9b2e626
  • Loading branch information
Cito committed Aug 1, 2019
1 parent 276aa31 commit b5532a4
Show file tree
Hide file tree
Showing 11 changed files with 160 additions and 48 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The current version 3.0.0a1 of GraphQL-core is up-to-date
with GraphQL.js version 14.4.0.

All parts of the API are covered by an extensive test suite
of currently 1885 unit tests.
of currently 1891 unit tests.


## Documentation
Expand Down
6 changes: 4 additions & 2 deletions src/graphql/execution/execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
OperationType,
SelectionSetNode,
)
from ..pyutils import inspect, is_invalid, is_nullish, AwaitableOrValue
from ..pyutils import inspect, is_invalid, is_nullish, AwaitableOrValue, FrozenList
from ..utilities import get_operation_root_type, type_from_ast
from ..type import (
GraphQLAbstractType,
Expand Down Expand Up @@ -274,7 +274,9 @@ def build(
variable_values = None
if operation:
coerced_variable_values = get_variable_values(
schema, operation.variable_definitions or [], raw_variable_values or {}
schema,
operation.variable_definitions or FrozenList(),
raw_variable_values or {},
)

if coerced_variable_values.errors:
Expand Down
4 changes: 2 additions & 2 deletions src/graphql/execution/values.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
VariableNode,
print_ast,
)
from ..pyutils import inspect
from ..pyutils import inspect, FrozenList
from ..type import (
GraphQLDirective,
GraphQLField,
Expand All @@ -36,7 +36,7 @@ class CoercedVariableValues(NamedTuple):

def get_variable_values(
schema: GraphQLSchema,
var_def_nodes: List[VariableDefinitionNode],
var_def_nodes: FrozenList[VariableDefinitionNode],
inputs: Dict[str, Any],
) -> CoercedVariableValues:
"""Get coerced variable values based on provided definitions.
Expand Down
82 changes: 45 additions & 37 deletions src/graphql/language/ast.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from copy import copy, deepcopy
from enum import Enum
from typing import List, NamedTuple, Optional, Union
from typing import NamedTuple, Optional, Union

from .source import Source
from .token_kind import TokenKind
from ..pyutils import camel_to_snake
from ..pyutils import camel_to_snake, FrozenList

__all__ = [
"Location",
Expand Down Expand Up @@ -110,6 +110,11 @@ def __eq__(self, other):
return other == self.desc
return False

def __hash__(self):
return hash(
(self.kind, self.start, self.end, self.line, self.column, self.value)
)

def __copy__(self):
"""Create a shallow copy of the token"""
return self.__class__(
Expand Down Expand Up @@ -163,8 +168,8 @@ def __eq__(self, other):
return self.start == other[0] and self.end == other[1]
return False

def __ne__(self, other):
return not self.__eq__(other)
def __hash__(self):
return hash((self.start, self.end))


class OperationType(Enum):
Expand All @@ -190,7 +195,10 @@ class Node:
def __init__(self, **kwargs):
"""Initialize the node with the given keyword arguments."""
for key in self.keys:
setattr(self, key, kwargs.get(key))
value = kwargs.get(key)
if isinstance(value, list) and not isinstance(value, FrozenList):
value = FrozenList(value)
setattr(self, key, value)

def __repr__(self):
"""Get a simple representation of the node."""
Expand All @@ -206,7 +214,7 @@ def __eq__(self, other):
)

def __hash__(self):
return id(self)
return hash(tuple(getattr(self, key) for key in self.keys))

def __copy__(self):
"""Create a shallow copy of the node."""
Expand Down Expand Up @@ -248,7 +256,7 @@ class NameNode(Node):
class DocumentNode(Node):
__slots__ = ("definitions",)

definitions: List["DefinitionNode"]
definitions: FrozenList["DefinitionNode"]


class DefinitionNode(Node):
Expand All @@ -259,8 +267,8 @@ class ExecutableDefinitionNode(DefinitionNode):
__slots__ = "name", "directives", "variable_definitions", "selection_set"

name: Optional[NameNode]
directives: Optional[List["DirectiveNode"]]
variable_definitions: List["VariableDefinitionNode"]
directives: Optional[FrozenList["DirectiveNode"]]
variable_definitions: FrozenList["VariableDefinitionNode"]
selection_set: "SelectionSetNode"


Expand All @@ -276,27 +284,27 @@ class VariableDefinitionNode(Node):
variable: "VariableNode"
type: "TypeNode"
default_value: Optional["ValueNode"]
directives: Optional[List["DirectiveNode"]]
directives: Optional[FrozenList["DirectiveNode"]]


class SelectionSetNode(Node):
__slots__ = ("selections",)

selections: List["SelectionNode"]
selections: FrozenList["SelectionNode"]


class SelectionNode(Node):
__slots__ = ("directives",)

directives: Optional[List["DirectiveNode"]]
directives: Optional[FrozenList["DirectiveNode"]]


class FieldNode(SelectionNode):
__slots__ = "alias", "name", "arguments", "selection_set"

alias: Optional[NameNode]
name: NameNode
arguments: Optional[List["ArgumentNode"]]
arguments: Optional[FrozenList["ArgumentNode"]]
selection_set: Optional[SelectionSetNode]


Expand Down Expand Up @@ -381,13 +389,13 @@ class EnumValueNode(ValueNode):
class ListValueNode(ValueNode):
__slots__ = ("values",)

values: List[ValueNode]
values: FrozenList[ValueNode]


class ObjectValueNode(ValueNode):
__slots__ = ("fields",)

fields: List["ObjectFieldNode"]
fields: FrozenList["ObjectFieldNode"]


class ObjectFieldNode(Node):
Expand All @@ -404,7 +412,7 @@ class DirectiveNode(Node):
__slots__ = "name", "arguments"

name: NameNode
arguments: List[ArgumentNode]
arguments: FrozenList[ArgumentNode]


# Type Reference
Expand Down Expand Up @@ -442,8 +450,8 @@ class TypeSystemDefinitionNode(DefinitionNode):
class SchemaDefinitionNode(TypeSystemDefinitionNode):
__slots__ = "directives", "operation_types"

directives: Optional[List[DirectiveNode]]
operation_types: List["OperationTypeDefinitionNode"]
directives: Optional[FrozenList[DirectiveNode]]
operation_types: FrozenList["OperationTypeDefinitionNode"]


class OperationTypeDefinitionNode(Node):
Expand All @@ -461,7 +469,7 @@ class TypeDefinitionNode(TypeSystemDefinitionNode):

description: Optional[StringValueNode]
name: NameNode
directives: Optional[List[DirectiveNode]]
directives: Optional[FrozenList[DirectiveNode]]


class ScalarTypeDefinitionNode(TypeDefinitionNode):
Expand All @@ -471,14 +479,14 @@ class ScalarTypeDefinitionNode(TypeDefinitionNode):
class ObjectTypeDefinitionNode(TypeDefinitionNode):
__slots__ = "interfaces", "fields"

interfaces: Optional[List[NamedTypeNode]]
fields: Optional[List["FieldDefinitionNode"]]
interfaces: Optional[FrozenList[NamedTypeNode]]
fields: Optional[FrozenList["FieldDefinitionNode"]]


class FieldDefinitionNode(TypeDefinitionNode):
__slots__ = "arguments", "type"

arguments: Optional[List["InputValueDefinitionNode"]]
arguments: Optional[FrozenList["InputValueDefinitionNode"]]
type: TypeNode


Expand All @@ -492,19 +500,19 @@ class InputValueDefinitionNode(TypeDefinitionNode):
class InterfaceTypeDefinitionNode(TypeDefinitionNode):
__slots__ = ("fields",)

fields: Optional[List["FieldDefinitionNode"]]
fields: Optional[FrozenList["FieldDefinitionNode"]]


class UnionTypeDefinitionNode(TypeDefinitionNode):
__slots__ = ("types",)

types: Optional[List[NamedTypeNode]]
types: Optional[FrozenList[NamedTypeNode]]


class EnumTypeDefinitionNode(TypeDefinitionNode):
__slots__ = ("values",)

values: Optional[List["EnumValueDefinitionNode"]]
values: Optional[FrozenList["EnumValueDefinitionNode"]]


class EnumValueDefinitionNode(TypeDefinitionNode):
Expand All @@ -514,7 +522,7 @@ class EnumValueDefinitionNode(TypeDefinitionNode):
class InputObjectTypeDefinitionNode(TypeDefinitionNode):
__slots__ = ("fields",)

fields: Optional[List[InputValueDefinitionNode]]
fields: Optional[FrozenList[InputValueDefinitionNode]]


# Directive Definitions
Expand All @@ -525,9 +533,9 @@ class DirectiveDefinitionNode(TypeSystemDefinitionNode):

description: Optional[StringValueNode]
name: NameNode
arguments: Optional[List[InputValueDefinitionNode]]
arguments: Optional[FrozenList[InputValueDefinitionNode]]
repeatable: bool
locations: List[NameNode]
locations: FrozenList[NameNode]


# Type System Extensions
Expand All @@ -536,8 +544,8 @@ class DirectiveDefinitionNode(TypeSystemDefinitionNode):
class SchemaExtensionNode(Node):
__slots__ = "directives", "operation_types"

directives: Optional[List[DirectiveNode]]
operation_types: Optional[List[OperationTypeDefinitionNode]]
directives: Optional[FrozenList[DirectiveNode]]
operation_types: Optional[FrozenList[OperationTypeDefinitionNode]]


# Type Extensions
Expand All @@ -547,7 +555,7 @@ class TypeExtensionNode(TypeSystemDefinitionNode):
__slots__ = "name", "directives"

name: NameNode
directives: Optional[List[DirectiveNode]]
directives: Optional[FrozenList[DirectiveNode]]


TypeSystemExtensionNode = Union[SchemaExtensionNode, TypeExtensionNode]
Expand All @@ -560,29 +568,29 @@ class ScalarTypeExtensionNode(TypeExtensionNode):
class ObjectTypeExtensionNode(TypeExtensionNode):
__slots__ = "interfaces", "fields"

interfaces: Optional[List[NamedTypeNode]]
fields: Optional[List[FieldDefinitionNode]]
interfaces: Optional[FrozenList[NamedTypeNode]]
fields: Optional[FrozenList[FieldDefinitionNode]]


class InterfaceTypeExtensionNode(TypeExtensionNode):
__slots__ = ("fields",)

fields: Optional[List[FieldDefinitionNode]]
fields: Optional[FrozenList[FieldDefinitionNode]]


class UnionTypeExtensionNode(TypeExtensionNode):
__slots__ = ("types",)

types: Optional[List[NamedTypeNode]]
types: Optional[FrozenList[NamedTypeNode]]


class EnumTypeExtensionNode(TypeExtensionNode):
__slots__ = ("values",)

values: Optional[List[EnumValueDefinitionNode]]
values: Optional[FrozenList[EnumValueDefinitionNode]]


class InputObjectTypeExtensionNode(TypeExtensionNode):
__slots__ = ("fields",)

fields: Optional[List[InputValueDefinitionNode]]
fields: Optional[FrozenList[InputValueDefinitionNode]]
12 changes: 12 additions & 0 deletions src/graphql/pyutils/frozen_dict.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from copy import deepcopy
from typing import Dict, TypeVar

from .frozen_error import FrozenError
Expand All @@ -23,6 +24,17 @@ def __add__(self, value):
def __iadd__(self, value):
raise FrozenError

def __hash__(self):
return hash(tuple(self.items()))

def __copy__(self):
return FrozenDict(self)

copy = __copy__

def __deepcopy__(self, memo):
return FrozenDict({k: deepcopy(v, memo) for k, v in self.items()})

def clear(self):
raise FrozenError

Expand Down
10 changes: 10 additions & 0 deletions src/graphql/pyutils/frozen_list.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from copy import deepcopy
from typing import List, TypeVar

from .frozen_error import FrozenError
Expand Down Expand Up @@ -31,6 +32,15 @@ def __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

Expand Down
4 changes: 2 additions & 2 deletions src/graphql/utilities/build_ast_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
parse,
Node,
)
from ..pyutils import inspect
from ..pyutils import inspect, FrozenList
from ..type import (
GraphQLArgument,
GraphQLDeprecatedDirective,
Expand Down Expand Up @@ -346,7 +346,7 @@ def _make_interface_def(
)

def _make_enum_def(self, ast_node: EnumTypeDefinitionNode) -> GraphQLEnumType:
value_nodes = ast_node.values or []
value_nodes = ast_node.values or FrozenList()

return GraphQLEnumType(
name=ast_node.name.value,
Expand Down
4 changes: 2 additions & 2 deletions src/graphql/utilities/lexicographic_sort_schema.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Dict, List, Tuple, cast

from ..pyutils import inspect
from ..pyutils import inspect, FrozenList
from ..type import (
GraphQLArgument,
GraphQLDirective,
Expand Down Expand Up @@ -82,7 +82,7 @@ def sort_input_fields(fields_map):
for name, field in sorted(fields_map.items())
}

def sort_types(arr: List[GraphQLNamedType]) -> List[GraphQLNamedType]:
def sort_types(arr: FrozenList[GraphQLNamedType]) -> List[GraphQLNamedType]:
return [
replace_named_type(type_) for type_ in sorted(arr, key=sort_by_name_key)
]
Expand Down
Loading

0 comments on commit b5532a4

Please sign in to comment.