diff options
author | Raymond Hettinger <python@rcn.com> | 2016-08-30 19:35:50 (GMT) |
---|---|---|
committer | Raymond Hettinger <python@rcn.com> | 2016-08-30 19:35:50 (GMT) |
commit | 43ca4528873b8d0bc8c9e63b3871f4fe4286259c (patch) | |
tree | 1b2f178f1b92934b39ac82a7279c4634a9d87c83 | |
parent | 15f44ab043b37c064d6891c7864205fed9fb0dd1 (diff) | |
download | cpython-43ca4528873b8d0bc8c9e63b3871f4fe4286259c.zip cpython-43ca4528873b8d0bc8c9e63b3871f4fe4286259c.tar.gz cpython-43ca4528873b8d0bc8c9e63b3871f4fe4286259c.tar.bz2 |
Issue #27842: The csv.DictReader now returns rows of type OrderedDict.
-rw-r--r-- | Doc/library/csv.rst | 39 | ||||
-rw-r--r-- | Lib/csv.py | 3 | ||||
-rw-r--r-- | Lib/test/test_csv.py | 16 | ||||
-rw-r--r-- | Misc/NEWS | 3 |
4 files changed, 45 insertions, 16 deletions
diff --git a/Doc/library/csv.rst b/Doc/library/csv.rst index 7fb4fc8..6341bc3 100644 --- a/Doc/library/csv.rst +++ b/Doc/library/csv.rst @@ -149,18 +149,25 @@ The :mod:`csv` module defines the following classes: .. class:: DictReader(csvfile, fieldnames=None, restkey=None, restval=None, \ dialect='excel', *args, **kwds) - Create an object which operates like a regular reader but maps the - information read into a dict whose keys are given by the optional - *fieldnames* parameter. The *fieldnames* parameter is a :mod:`sequence - <collections.abc>` whose elements are associated with the fields of the - input data in order. These elements become the keys of the resulting - dictionary. If the *fieldnames* parameter is omitted, the values in the - first row of the *csvfile* will be used as the fieldnames. If the row read - has more fields than the fieldnames sequence, the remaining data is added as - a sequence keyed by the value of *restkey*. If the row read has fewer - fields than the fieldnames sequence, the remaining keys take the value of - the optional *restval* parameter. Any other optional or keyword arguments - are passed to the underlying :class:`reader` instance. + Create an object that operates like a regular reader but maps the + information in each row to an :mod:`OrderedDict <collections.OrderedDict>` + whose keys are given by the optional *fieldnames* parameter. + + The *fieldnames* parameter is a :term:`sequence`. If *fieldnames* is + omitted, the values in the first row of the *csvfile* will be used as the + fieldnames. Regardless of how the fieldnames are determined, the ordered + dictionary preserves their original ordering. + + If a row has more fields than fieldnames, the remaining data is put in a + list and stored with the fieldname specified by *restkey* (which defaults + to ``None``). If a non-blank row has fewer fields than fieldnames, the + missing values are filled-in with ``None``. + + All other optional or keyword arguments are passed to the underlying + :class:`reader` instance. + + .. versionchanged:: 3.6 + Returned rows are now of type :class:`OrderedDict`. A short usage example:: @@ -170,9 +177,11 @@ The :mod:`csv` module defines the following classes: ... for row in reader: ... print(row['first_name'], row['last_name']) ... - Baked Beans - Lovely Spam - Wonderful Spam + Eric Idle + John Cleese + + >>> print(row) + OrderedDict([('first_name', 'John'), ('last_name', 'Cleese')]) .. class:: DictWriter(csvfile, fieldnames, restval='', extrasaction='raise', \ @@ -11,6 +11,7 @@ from _csv import Error, __version__, writer, reader, register_dialect, \ __doc__ from _csv import Dialect as _Dialect +from collections import OrderedDict from io import StringIO __all__ = ["QUOTE_MINIMAL", "QUOTE_ALL", "QUOTE_NONNUMERIC", "QUOTE_NONE", @@ -116,7 +117,7 @@ class DictReader: # values while row == []: row = next(self.reader) - d = dict(zip(self.fieldnames, row)) + d = OrderedDict(zip(self.fieldnames, row)) lf = len(self.fieldnames) lr = len(row) if lf < lr: diff --git a/Lib/test/test_csv.py b/Lib/test/test_csv.py index e97c9f3..9df4080 100644 --- a/Lib/test/test_csv.py +++ b/Lib/test/test_csv.py @@ -10,6 +10,7 @@ import csv import gc import pickle from test import support +from itertools import permutations class Test_Csv(unittest.TestCase): """ @@ -1092,6 +1093,21 @@ class TestUnicode(unittest.TestCase): fileobj.seek(0) self.assertEqual(fileobj.read(), expected) +class KeyOrderingTest(unittest.TestCase): + + def test_ordering_for_the_dict_reader_and_writer(self): + resultset = set() + for keys in permutations("abcde"): + with TemporaryFile('w+', newline='', encoding="utf-8") as fileobject: + dw = csv.DictWriter(fileobject, keys) + dw.writeheader() + fileobject.seek(0) + dr = csv.DictReader(fileobject) + kt = tuple(dr.fieldnames) + self.assertEqual(keys, kt) + resultset.add(kt) + # Final sanity check: were all permutations unique? + self.assertEqual(len(resultset), 120, "Key ordering: some key permutations not collected (expected 120)") class MiscTestCase(unittest.TestCase): def test__all__(self): @@ -64,6 +64,9 @@ Library match ``math.inf`` and ``math.nan``, and also ``cmath.infj`` and ``cmath.nanj`` to match the format used by complex repr. +- Issue #27842: The csv.DictReader now returns rows of type OrderedDict. + (Contributed by Steve Holden.) + - Issue #27861: Fixed a crash in sqlite3.Connection.cursor() when a factory creates not a cursor. Patch by Xiang Zhang. |