From 82b34c5dbebdd4c39dd310056b20e89d2bed63ed Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sun, 21 Feb 2010 12:57:35 +0000 Subject: [PATCH] Issue #5211: Fix complex type to avoid implicit calls to complex.__coerce__. Thanks Meador Inge for the patch. --- Doc/reference/datamodel.rst | 10 +++--- Lib/test/test_complex.py | 55 ++++++++++++++++++++++++++++ Misc/NEWS | 3 ++ Objects/complexobject.c | 71 +++++++++++++++++++++++-------------- 4 files changed, 109 insertions(+), 30 deletions(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 0dc41878d4..25427044e2 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2271,13 +2271,15 @@ will not be supported. * In the current implementation, the built-in numeric types :class:`int`, - :class:`long` and :class:`float` do not use coercion; the type :class:`complex` - however does use coercion for binary operators and rich comparisons, despite - the above rules. The difference can become apparent when subclassing these - types. Over time, the type :class:`complex` may be fixed to avoid coercion. + :class:`long`, :class:`float`, and :class:`complex` do not use coercion. All these types implement a :meth:`__coerce__` method, for use by the built-in :func:`coerce` function. + .. versionchanged:: 2.7 + + The complex type no longer makes implicit calls to the :meth:`__coerce__` + method for mixed-type binary arithmetic operations. + .. _context-managers: diff --git a/Lib/test/test_complex.py b/Lib/test/test_complex.py index 199caf08fd..2cf9a9e15d 100644 --- a/Lib/test/test_complex.py +++ b/Lib/test/test_complex.py @@ -358,6 +358,61 @@ class ComplexTest(unittest.TestCase): self.assertAlmostEqual(complex(complex1(1j)), 2j) self.assertRaises(TypeError, complex, complex2(1j)) + def test_subclass(self): + class xcomplex(complex): + def __add__(self,other): + return xcomplex(complex(self) + other) + __radd__ = __add__ + + def __sub__(self,other): + return xcomplex(complex(self) + other) + __rsub__ = __sub__ + + def __mul__(self,other): + return xcomplex(complex(self) * other) + __rmul__ = __mul__ + + def __div__(self,other): + return xcomplex(complex(self) / other) + + def __rdiv__(self,other): + return xcomplex(other / complex(self)) + + __truediv__ = __div__ + __rtruediv__ = __rdiv__ + + def __floordiv__(self,other): + return xcomplex(complex(self) // other) + + def __rfloordiv__(self,other): + return xcomplex(other // complex(self)) + + def __pow__(self,other): + return xcomplex(complex(self) ** other) + + def __rpow__(self,other): + return xcomplex(other ** complex(self) ) + + def __mod__(self,other): + return xcomplex(complex(self) % other) + + def __rmod__(self,other): + return xcomplex(other % complex(self)) + + infix_binops = ('+', '-', '*', '**', '%', '//', '/') + xcomplex_values = (xcomplex(1), xcomplex(123.0), + xcomplex(-10+2j), xcomplex(3+187j), + xcomplex(3-78j)) + test_values = (1, 123.0, 10-19j, xcomplex(1+2j), + xcomplex(1+87j), xcomplex(10+90j)) + + for op in infix_binops: + for x in xcomplex_values: + for y in test_values: + a = 'x %s y' % op + b = 'y %s x' % op + self.assertTrue(type(eval(a)) is type(eval(b)) is xcomplex) + def test_hash(self): for x in xrange(-30, 30): self.assertEqual(hash(x), hash(complex(x, 0))) diff --git a/Misc/NEWS b/Misc/NEWS index 57506050a6..9211137610 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -12,6 +12,9 @@ What's New in Python 2.7 alpha 4? Core and Builtins ----------------- +- Issue #5211: the complex type no longer uses implicit coercion in + mixed-type binary arithmetic operations. + Library ------- diff --git a/Objects/complexobject.c b/Objects/complexobject.c index 3317106f18..da2075a386 100644 --- a/Objects/complexobject.c +++ b/Objects/complexobject.c @@ -513,43 +513,54 @@ to_complex(PyObject **pobj, Py_complex *pc) static PyObject * -complex_add(PyComplexObject *v, PyComplexObject *w) +complex_add(PyObject *v, PyObject *w) { Py_complex result; + Py_complex a, b; + TO_COMPLEX(v, a); + TO_COMPLEX(w, b); PyFPE_START_PROTECT("complex_add", return 0) - result = c_sum(v->cval,w->cval); + result = c_sum(a, b); PyFPE_END_PROTECT(result) return PyComplex_FromCComplex(result); } static PyObject * -complex_sub(PyComplexObject *v, PyComplexObject *w) +complex_sub(PyObject *v, PyObject *w) { - Py_complex result; + Py_complex result; + Py_complex a, b; + TO_COMPLEX(v, a); + TO_COMPLEX(w, b);; PyFPE_START_PROTECT("complex_sub", return 0) - result = c_diff(v->cval,w->cval); + result = c_diff(a, b); PyFPE_END_PROTECT(result) return PyComplex_FromCComplex(result); } static PyObject * -complex_mul(PyComplexObject *v, PyComplexObject *w) +complex_mul(PyObject *v, PyObject *w) { Py_complex result; + Py_complex a, b; + TO_COMPLEX(v, a); + TO_COMPLEX(w, b); PyFPE_START_PROTECT("complex_mul", return 0) - result = c_prod(v->cval,w->cval); + result = c_prod(a, b); PyFPE_END_PROTECT(result) return PyComplex_FromCComplex(result); } static PyObject * -complex_div(PyComplexObject *v, PyComplexObject *w) +complex_div(PyObject *v, PyObject *w) { Py_complex quot; - + Py_complex a, b; + TO_COMPLEX(v, a); + TO_COMPLEX(w, b); PyFPE_START_PROTECT("complex_div", return 0) errno = 0; - quot = c_quot(v->cval,w->cval); + quot = c_quot(a, b); PyFPE_END_PROTECT(quot) if (errno == EDOM) { PyErr_SetString(PyExc_ZeroDivisionError, "complex division"); @@ -559,10 +570,12 @@ complex_div(PyComplexObject *v, PyComplexObject *w) } static PyObject * -complex_classic_div(PyComplexObject *v, PyComplexObject *w) +complex_classic_div(PyObject *v, PyObject *w) { Py_complex quot; - + Py_complex a, b; + TO_COMPLEX(v, a); + TO_COMPLEX(w, b); if (Py_DivisionWarningFlag >= 2 && PyErr_Warn(PyExc_DeprecationWarning, "classic complex division") < 0) @@ -570,7 +583,7 @@ complex_classic_div(PyComplexObject *v, PyComplexObject *w) PyFPE_START_PROTECT("complex_classic_div", return 0) errno = 0; - quot = c_quot(v->cval,w->cval); + quot = c_quot(a, b); PyFPE_END_PROTECT(quot) if (errno == EDOM) { PyErr_SetString(PyExc_ZeroDivisionError, "complex division"); @@ -580,47 +593,51 @@ complex_classic_div(PyComplexObject *v, PyComplexObject *w) } static PyObject * -complex_remainder(PyComplexObject *v, PyComplexObject *w) +complex_remainder(PyObject *v, PyObject *w) { Py_complex div, mod; - + Py_complex a, b; + TO_COMPLEX(v, a); + TO_COMPLEX(w, b); if (PyErr_Warn(PyExc_DeprecationWarning, "complex divmod(), // and % are deprecated") < 0) return NULL; errno = 0; - div = c_quot(v->cval,w->cval); /* The raw divisor value. */ + div = c_quot(a, b); /* The raw divisor value. */ if (errno == EDOM) { PyErr_SetString(PyExc_ZeroDivisionError, "complex remainder"); return NULL; } div.real = floor(div.real); /* Use the floor of the real part. */ div.imag = 0.0; - mod = c_diff(v->cval, c_prod(w->cval, div)); + mod = c_diff(a, c_prod(b, div)); return PyComplex_FromCComplex(mod); } static PyObject * -complex_divmod(PyComplexObject *v, PyComplexObject *w) +complex_divmod(PyObject *v, PyObject *w) { Py_complex div, mod; PyObject *d, *m, *z; - + Py_complex a, b; + TO_COMPLEX(v, a); + TO_COMPLEX(w, b); if (PyErr_Warn(PyExc_DeprecationWarning, "complex divmod(), // and % are deprecated") < 0) return NULL; errno = 0; - div = c_quot(v->cval,w->cval); /* The raw divisor value. */ + div = c_quot(a, b); /* The raw divisor value. */ if (errno == EDOM) { PyErr_SetString(PyExc_ZeroDivisionError, "complex divmod()"); return NULL; } div.real = floor(div.real); /* Use the floor of the real part. */ div.imag = 0.0; - mod = c_diff(v->cval, c_prod(w->cval, div)); + mod = c_diff(a, c_prod(b, div)); d = PyComplex_FromCComplex(div); m = PyComplex_FromCComplex(mod); z = PyTuple_Pack(2, d, m); @@ -638,8 +655,7 @@ complex_pow(PyObject *v, PyObject *w, PyObject *z) Py_complex a, b; TO_COMPLEX(v, a); TO_COMPLEX(w, b); - - if (z!=Py_None) { + if (z!=Py_None) { PyErr_SetString(PyExc_ValueError, "complex modulo"); return NULL; } @@ -668,10 +684,12 @@ complex_pow(PyObject *v, PyObject *w, PyObject *z) } static PyObject * -complex_int_div(PyComplexObject *v, PyComplexObject *w) +complex_int_div(PyObject *v, PyObject *w) { PyObject *t, *r; - + Py_complex a, b; + TO_COMPLEX(v, a); + TO_COMPLEX(w, b); if (PyErr_Warn(PyExc_DeprecationWarning, "complex divmod(), // and % are deprecated") < 0) return NULL; @@ -1282,7 +1300,8 @@ PyTypeObject PyComplex_Type = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES | + Py_TPFLAGS_BASETYPE, /* tp_flags */ complex_doc, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ -- 2.50.1