bpo-37691: Let math.dist() accept sequences and iterables for coordinates (GH-14975) (GH-14984)

(cherry picked from commit 6b5f1b496f0b20144592b640b9c975df43a29eb0)

Co-authored-by: Raymond Hettinger <rhettinger@users.noreply.github.com>
diff --git a/Modules/clinic/mathmodule.c.h b/Modules/clinic/mathmodule.c.h
index 966b99b..84561b9 100644
--- a/Modules/clinic/mathmodule.c.h
+++ b/Modules/clinic/mathmodule.c.h
@@ -297,8 +297,8 @@
 "\n"
 "Return the Euclidean distance between two points p and q.\n"
 "\n"
-"The points should be specified as tuples of coordinates.\n"
-"Both tuples must be the same size.\n"
+"The points should be specified as sequences (or iterables) of\n"
+"coordinates.  Both inputs must have the same dimension.\n"
 "\n"
 "Roughly equivalent to:\n"
 "    sqrt(sum((px - qx) ** 2.0 for px, qx in zip(p, q)))");
@@ -319,15 +319,7 @@
     if (!_PyArg_CheckPositional("dist", nargs, 2, 2)) {
         goto exit;
     }
-    if (!PyTuple_Check(args[0])) {
-        _PyArg_BadArgument("dist", 1, "tuple", args[0]);
-        goto exit;
-    }
     p = args[0];
-    if (!PyTuple_Check(args[1])) {
-        _PyArg_BadArgument("dist", 2, "tuple", args[1]);
-        goto exit;
-    }
     q = args[1];
     return_value = math_dist_impl(module, p, q);
 
@@ -720,4 +712,4 @@
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=0eb1e76a769cdd30 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=f93cfe13ab2fdb4e input=a9049054013a1b77]*/
diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c
index dd596c8..4e97337 100644
--- a/Modules/mathmodule.c
+++ b/Modules/mathmodule.c
@@ -2418,14 +2418,14 @@
 /*[clinic input]
 math.dist
 
-    p: object(subclass_of='&PyTuple_Type')
-    q: object(subclass_of='&PyTuple_Type')
+    p: object
+    q: object
     /
 
 Return the Euclidean distance between two points p and q.
 
-The points should be specified as tuples of coordinates.
-Both tuples must be the same size.
+The points should be specified as sequences (or iterables) of
+coordinates.  Both inputs must have the same dimension.
 
 Roughly equivalent to:
     sqrt(sum((px - qx) ** 2.0 for px, qx in zip(p, q)))
@@ -2433,16 +2433,34 @@
 
 static PyObject *
 math_dist_impl(PyObject *module, PyObject *p, PyObject *q)
-/*[clinic end generated code: output=56bd9538d06bbcfe input=937122eaa5f19272]*/
+/*[clinic end generated code: output=56bd9538d06bbcfe input=74e85e1b6092e68e]*/
 {
     PyObject *item;
     double max = 0.0;
     double x, px, qx, result;
     Py_ssize_t i, m, n;
-    int found_nan = 0;
+    int found_nan = 0, p_allocated = 0, q_allocated = 0;
     double diffs_on_stack[NUM_STACK_ELEMS];
     double *diffs = diffs_on_stack;
 
+    if (!PyTuple_Check(p)) {
+        p = PySequence_Tuple(p);
+        if (p == NULL) {
+            return NULL;
+        }
+        p_allocated = 1;
+    }
+    if (!PyTuple_Check(q)) {
+        q = PySequence_Tuple(q);
+        if (q == NULL) {
+            if (p_allocated) {
+                Py_DECREF(p);
+            }
+            return NULL;
+        }
+        q_allocated = 1;
+    }
+
     m = PyTuple_GET_SIZE(p);
     n = PyTuple_GET_SIZE(q);
     if (m != n) {
@@ -2473,12 +2491,24 @@
     if (diffs != diffs_on_stack) {
         PyObject_Free(diffs);
     }
+    if (p_allocated) {
+        Py_DECREF(p);
+    }
+    if (q_allocated) {
+        Py_DECREF(q);
+    }
     return PyFloat_FromDouble(result);
 
   error_exit:
     if (diffs != diffs_on_stack) {
         PyObject_Free(diffs);
     }
+    if (p_allocated) {
+        Py_DECREF(p);
+    }
+    if (q_allocated) {
+        Py_DECREF(q);
+    }
     return NULL;
 }