Skip to main content

Modules/_csv.c (part 4)

Source:

cpython 3.14 @ ab2d84fe1023/Modules/_csv.c

This annotation covers CSV writing and dialect management. See modules_csv3_detail for csv.reader, csv.DictReader, and field parsing.

Map

LinesSymbolRole
1-80csv_writerowWrite one row as a CSV line
81-160Quoting logicQUOTE_ALL, QUOTE_MINIMAL, QUOTE_NONNUMERIC, QUOTE_NONE
161-260csv.DictWriter.writerowWrite from a dict using fieldnames
261-360Dialect registrationcsv.register_dialect, csv.unregister_dialect
361-500Dialect lookupcsv.get_dialect, csv.list_dialects

Reading

csv_writerow

// CPython: Modules/_csv.c:820 csv_writerow
static PyObject *
csv_writerow(WriterObj *self, PyObject *seq)
{
DialectObj *dialect = self->dialect;
Py_ssize_t len = PySequence_Length(seq);
for (Py_ssize_t field_num = 0; field_num < len; field_num++) {
PyObject *field = PySequence_GetItem(seq, field_num);
int append_ok = csv_join_append(self, field, field_num == len - 1);
...
}
/* Write accumulated row to the underlying writer */
PyObject *line = join_finalize(self);
return PyObject_CallOneArg(self->writeline, line);
}

Each field is processed by csv_join_append which applies quoting/escaping rules. The final line (without trailing newline) is passed to the writer object's callable (typically file.write).

Quoting modes

// CPython: Modules/_csv.c:740 csv_join_append
static int
csv_join_append(WriterObj *self, PyObject *field, int quoted)
{
/* QUOTE_ALL: always quote
QUOTE_MINIMAL: quote only if field contains delimiter, quotechar, or lineterminator
QUOTE_NONNUMERIC: quote non-numeric fields
QUOTE_NONE: never quote, escape special chars */
switch (self->dialect->quoting) {
case QUOTE_ALL: quoted = 1; break;
case QUOTE_NONNUMERIC:
if (!PyFloat_Check(field) && !PyLong_Check(field)) quoted = 1;
break;
case QUOTE_MINIMAL:
/* Check if field contains special characters */
...
break;
case QUOTE_NONE:
/* Use escapechar instead of quotechar */
...
break;
}
...
}

QUOTE_NONNUMERIC on reading converts all unquoted fields to float. This is a useful round-trip convention: write with QUOTE_NONNUMERIC, read with QUOTE_NONNUMERIC and all numbers come back as floats.

Dialect registration

// CPython: Modules/_csv.c:1080 csv_register_dialect
static PyObject *
csv_register_dialect(PyObject *module, PyObject *args, PyObject *kwargs)
{
PyObject *name, *dialect_obj = NULL;
/* ... parse args: name [, dialect] [, **fmtparams] */
DialectObj *dialect = (DialectObj *)_call_dialect(dialect_obj, kwargs);
PyDict_SetItem(module_state->dialects, name, (PyObject *)dialect);
Py_RETURN_NONE;
}

csv.register_dialect('pipes', delimiter='|') saves a named dialect in the module's dialect dict. csv.reader(f, 'pipes') then looks up the dialect by name. The built-in dialects 'excel', 'excel-tab', and 'unix' are registered at module init.

gopy notes

csv_writerow is module/csv.WriterWriteRow in module/csv/module.go. The quoting logic is a Go switch on dialect.Quoting. Dialect registration stores *csvDialect objects in a map[string]*csvDialect in module state. DictWriter.writerow maps fieldnames via objects.DictGetItem.