summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorGeorge Pittock <66332098+georgepittock@users.noreply.github.com>2024-10-17 16:34:37 (GMT)
committerGitHub <noreply@github.com>2024-10-17 16:34:37 (GMT)
commit04d6dd23e2d8a3132772cf7ce928676e26313585 (patch)
tree269d2986486dbb4ccf0a1757129702040c34138c /Lib
parentad3eac1963a5f195ef9b2c1dbb5e44fa3cce4c72 (diff)
downloadcpython-04d6dd23e2d8a3132772cf7ce928676e26313585.zip
cpython-04d6dd23e2d8a3132772cf7ce928676e26313585.tar.gz
cpython-04d6dd23e2d8a3132772cf7ce928676e26313585.tar.bz2
gh-113570: reprlib.repr does not use builtin __repr__ for reshadowed builtins (GH-113577)
Diffstat (limited to 'Lib')
-rw-r--r--Lib/reprlib.py31
-rw-r--r--Lib/test/test_reprlib.py44
2 files changed, 70 insertions, 5 deletions
diff --git a/Lib/reprlib.py b/Lib/reprlib.py
index 05bb1a0..19dbe3a 100644
--- a/Lib/reprlib.py
+++ b/Lib/reprlib.py
@@ -36,6 +36,17 @@ def recursive_repr(fillvalue='...'):
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,
@@ -60,14 +71,24 @@ class Repr:
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:
diff --git a/Lib/test/test_reprlib.py b/Lib/test/test_reprlib.py
index 3e93b56..ffeb1fb 100644
--- a/Lib/test/test_reprlib.py
+++ b/Lib/test/test_reprlib.py
@@ -580,6 +580,50 @@ class ReprTests(unittest.TestCase):
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)