Repository navigation
csv writer needs more quoting rules #67230
Copy link
Copy link
Closed
Labels
3.13only security fixesonly security fixesstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytype-featureA feature request or enhancementA feature request or enhancement
Description
| BPO | 23041 |
|---|---|
| Nosy | @smontanaro, @rhettinger, @bitdancer, @berkerpeksag, @serhiy-storchaka, @yoonghm, @erdnaxeli, @msetina |
| PRs | |
| Files |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
assignee = 'https://github.com/smontanaro'
closed_at = None
created_at = <Date 2014-12-12.16:36:45.576>
labels = ['easy', 'type-feature', 'library', '3.11']
title = 'csv needs more quoting rules'
updated_at = <Date 2022-02-24.00:07:17.586>
user = 'https://bugs.python.org/samwyse'bugs.python.org fields:
activity = <Date 2022-02-24.00:07:17.586>
actor = 'samwyse'
assignee = 'skip.montanaro'
closed = False
closed_date = None
closer = None
components = ['Library (Lib)']
creation = <Date 2014-12-12.16:36:45.576>
creator = 'samwyse'
dependencies = []
files = ['37444', '37445']
hgrepos = []
issue_num = 23041
keywords = ['patch', 'easy']
message_count = 26.0
messages = ['232560', '232561', '232563', '232596', '232607', '232630', '232676', '232677', '232681', '261141', '261146', '261147', '341460', '358461', '396621', '396641', '396642', '396643', '401607', '401608', '405951', '406013', '406015', '406017', '412463', '413867']
nosy_count = 11.0
nosy_names = ['skip.montanaro', 'rhettinger', 'samwyse', 'r.david.murray', 'berker.peksag', 'serhiy.storchaka', 'krypten', 'yoonghm', 'tegdev', 'erdnaxeli', 'msetina']
pr_nums = ['29469']
priority = 'normal'
resolution = None
stage = 'patch review'
status = 'open'
superseder = None
type = 'enhancement'
url = 'https://bugs.python.org/issue23041'
versions = ['Python 3.11']Linked PRs
- gh-67230: document new csv quoting modes in whatsnew (gh-103491) #103491
- gh-67230: Update Misc/whatsnew/3.12.rst for csv module changes #103598
- gh-67230: Add versionadded notes for QUOTE_NOTNULL and QUOTE_STRINGS #114816
- [3.12] gh-67230: Add versionadded notes for QUOTE_NOTNULL and QUOTE_STRINGS (GH-114816) #114840
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
3.13only security fixesonly security fixesstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytype-featureA feature request or enhancementA feature request or enhancement
Projects
- StatusShow more project fieldsDone
Activity
samwyse commentedon Dec 12, 2014
The csv module currently implements four quoting rules for dialects: QUOTE_MINIMAL, QUOTE_ALL, QUOTE_NONNUMERIC and QUOTE_NONE. These rules treat values of None the same as an empty string, i.e. by outputting two consecutive quotes. I propose the addition of two new rules, QUOTE_NOTNULL and QUOTE_STRINGS. The former behaves like QUOTE_ALL while the later behaves like QUOTE_NONNUMERIC, except that in both cases values of None are output as an empty field. Examples follow.
Current behavior (which will remain unchanged)
Proposed behavior
bitdancer commentedon Dec 12, 2014
As an enhancement, this could be added only to 3.5. The proposal sounds reasonable to me.
samwyse commentedon Dec 12, 2014
David: That's not a problem for me.
Sorry I can't provide real patches, but I'm not in a position to compile (much less test) the C implementation of _csv. I've looked at the code online and below are the changes that I think need to be made. My use cases don't require special handing when reading empty fields, so the only changes I've made are to the code for writers. I did verify that the reader code mostly only checks for QUOTE_NOTNULL when parsing. This means that completely empty fields will continue to load as zero-length strings, not None. I won't stand in the way of anyone wanting to "fix" that for these new rules.
typedef enum {
QUOTE_MINIMAL, QUOTE_ALL, QUOTE_NONNUMERIC, QUOTE_NONE,
QUOTE_STRINGS, QUOTE_NOTNULL
} QuoteStyle;
static StyleDesc quote_styles[] = {
{ QUOTE_MINIMAL, "QUOTE_MINIMAL" },
{ QUOTE_ALL, "QUOTE_ALL" },
{ QUOTE_NONNUMERIC, "QUOTE_NONNUMERIC" },
{ QUOTE_NONE, "QUOTE_NONE" },
{ QUOTE_STRINGS, "QUOTE_STRINGS" },
{ QUOTE_NOTNULL, "QUOTE_NOTNULL" },
{ 0 }
};
" csv.QUOTE_MINIMAL means only when required, for example, when a\n"
" field contains either the quotechar or the delimiter\n"
" csv.QUOTE_ALL means that quotes are always placed around fields.\n"
" csv.QUOTE_NONNUMERIC means that quotes are always placed around\n"
" fields which do not parse as integers or floating point\n"
" numbers.\n"
" csv.QUOTE_STRINGS means that quotes are always placed around\n"
" fields which are strings. Note that the Python value None\n"
" is not a string.\n"
" csv.QUOTE_NOTNULL means that quotes are only placed around fields\n"
" that are not the Python value None.\n"
rhettinger commentedon Dec 13, 2014
Samwyse, are these suggestions just based on ideas of what could be done or have you encountered real-world CSV data exchanges that couldn't be handled by the CSV module?
smontanaro commentedon Dec 13, 2014
It doesn't look like a difficult change, but is it really needed? I guess my reaction is the same as Raymond's. Are there real-world uses where the current set of quoting styles isn't sufficient?
krypten commentedon Dec 14, 2014
Used function PyUnicode_Check instead of PyString_Check
samwyse commentedon Dec 15, 2014
Yes, it's based on a real-world need. I work for a Fortune 500 company and we have an internal tool that exports CSV files using what I've described as the QUOTE_NOTNULL rules. I need to create similar files for re-importation. Right now, I have to post-process the output of my Python program to get it right. I added in the QUOTE_STRINGS rule for completeness. I think these two new rules would be useful for anyone wanting to create sparse CSV files.
smontanaro commentedon Dec 15, 2014
If I understand correctly, your software needs to distinguish between
# wrote ["foo", "", 42, None] with quote_all in effect
"foo","","42",""
and
# wrote ["foo", None, 42, ""] with quote_nonnull in effect
"foo",,"42",""
so you in effect want to transmit some type information through a CSV file?
samwyse commentedon Dec 15, 2014
Skip, I don't have any visibility into how the Java program I'm feeding data into works, I'm just trying to replicate the csv files that it exports as accurately as possible. It has several other quirks, but I can replicate all of them using Dialects; this is the only "feature" I can't. The files I'm looking at have quoted strings and numbers, but there aren't any quoted empty strings. I'm using a DictWriter to create similar csv files, where missing keys are treated as values of None, so I'd like those printed without quotes. If we also want to print empty strings without quotes, that wouldn't impact me at all.
Besides my selfish needs, this could be useful to anyone wanting to reduce the save of csv files that have lots of empty fields, but wants to quote all non-empty values. This may be an empty set, I don't know.
smontanaro commentedon Mar 2, 2016
Thanks for the update berker.peksag. I'm still not convinced that the csv module should be modified just so one user (sorry samwyse) can match the input format of someone's Java program. It seems a bit like trying to make the csv module type-sensitive. What happens when someone finds a csv file containing timestamps in a format other than the datetime.datetime object will produce by default? Why is None special as an object where bool(obj) is False?
I think the better course here is to either:
subclass csv.DictWriter, use dictionaries as your element type, and have its writerow method do the application-specific work.
define a writerow() function which does something similar (essentially wrapping csv.writerow()).
If someone else thinks this is something which belongs in Python's csv module, feel free to reopen and assign it to yourself.
serhiy-storchaka commentedon Mar 3, 2016
The csv module is already type-sensitive (with QUOTE_NONNUMERIC). I agree, that we shouldn't modify the csv module just for one user and one program.
If a standard CVS library in Java (or other popular laguages) differentiates between empty string and null value when read from CSV, it would be a serious argument to support this in Python. Quick search don't find this.
71 remaining items
erlend-aasland commentedon Jan 5, 2024
I'll try to summarise the state of this issue:
The proposed feature was to add more quoting rules to the
csvwriter. This has been implemented (with docs and tests)1. @serhiy-storchaka requested in #67230 (comment) that the issue be held open:@samwyse remarked in #67230 (comment):
@smontanaro #67230 (comment):
@smontanaro #67230 (comment):
I suggest to close this as completed (the csv writer feature was implemented) and to open up a new issue regarding what to do with the csv reader.
Footnotes
we should amend the issue title to reflect that the feature request was for the csv writer only ↩
[-]csv needs more quoting rules[/-][+]csv writer needs more quoting rules[/+]serhiy-storchaka commentedon Jan 5, 2024
But it does.
If quoting style is QUOTE_NONNUMERIC, it returns non-quoted field as numeric. I expect the same for None.
erlend-aasland commentedon Jan 5, 2024
As I already wrote, I suggest to summarise the needed reader changes in a new issue.
smontanaro commentedon Jan 5, 2024
That's not really inferring the type in my book. It's effectively being told by the programmer that unquoted numeric strings are to be returned as numbers. If it was inferring the type, your first example would also spit out a float as the first value.
serhiy-storchaka commentedon Jan 5, 2024
Right, and I suggest that QUOTE_NOTNULL is told by the programmer that unquoted empty strings are to be returned as None.
erlend-aasland commentedon Jan 5, 2024
Can you please suggest this csv reader change in a new issue?
serhiy-storchaka commentedon Jan 5, 2024
It is perhaps too late to make this change in 3.12, so we can open a new issue.
serhiy-storchaka commentedon Jan 5, 2024
Opened #113732.
gh-67230: Add versionadded notes for QUOTE_NOTNULL and QUOTE_STRINGS (#…
pythongh-67230: Add versionadded notes for QUOTE_NOTNULL and QUOTE_ST…
[3.12] gh-67230: Add versionadded notes for QUOTE_NOTNULL and QUOTE_S…
pythongh-67230: Add versionadded notes for QUOTE_NOTNULL and QUOTE_ST…