Skip to content

Commit 82b34c5

Browse files
committed
Issue #5211: Fix complex type to avoid implicit calls to
complex.__coerce__. Thanks Meador Inge for the patch.
1 parent 51f1204 commit 82b34c5

4 files changed

Lines changed: 109 additions & 30 deletions

File tree

‎Doc/reference/datamodel.rst‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2271,13 +2271,15 @@ will not be supported.
22712271
*
22722272

22732273
In the current implementation, the built-in numeric types :class:`int`,
2274-
:class:`long` and :class:`float` do not use coercion; the type :class:`complex`
2275-
however does use coercion for binary operators and rich comparisons, despite
2276-
the above rules. The difference can become apparent when subclassing these
2277-
types. Over time, the type :class:`complex` may be fixed to avoid coercion.
2274+
:class:`long`, :class:`float`, and :class:`complex` do not use coercion.
22782275
All these types implement a :meth:`__coerce__` method, for use by the built-in
22792276
:func:`coerce` function.
22802277

2278+
.. versionchanged:: 2.7
2279+
2280+
The complex type no longer makes implicit calls to the :meth:`__coerce__`
2281+
method for mixed-type binary arithmetic operations.
2282+
22812283

22822284
.. _context-managers:
22832285

‎Lib/test/test_complex.py‎

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,61 @@ def __complex__(self):
358358
self.assertAlmostEqual(complex(complex1(1j)), 2j)
359359
self.assertRaises(TypeError, complex, complex2(1j))
360360

361+
def test_subclass(self):
362+
class xcomplex(complex):
363+
def __add__(self,other):
364+
return xcomplex(complex(self) + other)
365+
__radd__ = __add__
366+
367+
def __sub__(self,other):
368+
return xcomplex(complex(self) + other)
369+
__rsub__ = __sub__
370+
371+
def __mul__(self,other):
372+
return xcomplex(complex(self) * other)
373+
__rmul__ = __mul__
374+
375+
def __div__(self,other):
376+
return xcomplex(complex(self) / other)
377+
378+
def __rdiv__(self,other):
379+
return xcomplex(other / complex(self))
380+
381+
__truediv__ = __div__
382+
__rtruediv__ = __rdiv__
383+
384+
def __floordiv__(self,other):
385+
return xcomplex(complex(self) // other)
386+
387+
def __rfloordiv__(self,other):
388+
return xcomplex(other // complex(self))
389+
390+
def __pow__(self,other):
391+
return xcomplex(complex(self) ** other)
392+
393+
def __rpow__(self,other):
394+
return xcomplex(other ** complex(self) )
395+
396+
def __mod__(self,other):
397+
return xcomplex(complex(self) % other)
398+
399+
def __rmod__(self,other):
400+
return xcomplex(other % complex(self))
401+
402+
infix_binops = ('+', '-', '*', '**', '%', '//', '/')
403+
xcomplex_values = (xcomplex(1), xcomplex(123.0),
404+
xcomplex(-10+2j), xcomplex(3+187j),
405+
xcomplex(3-78j))
406+
test_values = (1, 123.0, 10-19j, xcomplex(1+2j),
407+
xcomplex(1+87j), xcomplex(10+90j))
408+
409+
for op in infix_binops:
410+
for x in xcomplex_values:
411+
for y in test_values:
412+
a = 'x %s y' % op
413+
b = 'y %s x' % op
414+
self.assertTrue(type(eval(a)) is type(eval(b)) is xcomplex)
415+
361416
def test_hash(self):
362417
for x in xrange(-30, 30):
363418
self.assertEqual(hash(x), hash(complex(x, 0)))

‎Misc/NEWS‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ What's New in Python 2.7 alpha 4?
1212
Core and Builtins
1313
-----------------
1414

15+
- Issue #5211: the complex type no longer uses implicit coercion in
16+
mixed-type binary arithmetic operations.
17+
1518
Library
1619
-------
1720

‎Objects/complexobject.c‎

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -513,43 +513,54 @@ to_complex(PyObject **pobj, Py_complex *pc)
513513

514514

515515
static PyObject *
516-
complex_add(PyComplexObject *v, PyComplexObject *w)
516+
complex_add(PyObject *v, PyObject *w)
517517
{
518518
Py_complex result;
519+
Py_complex a, b;
520+
TO_COMPLEX(v, a);
521+
TO_COMPLEX(w, b);
519522
PyFPE_START_PROTECT("complex_add", return 0)
520-
result = c_sum(v->cval,w->cval);
523+
result = c_sum(a, b);
521524
PyFPE_END_PROTECT(result)
522525
return PyComplex_FromCComplex(result);
523526
}
524527

525528
static PyObject *
526-
complex_sub(PyComplexObject *v, PyComplexObject *w)
529+
complex_sub(PyObject *v, PyObject *w)
527530
{
528-
Py_complex result;
531+
Py_complex result;
532+
Py_complex a, b;
533+
TO_COMPLEX(v, a);
534+
TO_COMPLEX(w, b);;
529535
PyFPE_START_PROTECT("complex_sub", return 0)
530-
result = c_diff(v->cval,w->cval);
536+
result = c_diff(a, b);
531537
PyFPE_END_PROTECT(result)
532538
return PyComplex_FromCComplex(result);
533539
}
534540

535541
static PyObject *
536-
complex_mul(PyComplexObject *v, PyComplexObject *w)
542+
complex_mul(PyObject *v, PyObject *w)
537543
{
538544
Py_complex result;
545+
Py_complex a, b;
546+
TO_COMPLEX(v, a);
547+
TO_COMPLEX(w, b);
539548
PyFPE_START_PROTECT("complex_mul", return 0)
540-
result = c_prod(v->cval,w->cval);
549+
result = c_prod(a, b);
541550
PyFPE_END_PROTECT(result)
542551
return PyComplex_FromCComplex(result);
543552
}
544553

545554
static PyObject *
546-
complex_div(PyComplexObject *v, PyComplexObject *w)
555+
complex_div(PyObject *v, PyObject *w)
547556
{
548557
Py_complex quot;
549-
558+
Py_complex a, b;
559+
TO_COMPLEX(v, a);
560+
TO_COMPLEX(w, b);
550561
PyFPE_START_PROTECT("complex_div", return 0)
551562
errno = 0;
552-
quot = c_quot(v->cval,w->cval);
563+
quot = c_quot(a, b);
553564
PyFPE_END_PROTECT(quot)
554565
if (errno == EDOM) {
555566
PyErr_SetString(PyExc_ZeroDivisionError, "complex division");
@@ -559,18 +570,20 @@ complex_div(PyComplexObject *v, PyComplexObject *w)
559570
}
560571

561572
static PyObject *
562-
complex_classic_div(PyComplexObject *v, PyComplexObject *w)
573+
complex_classic_div(PyObject *v, PyObject *w)
563574
{
564575
Py_complex quot;
565-
576+
Py_complex a, b;
577+
TO_COMPLEX(v, a);
578+
TO_COMPLEX(w, b);
566579
if (Py_DivisionWarningFlag >= 2 &&
567580
PyErr_Warn(PyExc_DeprecationWarning,
568581
"classic complex division") < 0)
569582
return NULL;
570583

571584
PyFPE_START_PROTECT("complex_classic_div", return 0)
572585
errno = 0;
573-
quot = c_quot(v->cval,w->cval);
586+
quot = c_quot(a, b);
574587
PyFPE_END_PROTECT(quot)
575588
if (errno == EDOM) {
576589
PyErr_SetString(PyExc_ZeroDivisionError, "complex division");
@@ -580,47 +593,51 @@ complex_classic_div(PyComplexObject *v, PyComplexObject *w)
580593
}
581594

582595
static PyObject *
583-
complex_remainder(PyComplexObject *v, PyComplexObject *w)
596+
complex_remainder(PyObject *v, PyObject *w)
584597
{
585598
Py_complex div, mod;
586-
599+
Py_complex a, b;
600+
TO_COMPLEX(v, a);
601+
TO_COMPLEX(w, b);
587602
if (PyErr_Warn(PyExc_DeprecationWarning,
588603
"complex divmod(), // and % are deprecated") < 0)
589604
return NULL;
590605

591606
errno = 0;
592-
div = c_quot(v->cval,w->cval); /* The raw divisor value. */
607+
div = c_quot(a, b); /* The raw divisor value. */
593608
if (errno == EDOM) {
594609
PyErr_SetString(PyExc_ZeroDivisionError, "complex remainder");
595610
return NULL;
596611
}
597612
div.real = floor(div.real); /* Use the floor of the real part. */
598613
div.imag = 0.0;
599-
mod = c_diff(v->cval, c_prod(w->cval, div));
614+
mod = c_diff(a, c_prod(b, div));
600615

601616
return PyComplex_FromCComplex(mod);
602617
}
603618

604619

605620
static PyObject *
606-
complex_divmod(PyComplexObject *v, PyComplexObject *w)
621+
complex_divmod(PyObject *v, PyObject *w)
607622
{
608623
Py_complex div, mod;
609624
PyObject *d, *m, *z;
610-
625+
Py_complex a, b;
626+
TO_COMPLEX(v, a);
627+
TO_COMPLEX(w, b);
611628
if (PyErr_Warn(PyExc_DeprecationWarning,
612629
"complex divmod(), // and % are deprecated") < 0)
613630
return NULL;
614631

615632
errno = 0;
616-
div = c_quot(v->cval,w->cval); /* The raw divisor value. */
633+
div = c_quot(a, b); /* The raw divisor value. */
617634
if (errno == EDOM) {
618635
PyErr_SetString(PyExc_ZeroDivisionError, "complex divmod()");
619636
return NULL;
620637
}
621638
div.real = floor(div.real); /* Use the floor of the real part. */
622639
div.imag = 0.0;
623-
mod = c_diff(v->cval, c_prod(w->cval, div));
640+
mod = c_diff(a, c_prod(b, div));
624641
d = PyComplex_FromCComplex(div);
625642
m = PyComplex_FromCComplex(mod);
626643
z = PyTuple_Pack(2, d, m);
@@ -638,8 +655,7 @@ complex_pow(PyObject *v, PyObject *w, PyObject *z)
638655
Py_complex a, b;
639656
TO_COMPLEX(v, a);
640657
TO_COMPLEX(w, b);
641-
642-
if (z!=Py_None) {
658+
if (z!=Py_None) {
643659
PyErr_SetString(PyExc_ValueError, "complex modulo");
644660
return NULL;
645661
}
@@ -668,10 +684,12 @@ complex_pow(PyObject *v, PyObject *w, PyObject *z)
668684
}
669685

670686
static PyObject *
671-
complex_int_div(PyComplexObject *v, PyComplexObject *w)
687+
complex_int_div(PyObject *v, PyObject *w)
672688
{
673689
PyObject *t, *r;
674-
690+
Py_complex a, b;
691+
TO_COMPLEX(v, a);
692+
TO_COMPLEX(w, b);
675693
if (PyErr_Warn(PyExc_DeprecationWarning,
676694
"complex divmod(), // and % are deprecated") < 0)
677695
return NULL;
@@ -1282,7 +1300,8 @@ PyTypeObject PyComplex_Type = {
12821300
PyObject_GenericGetAttr, /* tp_getattro */
12831301
0, /* tp_setattro */
12841302
0, /* tp_as_buffer */
1285-
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
1303+
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
1304+
Py_TPFLAGS_BASETYPE, /* tp_flags */
12861305
complex_doc, /* tp_doc */
12871306
0, /* tp_traverse */
12881307
0, /* tp_clear */

0 commit comments

Comments
 (0)