From 6649fa42f87397c701e528f77374d21bf00d2ca3 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Fri, 24 Apr 2009 13:25:20 +0000 Subject: Make sure that complex parsing code and corresponding tests match for 2.7 and 3.1, and that 3.1 continues to accept complex('j') and complex('4-j') --- Lib/test/test_complex.py | 14 +++++- Objects/complexobject.c | 111 ++++++++++++++++++++++++++++++++++++----------- 2 files changed, 98 insertions(+), 27 deletions(-) diff --git a/Lib/test/test_complex.py b/Lib/test/test_complex.py index 836360a..d029d86 100644 --- a/Lib/test/test_complex.py +++ b/Lib/test/test_complex.py @@ -227,6 +227,15 @@ class ComplexTest(unittest.TestCase): self.assertAlmostEqual(complex("(1.3+2.2j)"), 1.3+2.2j) self.assertAlmostEqual(complex("3.14+1J"), 3.14+1j) self.assertAlmostEqual(complex(" ( +3.14-6J )"), 3.14-6j) + self.assertAlmostEqual(complex(" ( +3.14-J )"), 3.14-1j) + self.assertAlmostEqual(complex(" ( +3.14+j )"), 3.14+1j) + self.assertAlmostEqual(complex("J"), 1j) + self.assertAlmostEqual(complex("( j )"), 1j) + self.assertAlmostEqual(complex("+J"), 1j) + self.assertAlmostEqual(complex("( -j)"), -1j) + self.assertAlmostEqual(complex('1e-500'), 0.0 + 0.0j) + self.assertAlmostEqual(complex('-1e-500j'), 0.0 - 0.0j) + self.assertAlmostEqual(complex('-1e-500+1e-500j'), -0.0 + 0.0j) class complex2(complex): pass self.assertAlmostEqual(complex(complex2(1+1j)), 1+1j) @@ -277,11 +286,14 @@ class ComplexTest(unittest.TestCase): self.assertRaises(ValueError, complex, "(1+2j)123") self.assertRaises(ValueError, complex, "1"*500) self.assertRaises(ValueError, complex, "x") - self.assertRaises(ValueError, complex, "J") self.assertRaises(ValueError, complex, "1j+2") self.assertRaises(ValueError, complex, "1e1ej") self.assertRaises(ValueError, complex, "1e++1ej") self.assertRaises(ValueError, complex, ")1+2j(") + # the following three are accepted by Python 2.6 + self.assertRaises(ValueError, complex, "1..1j") + self.assertRaises(ValueError, complex, "1.11.1j") + self.assertRaises(ValueError, complex, "1e1.1j") class EvilExc(Exception): pass diff --git a/Objects/complexobject.c b/Objects/complexobject.c index 27765ad..fa5ea61 100644 --- a/Objects/complexobject.c +++ b/Objects/complexobject.c @@ -760,50 +760,109 @@ complex_subtype_from_string(PyTypeObject *type, PyObject *v) s++; } - /* get float---might be real or imaginary part */ + /* a valid complex string usually takes one of the three forms: + + - real part only + j - imaginary part only + j - real and imaginary parts + + where represents any numeric string that's accepted by the + float constructor (including 'nan', 'inf', 'infinity', etc.), and + is any string of the form whose first + character is '+' or '-'. + + For backwards compatibility, the extra forms + + j + j + j + + are also accepted, though support for these forms may be removed from + a future version of Python. + */ + + /* first look for forms starting with */ z = PyOS_ascii_strtod(s, &end); - if (end == s) - goto error; - s = end; - if (*s == '+' || *s == '-') { - /* we've got a real part *and* an imaginary part */ - x = z; - y = PyOS_ascii_strtod(s, &end); - if (end == s || !(*end == 'j' || *end == 'J')) - goto error; - s = ++end; + if (end == s && errno == ENOMEM) + return PyErr_NoMemory(); + if (errno == ERANGE && fabs(z) >= 1.0) + goto overflow; + + if (end != s) { + /* all 4 forms starting with land here */ + s = end; + if (*s == '+' || *s == '-') { + /* j | j */ + x = z; + y = PyOS_ascii_strtod(s, &end); + if (end == s && errno == ENOMEM) + return PyErr_NoMemory(); + if (errno == ERANGE && fabs(z) >= 1.0) + goto overflow; + if (end != s) + /* j */ + s = end; + else { + /* j */ + y = *s == '+' ? 1.0 : -1.0; + s++; + } + if (!(*s == 'j' || *s == 'J')) + goto parse_error; + s++; + } + else if (*s == 'j' || *s == 'J') { + /* j */ + s++; + y = z; + } + else + /* */ + x = z; } - else if (*s == 'j' || *s == 'J') { - /* no real part; z was the imaginary part */ + else { + /* not starting with ; must be j or j */ + if (*s == '+' || *s == '-') { + /* j */ + y = *s == '+' ? 1.0 : -1.0; + s++; + } + else + /* j */ + y = 1.0; + if (!(*s == 'j' || *s == 'J')) + goto parse_error; s++; - y = z; } - else - /* no imaginary part */ - x = z; /* trailing whitespace and closing bracket */ while (*s && isspace(Py_CHARMASK(*s))) s++; - if (got_bracket && *s == ')') { - got_bracket = 0; + if (got_bracket) { + /* if there was an opening parenthesis, then the corresponding + closing parenthesis should be right here */ + if (*s != ')') + goto parse_error; s++; while (*s && isspace(Py_CHARMASK(*s))) - s++; + s++; } + /* we should now be at the end of the string */ - if (s-start != len || got_bracket) - goto error; + if (s-start != len) + goto parse_error; return complex_subtype_from_doubles(type, x, y); - error: - /* check for PyOS_ascii_strtod failure due to lack of memory */ - if (errno == ENOMEM) - return PyErr_NoMemory(); + parse_error: PyErr_SetString(PyExc_ValueError, "complex() arg is a malformed string"); return NULL; + + overflow: + PyErr_SetString(PyExc_OverflowError, + "complex() arg overflow"); + return NULL; } static PyObject * -- cgit v0.12