bpo-32499: Add dataclasses.is_dataclass(obj), which returns True if obj is a dataclass or an instance of one. (#5113) 

diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py
index eaaed63..b4786bf 100644
--- a/Lib/dataclasses.py
+++ b/Lib/dataclasses.py
@@ -16,6 +16,7 @@
            'astuple',
            'make_dataclass',
            'replace',
+           'is_dataclass',
            ]
 
 # Raised when an attempt is made to modify a frozen class.
@@ -615,11 +616,17 @@
     return tuple(f for f in fields.values() if f._field_type is _FIELD)
 
 
-def _isdataclass(obj):
+def _is_dataclass_instance(obj):
     """Returns True if obj is an instance of a dataclass."""
     return not isinstance(obj, type) and hasattr(obj, _MARKER)
 
 
+def is_dataclass(obj):
+    """Returns True if obj is a dataclass or an instance of a
+    dataclass."""
+    return hasattr(obj, _MARKER)
+
+
 def asdict(obj, *, dict_factory=dict):
     """Return the fields of a dataclass instance as a new dictionary mapping
     field names to field values.
@@ -639,12 +646,12 @@
     dataclass instances. This will also look into built-in containers:
     tuples, lists, and dicts.
     """
-    if not _isdataclass(obj):
+    if not _is_dataclass_instance(obj):
         raise TypeError("asdict() should be called on dataclass instances")
     return _asdict_inner(obj, dict_factory)
 
 def _asdict_inner(obj, dict_factory):
-    if _isdataclass(obj):
+    if _is_dataclass_instance(obj):
         result = []
         for f in fields(obj):
             value = _asdict_inner(getattr(obj, f.name), dict_factory)
@@ -678,12 +685,12 @@
     tuples, lists, and dicts.
     """
 
-    if not _isdataclass(obj):
+    if not _is_dataclass_instance(obj):
         raise TypeError("astuple() should be called on dataclass instances")
     return _astuple_inner(obj, tuple_factory)
 
 def _astuple_inner(obj, tuple_factory):
-    if _isdataclass(obj):
+    if _is_dataclass_instance(obj):
         result = []
         for f in fields(obj):
             value = _astuple_inner(getattr(obj, f.name), tuple_factory)
@@ -751,7 +758,7 @@
     # We're going to mutate 'changes', but that's okay because it's a new
     #  dict, even if called with 'replace(obj, **my_changes)'.
 
-    if not _isdataclass(obj):
+    if not _is_dataclass_instance(obj):
         raise TypeError("replace() should be called on dataclass instances")
 
     # It's an error to have init=False fields in 'changes'.