Skip to content

Commit

Permalink
gh-113570: reprlib.repr does not use builtin __repr__ for reshadowed …
Browse files Browse the repository at this point in the history
…builtins (GH-113577)
  • Loading branch information
georgepittock authored Oct 17, 2024
1 parent ad3eac1 commit 04d6dd2
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 5 deletions.
31 changes: 26 additions & 5 deletions Lib/reprlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,17 @@ def wrapper(self):
return decorating_function

class Repr:
_lookup = {
'tuple': 'builtins',
'list': 'builtins',
'array': 'array',
'set': 'builtins',
'frozenset': 'builtins',
'deque': 'collections',
'dict': 'builtins',
'str': 'builtins',
'int': 'builtins'
}

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

def repr1(self, x, level):
typename = type(x).__name__
cls = type(x)
typename = cls.__name__

if ' ' in typename:
parts = typename.split()
typename = '_'.join(parts)
if hasattr(self, 'repr_' + typename):
return getattr(self, 'repr_' + typename)(x, level)
else:
return self.repr_instance(x, level)

method = getattr(self, 'repr_' + typename, None)
if method:
# not defined in this class
if typename not in self._lookup:
return method(x, level)
module = getattr(cls, '__module__', None)
# defined in this class and is the module intended
if module == self._lookup[typename]:
return method(x, level)

return self.repr_instance(x, level)

def _join(self, pieces, level):
if self.indent is None:
Expand Down
44 changes: 44 additions & 0 deletions Lib/test/test_reprlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,50 @@ def test_invalid_indent(self):
with self.assertRaisesRegex(expected_error, expected_msg):
r.repr(test_object)

def test_shadowed_stdlib_array(self):
# Issue #113570: repr() should not be fooled by an array
class array:
def __repr__(self):
return "not array.array"

self.assertEqual(r(array()), "not array.array")

def test_shadowed_builtin(self):
# Issue #113570: repr() should not be fooled
# by a shadowed builtin function
class list:
def __repr__(self):
return "not builtins.list"

self.assertEqual(r(list()), "not builtins.list")

def test_custom_repr(self):
class MyRepr(Repr):

def repr_TextIOWrapper(self, obj, level):
if obj.name in {'<stdin>', '<stdout>', '<stderr>'}:
return obj.name
return repr(obj)

aRepr = MyRepr()
self.assertEqual(aRepr.repr(sys.stdin), "<stdin>")

def test_custom_repr_class_with_spaces(self):
class TypeWithSpaces:
pass

t = TypeWithSpaces()
type(t).__name__ = "type with spaces"
self.assertEqual(type(t).__name__, "type with spaces")

class MyRepr(Repr):
def repr_type_with_spaces(self, obj, level):
return "Type With Spaces"


aRepr = MyRepr()
self.assertEqual(aRepr.repr(t), "Type With Spaces")

def write_file(path, text):
with open(path, 'w', encoding='ASCII') as fp:
fp.write(text)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed a bug in ``reprlib.repr`` where it incorrectly called the repr method on shadowed Python built-in types.

0 comments on commit 04d6dd2

Please sign in to comment.