Skip to content

Commit d24f556

Browse files
committed
Use inspect() instead of repr() for error messages (#12)
We don't want to leak too much of the inner representation of Python objects in the client-facing error messages.
1 parent 0fa5ff5 commit d24f556

File tree

16 files changed

+102
-63
lines changed

16 files changed

+102
-63
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ a query language for APIs created by Facebook.
1212
[![Python 3 Status](https://pyup.io/repos/github/graphql-python/graphql-core-next/python-3-shield.svg)](https://pyup.io/repos/github/graphql-python/graphql-core-next/)
1313

1414
The current version 1.0.1 of GraphQL-core-next is up-to-date with GraphQL.js version
15-
14.0.2. All parts of the API are covered by an extensive test suite of currently 1655
15+
14.0.2. All parts of the API are covered by an extensive test suite of currently 1656
1616
unit tests.
1717

1818

graphql/execution/execute.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
SelectionSetNode,
2828
)
2929
from .middleware import MiddlewareManager
30-
from ..pyutils import is_invalid, is_nullish, MaybeAwaitable
30+
from ..pyutils import inspect, is_invalid, is_nullish, MaybeAwaitable
3131
from ..utilities import get_operation_root_type, type_from_ast
3232
from ..type import (
3333
GraphQLAbstractType,
@@ -234,7 +234,7 @@ def build(
234234
raise TypeError(
235235
"Middleware must be passed as a list or tuple of functions"
236236
" or objects, or as a single MiddlewareManager object."
237-
f" Got {middleware!r} instead."
237+
f" Got {inspect(middleware)} instead."
238238
)
239239

240240
for definition in document.definitions:
@@ -776,7 +776,9 @@ def complete_value(
776776
)
777777

778778
# Not reachable. All possible output types have been considered.
779-
raise TypeError(f"Cannot complete value of unexpected type {return_type}.")
779+
raise TypeError(
780+
f"Cannot complete value of unexpected type '{inspect(return_type)}'."
781+
)
780782

781783
def complete_list_value(
782784
self,
@@ -842,7 +844,8 @@ def complete_leaf_value(return_type: GraphQLLeafType, result: Any) -> Any:
842844
serialized_result = return_type.serialize(result)
843845
if is_invalid(serialized_result):
844846
raise TypeError(
845-
f"Expected a value of type '{return_type}' but received: {result!r}"
847+
f"Expected a value of type '{inspect(return_type)}'"
848+
f" but received: {inspect(result)}"
846849
)
847850
return serialized_result
848851

@@ -914,7 +917,7 @@ def ensure_valid_runtime_type(
914917
f"Abstract type {return_type.name} must resolve"
915918
" to an Object type at runtime"
916919
f" for field {info.parent_type.name}.{info.field_name}"
917-
f" with value {result!r}, received '{runtime_type}'."
920+
f" with value {inspect(result)}, received '{inspect(runtime_type)}'."
918921
f" Either the {return_type.name} type should provide"
919922
' a "resolve_type" function or each possible type should'
920923
' provide an "is_type_of" function.',
@@ -1084,7 +1087,8 @@ def invalid_return_type_error(
10841087
) -> GraphQLError:
10851088
"""Create a GraphQLError for an invalid return type."""
10861089
return GraphQLError(
1087-
f"Expected value of type '{return_type.name}' but got: {result!r}.", field_nodes
1090+
f"Expected value of type '{return_type.name}' but got: {inspect(result)}.",
1091+
field_nodes,
10881092
)
10891093

10901094

graphql/execution/values.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
VariableNode,
1616
print_ast,
1717
)
18+
from ..pyutils import inspect
1819
from ..type import (
1920
GraphQLDirective,
2021
GraphQLField,
@@ -95,7 +96,7 @@ def get_variable_values(
9596
for error in coercion_errors:
9697
error.message = (
9798
f"Variable '${var_name}' got invalid"
98-
f" value {value!r}; {error.message}"
99+
f" value {inspect(value)}; {error.message}"
99100
)
100101
errors.extend(coercion_errors)
101102
else:

graphql/language/parser.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
from .lexer import Lexer, Token, TokenKind
6060
from .source import Source
6161
from ..error import GraphQLError, GraphQLSyntaxError
62+
from ..pyutils import inspect
6263

6364
__all__ = ["parse", "parse_type", "parse_value"]
6465

@@ -91,7 +92,7 @@ def parse(
9192
if isinstance(source, str):
9293
source = Source(source)
9394
elif not isinstance(source, Source):
94-
raise TypeError(f"Must provide Source. Received: {source!r}")
95+
raise TypeError(f"Must provide Source. Received: {inspect(source)}")
9596
lexer = Lexer(
9697
source,
9798
no_location=no_location,

graphql/language/visitor.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
Union,
1111
)
1212

13-
from ..pyutils import snake_to_camel
13+
from ..pyutils import inspect, snake_to_camel
1414
from . import ast
1515

1616
from .ast import Node
@@ -210,9 +210,9 @@ def visit(root: Node, visitor: Visitor, visitor_keys=None) -> Node:
210210
dictionary visitor_keys mapping node kinds to node attributes.
211211
"""
212212
if not isinstance(root, Node):
213-
raise TypeError(f"Not an AST Node: {root!r}")
213+
raise TypeError(f"Not an AST Node: {inspect(root)}")
214214
if not isinstance(visitor, Visitor):
215-
raise TypeError(f"Not an AST Visitor class: {visitor!r}")
215+
raise TypeError(f"Not an AST Visitor class: {inspect(visitor)}")
216216
if visitor_keys is None:
217217
visitor_keys = QUERY_DOCUMENT_KEYS
218218
stack: Any = None
@@ -280,7 +280,7 @@ def visit(root: Node, visitor: Visitor, visitor_keys=None) -> Node:
280280
result = None
281281
else:
282282
if not isinstance(node, Node):
283-
raise TypeError(f"Not an AST Node: {node!r}")
283+
raise TypeError(f"Not an AST Node: {inspect(node)}")
284284
visit_fn = visitor.get_visit_fn(node.kind, is_leaving)
285285
if visit_fn:
286286
result = visit_fn(visitor, node, key, parent, path, ancestors)

graphql/pyutils/inspect.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,13 @@ def inspect(value: Any) -> str:
6464
elif isgenerator(value):
6565
type_ = "generator"
6666
else:
67+
# stringify (only) the well-known GraphQL types
68+
from ..type import GraphQLNamedType, GraphQLScalarType, GraphQLWrappingType
69+
70+
if isinstance(
71+
value, (GraphQLNamedType, GraphQLScalarType, GraphQLWrappingType)
72+
):
73+
return str(value)
6774
try:
6875
name = type(value).__name__
6976
if not name or "<" in name or ">" in name:

graphql/type/definition.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
UnionTypeExtensionNode,
4343
ValueNode,
4444
)
45-
from ..pyutils import MaybeAwaitable, cached_property
45+
from ..pyutils import MaybeAwaitable, cached_property, inspect
4646
from ..utilities.value_from_ast_untyped import value_from_ast_untyped
4747

4848
if TYPE_CHECKING: # pragma: no cover
@@ -344,6 +344,12 @@ def __init__(
344344
self.parse_value = parse_value or default_value_parser
345345
self.parse_literal = parse_literal or value_from_ast_untyped
346346

347+
def __str__(self):
348+
return self.name
349+
350+
def __repr__(self):
351+
return f"<{self.__class__.__name__}({self})>"
352+
347353

348354
def is_scalar_type(type_: Any) -> bool:
349355
return isinstance(type_, GraphQLScalarType)
@@ -400,7 +406,7 @@ def __init__(
400406
if resolve is not None and not callable(resolve):
401407
raise TypeError(
402408
"Field resolver must be a function if provided, "
403-
f" but got: {resolve!r}."
409+
f" but got: {inspect(resolve)}."
404410
)
405411
if description is not None and not isinstance(description, str):
406412
raise TypeError("The description must be a string.")
@@ -576,7 +582,7 @@ def __init__(
576582
if is_type_of is not None and not callable(is_type_of):
577583
raise TypeError(
578584
f"{name} must provide 'is_type_of' as a function,"
579-
f" but got: {is_type_of!r}."
585+
f" but got: {inspect(is_type_of)}."
580586
)
581587
if ast_node and not isinstance(ast_node, ObjectTypeDefinitionNode):
582588
raise TypeError(f"{name} AST node must be an ObjectTypeDefinitionNode.")
@@ -686,7 +692,7 @@ def __init__(
686692
if resolve_type is not None and not callable(resolve_type):
687693
raise TypeError(
688694
f"{name} must provide 'resolve_type' as a function,"
689-
f" but got: {resolve_type!r}."
695+
f" but got: {inspect(resolve_type)}."
690696
)
691697
if ast_node and not isinstance(ast_node, InterfaceTypeDefinitionNode):
692698
raise TypeError(f"{name} AST node must be an InterfaceTypeDefinitionNode.")
@@ -784,7 +790,7 @@ def __init__(
784790
if resolve_type is not None and not callable(resolve_type):
785791
raise TypeError(
786792
f"{name} must provide 'resolve_type' as a function,"
787-
f" but got: {resolve_type!r}."
793+
f" but got: {inspect(resolve_type)}."
788794
)
789795
if ast_node and not isinstance(ast_node, UnionTypeDefinitionNode):
790796
raise TypeError(f"{name} AST node must be a UnionTypeDefinitionNode.")

graphql/type/scalars.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from typing import Any
33

44
from ..error import INVALID
5-
from ..pyutils import is_finite, is_integer
5+
from ..pyutils import inspect, is_finite, is_integer
66
from ..language.ast import (
77
BooleanValueNode,
88
FloatValueNode,
@@ -51,20 +51,20 @@ def serialize_int(value: Any) -> int:
5151
if num != float_value:
5252
raise ValueError
5353
except (OverflowError, ValueError, TypeError):
54-
raise TypeError(f"Int cannot represent non-integer value: {value!r}")
54+
raise TypeError(f"Int cannot represent non-integer value: {inspect(value)}")
5555
if not MIN_INT <= num <= MAX_INT:
5656
raise TypeError(
57-
f"Int cannot represent non 32-bit signed integer value: {value!r}"
57+
f"Int cannot represent non 32-bit signed integer value: {inspect(value)}"
5858
)
5959
return num
6060

6161

6262
def coerce_int(value: Any) -> int:
6363
if not is_integer(value):
64-
raise TypeError(f"Int cannot represent non-integer value: {value!r}")
64+
raise TypeError(f"Int cannot represent non-integer value: {inspect(value)}")
6565
if not MIN_INT <= value <= MAX_INT:
6666
raise TypeError(
67-
f"Int cannot represent non 32-bit signed integer value: {value!r}"
67+
f"Int cannot represent non 32-bit signed integer value: {inspect(value)}"
6868
)
6969
return int(value)
7070

@@ -100,13 +100,13 @@ def serialize_float(value: Any) -> float:
100100
if not isfinite(num):
101101
raise ValueError
102102
except (ValueError, TypeError):
103-
raise TypeError(f"Float cannot represent non numeric value: {value!r}")
103+
raise TypeError(f"Float cannot represent non numeric value: {inspect(value)}")
104104
return num
105105

106106

107107
def coerce_float(value: Any) -> float:
108108
if not is_finite(value):
109-
raise TypeError(f"Float cannot represent non numeric value: {value!r}")
109+
raise TypeError(f"Float cannot represent non numeric value: {inspect(value)}")
110110
return float(value)
111111

112112

@@ -139,13 +139,13 @@ def serialize_string(value: Any) -> str:
139139
# do not serialize builtin types as strings, but allow serialization of custom
140140
# types via their `__str__` method
141141
if type(value).__module__ == "builtins":
142-
raise TypeError(f"String cannot represent value: {value!r}")
142+
raise TypeError(f"String cannot represent value: {inspect(value)}")
143143
return str(value)
144144

145145

146146
def coerce_string(value: Any) -> str:
147147
if not isinstance(value, str):
148-
raise TypeError(f"String cannot represent a non string value: {value!r}")
148+
raise TypeError(f"String cannot represent a non string value: {inspect(value)}")
149149
return value
150150

151151

@@ -173,12 +173,14 @@ def serialize_boolean(value: Any) -> bool:
173173
return value
174174
if is_finite(value):
175175
return bool(value)
176-
raise TypeError(f"Boolean cannot represent a non boolean value: {value!r}")
176+
raise TypeError(f"Boolean cannot represent a non boolean value: {inspect(value)}")
177177

178178

179179
def coerce_boolean(value: Any) -> bool:
180180
if not isinstance(value, bool):
181-
raise TypeError(f"Boolean cannot represent a non boolean value: {value!r}")
181+
raise TypeError(
182+
f"Boolean cannot represent a non boolean value: {inspect(value)}"
183+
)
182184
return value
183185

184186

@@ -206,13 +208,13 @@ def serialize_id(value: Any) -> str:
206208
# do not serialize builtin types as IDs, but allow serialization of custom types
207209
# via their `__str__` method
208210
if type(value).__module__ == "builtins":
209-
raise TypeError(f"ID cannot represent value: {value!r}")
211+
raise TypeError(f"ID cannot represent value: {inspect(value)}")
210212
return str(value)
211213

212214

213215
def coerce_id(value: Any) -> str:
214216
if not isinstance(value, str) and not is_integer(value):
215-
raise TypeError(f"ID cannot represent value: {value!r}")
217+
raise TypeError(f"ID cannot represent value: {inspect(value)}")
216218
if isinstance(value, float):
217219
value = int(value)
218220
return str(value)

graphql/type/validate.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
is_union_type,
2929
is_required_argument,
3030
)
31+
from ..pyutils import inspect
3132
from ..utilities.assert_valid_name import is_valid_name_error
3233
from ..utilities.type_comparators import is_equal_type, is_type_sub_type_of
3334
from .directives import GraphQLDirective, is_directive
@@ -48,7 +49,7 @@ def validate_schema(schema: GraphQLSchema) -> List[GraphQLError]:
4849
"""
4950
# First check to ensure the provided value is in fact a GraphQLSchema.
5051
if not is_schema(schema):
51-
raise TypeError(f"Expected {schema!r} to be a GraphQL schema.")
52+
raise TypeError(f"Expected {inspect(schema)} to be a GraphQL schema.")
5253

5354
# If this Schema has already been validated, return the previous results.
5455
# noinspection PyProtectedMember
@@ -140,7 +141,7 @@ def validate_directives(self):
140141
# Ensure all directives are in fact GraphQL directives.
141142
if not is_directive(directive):
142143
self.report_error(
143-
f"Expected directive but got: {directive!r}.",
144+
f"Expected directive but got: {inspect(directive)}.",
144145
getattr(directive, "ast_node", None),
145146
)
146147
continue
@@ -168,7 +169,7 @@ def validate_directives(self):
168169
if not is_input_type(arg.type):
169170
self.report_error(
170171
f"The type of @{directive.name}({arg_name}:)"
171-
f" must be Input Type but got: {arg.type!r}.",
172+
f" must be Input Type but got: {inspect(arg.type)}.",
172173
get_directive_arg_type_node(directive, arg_name),
173174
)
174175

@@ -192,7 +193,7 @@ def validate_types(self):
192193
# Ensure all provided types are in fact GraphQL type.
193194
if not is_named_type(type_):
194195
self.report_error(
195-
f"Expected GraphQL named type but got: {type_!r}.",
196+
f"Expected GraphQL named type but got: {inspect(type)}.",
196197
type_.ast_node if type_ else None,
197198
)
198199
continue
@@ -253,7 +254,7 @@ def validate_fields(self, type_: Union[GraphQLObjectType, GraphQLInterfaceType])
253254
if not is_output_type(field.type):
254255
self.report_error(
255256
f"The type of {type_.name}.{field_name}"
256-
" must be Output Type but got: {field.type!r}.",
257+
" must be Output Type but got: {inspect(field.type)}.",
257258
get_field_type_node(type_, field_name),
258259
)
259260

@@ -279,7 +280,7 @@ def validate_fields(self, type_: Union[GraphQLObjectType, GraphQLInterfaceType])
279280
self.report_error(
280281
"Field argument"
281282
f" {type_.name}.{field_name}({arg_name}:)"
282-
f" must be Input Type but got: {arg.type!r}.",
283+
f" must be Input Type but got: {inspect(arg.type)}.",
283284
get_field_arg_type_node(type_, field_name, arg_name),
284285
)
285286

@@ -289,7 +290,7 @@ def validate_object_interfaces(self, obj: GraphQLObjectType):
289290
if not is_interface_type(iface):
290291
self.report_error(
291292
f"Type {obj.name} must only implement Interface"
292-
f" types, it cannot implement {iface!r}.",
293+
f" types, it cannot implement {inspect(iface)}.",
293294
get_implements_interface_node(obj, iface),
294295
)
295296
continue
@@ -450,7 +451,7 @@ def validate_input_fields(self, input_obj: GraphQLInputObjectType):
450451
if not is_input_type(field.type):
451452
self.report_error(
452453
f"The type of {input_obj.name}.{field_name}"
453-
f" must be Input Type but got: {field.type!r}.",
454+
f" must be Input Type but got: {inspect(field.type)}.",
454455
field.ast_node.type if field.ast_node else None,
455456
)
456457

graphql/utilities/ast_from_value.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
StringValueNode,
1515
ValueNode,
1616
)
17-
from ..pyutils import is_nullish, is_invalid
17+
from ..pyutils import inspect, is_nullish, is_invalid
1818
from ..type import (
1919
GraphQLID,
2020
GraphQLInputType,
@@ -124,6 +124,6 @@ def ast_from_value(value: Any, type_: GraphQLInputType) -> Optional[ValueNode]:
124124

125125
return StringValueNode(value=serialized)
126126

127-
raise TypeError(f"Cannot convert value to AST: {serialized!r}")
127+
raise TypeError(f"Cannot convert value to AST: {inspect(serialized)}")
128128

129-
raise TypeError(f"Unknown type: {type_!r}.")
129+
raise TypeError(f"Unknown type: {inspect(type_)}.")

0 commit comments

Comments
 (0)