bpo-43080: pprint for dataclass instances (GH-24389)

* Added pprint support for dataclass instances which don't have a custom __repr__.
diff --git a/Lib/pprint.py b/Lib/pprint.py
index b45cfdd..13819f3 100644
--- a/Lib/pprint.py
+++ b/Lib/pprint.py
@@ -35,6 +35,7 @@
 """
 
 import collections as _collections
+import dataclasses as _dataclasses
 import re
 import sys as _sys
 import types as _types
@@ -178,8 +179,26 @@ def _format(self, object, stream, indent, allowance, context, level):
                 p(self, object, stream, indent, allowance, context, level + 1)
                 del context[objid]
                 return
+            elif (_dataclasses.is_dataclass(object) and
+                  not isinstance(object, type) and
+                  object.__dataclass_params__.repr and
+                  # Check dataclass has generated repr method.
+                  hasattr(object.__repr__, "__wrapped__") and
+                  "__create_fn__" in object.__repr__.__wrapped__.__qualname__):
+                context[objid] = 1
+                self._pprint_dataclass(object, stream, indent, allowance, context, level + 1)
+                del context[objid]
+                return
         stream.write(rep)
 
+    def _pprint_dataclass(self, object, stream, indent, allowance, context, level):
+        cls_name = object.__class__.__name__
+        indent += len(cls_name) + 1
+        items = [(f.name, getattr(object, f.name)) for f in _dataclasses.fields(object) if f.repr]
+        stream.write(cls_name + '(')
+        self._format_namespace_items(items, stream, indent, allowance, context, level)
+        stream.write(')')
+
     _dispatch = {}
 
     def _pprint_dict(self, object, stream, indent, allowance, context, level):
@@ -346,21 +365,9 @@ def _pprint_simplenamespace(self, object, stream, indent, allowance, context, le
         else:
             cls_name = object.__class__.__name__
         indent += len(cls_name) + 1
-        delimnl = ',\n' + ' ' * indent
         items = object.__dict__.items()
-        last_index = len(items) - 1
-
         stream.write(cls_name + '(')
-        for i, (key, ent) in enumerate(items):
-            stream.write(key)
-            stream.write('=')
-
-            last = i == last_index
-            self._format(ent, stream, indent + len(key) + 1,
-                         allowance if last else 1,
-                         context, level)
-            if not last:
-                stream.write(delimnl)
+        self._format_namespace_items(items, stream, indent, allowance, context, level)
         stream.write(')')
 
     _dispatch[_types.SimpleNamespace.__repr__] = _pprint_simplenamespace
@@ -382,6 +389,25 @@ def _format_dict_items(self, items, stream, indent, allowance, context,
             if not last:
                 write(delimnl)
 
+    def _format_namespace_items(self, items, stream, indent, allowance, context, level):
+        write = stream.write
+        delimnl = ',\n' + ' ' * indent
+        last_index = len(items) - 1
+        for i, (key, ent) in enumerate(items):
+            last = i == last_index
+            write(key)
+            write('=')
+            if id(ent) in context:
+                # Special-case representation of recursion to match standard
+                # recursive dataclass repr.
+                write("...")
+            else:
+                self._format(ent, stream, indent + len(key) + 1,
+                             allowance if last else 1,
+                             context, level)
+            if not last:
+                write(delimnl)
+
     def _format_items(self, items, stream, indent, allowance, context, level):
         write = stream.write
         indent += self._indent_per_level