Skip to content

Commit b5532a4

Browse files
committed
Use FrozenLists in AST nodes, fix hash (#45)
Partially replicates graphql/graphql-js@9b2e626
1 parent 276aa31 commit b5532a4

File tree

11 files changed

+160
-48
lines changed

11 files changed

+160
-48
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ The current version 3.0.0a1 of GraphQL-core is up-to-date
1616
with GraphQL.js version 14.4.0.
1717

1818
All parts of the API are covered by an extensive test suite
19-
of currently 1885 unit tests.
19+
of currently 1891 unit tests.
2020

2121

2222
## Documentation

src/graphql/execution/execute.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
OperationType,
2727
SelectionSetNode,
2828
)
29-
from ..pyutils import inspect, is_invalid, is_nullish, AwaitableOrValue
29+
from ..pyutils import inspect, is_invalid, is_nullish, AwaitableOrValue, FrozenList
3030
from ..utilities import get_operation_root_type, type_from_ast
3131
from ..type import (
3232
GraphQLAbstractType,
@@ -274,7 +274,9 @@ def build(
274274
variable_values = None
275275
if operation:
276276
coerced_variable_values = get_variable_values(
277-
schema, operation.variable_definitions or [], raw_variable_values or {}
277+
schema,
278+
operation.variable_definitions or FrozenList(),
279+
raw_variable_values or {},
278280
)
279281

280282
if coerced_variable_values.errors:

src/graphql/execution/values.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
VariableNode,
1616
print_ast,
1717
)
18-
from ..pyutils import inspect
18+
from ..pyutils import inspect, FrozenList
1919
from ..type import (
2020
GraphQLDirective,
2121
GraphQLField,
@@ -36,7 +36,7 @@ class CoercedVariableValues(NamedTuple):
3636

3737
def get_variable_values(
3838
schema: GraphQLSchema,
39-
var_def_nodes: List[VariableDefinitionNode],
39+
var_def_nodes: FrozenList[VariableDefinitionNode],
4040
inputs: Dict[str, Any],
4141
) -> CoercedVariableValues:
4242
"""Get coerced variable values based on provided definitions.

src/graphql/language/ast.py

Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
from copy import copy, deepcopy
22
from enum import Enum
3-
from typing import List, NamedTuple, Optional, Union
3+
from typing import NamedTuple, Optional, Union
44

55
from .source import Source
66
from .token_kind import TokenKind
7-
from ..pyutils import camel_to_snake
7+
from ..pyutils import camel_to_snake, FrozenList
88

99
__all__ = [
1010
"Location",
@@ -110,6 +110,11 @@ def __eq__(self, other):
110110
return other == self.desc
111111
return False
112112

113+
def __hash__(self):
114+
return hash(
115+
(self.kind, self.start, self.end, self.line, self.column, self.value)
116+
)
117+
113118
def __copy__(self):
114119
"""Create a shallow copy of the token"""
115120
return self.__class__(
@@ -163,8 +168,8 @@ def __eq__(self, other):
163168
return self.start == other[0] and self.end == other[1]
164169
return False
165170

166-
def __ne__(self, other):
167-
return not self.__eq__(other)
171+
def __hash__(self):
172+
return hash((self.start, self.end))
168173

169174

170175
class OperationType(Enum):
@@ -190,7 +195,10 @@ class Node:
190195
def __init__(self, **kwargs):
191196
"""Initialize the node with the given keyword arguments."""
192197
for key in self.keys:
193-
setattr(self, key, kwargs.get(key))
198+
value = kwargs.get(key)
199+
if isinstance(value, list) and not isinstance(value, FrozenList):
200+
value = FrozenList(value)
201+
setattr(self, key, value)
194202

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

208216
def __hash__(self):
209-
return id(self)
217+
return hash(tuple(getattr(self, key) for key in self.keys))
210218

211219
def __copy__(self):
212220
"""Create a shallow copy of the node."""
@@ -248,7 +256,7 @@ class NameNode(Node):
248256
class DocumentNode(Node):
249257
__slots__ = ("definitions",)
250258

251-
definitions: List["DefinitionNode"]
259+
definitions: FrozenList["DefinitionNode"]
252260

253261

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

261269
name: Optional[NameNode]
262-
directives: Optional[List["DirectiveNode"]]
263-
variable_definitions: List["VariableDefinitionNode"]
270+
directives: Optional[FrozenList["DirectiveNode"]]
271+
variable_definitions: FrozenList["VariableDefinitionNode"]
264272
selection_set: "SelectionSetNode"
265273

266274

@@ -276,27 +284,27 @@ class VariableDefinitionNode(Node):
276284
variable: "VariableNode"
277285
type: "TypeNode"
278286
default_value: Optional["ValueNode"]
279-
directives: Optional[List["DirectiveNode"]]
287+
directives: Optional[FrozenList["DirectiveNode"]]
280288

281289

282290
class SelectionSetNode(Node):
283291
__slots__ = ("selections",)
284292

285-
selections: List["SelectionNode"]
293+
selections: FrozenList["SelectionNode"]
286294

287295

288296
class SelectionNode(Node):
289297
__slots__ = ("directives",)
290298

291-
directives: Optional[List["DirectiveNode"]]
299+
directives: Optional[FrozenList["DirectiveNode"]]
292300

293301

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

297305
alias: Optional[NameNode]
298306
name: NameNode
299-
arguments: Optional[List["ArgumentNode"]]
307+
arguments: Optional[FrozenList["ArgumentNode"]]
300308
selection_set: Optional[SelectionSetNode]
301309

302310

@@ -381,13 +389,13 @@ class EnumValueNode(ValueNode):
381389
class ListValueNode(ValueNode):
382390
__slots__ = ("values",)
383391

384-
values: List[ValueNode]
392+
values: FrozenList[ValueNode]
385393

386394

387395
class ObjectValueNode(ValueNode):
388396
__slots__ = ("fields",)
389397

390-
fields: List["ObjectFieldNode"]
398+
fields: FrozenList["ObjectFieldNode"]
391399

392400

393401
class ObjectFieldNode(Node):
@@ -404,7 +412,7 @@ class DirectiveNode(Node):
404412
__slots__ = "name", "arguments"
405413

406414
name: NameNode
407-
arguments: List[ArgumentNode]
415+
arguments: FrozenList[ArgumentNode]
408416

409417

410418
# Type Reference
@@ -442,8 +450,8 @@ class TypeSystemDefinitionNode(DefinitionNode):
442450
class SchemaDefinitionNode(TypeSystemDefinitionNode):
443451
__slots__ = "directives", "operation_types"
444452

445-
directives: Optional[List[DirectiveNode]]
446-
operation_types: List["OperationTypeDefinitionNode"]
453+
directives: Optional[FrozenList[DirectiveNode]]
454+
operation_types: FrozenList["OperationTypeDefinitionNode"]
447455

448456

449457
class OperationTypeDefinitionNode(Node):
@@ -461,7 +469,7 @@ class TypeDefinitionNode(TypeSystemDefinitionNode):
461469

462470
description: Optional[StringValueNode]
463471
name: NameNode
464-
directives: Optional[List[DirectiveNode]]
472+
directives: Optional[FrozenList[DirectiveNode]]
465473

466474

467475
class ScalarTypeDefinitionNode(TypeDefinitionNode):
@@ -471,14 +479,14 @@ class ScalarTypeDefinitionNode(TypeDefinitionNode):
471479
class ObjectTypeDefinitionNode(TypeDefinitionNode):
472480
__slots__ = "interfaces", "fields"
473481

474-
interfaces: Optional[List[NamedTypeNode]]
475-
fields: Optional[List["FieldDefinitionNode"]]
482+
interfaces: Optional[FrozenList[NamedTypeNode]]
483+
fields: Optional[FrozenList["FieldDefinitionNode"]]
476484

477485

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

481-
arguments: Optional[List["InputValueDefinitionNode"]]
489+
arguments: Optional[FrozenList["InputValueDefinitionNode"]]
482490
type: TypeNode
483491

484492

@@ -492,19 +500,19 @@ class InputValueDefinitionNode(TypeDefinitionNode):
492500
class InterfaceTypeDefinitionNode(TypeDefinitionNode):
493501
__slots__ = ("fields",)
494502

495-
fields: Optional[List["FieldDefinitionNode"]]
503+
fields: Optional[FrozenList["FieldDefinitionNode"]]
496504

497505

498506
class UnionTypeDefinitionNode(TypeDefinitionNode):
499507
__slots__ = ("types",)
500508

501-
types: Optional[List[NamedTypeNode]]
509+
types: Optional[FrozenList[NamedTypeNode]]
502510

503511

504512
class EnumTypeDefinitionNode(TypeDefinitionNode):
505513
__slots__ = ("values",)
506514

507-
values: Optional[List["EnumValueDefinitionNode"]]
515+
values: Optional[FrozenList["EnumValueDefinitionNode"]]
508516

509517

510518
class EnumValueDefinitionNode(TypeDefinitionNode):
@@ -514,7 +522,7 @@ class EnumValueDefinitionNode(TypeDefinitionNode):
514522
class InputObjectTypeDefinitionNode(TypeDefinitionNode):
515523
__slots__ = ("fields",)
516524

517-
fields: Optional[List[InputValueDefinitionNode]]
525+
fields: Optional[FrozenList[InputValueDefinitionNode]]
518526

519527

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

526534
description: Optional[StringValueNode]
527535
name: NameNode
528-
arguments: Optional[List[InputValueDefinitionNode]]
536+
arguments: Optional[FrozenList[InputValueDefinitionNode]]
529537
repeatable: bool
530-
locations: List[NameNode]
538+
locations: FrozenList[NameNode]
531539

532540

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

539-
directives: Optional[List[DirectiveNode]]
540-
operation_types: Optional[List[OperationTypeDefinitionNode]]
547+
directives: Optional[FrozenList[DirectiveNode]]
548+
operation_types: Optional[FrozenList[OperationTypeDefinitionNode]]
541549

542550

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

549557
name: NameNode
550-
directives: Optional[List[DirectiveNode]]
558+
directives: Optional[FrozenList[DirectiveNode]]
551559

552560

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

563-
interfaces: Optional[List[NamedTypeNode]]
564-
fields: Optional[List[FieldDefinitionNode]]
571+
interfaces: Optional[FrozenList[NamedTypeNode]]
572+
fields: Optional[FrozenList[FieldDefinitionNode]]
565573

566574

567575
class InterfaceTypeExtensionNode(TypeExtensionNode):
568576
__slots__ = ("fields",)
569577

570-
fields: Optional[List[FieldDefinitionNode]]
578+
fields: Optional[FrozenList[FieldDefinitionNode]]
571579

572580

573581
class UnionTypeExtensionNode(TypeExtensionNode):
574582
__slots__ = ("types",)
575583

576-
types: Optional[List[NamedTypeNode]]
584+
types: Optional[FrozenList[NamedTypeNode]]
577585

578586

579587
class EnumTypeExtensionNode(TypeExtensionNode):
580588
__slots__ = ("values",)
581589

582-
values: Optional[List[EnumValueDefinitionNode]]
590+
values: Optional[FrozenList[EnumValueDefinitionNode]]
583591

584592

585593
class InputObjectTypeExtensionNode(TypeExtensionNode):
586594
__slots__ = ("fields",)
587595

588-
fields: Optional[List[InputValueDefinitionNode]]
596+
fields: Optional[FrozenList[InputValueDefinitionNode]]

src/graphql/pyutils/frozen_dict.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from copy import deepcopy
12
from typing import Dict, TypeVar
23

34
from .frozen_error import FrozenError
@@ -23,6 +24,17 @@ def __add__(self, value):
2324
def __iadd__(self, value):
2425
raise FrozenError
2526

27+
def __hash__(self):
28+
return hash(tuple(self.items()))
29+
30+
def __copy__(self):
31+
return FrozenDict(self)
32+
33+
copy = __copy__
34+
35+
def __deepcopy__(self, memo):
36+
return FrozenDict({k: deepcopy(v, memo) for k, v in self.items()})
37+
2638
def clear(self):
2739
raise FrozenError
2840

src/graphql/pyutils/frozen_list.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from copy import deepcopy
12
from typing import List, TypeVar
23

34
from .frozen_error import FrozenError
@@ -31,6 +32,15 @@ def __mul__(self, value):
3132
def __imul__(self, value):
3233
raise FrozenError
3334

35+
def __hash__(self):
36+
return hash(tuple(self))
37+
38+
def __copy__(self):
39+
return FrozenList(self)
40+
41+
def __deepcopy__(self, memo):
42+
return FrozenList(deepcopy(value, memo) for value in self)
43+
3444
def append(self, x):
3545
raise FrozenError
3646

src/graphql/utilities/build_ast_schema.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
parse,
2525
Node,
2626
)
27-
from ..pyutils import inspect
27+
from ..pyutils import inspect, FrozenList
2828
from ..type import (
2929
GraphQLArgument,
3030
GraphQLDeprecatedDirective,
@@ -346,7 +346,7 @@ def _make_interface_def(
346346
)
347347

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

351351
return GraphQLEnumType(
352352
name=ast_node.name.value,

src/graphql/utilities/lexicographic_sort_schema.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from typing import Dict, List, Tuple, cast
22

3-
from ..pyutils import inspect
3+
from ..pyutils import inspect, FrozenList
44
from ..type import (
55
GraphQLArgument,
66
GraphQLDirective,
@@ -82,7 +82,7 @@ def sort_input_fields(fields_map):
8282
for name, field in sorted(fields_map.items())
8383
}
8484

85-
def sort_types(arr: List[GraphQLNamedType]) -> List[GraphQLNamedType]:
85+
def sort_types(arr: FrozenList[GraphQLNamedType]) -> List[GraphQLNamedType]:
8686
return [
8787
replace_named_type(type_) for type_ in sorted(arr, key=sort_by_name_key)
8888
]

0 commit comments

Comments
 (0)