Skip to content

Commit 0ef2251

Browse files
committed
Quote suggested values, unify rest of error messages
Replicates graphql/graphql-js@0d6d9b7 and graphql/graphql-js@10b8e95 Also adds Oxford comma to suggested values as in original.
1 parent 2ed3a7b commit 0ef2251

20 files changed

+112
-94
lines changed

src/graphql/execution/execute.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -797,7 +797,7 @@ def complete_list_value(
797797
if not isinstance(result, Iterable) or isinstance(result, str):
798798
raise GraphQLError(
799799
"Expected Iterable, but did not find one for field"
800-
f" {info.parent_type.name}.{info.field_name}."
800+
f" '{info.parent_type.name}.{info.field_name}'."
801801
)
802802

803803
# This is specified as a simple map, however we're optimizing the path where
@@ -916,13 +916,13 @@ def ensure_valid_runtime_type(
916916

917917
if not is_object_type(runtime_type):
918918
raise GraphQLError(
919-
f"Abstract type {return_type.name} must resolve"
919+
f"Abstract type '{return_type.name}' must resolve"
920920
" to an Object type at runtime"
921-
f" for field {info.parent_type.name}.{info.field_name}"
921+
f" for field '{info.parent_type.name}.{info.field_name}'"
922922
f" with value {inspect(result)}, received '{inspect(runtime_type)}'."
923-
f" Either the {return_type.name} type should provide"
924-
' a "resolve_type" function or each possible type should'
925-
' provide an "is_type_of" function.',
923+
f" Either the '{return_type.name}' type should provide"
924+
" a 'resolve_type' function or each possible type should"
925+
" provide an 'is_type_of' function.",
926926
field_nodes,
927927
)
928928
runtime_type = cast(GraphQLObjectType, runtime_type)

src/graphql/pyutils/did_you_mean.py

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Sequence, List
1+
from typing import Sequence
22

33
__all__ = ["did_you_mean"]
44

@@ -7,15 +7,22 @@
77

88
def did_you_mean(suggestions: Sequence[str], sub_message: str = None) -> str:
99
"""Given [ A, B, C ] return ' Did you mean A, B, or C?'"""
10-
parts: List[str] = []
11-
if suggestions:
12-
append = parts.append
13-
append(" Did you mean")
14-
if sub_message:
15-
append(sub_message)
16-
suggestions = suggestions[:MAX_LENGTH]
17-
if len(suggestions) > 1:
18-
append(", ".join(suggestions[:-1]))
19-
append("or")
20-
append(suggestions[-1] + "?")
21-
return " ".join(parts)
10+
if not suggestions:
11+
return ""
12+
parts = [" Did you mean "]
13+
if sub_message:
14+
parts.extend([sub_message, " "])
15+
suggestions = suggestions[:MAX_LENGTH]
16+
n = len(suggestions)
17+
if n == 1:
18+
parts.append(f"'{suggestions[0]}'?")
19+
elif n == 2:
20+
parts.append(f"'{suggestions[0]}' or '{suggestions[1]}'?")
21+
else:
22+
parts.extend(
23+
[
24+
", ".join(f"'{s}'" for s in suggestions[:-1]),
25+
f", or '{suggestions[-1]}'?",
26+
]
27+
)
28+
return "".join(parts)

src/graphql/utilities/coerce_input_value.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def coerce_input_value(
4848
path.as_list() if path else [],
4949
input_value,
5050
GraphQLError(
51-
f"Expected non-nullable type {inspect(type_)} not to be None."
51+
f"Expected non-nullable type '{inspect(type_)}' not to be None."
5252
),
5353
)
5454
return INVALID
@@ -79,7 +79,7 @@ def coerce_input_value(
7979
on_error(
8080
path.as_list() if path else [],
8181
input_value,
82-
GraphQLError(f"Expected type {type_.name} to be a dict."),
82+
GraphQLError(f"Expected type '{type_.name}' to be a dict."),
8383
)
8484
return INVALID
8585

@@ -99,7 +99,7 @@ def coerce_input_value(
9999
path.as_list() if path else [],
100100
input_value,
101101
GraphQLError(
102-
f"Field {field_name} of required type {type_str}"
102+
f"Field '{field_name}' of required type '{type_str}'"
103103
" was not provided."
104104
),
105105
)
@@ -117,7 +117,7 @@ def coerce_input_value(
117117
path.as_list() if path else [],
118118
input_value,
119119
GraphQLError(
120-
f"Field '{field_name}' is not defined by type {type_.name}."
120+
f"Field '{field_name}' is not defined by type '{type_.name}'."
121121
+ did_you_mean(suggestions)
122122
),
123123
)
@@ -134,15 +134,15 @@ def coerce_input_value(
134134
path.as_list() if path else [],
135135
input_value,
136136
GraphQLError(
137-
f"Expected type {type_.name}. {error}", original_error=error
137+
f"Expected type '{type_.name}'. {error}", original_error=error
138138
),
139139
)
140140
return INVALID
141141
if parse_result is INVALID:
142142
on_error(
143143
path.as_list() if path else [],
144144
input_value,
145-
GraphQLError(f"Expected type {type_.name}."),
145+
GraphQLError(f"Expected type '{type_.name}'."),
146146
)
147147
return parse_result
148148

@@ -157,7 +157,10 @@ def coerce_input_value(
157157
on_error(
158158
path.as_list() if path else [],
159159
input_value,
160-
GraphQLError(f"Expected type {type_.name}." + did_you_mean(suggestions)),
160+
GraphQLError(
161+
f"Expected type '{type_.name}'."
162+
+ did_you_mean(suggestions, "the enum value")
163+
),
161164
)
162165
return INVALID
163166

src/graphql/utilities/find_deprecated_usages.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def enter_field(self, node, *_args):
4040
reason = field_def.deprecation_reason
4141
self.errors.append(
4242
GraphQLError(
43-
f"The field {parent_type.name}.{field_name}"
43+
f"The field '{parent_type.name}.{field_name}'"
4444
" is deprecated." + (f" {reason}" if reason else ""),
4545
node,
4646
)
@@ -55,7 +55,7 @@ def enter_enum_value(self, node, *_args):
5555
reason = enum_val.deprecation_reason
5656
self.errors.append(
5757
GraphQLError(
58-
f"The enum value {type_.name}.{enum_val_name}"
58+
f"The enum value '{type_.name}.{enum_val_name}'"
5959
" is deprecated." + (f" {reason}" if reason else ""),
6060
node,
6161
)

src/graphql/validation/rules/fields_on_correct_type.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,12 @@ def enter_field(self, node: FieldNode, *_args):
4242
)
4343

4444
# Report an error, including helpful suggestions.
45-
quoted_type_names = [f"'{s}'" for s in suggested_type_names]
46-
quoted_field_names = [f"'{s}'" for s in suggested_field_names]
4745
self.report_error(
4846
GraphQLError(
4947
f"Cannot query field '{field_name}' on type '{type_}'."
5048
+ (
51-
did_you_mean(quoted_type_names, "to use an inline fragment on")
52-
or did_you_mean(quoted_field_names)
49+
did_you_mean(suggested_type_names, "to use an inline fragment on")
50+
or did_you_mean(suggested_field_names)
5351
),
5452
node,
5553
)

src/graphql/validation/rules/known_argument_names.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def enter_directive(self, directive_node: DirectiveNode, *_args):
4747
GraphQLError(
4848
f"Unknown argument '{arg_name}'"
4949
f" on directive '@{directive_name}'."
50-
+ did_you_mean([f"'{s}'" for s in suggestions]),
50+
+ did_you_mean(suggestions),
5151
arg_node,
5252
)
5353
)
@@ -79,7 +79,7 @@ def enter_argument(self, arg_node: ArgumentNode, *args):
7979
GraphQLError(
8080
f"Unknown argument '{arg_name}'"
8181
f" on field '{parent_type.name}.{field_name}'."
82-
+ did_you_mean([f"'{s}'" for s in suggestions]),
82+
+ did_you_mean(suggestions),
8383
arg_node,
8484
)
8585
)

src/graphql/validation/rules/known_type_names.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,7 @@ def enter_named_type(
6161
)
6262
self.report_error(
6363
GraphQLError(
64-
f"Unknown type '{type_name}'."
65-
+ did_you_mean([f"'{s}'" for s in suggested_types]),
64+
f"Unknown type '{type_name}'." + did_you_mean(suggested_types),
6665
node,
6766
)
6867
)

src/graphql/validation/rules/possible_type_extensions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def check_extension(self, node: TypeExtensionNode, *_args):
6464
self.report_error(
6565
GraphQLError(
6666
f"Cannot extend type '{type_name}' because it is not defined."
67-
+ did_you_mean([f"'{s}'" for s in suggested_types]),
67+
+ did_you_mean(suggested_types),
6868
node.name,
6969
)
7070
)

src/graphql/validation/rules/values_of_correct_type.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ def enter_object_field(self, node: ObjectFieldNode, *_args):
7474
GraphQLError(
7575
f"Field '{node.name.value}'"
7676
f" is not defined by type '{parent_type.name}'."
77-
+ did_you_mean([f"'{name}'" for name in suggestions]),
77+
+ did_you_mean(suggestions),
7878
node,
7979
)
8080
)
@@ -119,9 +119,7 @@ def is_valid_value_node(self, node: ValueNode) -> None:
119119
if is_enum_type(type_):
120120
if not isinstance(node, EnumValueNode) or node.value not in type_.values:
121121
all_names = list(type_.values)
122-
suggested_values = [
123-
f"'{name}'" for name in suggestion_list(print_ast(node), all_names)
124-
]
122+
suggested_values = suggestion_list(print_ast(node), all_names)
125123
self.report_error(
126124
GraphQLError(
127125
f"Expected value of type '{type_.name}',"

tests/execution/test_abstract.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -361,11 +361,11 @@ def returning_invalid_value_from_resolve_type_yields_useful_error():
361361
{"foo": None},
362362
[
363363
{
364-
"message": "Abstract type FooInterface must resolve to an"
365-
" Object type at runtime for field Query.foo with value 'dummy',"
366-
" received '[]'. Either the FooInterface type should provide"
367-
' a "resolve_type" function or each possible type'
368-
' should provide an "is_type_of" function.',
364+
"message": "Abstract type 'FooInterface' must resolve to an"
365+
" Object type at runtime for field 'Query.foo' with value 'dummy',"
366+
" received '[]'. Either the 'FooInterface' type should provide"
367+
" a 'resolve_type' function or each possible type"
368+
" should provide an 'is_type_of' function.",
369369
"locations": [(1, 3)],
370370
"path": ["foo"],
371371
}

0 commit comments

Comments
 (0)