From 74f78d299263e4abd497ff9da2dd52811ca7b0a1 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Fri, 26 Jul 2019 16:56:53 -0700 Subject: [PATCH 1/7] Allow sequence arguments to math_dist() --- Modules/mathmodule.c | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 4c1dbbe15ecc22..ecb1c894c42ced 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2411,12 +2411,10 @@ vector_norm(Py_ssize_t n, double *vec, double max, int found_nan) } for (i=0 ; i < n ; i++) { x = vec[i]; - assert(Py_IS_FINITE(x) && fabs(x) <= max); x /= max; x = x*x; oldcsum = csum; csum += x; - assert(csum >= x); frac += (oldcsum - csum) + x; } return max * sqrt(csum - 1.0 + frac); @@ -2427,8 +2425,8 @@ vector_norm(Py_ssize_t n, double *vec, double max, int found_nan) /*[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. @@ -2448,10 +2446,28 @@ math_dist_impl(PyObject *module, PyObject *p, PyObject *q) 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) { @@ -2482,12 +2498,24 @@ math_dist_impl(PyObject *module, PyObject *p, PyObject *q) 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; } From 634c470af911d576b56dd6f97c0e43391de10431 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Fri, 26 Jul 2019 17:13:19 -0700 Subject: [PATCH 2/7] Update the argument clinic --- Modules/clinic/mathmodule.c.h | 10 +--------- Modules/mathmodule.c | 2 +- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/Modules/clinic/mathmodule.c.h b/Modules/clinic/mathmodule.c.h index 966b99b6a36900..fceeaa6ac3d250 100644 --- a/Modules/clinic/mathmodule.c.h +++ b/Modules/clinic/mathmodule.c.h @@ -319,15 +319,7 @@ math_dist(PyObject *module, PyObject *const *args, Py_ssize_t nargs) 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 @@ math_comb(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=0eb1e76a769cdd30 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=4125131c06ce153e input=a9049054013a1b77]*/ diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index ecb1c894c42ced..4a772803ac5ee8 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2440,7 +2440,7 @@ Roughly equivalent to: 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=8c83c07c7a524664]*/ { PyObject *item; double max = 0.0; From a9e3d42d5c2f196a92eb12ebba88bdacc71b30ac Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Fri, 26 Jul 2019 17:18:44 -0700 Subject: [PATCH 3/7] Update tests --- Lib/test/test_math.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 567a5c694c152c..c237bc1942e655 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -833,6 +833,10 @@ def testDist(self): sqrt(sum((px - qx) ** 2.0 for px, qx in zip(p, q))) ) + # Test non-tuple inputs + self.assertEqual(dist([1.0, 2.0, 3.0], [4.0, 2.0, -1.0]), 5.0) + self.assertEqual(dist(iter([1.0, 2.0, 3.0]), iter([4.0, 2.0, -1.0])), 5.0) + # Test allowable types (those with __float__) self.assertEqual(dist((14.0, 1.0), (2.0, -4.0)), 13.0) self.assertEqual(dist((14, 1), (2, -4)), 13) @@ -873,8 +877,6 @@ class T(tuple): dist((1, 2, 3), (4, 5, 6), (7, 8, 9)) with self.assertRaises(TypeError): # Scalars not allowed dist(1, 2) - with self.assertRaises(TypeError): # Lists not allowed - dist([1, 2, 3], [4, 5, 6]) with self.assertRaises(TypeError): # Reject values without __float__ dist((1.1, 'string', 2.2), (1, 2, 3)) with self.assertRaises(ValueError): # Check dimension agree From 3b18d772f60449b1bf3bd66d3c53c709174fd9ac Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Fri, 26 Jul 2019 17:21:22 -0700 Subject: [PATCH 4/7] Update the argument clinic --- Modules/mathmodule.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 4a772803ac5ee8..8df40d43b4f3ee 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2431,8 +2431,8 @@ math.dist 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 should should have the same dimension. Roughly equivalent to: sqrt(sum((px - qx) ** 2.0 for px, qx in zip(p, q))) @@ -2466,8 +2466,8 @@ math_dist_impl(PyObject *module, PyObject *p, PyObject *q) return NULL; } q_allocated = 1; - } - + } + m = PyTuple_GET_SIZE(p); n = PyTuple_GET_SIZE(q); if (m != n) { From 2c341ca8a70ce81cde210a3312a5e1919424de9f Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Fri, 26 Jul 2019 17:30:27 -0700 Subject: [PATCH 5/7] Update the documentation --- Doc/library/math.rst | 3 ++- Modules/clinic/mathmodule.c.h | 6 +++--- Modules/mathmodule.c | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Doc/library/math.rst b/Doc/library/math.rst index be953cfe959948..43eaba935a164a 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -400,7 +400,8 @@ Trigonometric functions .. function:: dist(p, q) Return the Euclidean distance between two points *p* and *q*, each - given as a tuple of coordinates. The two tuples must be the same size. + given as a sequence (or iterable) of coordinates. The two points + must have the same dimension. Roughly equivalent to:: diff --git a/Modules/clinic/mathmodule.c.h b/Modules/clinic/mathmodule.c.h index fceeaa6ac3d250..84561b955787b7 100644 --- a/Modules/clinic/mathmodule.c.h +++ b/Modules/clinic/mathmodule.c.h @@ -297,8 +297,8 @@ PyDoc_STRVAR(math_dist__doc__, "\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)))"); @@ -712,4 +712,4 @@ math_comb(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=4125131c06ce153e input=a9049054013a1b77]*/ +/*[clinic end generated code: output=f93cfe13ab2fdb4e input=a9049054013a1b77]*/ diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 8df40d43b4f3ee..43a899f1414193 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2432,7 +2432,7 @@ math.dist Return the Euclidean distance between two points p and q. The points should be specified as sequences (or iterables) of -coordinates. Both inputs should should have the same dimension. +coordinates. Both inputs must have the same dimension. Roughly equivalent to: sqrt(sum((px - qx) ** 2.0 for px, qx in zip(p, q))) @@ -2440,7 +2440,7 @@ Roughly equivalent to: static PyObject * math_dist_impl(PyObject *module, PyObject *p, PyObject *q) -/*[clinic end generated code: output=56bd9538d06bbcfe input=8c83c07c7a524664]*/ +/*[clinic end generated code: output=56bd9538d06bbcfe input=74e85e1b6092e68e]*/ { PyObject *item; double max = 0.0; From 95df88c1fb7dccd21829f028ee5845c50cfbec2d Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Fri, 26 Jul 2019 22:30:09 -0700 Subject: [PATCH 6/7] Add blurb --- .../next/Library/2019-07-26-22-30-01.bpo-37691.1Li3rx.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2019-07-26-22-30-01.bpo-37691.1Li3rx.rst diff --git a/Misc/NEWS.d/next/Library/2019-07-26-22-30-01.bpo-37691.1Li3rx.rst b/Misc/NEWS.d/next/Library/2019-07-26-22-30-01.bpo-37691.1Li3rx.rst new file mode 100644 index 00000000000000..048478c008e806 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-26-22-30-01.bpo-37691.1Li3rx.rst @@ -0,0 +1,2 @@ +Let math.dist() accept coordinates as sequences (or iterables) rather than +just tuples. From 430307304cc5fbaf4c0534cd055a1a1e56cfb408 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Fri, 26 Jul 2019 22:35:08 -0700 Subject: [PATCH 7/7] Restore assertions --- Modules/mathmodule.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 43a899f1414193..e1b46ec384a373 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2411,10 +2411,12 @@ vector_norm(Py_ssize_t n, double *vec, double max, int found_nan) } for (i=0 ; i < n ; i++) { x = vec[i]; + assert(Py_IS_FINITE(x) && fabs(x) <= max); x /= max; x = x*x; oldcsum = csum; csum += x; + assert(csum >= x); frac += (oldcsum - csum) + x; } return max * sqrt(csum - 1.0 + frac);