Skip to content

Commit 04d6dd2

Browse files
gh-113570: reprlib.repr does not use builtin __repr__ for reshadowed builtins (GH-113577)
1 parent ad3eac1 commit 04d6dd2

File tree

3 files changed

+71
-5
lines changed

3 files changed

+71
-5
lines changed

Lib/reprlib.py

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,17 @@ def wrapper(self):
3636
return decorating_function
3737

3838
class Repr:
39+
_lookup = {
40+
'tuple': 'builtins',
41+
'list': 'builtins',
42+
'array': 'array',
43+
'set': 'builtins',
44+
'frozenset': 'builtins',
45+
'deque': 'collections',
46+
'dict': 'builtins',
47+
'str': 'builtins',
48+
'int': 'builtins'
49+
}
3950

4051
def __init__(
4152
self, *, maxlevel=6, maxtuple=6, maxlist=6, maxarray=5, maxdict=4,
@@ -60,14 +71,24 @@ def repr(self, x):
6071
return self.repr1(x, self.maxlevel)
6172

6273
def repr1(self, x, level):
63-
typename = type(x).__name__
74+
cls = type(x)
75+
typename = cls.__name__
76+
6477
if ' ' in typename:
6578
parts = typename.split()
6679
typename = '_'.join(parts)
67-
if hasattr(self, 'repr_' + typename):
68-
return getattr(self, 'repr_' + typename)(x, level)
69-
else:
70-
return self.repr_instance(x, level)
80+
81+
method = getattr(self, 'repr_' + typename, None)
82+
if method:
83+
# not defined in this class
84+
if typename not in self._lookup:
85+
return method(x, level)
86+
module = getattr(cls, '__module__', None)
87+
# defined in this class and is the module intended
88+
if module == self._lookup[typename]:
89+
return method(x, level)
90+
91+
return self.repr_instance(x, level)
7192

7293
def _join(self, pieces, level):
7394
if self.indent is None:

Lib/test/test_reprlib.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,50 @@ def test_invalid_indent(self):
580580
with self.assertRaisesRegex(expected_error, expected_msg):
581581
r.repr(test_object)
582582

583+
def test_shadowed_stdlib_array(self):
584+
# Issue #113570: repr() should not be fooled by an array
585+
class array:
586+
def __repr__(self):
587+
return "not array.array"
588+
589+
self.assertEqual(r(array()), "not array.array")
590+
591+
def test_shadowed_builtin(self):
592+
# Issue #113570: repr() should not be fooled
593+
# by a shadowed builtin function
594+
class list:
595+
def __repr__(self):
596+
return "not builtins.list"
597+
598+
self.assertEqual(r(list()), "not builtins.list")
599+
600+
def test_custom_repr(self):
601+
class MyRepr(Repr):
602+
603+
def repr_TextIOWrapper(self, obj, level):
604+
if obj.name in {'<stdin>', '<stdout>', '<stderr>'}:
605+
return obj.name
606+
return repr(obj)
607+
608+
aRepr = MyRepr()
609+
self.assertEqual(aRepr.repr(sys.stdin), "<stdin>")
610+
611+
def test_custom_repr_class_with_spaces(self):
612+
class TypeWithSpaces:
613+
pass
614+
615+
t = TypeWithSpaces()
616+
type(t).__name__ = "type with spaces"
617+
self.assertEqual(type(t).__name__, "type with spaces")
618+
619+
class MyRepr(Repr):
620+
def repr_type_with_spaces(self, obj, level):
621+
return "Type With Spaces"
622+
623+
624+
aRepr = MyRepr()
625+
self.assertEqual(aRepr.repr(t), "Type With Spaces")
626+
583627
def write_file(path, text):
584628
with open(path, 'w', encoding='ASCII') as fp:
585629
fp.write(text)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed a bug in ``reprlib.repr`` where it incorrectly called the repr method on shadowed Python built-in types.

0 commit comments

Comments
 (0)