[Python-checkins] r65605 - in python/trunk: Doc/library/csv.rst Lib/csv.py Lib/test/test_csv.py Misc/NEWS

skip.montanaro python-checkins at python.org
Sat Aug 9 00:52:52 CEST 2008


Author: skip.montanaro
Date: Sat Aug  9 00:52:51 2008
New Revision: 65605

Log:
accept issue 3436

Modified:
   python/trunk/Doc/library/csv.rst
   python/trunk/Lib/csv.py
   python/trunk/Lib/test/test_csv.py
   python/trunk/Misc/NEWS

Modified: python/trunk/Doc/library/csv.rst
==============================================================================
--- python/trunk/Doc/library/csv.rst	(original)
+++ python/trunk/Doc/library/csv.rst	Sat Aug  9 00:52:51 2008
@@ -382,6 +382,18 @@
    .. versionadded:: 2.5
 
 
+DictReader objects have the following public attribute:
+
+
+.. attribute:: csvreader.fieldnames
+
+   If not passed as a parameter when creating the object, this attribute is
+   initialized upon first access or when the first record is read from the
+   file.
+
+   .. versionchanged:: 2.6
+
+
 Writer Objects
 --------------
 

Modified: python/trunk/Lib/csv.py
==============================================================================
--- python/trunk/Lib/csv.py	(original)
+++ python/trunk/Lib/csv.py	Sat Aug  9 00:52:51 2008
@@ -71,7 +71,7 @@
 class DictReader:
     def __init__(self, f, fieldnames=None, restkey=None, restval=None,
                  dialect="excel", *args, **kwds):
-        self.fieldnames = fieldnames    # list of keys for the dict
+        self._fieldnames = fieldnames   # list of keys for the dict
         self.restkey = restkey          # key to catch long rows
         self.restval = restval          # default value for short rows
         self.reader = reader(f, dialect, *args, **kwds)
@@ -81,11 +81,25 @@
     def __iter__(self):
         return self
 
+    @property
+    def fieldnames(self):
+        if self._fieldnames is None:
+            try:
+                self._fieldnames = self.reader.next()
+            except StopIteration:
+                pass
+        self.line_num = self.reader.line_num
+        return self._fieldnames
+
+    @fieldnames.setter
+    def fieldnames(self, value):
+        self._fieldnames = value
+
     def next(self):
+        if self.line_num == 0:
+            # Used only for its side effect.
+            self.fieldnames
         row = self.reader.next()
-        if self.fieldnames is None:
-            self.fieldnames = row
-            row = self.reader.next()
         self.line_num = self.reader.line_num
 
         # unlike the basic reader, we prefer not to return blanks,

Modified: python/trunk/Lib/test/test_csv.py
==============================================================================
--- python/trunk/Lib/test/test_csv.py	(original)
+++ python/trunk/Lib/test/test_csv.py	Sat Aug  9 00:52:51 2008
@@ -611,11 +611,43 @@
             fileobj.write("f1,f2,f3\r\n1,2,abc\r\n")
             fileobj.seek(0)
             reader = csv.DictReader(fileobj)
+            self.assertEqual(reader.fieldnames, ["f1", "f2", "f3"])
             self.assertEqual(reader.next(), {"f1": '1', "f2": '2', "f3": 'abc'})
         finally:
             fileobj.close()
             os.unlink(name)
 
+    # Two test cases to make sure existing ways of implicitly setting
+    # fieldnames continue to work.  Both arise from discussion in issue3436.
+    def test_read_dict_fieldnames_from_file(self):
+        fd, name = tempfile.mkstemp()
+        f = os.fdopen(fd, "w+b")
+        try:
+            f.write("f1,f2,f3\r\n1,2,abc\r\n")
+            f.seek(0)
+            reader = csv.DictReader(f, fieldnames=csv.reader(f).next())
+            self.assertEqual(reader.fieldnames, ["f1", "f2", "f3"])
+            self.assertEqual(reader.next(), {"f1": '1', "f2": '2', "f3": 'abc'})
+        finally:
+            f.close()
+            os.unlink(name)
+
+    def test_read_dict_fieldnames_chain(self):
+        import itertools
+        fd, name = tempfile.mkstemp()
+        f = os.fdopen(fd, "w+b")
+        try:
+            f.write("f1,f2,f3\r\n1,2,abc\r\n")
+            f.seek(0)
+            reader = csv.DictReader(f)
+            first = next(reader)
+            for row in itertools.chain([first], reader):
+                self.assertEqual(reader.fieldnames, ["f1", "f2", "f3"])
+                self.assertEqual(row, {"f1": '1', "f2": '2', "f3": 'abc'})
+        finally:
+            f.close()
+            os.unlink(name)
+
     def test_read_long(self):
         fd, name = tempfile.mkstemp()
         fileobj = os.fdopen(fd, "w+b")
@@ -651,6 +683,7 @@
             fileobj.write("f1,f2\r\n1,2,abc,4,5,6\r\n")
             fileobj.seek(0)
             reader = csv.DictReader(fileobj, restkey="_rest")
+            self.assertEqual(reader.fieldnames, ["f1", "f2"])
             self.assertEqual(reader.next(), {"f1": '1', "f2": '2',
                                              "_rest": ["abc", "4", "5", "6"]})
         finally:

Modified: python/trunk/Misc/NEWS
==============================================================================
--- python/trunk/Misc/NEWS	(original)
+++ python/trunk/Misc/NEWS	Sat Aug  9 00:52:51 2008
@@ -41,6 +41,10 @@
 Library
 -------
 
+- Issue #3436: Make csv.DictReader's fieldnames attribute a property so that
+  upon first access it can be automatically initialized from the csv file if
+  it wasn't initialized during instantiation.
+
 - Issue #2338: Create imp.reload() to help with transitioning to Python 3.0 as
   the reload() built-in has been removed.
 


More information about the Python-checkins mailing list