diff -r a9a136c9d857 Doc/library/csv.rst --- a/Doc/library/csv.rst Fri Oct 21 12:32:46 2016 +0900 +++ b/Doc/library/csv.rst Thu Oct 20 21:27:41 2016 -0700 @@ -195,7 +195,10 @@ written if the dictionary is missing a key in *fieldnames*. If the dictionary passed to the :meth:`writerow` method contains a key not found in *fieldnames*, the optional *extrasaction* parameter indicates what action to - take. If it is set to ``'raise'`` a :exc:`ValueError` is raised. If it is + take. + If it is set to ``'raise'``, the default value, a :exc:`ValueError` + is raised. + If it is set to ``'ignore'``, extra values in the dictionary are ignored. Any other optional or keyword arguments are passed to the underlying :class:`writer` instance. diff -r a9a136c9d857 Lib/csv.py --- a/Lib/csv.py Fri Oct 21 12:32:46 2016 +0900 +++ b/Lib/csv.py Thu Oct 20 21:27:41 2016 -0700 @@ -131,7 +131,8 @@ class DictWriter: def __init__(self, f, fieldnames, restval="", extrasaction="raise", dialect="excel", *args, **kwds): - self.fieldnames = fieldnames # list of keys for the dict + self._fieldnames = fieldnames # list of keys for the dict + self._fieldnames_set = set(self._fieldnames) self.restval = restval # for writing short dicts if extrasaction.lower() not in ("raise", "ignore"): raise ValueError("extrasaction (%s) must be 'raise' or 'ignore'" @@ -139,13 +140,22 @@ self.extrasaction = extrasaction self.writer = writer(f, dialect, *args, **kwds) + @property + def fieldnames(self): + return self._fieldnames + + @fieldnames.setter + def fieldnames(self, value): + self._fieldnames = value + self._fieldnames_set = set(self._fieldnames) + def writeheader(self): header = dict(zip(self.fieldnames, self.fieldnames)) self.writerow(header) def _dict_to_list(self, rowdict): if self.extrasaction == "raise": - wrong_fields = [k for k in rowdict if k not in self.fieldnames] + wrong_fields = rowdict.keys() - self._fieldnames_set if wrong_fields: raise ValueError("dict contains fields not in fieldnames: " + ", ".join([repr(x) for x in wrong_fields])) diff -r a9a136c9d857 Lib/test/test_csv.py --- a/Lib/test/test_csv.py Fri Oct 21 12:32:46 2016 +0900 +++ b/Lib/test/test_csv.py Thu Oct 20 21:27:41 2016 -0700 @@ -626,6 +626,31 @@ self.assertNotIn("'f2'", exception) self.assertIn("1", exception) + def test_typo_in_extrasaction_raises_error(self): + fileobj = StringIO() + self.assertRaises(ValueError, csv.DictWriter, fileobj, ['f1', 'f2'], extrasaction="raised") + + def test_uppercase_extrasaction_raises_error(self): + fileobj = StringIO() + csv + self.assertRaises(ValueError, csv.DictWriter, fileobj, ['f1', 'f2'], extrasaction="RAise") + + def test_extrasaction_ignore(self): + fileobj = StringIO() + csv.DictWriter(fileobj, ['f1', 'f2'], extrasaction="ignore") + + def test_write_field_not_in_field_names_raise(self): + fileobj = StringIO() + writer = csv.DictWriter(fileobj, ['f1','f2'], extrasaction="raise") + dictrow = {'f0':0, 'f1':1, 'f2':2, 'f3':3} + self.assertRaises(ValueError, csv.DictWriter.writerow, writer, dictrow) + + def test_write_field_not_in_field_names_ignore(self): + fileobj = StringIO() + writer = csv.DictWriter(fileobj, ['f1','f2'], extrasaction="ignore") + dictrow = {'f0':0, 'f1':1, 'f2':2, 'f3':3} + csv.DictWriter.writerow(writer, dictrow) + def test_read_dict_fields(self): with TemporaryFile("w+") as fileobj: fileobj.write("1,2,abc\r\n")