Skip to content

csv.writer: QUOTE_NONNUMERIC does not quote numeric-looking strings #8413

Description

@jinmay

Feature

Follow-up from #8302 / #8322.

With QUOTE_NONNUMERIC, csv.writer decides quoting from the serialized bytes instead of the original Python object, so numeric-looking strings such as "123" are written unquoted. Reading the output back with QUOTE_NONNUMERIC then silently turns them into floats.

import csv, io
sio = io.StringIO()
csv.writer(sio, quoting=csv.QUOTE_NONNUMERIC).writerow(["123", 123, "a"])
print(repr(sio.getvalue()))
# CPython:    '"123",123,"a"\r\n'
# RustPython: '123,123,"a"\r\n'

CPython decides before stringification, on the original object: quoted = !PyNumber_Check(field) (Modules/_csv.c, csv_writerow). Since str "123" and int 123 stringify to identical bytes, this cannot be expressed through csv_core::Writer, which still handles QUOTE_ALL/QUOTE_NONNUMERIC — the other four quoting modes were already moved to hand-written paths in #8260 / #8304 / #8315, one function per mode, duplicating the same row-serialization skeleton.

Rather than adding a fourth and fifth copy, I'd like to fix this by unifying the writer into a single serialization loop with one per-field quote-decision function covering all six modes. That makes the csv_core::Writer path dead code, so the stateful writer, its buffer machinery, and the single-byte terminator sentinel workaround from #8328 can be removed. Scope is writer-only: the reader keeps using csv-core, and non-ASCII dialect characters remain a separate follow-up (#8310 territory).

QUOTE_ALL is unaffected by the bug (it always quotes). test_write_quoting (Lib/test/test_csv.py) does not catch this because it has no numeric-looking string case.

Python Documentation or reference to CPython source code

https://docs.python.org/3/library/csv.html#csv.QUOTE_NONNUMERIC

Drafted with AI assistance (Claude Code), reviewed by me.

Metadata

Metadata

Assignees

Labels

z-ca-2026Tag to track Contribution Academy 2026

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions