SF Patch #744104: Remove eval() from csv

Eliminates the eval() step in the csv module resulting in better
security, more clarity, and a little speed.

The idea is to make successive attempts to coerce the string to
a python type:
    int(s), long(s), float(s), etc.

As a by-product, eliminates a bare 'except' statement.
diff --git a/Lib/csv.py b/Lib/csv.py
index 83b8aa4..8511502 100644
--- a/Lib/csv.py
+++ b/Lib/csv.py
@@ -148,6 +148,11 @@
             rows.append(self._dict_to_list(rowdict))
         return self.writer.writerows(rows)
 
+# Guard Sniffer's type checking against builds that exclude complex()
+try:
+    complex
+except NameError:
+    complex = float
 
 class Sniffer:
     '''
@@ -360,13 +365,6 @@
         # Finally, a 'vote' is taken at the end for each column, adding or
         # subtracting from the likelihood of the first row being a header.
 
-        def seval(item):
-            """
-            Strips parens from item prior to calling eval in an
-            attempt to make it safer
-            """
-            return eval(item.replace('(', '').replace(')', ''))
-
         rdr = reader(StringIO(sample), self.sniff(sample))
 
         header = rdr.next() # assume first row is header
@@ -386,18 +384,21 @@
                 continue # skip rows that have irregular number of columns
 
             for col in columnTypes.keys():
-                try:
+
+                for thisType in [int, long, float, complex]:
                     try:
-                        # is it a built-in type (besides string)?
-                        thisType = type(seval(row[col]))
-                    except OverflowError:
-                        # a long int?
-                        thisType = type(seval(row[col] + 'L'))
-                        thisType = type(0) # treat long ints as int
-                except:
+                        thisType(row[col])
+                        break
+                    except ValueError, OverflowError:
+                        pass
+                else:
                     # fallback to length of string
                     thisType = len(row[col])
 
+                # treat longs as ints
+                if thisType == long:
+                    thisType = int
+
                 if thisType != columnTypes[col]:
                     if columnTypes[col] is None: # add new column type
                         columnTypes[col] = thisType
@@ -417,8 +418,8 @@
                     hasHeader -= 1
             else: # attempt typecast
                 try:
-                    eval("%s(%s)" % (colType.__name__, header[col]))
-                except:
+                    colType(header[col])
+                except ValueError, TypeError:
                     hasHeader += 1
                 else:
                     hasHeader -= 1