accept issue 3436
This commit is contained in:
parent
b04d4853a7
commit
a032bf41f6
|
@ -382,6 +382,18 @@ Reader objects have the following public attributes:
|
||||||
.. versionadded:: 2.5
|
.. 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
|
Writer Objects
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
|
22
Lib/csv.py
22
Lib/csv.py
|
@ -71,7 +71,7 @@ register_dialect("excel-tab", excel_tab)
|
||||||
class DictReader:
|
class DictReader:
|
||||||
def __init__(self, f, fieldnames=None, restkey=None, restval=None,
|
def __init__(self, f, fieldnames=None, restkey=None, restval=None,
|
||||||
dialect="excel", *args, **kwds):
|
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.restkey = restkey # key to catch long rows
|
||||||
self.restval = restval # default value for short rows
|
self.restval = restval # default value for short rows
|
||||||
self.reader = reader(f, dialect, *args, **kwds)
|
self.reader = reader(f, dialect, *args, **kwds)
|
||||||
|
@ -81,11 +81,25 @@ class DictReader:
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return 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):
|
def next(self):
|
||||||
|
if self.line_num == 0:
|
||||||
|
# Used only for its side effect.
|
||||||
|
self.fieldnames
|
||||||
row = self.reader.next()
|
row = self.reader.next()
|
||||||
if self.fieldnames is None:
|
|
||||||
self.fieldnames = row
|
|
||||||
row = self.reader.next()
|
|
||||||
self.line_num = self.reader.line_num
|
self.line_num = self.reader.line_num
|
||||||
|
|
||||||
# unlike the basic reader, we prefer not to return blanks,
|
# unlike the basic reader, we prefer not to return blanks,
|
||||||
|
|
|
@ -611,11 +611,43 @@ class TestDictFields(unittest.TestCase):
|
||||||
fileobj.write("f1,f2,f3\r\n1,2,abc\r\n")
|
fileobj.write("f1,f2,f3\r\n1,2,abc\r\n")
|
||||||
fileobj.seek(0)
|
fileobj.seek(0)
|
||||||
reader = csv.DictReader(fileobj)
|
reader = csv.DictReader(fileobj)
|
||||||
|
self.assertEqual(reader.fieldnames, ["f1", "f2", "f3"])
|
||||||
self.assertEqual(reader.next(), {"f1": '1', "f2": '2', "f3": 'abc'})
|
self.assertEqual(reader.next(), {"f1": '1', "f2": '2', "f3": 'abc'})
|
||||||
finally:
|
finally:
|
||||||
fileobj.close()
|
fileobj.close()
|
||||||
os.unlink(name)
|
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):
|
def test_read_long(self):
|
||||||
fd, name = tempfile.mkstemp()
|
fd, name = tempfile.mkstemp()
|
||||||
fileobj = os.fdopen(fd, "w+b")
|
fileobj = os.fdopen(fd, "w+b")
|
||||||
|
@ -651,6 +683,7 @@ class TestDictFields(unittest.TestCase):
|
||||||
fileobj.write("f1,f2\r\n1,2,abc,4,5,6\r\n")
|
fileobj.write("f1,f2\r\n1,2,abc,4,5,6\r\n")
|
||||||
fileobj.seek(0)
|
fileobj.seek(0)
|
||||||
reader = csv.DictReader(fileobj, restkey="_rest")
|
reader = csv.DictReader(fileobj, restkey="_rest")
|
||||||
|
self.assertEqual(reader.fieldnames, ["f1", "f2"])
|
||||||
self.assertEqual(reader.next(), {"f1": '1', "f2": '2',
|
self.assertEqual(reader.next(), {"f1": '1', "f2": '2',
|
||||||
"_rest": ["abc", "4", "5", "6"]})
|
"_rest": ["abc", "4", "5", "6"]})
|
||||||
finally:
|
finally:
|
||||||
|
|
|
@ -41,6 +41,10 @@ Core and Builtins
|
||||||
Library
|
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
|
- Issue #2338: Create imp.reload() to help with transitioning to Python 3.0 as
|
||||||
the reload() built-in has been removed.
|
the reload() built-in has been removed.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue