Skip to content

Commit e0eb6fb

Browse files
committed
Correctly handle custom inspect fn that returns object value
Replicates graphql/graphql-js@1632bd6
1 parent 2de9352 commit e0eb6fb

File tree

7 files changed

+41
-17
lines changed

7 files changed

+41
-17
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ a query language for APIs created by Facebook.
1313
[![Code Style](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black)
1414

1515
The current version 1.0.1 of GraphQL-core-next is up-to-date with GraphQL.js version
16-
14.0.2. All parts of the API are covered by an extensive test suite of currently 1681
16+
14.0.2. All parts of the API are covered by an extensive test suite of currently 1682
1717
unit tests.
1818

1919

graphql/language/ast.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,13 @@ class Location(NamedTuple):
8181
def __str__(self):
8282
return f"{self.start}:{self.end}"
8383

84+
def __repr__(self):
85+
"""Print a simplified form when appearing in repr() or inspect()."""
86+
return "<Location {}:{}>".format(self.start, self.end)
87+
88+
def __inspect__(self):
89+
return repr(self)
90+
8491
def __eq__(self, other):
8592
if isinstance(other, Location):
8693
return self.start == other.start and self.end == other.end

graphql/language/lexer.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,15 @@ def __init__(
5656
self.next: Optional[Token] = None
5757
self.value: Optional[str] = value
5858

59+
def __str__(self):
60+
return self.desc
61+
5962
def __repr__(self):
60-
return "<Token {} at {}-{} ({}/{})>".format(
61-
self.desc, self.start, self.end, self.line, self.column
62-
)
63+
"""Print a simplified form when appearing in repr() or inspect()."""
64+
return "<Token {} {}/{}>".format(self.desc, self.line, self.column)
65+
66+
def __inspect__(self):
67+
return repr(self)
6368

6469
def __eq__(self, other):
6570
if isinstance(other, Token):

graphql/pyutils/inspect.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ def inspect(value: Any) -> str:
2424
"""
2525
if isinstance(value, (bool, int, float, str)) or value in (None, INVALID):
2626
return repr(value)
27+
# check if we have a custom inspect method
28+
try:
29+
inspect_method = value.__inspect__
30+
if callable(inspect_method):
31+
value = inspect_method()
32+
return value if isinstance(value, str) else inspect(value)
33+
except AttributeError:
34+
pass
2735
if isinstance(value, list):
2836
return f"[{', '.join(map(inspect, value))}]"
2937
if isinstance(value, tuple):
@@ -71,15 +79,6 @@ def inspect(value: Any) -> str:
7179
value, (GraphQLNamedType, GraphQLScalarType, GraphQLWrappingType)
7280
):
7381
return str(value)
74-
# check if we have a custom inspect method
75-
try:
76-
inspect_method = value.__inspect__
77-
if not callable(inspect_method):
78-
raise AttributeError
79-
except AttributeError:
80-
pass
81-
else:
82-
return inspect_method()
8382
try:
8483
name = type(value).__name__
8584
if not name or "<" in name or ">" in name:

tests/language/test_lexer.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from graphql.error import GraphQLSyntaxError
44
from graphql.language import Lexer, Source, SourceLocation, Token, TokenKind
5-
from graphql.pyutils import dedent
5+
from graphql.pyutils import dedent, inspect
66

77

88
def lex_one(s: str) -> Token:
@@ -40,10 +40,12 @@ def records_line_and_column():
4040
token = lex_one("\n \r\n \r foo\n")
4141
assert token == Token(TokenKind.NAME, 8, 11, 4, 3, None, "foo")
4242

43-
def can_be_stringified():
43+
def can_be_stringified_or_pyutils_inspected():
4444
token = lex_one("foo")
45-
assert repr(token) == "<Token Name 'foo' at 0-3 (1/1)>"
4645
assert token.desc == "Name 'foo'"
46+
assert str(token) == token.desc
47+
assert repr(token) == "<Token Name 'foo' 1/1>"
48+
assert inspect(token) == repr(token)
4749

4850
# noinspection PyArgumentEqualDefault
4951
def skips_whitespace_and_comments():

tests/language/test_parser.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from pytest import raises
44

5-
from graphql.pyutils import dedent
5+
from graphql.pyutils import dedent, inspect
66
from graphql.error import GraphQLSyntaxError
77
from graphql.language import (
88
ArgumentNode,
@@ -367,6 +367,8 @@ def experimental_allows_parsing_fragment_defined_variables():
367367
def contains_location_information_that_only_stringifies_start_end():
368368
result = parse("{ id }")
369369
assert str(result.loc) == "0:6"
370+
assert repr(result.loc) == "<Location 0:6>"
371+
assert inspect(result.loc) == repr(result.loc)
370372

371373
def contains_references_to_source():
372374
source = Source("{ id }")

tests/pyutils/test_inspect.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,3 +167,12 @@ def __inspect__():
167167
return "<custom magic method inspect>"
168168

169169
assert inspect(TestClass()) == "<custom magic method inspect>"
170+
171+
def custom_inspect_that_uses_self():
172+
class TestClass:
173+
str = "Hello World!"
174+
175+
def __inspect__(self):
176+
return self.str
177+
178+
assert inspect(TestClass()) == "Hello World!"

0 commit comments

Comments
 (0)