8000 gh-109218: Deprecate weird cases in the complex() constructor · python/cpython@9ec7587 · GitHub
[go: up one dir, main page]

Skip to content

Commit 9ec7587

Browse files
gh-109218: Deprecate weird cases in the complex() constructor
* Passing a string as the "real" keyword argument is now an error; it should only be passed as a single positional argument. * Passing a complex number as the *real* or *imag* argument is now deprecated; it should only be passed as a single positional argument.
1 parent 59630f9 commit 9ec7587

File tree

6 files changed

+185
-59
lines changed

6 files changed

+185
-59
lines changed

Doc/library/functions.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,10 @@ are always available. They are listed here in alphabetical order.
404404
Falls back to :meth:`~object.__index__` if :meth:`~object.__complex__` and
405405
:meth:`~object.__float__` are not defined.
406406

407+
.. deprecated:: 3.14
408+
Passing a complex number as the *real* or *imag* argument is now
409+
deprecated; it should only be passed as a single positional argument.
410+
407411

408412
.. function:: delattr(object, name)
409413

Doc/whatsnew/3.14.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ Optimizations
103103
Deprecated
104104
==========
105105

106+
* Passing a complex number as the *real* or *imag* argument in the
107+
:func:`complex` constructor is now deprecated; it should only be passed
108+
as a single positional argument.
109+
8000 (Contributed by Serhiy Storchaka in :issue:`109218`.)
106110

107111

108112
Removed

Lib/test/test_complex.py

Lines changed: 74 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@
2121
(1, 0+0j),
2222
)
2323

24+
class ComplexSubclass(complex):
25+
pass
26+
27+
class MockComplex:
28+
def __init__(self, value):
29+
self.value = value
30+
def __complex__(self):
31+
return self.value
32+
2433
class ComplexTest(unittest.TestCase):
2534

2635
def assertAlmostEqual(self, a, b):
@@ -340,16 +349,13 @@ def test_conjugate(self):
340349
self.assertClose(complex(5.3, 9.8).conjugate(), 5.3-9.8j)
341350

342351
def test_constructor(self):
343-
class NS:
344-
def __init__(self, value): self.value = value
345-
def __complex__(self): return self.value
346-
self.assertEqual(complex(NS(1+10j)), 1+10j)
347-
self.assertRaises(TypeError, complex, NS(None))
352+
self.assertEqual(complex(MockComplex(1+10j)), 1+10j)
353+
self.assertRaises(TypeError, complex, MockComplex(None))
348354
self.assertRaises(TypeError, complex, {})
349-
self.assertRaises(TypeError, complex, NS(1.5))
350-
self.assertRaises(TypeError, complex, NS(1))
355+
self.assertRaises(TypeError, complex, MockComplex(1.5))
356+
self.assertRaises(TypeError, complex, MockComplex(1))
351357
self.assertRaises(TypeError, complex, object())
352-
self.assertRaises(TypeError, complex, NS(4.25+0.5j), object())
358+
self.assertRaises(TypeError, complex, MockComplex(4.25+0.5j), object())
353359

354360
self.assertAlmostEqual(complex("1+10j"), 1+10j)
355361
self.assertAlmostEqual(complex(10), 10+0j)
@@ -369,13 +375,33 @@ def __complex__(self): return self.value
369375
self.assertAlmostEqual(complex(3.14), 3.14+0j)
370376
self.assertAlmostEqual(complex(314), 314.0+0j)
371377
self.assertAlmostEqual(complex(314), 314.0+0j)
372-
self.assertAlmostEqual(complex(3.14+0j, 0j), 3.14+0j)
378+
with self.assertWarnsRegex(DeprecationWarning,
379+
"argument 'imag' must be a real number, not complex"):
380+
self.assertAlmostEqual(complex(3.14+0j, 0j), 3.14+0j)
373381
self.assertAlmostEqual(complex(3.14, 0.0), 3.14+0j)
374382
self.assertAlmostEqual(complex(314, 0), 314.0+0j)
375383
self.assertAlmostEqual(complex(314, 0), 314.0+0j)
376-
self.assertAlmostEqual(complex(0j, 3.14j), -3.14+0j)
377-
self.assertAlmostEqual(complex(0.0, 3.14j), -3.14+0j)
378-
self.assertAlmostEqual(complex(0j, 3.14), 3.14j)
384+
with self.assertWarnsRegex(DeprecationWarning,
385+
"argument 'real' must be a real number, not complex"):
386+
self.assertAlmostEqual(complex(0j, 3.14j), -3.14+0j)
387+
with self.assertWarnsRegex(DeprecationWarning,
388+
"argument 'imag' must be a real number, not complex"):
389+
self.assertAlmostEqual(complex(0.0, 3.14j), -3.14+0j)
390+
with self.assertWarnsRegex(DeprecationWarning,
391+
"argument 'real' must be a real number, not complex"):
392+
self.assertAlmostEqual(complex(0j, 3.14), 3.14j)
393+
with self.assertWarnsRegex(DeprecationWarning,
394+
"argument 'real' must be a real number, not complex"):
395+
self.assertAlmostEqual(complex(3.14+0j, 0), 3.14+0j)
396+
with self.assertWarnsRegex(DeprecationWarning,
397+
"argument 'real' must be a real number, not .*MockComplex"):
398+
self.assertAlmostEqual(complex(MockComplex(3.14+0j), 0), 3.14+0j)
399+
with self.assertWarnsRegex(DeprecationWarning,
400+
"argument 'imag' must be a real number, not .*complex"):
401+
self.assertAlmostEqual(complex(0, 3.14+0j), 3.14j)
402+
with self.assertRaisesRegex(TypeError,
403+
"argument 'imag' must be a real number, not .*MockComplex"):
404+
complex(0, MockComplex(3.14+0j))
379405
self.assertAlmostEqual(complex(0.0, 3.14), 3.14j)
380406
self.assertAlmostEqual(complex("1"), 1+0j)
381407
self.assertAlmostEqual(complex("1j"), 1j)
@@ -398,12 +424,32 @@ def __complex__(self): return self.value
398424
self.assertEqual(complex('1-1j'), 1.0 - 1j)
399425
self.assertEqual(complex('1J'), 1j)
400426

401-
class complex2(complex): pass
402-
self.assertAlmostEqual(complex(complex2(1+1j)), 1+1j)
427+
self.assertAlmostEqual(complex(ComplexSubclass(1+1j)), 1+1j)
403428
self.assertAlmostEqual(complex(real=17, imag=23), 17+23j)
404-
self.assertAlmostEqual(complex(real=17+23j), 17+23j)
405-
self.assertAlmostEqual(complex(real=17+23j, imag=23), 17+46j)
406-
self.assertAlmostEqual(complex(real=1+2j, imag=3+4j), -3+5j)
429+
with self.assertWarnsRegex(DeprecationWarning,
430+
"argument 'real' must be a real number, not complex"):
431+
self.assertAlmostEqual(complex(real=17+23j), 17+23j)
432+
with self.assertWarnsRegex(DeprecationWarning,
433+
"argument 'real' must be a real number, not complex"):
434+
self.assertAlmostEqual(complex(real=17+23j, imag=23), 17+46j)
435+
with self.assertWarnsRegex(DeprecationWarning,
436+
"argument 'real' must be a real number, not complex"):
437+
self.assertAlmostEqual(complex(real=1+2j, imag=3+4j), -3+5j)
438+
with self.assertWarnsRegex(DeprecationWarning,
439+
"argument 'real' must be a real number, not complex"):
440+
self.assertAlmostEqual(complex(real=3.14+0j), 3.14+0j)
441+
with self.assertWarnsRegex(DeprecationWarning,
442+
"argument 'real' must be a real number, not .*MockComplex"):
443+
self.assertAlmostEqual(complex(real=MockComplex(3.14+0j)), 3.14+0j)
444+
with self.assertRaisesRegex(TypeError,
445+
"argument 'real' must be a real number, not str"):
446+
complex(real='1')
447+
with self.assertRaisesRegex(TypeError,
448+
"argument 'real' must be a real number, not str"):
449+
complex('1', 0)
450+
with self.assertRaisesRegex(TypeError,
451+
"argument 'imag' must be a real number, not str"):
452+
complex(0, '1')
407453

408454
# check that the sign of a zero in the real or imaginary part
409455
# is preserved when constructing from two floats. (These checks
@@ -432,8 +478,9 @@ def split_zeros(x):
432478
self.assertRaises(TypeError, int, 5+3j)
433479
self.assertRaises(TypeError, float, 5+3j)
434480
self.assertRaises(ValueError, complex, "")
435-
self.assertRaises(TypeError, complex, None)
436-
self.assertRaisesRegex(TypeError, "not 'NoneType'", complex, None)
481+
self.assertRaisesRegex(TypeError,
482+
"argument must be a string or a number, not NoneType",
483+
complex, None)
437484
self.assertRaises(ValueError, complex, "\0")
438485
self.assertRaises(ValueError, complex, "3\09")
439486
self.assertRaises(TypeError, complex, "1", "2")
@@ -453,11 +500,11 @@ def split_zeros(x):
453500
self.assertRaises(ValueError, complex, ")1+2j(")
454501
self.assertRaisesRegex(
455502
TypeError,
456-
"first argument must be a string or a number, not 'dict'",
503+
"argument 'real' must be a real number, not dict",
457504
complex, {1:2}, 1)
458505
self.assertRaisesRegex(
459506
TypeError,
460-
"second argument must be a number, not 'dict'",
507+
"argument 'imag' must be a real number, not dict",
461508
complex, 1, {1:2})
462509
# the following three are accepted by Python 2.6
463510
self.assertRaises(ValueError, complex, "1..1j")
@@ -537,33 +584,28 @@ def test___complex__(self):
537584
self.assertEqual(z.__complex__(), z)
538585
self.assertEqual(type(z.__complex__()), complex)
539586

540-
class complex_subclass(complex):
541-
pass
542-
543-
z = complex_subclass(3 + 4j)
587+
z = ComplexSubclass(3 + 4j)
544588
self.assertEqual(z.__complex__(), 3 + 4j)
545589
self.assertEqual(type(z.__complex__()), complex)
546590

547591
@support.requires_IEEE_754
548592
def test_constructor_special_numbers(self):
549-
class complex2(complex):
550-
pass
551593
for x in 0.0, -0.0, INF, -INF, NAN:
552594
for y in 0.0, -0.0, INF, -INF, NAN:
553595
with self.subTest(x=x, y=y):
554596
z = complex(x, y)
555597
self.assertFloatsAreIdentical(z.real, x)
556598
self.assertFloatsAreIdentical(z.imag, y)
557-
z = complex2(x, y)
558-
self.assertIs(type(z), complex2)
599+
z = ComplexSubclass(x, y)
600+
self.assertIs(type(z), ComplexSubclass)
559601
self.assertFloatsAreIdentical(z.real, x)
560602
self.assertFloatsAreIdentical(z.imag, y)
561-
z = complex(complex2(x, y))
603+
z = complex(ComplexSubclass(x, y))
562604
self.assertIs(type(z), complex)
563605
self.assertFloatsAreIdentical(z.real, x)
564606
self.assertFloatsAreIdentical(z.imag, y)
565-
z = complex2(complex(x, y))
566-
self.assertIs(type(z), complex2)
607+
z = ComplexSubclass(complex(x, y))
608+
self.assertIs(type(z), ComplexSubclass)
567609
self.assertFloatsAreIdentical(z.real, x)
568610
self.assertFloatsAreIdentical(z.imag, y)
569611

@@ -645,9 +687,6 @@ def test(v, expected, test_fn=self.assertEqual):
645687
test(complex(-0., -0.), "(-0-0j)")
646688

647689
def test_pos(self):
648-
class ComplexSubclass(complex):
649-
pass
650-
651690
self.assertEqual(+(1+6j), 1+6j)
652691
self.assertEqual(+ComplexSubclass(1, 6), 1+6j)
653692
self.assertIs(type(+ComplexSubclass(1, 6)), complex)

Lib/test/test_fractions.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -806,7 +806,10 @@ def testMixedMultiplication(self):
806806
self.assertTypedEquals(F(3, 2) * Polar(4, 2), Polar(F(6, 1), 2))
807807
self.assertTypedEquals(F(3, 2) * Polar(4.0, 2), Polar(6.0, 2))
808808
self.assertTypedEquals(F(3, 2) * Rect(4, 3), Rect(F(6, 1), F(9, 2)))
809-
self.assertTypedEquals(F(3, 2) * RectComplex(4, 3), RectComplex(6.0+0j, 4.5+0j))
809+
with self.assertWarnsRegex(DeprecationWarning,
810+
"argument 'real' must be a real number, not complex"):
811+
self.assertTypedEquals(F(3, 2) * RectComplex(4, 3),
812+
RectComplex(6.0+0j, 4.5+0j))
810813
self.assertRaises(TypeError, operator.mul, Polar(4, 2), F(3, 2))
811814
self.assertTypedEquals(Rect(4, 3) * F(3, 2), 6.0 + 4.5j)
812815
self.assertEqual(F(3, 2) * SymbolicComplex('X'), SymbolicComplex('3/2 * X'))
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:func:`complex` accepts now a string only as a positional argument. Passing
2+
a complex number as the "real" or "imag" argument is deprecated; it should
3+
only be passed as a single positional argument.

0 commit comments

Comments
 (0)
0