bpo-39994: Fix pprint handling of dict subclasses that override __repr__ (GH-21892)

Co-authored-by: Palak Kumar Jha
diff --git a/Lib/test/test_pprint.py b/Lib/test/test_pprint.py
index cf3e4f0..8ee18e8 100644
--- a/Lib/test/test_pprint.py
+++ b/Lib/test/test_pprint.py
@@ -18,6 +18,10 @@
     def __repr__(self):
         return list.__repr__(self)
 
+class list_custom_repr(list):
+    def __repr__(self):
+        return '*'*len(list.__repr__(self))
+
 class tuple2(tuple):
     pass
 
@@ -25,6 +29,10 @@
     def __repr__(self):
         return tuple.__repr__(self)
 
+class tuple_custom_repr(tuple):
+    def __repr__(self):
+        return '*'*len(tuple.__repr__(self))
+
 class set2(set):
     pass
 
@@ -32,6 +40,10 @@
     def __repr__(self):
         return set.__repr__(self)
 
+class set_custom_repr(set):
+    def __repr__(self):
+        return '*'*len(set.__repr__(self))
+
 class frozenset2(frozenset):
     pass
 
@@ -39,6 +51,10 @@
     def __repr__(self):
         return frozenset.__repr__(self)
 
+class frozenset_custom_repr(frozenset):
+    def __repr__(self):
+        return '*'*len(frozenset.__repr__(self))
+
 class dict2(dict):
     pass
 
@@ -46,6 +62,10 @@
     def __repr__(self):
         return dict.__repr__(self)
 
+class dict_custom_repr(dict):
+    def __repr__(self):
+        return '*'*len(dict.__repr__(self))
+
 class Unorderable:
     def __repr__(self):
         return str(id(self))
@@ -155,7 +175,8 @@
                              "expected not isreadable for %r" % (unreadable,))
 
     def test_same_as_repr(self):
-        # Simple objects, small containers and classes that overwrite __repr__
+        # Simple objects, small containers and classes that override __repr__
+        # to directly call super's __repr__.
         # For those the result should be the same as repr().
         # Ahem.  The docs don't say anything about that -- this appears to
         # be testing an implementation quirk.  Starting in Python 2.5, it's
@@ -187,6 +208,32 @@
                              .replace('\n', ' '), native)
             self.assertEqual(pprint.saferepr(simple), native)
 
+    def test_container_repr_override_called(self):
+        N = 1000
+        # Ensure that __repr__ override is called for subclasses of containers
+
+        for cont in (list_custom_repr(),
+                     list_custom_repr([1,2,3]),
+                     list_custom_repr(range(N)),
+                     tuple_custom_repr(),
+                     tuple_custom_repr([1,2,3]),
+                     tuple_custom_repr(range(N)),
+                     set_custom_repr(),
+                     set_custom_repr([1,2,3]),
+                     set_custom_repr(range(N)),
+                     frozenset_custom_repr(),
+                     frozenset_custom_repr([1,2,3]),
+                     frozenset_custom_repr(range(N)),
+                     dict_custom_repr(),
+                     dict_custom_repr({5: 6}),
+                     dict_custom_repr(zip(range(N),range(N))),
+                    ):
+            native = repr(cont)
+            expected = '*' * len(native)
+            self.assertEqual(pprint.pformat(cont), expected)
+            self.assertEqual(pprint.pformat(cont, width=1, indent=0), expected)
+            self.assertEqual(pprint.saferepr(cont), expected)
+
     def test_basic_line_wrap(self):
         # verify basic line-wrapping operation
         o = {'RPM_cal': 0,