Index: Python/pystrtod.c =================================================================== --- Python/pystrtod.c (revision 71809) +++ Python/pystrtod.c (working copy) @@ -733,6 +733,10 @@ so convert Infinity to inf and NaN to nan, and ignore sign of nan. Then return. */ + /* ignore the actual sign of a nan */ + if (digits[0] == 'n' || digits[0] == 'N') + sign = 0; + /* We only need 5 bytes to hold the result "+inf\0" . */ bufsize = 5; /* Used later in an assert. */ buf = (char *)PyMem_Malloc(bufsize); @@ -742,13 +746,13 @@ } p = buf; + if (sign == 1) { + *p++ = '-'; + } + else if (always_add_sign) { + *p++ = '+'; + } if (digits[0] == 'i' || digits[0] == 'I') { - if (sign == 1) { - *p++ = '-'; - } - else if (always_add_sign) { - *p++ = '+'; - } strncpy(p, float_strings[OFS_INF], 3); p += 3; @@ -756,8 +760,6 @@ *type = Py_DTST_INFINITE; } else if (digits[0] == 'n' || digits[0] == 'N') { - /* note that we *never* add a sign for a nan, - even if one has explicitly been requested */ strncpy(p, float_strings[OFS_NAN], 3); p += 3; Index: Objects/complexobject.c =================================================================== --- Objects/complexobject.c (revision 71809) +++ Objects/complexobject.c (working copy) @@ -347,65 +347,32 @@ char *lead = ""; char *tail = ""; - - if (v->cval.real == 0.) { + if (v->cval.real == 0. && copysign(1.0, v->cval.real)==1.0) { re = ""; - if (!Py_IS_FINITE(v->cval.imag)) { - if (Py_IS_NAN(v->cval.imag)) - im = "nan*"; - else if (copysign(1, v->cval.imag) == 1) - im = "inf*"; - else - im = "-inf*"; + pim = PyOS_double_to_string(v->cval.imag, format_code, + 0, 0, NULL); + if (!pim) { + PyErr_NoMemory(); + goto done; } - else { - pim = PyOS_double_to_string(v->cval.imag, format_code, - 0, 0, NULL); - if (!pim) { - PyErr_NoMemory(); - goto done; - } - im = pim; - } + im = pim; } else { /* Format imaginary part with sign, real part without */ - if (!Py_IS_FINITE(v->cval.real)) { - if (Py_IS_NAN(v->cval.real)) - re = "nan"; - /* else if (copysign(1, v->cval.real) == 1) */ - else if (v->cval.real > 0) - re = "inf"; - else - re = "-inf"; - } - else { - pre = PyOS_double_to_string(v->cval.real, format_code, + pre = PyOS_double_to_string(v->cval.real, format_code, 0, 0, NULL); - if (!pre) { - PyErr_NoMemory(); - goto done; - } - re = pre; + if (!pre) { + PyErr_NoMemory(); + goto done; } + re = pre; - if (!Py_IS_FINITE(v->cval.imag)) { - if (Py_IS_NAN(v->cval.imag)) - im = "+nan*"; - /* else if (copysign(1, v->cval.imag) == 1) */ - else if (v->cval.imag > 0) - im = "+inf*"; - else - im = "-inf*"; - } - else { - pim = PyOS_double_to_string(v->cval.imag, format_code, + pim = PyOS_double_to_string(v->cval.imag, format_code, 0, Py_DTSF_SIGN, NULL); - if (!pim) { - PyErr_NoMemory(); - goto done; - } - im = pim; + if (!pim) { + PyErr_NoMemory(); + goto done; } + im = pim; lead = "("; tail = ")"; } @@ -756,12 +723,8 @@ { const char *s, *start; char *end; - double x=0.0, y=0.0, z; - int got_re=0, got_im=0, got_bracket=0, done=0; - int digit_or_dot; - int sw_error=0; - int sign; - char buffer[256]; /* For errors */ + double x, y; + int got_bracket=0; char s_buffer[256]; Py_ssize_t len; @@ -785,16 +748,13 @@ return NULL; } + errno = 0; + /* position on first nonblank */ start = s; while (*s && isspace(Py_CHARMASK(*s))) s++; - if (s[0] == '\0') { - PyErr_SetString(PyExc_ValueError, - "complex() arg is an empty string"); - return NULL; - } - if (s[0] == '(') { + if (*s == '(') { /* Skip over possible bracket from repr(). */ got_bracket = 1; s++; @@ -802,120 +762,51 @@ s++; } - z = -1.0; - sign = 1; - do { + /* get float */ + x = 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 */ + y = PyOS_ascii_strtod(s, &end); + if (end == s || !(*end == 'j' || *end == 'J')) + goto error; + s = ++end; + } + else if (*s == 'j' || *s == 'J') { + /* no real part; x is the imaginary part; swap around */ + s++; + y = x; + x = 0.0; + } + else + /* no imaginary part */ + y = 0.0; - switch (*s) { - - case '\0': - if (s-start != len) { - PyErr_SetString( - PyExc_ValueError, - "complex() arg contains a null byte"); - return NULL; - } - if(!done) sw_error=1; - break; - - case ')': - if (!got_bracket || !(got_re || got_im)) { - sw_error=1; - break; - } - got_bracket=0; - done=1; - s++; - while (*s && isspace(Py_CHARMASK(*s))) - s++; - if (*s) sw_error=1; - break; - - case '-': - sign = -1; - /* Fallthrough */ - case '+': - if (done) sw_error=1; - s++; - if ( *s=='\0'||*s=='+'||*s=='-'||*s==')'|| - isspace(Py_CHARMASK(*s)) ) sw_error=1; - break; - - case 'J': - case 'j': - if (got_im || done) { - sw_error = 1; - break; - } - if (z<0.0) { - y=sign; - } - else{ - y=sign*z; - } - got_im=1; - s++; - if (*s!='+' && *s!='-' ) - done=1; - break; - - default: - if (isspace(Py_CHARMASK(*s))) { - while (*s && isspace(Py_CHARMASK(*s))) - s++; - if (*s && *s != ')') - sw_error=1; - else - done = 1; - break; - } - digit_or_dot = - (*s=='.' || isdigit(Py_CHARMASK(*s))); - if (done||!digit_or_dot) { - sw_error=1; - break; - } - errno = 0; - PyFPE_START_PROTECT("strtod", return 0) - z = PyOS_ascii_strtod(s, &end) ; - PyFPE_END_PROTECT(z) - if (errno != 0) { - PyOS_snprintf(buffer, sizeof(buffer), - "float() out of range: %.150s", s); - PyErr_SetString( - PyExc_ValueError, - buffer); - return NULL; - } - s=end; - if (*s=='J' || *s=='j') { - - break; - } - if (got_re) { - sw_error=1; - break; - } - - /* accept a real part */ - x=sign*z; - got_re=1; - if (got_im) done=1; - z = -1.0; - sign = 1; - break; - - } /* end of switch */ - - } while (s - start < len && !sw_error); - - if (sw_error || got_bracket) { - PyErr_SetString(PyExc_ValueError, - "complex() arg is a malformed string"); - return NULL; + /* trailing whitespace and closing bracket */ + while (*s && isspace(Py_CHARMASK(*s))) + s++; + if (got_bracket && *s == ')') { + got_bracket = 0; + s++; + while (*s && isspace(Py_CHARMASK(*s))) + s++; } + /* we should now be at the end of the string */ + if (*s != '\0' || s-start != len || got_bracket) + goto 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(); + PyErr_SetString(PyExc_ValueError, + "complex() arg is a malformed string"); + return NULL; + } static PyObject * Index: Lib/test/test_complex.py =================================================================== --- Lib/test/test_complex.py (revision 71809) +++ Lib/test/test_complex.py (working copy) @@ -202,6 +202,8 @@ self.assertAlmostEqual(complex("+1"), +1) self.assertAlmostEqual(complex("(1+2j)"), 1+2j) 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) class complex2(complex): pass self.assertAlmostEqual(complex(complex2(1+1j)), 1+1j) @@ -229,8 +231,6 @@ self.assertRaises(TypeError, complex, "1", "1") self.assertRaises(TypeError, complex, 1, "1") - self.assertEqual(complex(" 3.14+J "), 3.14+1j) - # SF bug 543840: complex(string) accepts strings with \0 # Fixed in 2.3. self.assertRaises(ValueError, complex, '1+1j\0j') @@ -254,6 +254,11 @@ 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(") class EvilExc(Exception): pass @@ -318,17 +323,17 @@ self.assertEqual(-6j,complex(repr(-6j))) self.assertEqual(6j,complex(repr(6j))) - self.assertEqual(repr(complex(1., INF)), "(1+inf*j)") - self.assertEqual(repr(complex(1., -INF)), "(1-inf*j)") + self.assertEqual(repr(complex(1., INF)), "(1+infj)") + self.assertEqual(repr(complex(1., -INF)), "(1-infj)") self.assertEqual(repr(complex(INF, 1)), "(inf+1j)") - self.assertEqual(repr(complex(-INF, INF)), "(-inf+inf*j)") + self.assertEqual(repr(complex(-INF, INF)), "(-inf+infj)") self.assertEqual(repr(complex(NAN, 1)), "(nan+1j)") - self.assertEqual(repr(complex(1, NAN)), "(1+nan*j)") - self.assertEqual(repr(complex(NAN, NAN)), "(nan+nan*j)") + self.assertEqual(repr(complex(1, NAN)), "(1+nanj)") + self.assertEqual(repr(complex(NAN, NAN)), "(nan+nanj)") - self.assertEqual(repr(complex(0, INF)), "inf*j") - self.assertEqual(repr(complex(0, -INF)), "-inf*j") - self.assertEqual(repr(complex(0, NAN)), "nan*j") + self.assertEqual(repr(complex(0, INF)), "infj") + self.assertEqual(repr(complex(0, -INF)), "-infj") + self.assertEqual(repr(complex(0, NAN)), "nanj") def test_neg(self): self.assertEqual(-(1+6j), -1-6j)