[Python-checkins] r71829 - in python/branches/py3k: Lib/test/test_complex.py Objects/complexobject.c

mark.dickinson python-checkins at python.org
Fri Apr 24 15:25:20 CEST 2009


Author: mark.dickinson
Date: Fri Apr 24 15:25:20 2009
New Revision: 71829

Log:
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')


Modified:
   python/branches/py3k/Lib/test/test_complex.py
   python/branches/py3k/Objects/complexobject.c

Modified: python/branches/py3k/Lib/test/test_complex.py
==============================================================================
--- python/branches/py3k/Lib/test/test_complex.py	(original)
+++ python/branches/py3k/Lib/test/test_complex.py	Fri Apr 24 15:25:20 2009
@@ -227,6 +227,15 @@
         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 @@
         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

Modified: python/branches/py3k/Objects/complexobject.c
==============================================================================
--- python/branches/py3k/Objects/complexobject.c	(original)
+++ python/branches/py3k/Objects/complexobject.c	Fri Apr 24 15:25:20 2009
@@ -760,50 +760,109 @@
 			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 *


More information about the Python-checkins mailing list