]> granicus.if.org Git - python/commitdiff
Make sure that complex parsing code and corresponding tests
authorMark Dickinson <dickinsm@gmail.com>
Fri, 24 Apr 2009 13:25:20 +0000 (13:25 +0000)
committerMark Dickinson <dickinsm@gmail.com>
Fri, 24 Apr 2009 13:25:20 +0000 (13:25 +0000)
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
Objects/complexobject.c

index 836360a107beb7991bcf77cbb4f8af17142a6451..d029d86ddf59c67e5bec69671d9dddacf81bf854 100644 (file)
@@ -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
index 27765adf74ae26d5fdd0c9eb739d1988d7773543..fa5ea61e02a3a6ba13a156929040a2f107d9148e 100644 (file)
@@ -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:
+
+            <float>                  - real part only
+            <float>j                 - imaginary part only
+            <float><signed-float>j   - real and imaginary parts
+
+          where <float> represents any numeric string that's accepted by the
+          float constructor (including 'nan', 'inf', 'infinity', etc.), and
+          <signed-float> is any string of the form <float> whose first
+          character is '+' or '-'.
+
+          For backwards compatibility, the extra forms
+
+            <float><sign>j
+            <sign>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 <float> */
        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 <float> land here */
+               s = end;
+               if (*s == '+' || *s == '-') {
+                       /* <float><signed-float>j | <float><sign>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)
+                               /* <float><signed-float>j */
+                               s = end;
+                       else {
+                               /* <float><sign>j */
+                               y = *s == '+' ? 1.0 : -1.0;
+                               s++;
+                       }
+                       if (!(*s == 'j' || *s == 'J'))
+                               goto parse_error;
+                       s++;
+               }
+               else if (*s == 'j' || *s == 'J') {
+                       /* <float>j */
+                       s++;
+                       y = z;
+               }
+               else
+                       /* <float> */
+                       x = z;
        }
-       else if (*s == 'j' || *s == 'J') {
-               /* no real part; z was the imaginary part */
+       else {
+               /* not starting with <float>; must be <sign>j or j */
+               if (*s == '+' || *s == '-') {
+                       /* <sign>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 *