gh-113570: reprlib.repr does not use builtin __repr__ for reshadowed builtins (GH-113577)

This commit is contained in:
George Pittock 2024-10-17 17:34:37 +01:00 committed by GitHub
parent ad3eac1963
commit 04d6dd23e2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 71 additions and 5 deletions

View File

@ -36,6 +36,17 @@ def recursive_repr(fillvalue='...'):
return decorating_function return decorating_function
class Repr: class Repr:
_lookup = {
'tuple': 'builtins',
'list': 'builtins',
'array': 'array',
'set': 'builtins',
'frozenset': 'builtins',
'deque': 'collections',
'dict': 'builtins',
'str': 'builtins',
'int': 'builtins'
}
def __init__( def __init__(
self, *, maxlevel=6, maxtuple=6, maxlist=6, maxarray=5, maxdict=4, self, *, maxlevel=6, maxtuple=6, maxlist=6, maxarray=5, maxdict=4,
@ -60,14 +71,24 @@ class Repr:
return self.repr1(x, self.maxlevel) return self.repr1(x, self.maxlevel)
def repr1(self, x, level): def repr1(self, x, level):
typename = type(x).__name__ cls = type(x)
typename = cls.__name__
if ' ' in typename: if ' ' in typename:
parts = typename.split() parts = typename.split()
typename = '_'.join(parts) typename = '_'.join(parts)
if hasattr(self, 'repr_' + typename):
return getattr(self, 'repr_' + typename)(x, level) method = getattr(self, 'repr_' + typename, None)
else: if method:
return self.repr_instance(x, level) # 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): def _join(self, pieces, level):
if self.indent is None: if self.indent is None:

View File

@ -580,6 +580,50 @@ class ReprTests(unittest.TestCase):
with self.assertRaisesRegex(expected_error, expected_msg): with self.assertRaisesRegex(expected_error, expected_msg):
r.repr(test_object) 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): def write_file(path, text):
with open(path, 'w', encoding='ASCII') as fp: with open(path, 'w', encoding='ASCII') as fp:
fp.write(text) fp.write(text)

View File

@ -0,0 +1 @@
Fixed a bug in ``reprlib.repr`` where it incorrectly called the repr method on shadowed Python built-in types.