Desk Clarity

Everyday document & browser help

Python CSV Turns None Into Empty Text: Define a Reversible Null Contract

By ON Lab · Sources checked November 9, 2026

Check the serialized data contract before blaming the reader. Default CSV writing can erase the distinction between None and empty text; preserve it explicitly if downstream logic needs it.

Reproduce the boundary in a disposable example

A CSV round trip cannot distinguish a missing value from an empty string.

import csv, io
stream = io.StringIO(newline="")
csv.writer(stream).writerow([None, ""])
assert next(csv.reader(io.StringIO(stream.getvalue()))) == ["", ""]
# An explicit null flag is understood by ordinary CSV readers.
stream = io.StringIO(newline="")
writer = csv.writer(stream)
writer.writerow(["value", "is_null"])
for value in [None, "", "NULL", "hello"]:
    writer.writerow(["" if value is None else value,
                     "1" if value is None else "0"])
reader = csv.DictReader(io.StringIO(stream.getvalue()))
restored = [None if row["is_null"] == "1" else row["value"]
            for row in reader]
assert restored == [None, "", "NULL", "hello"]
print("explicit null column round trip verified")

Read the result before changing application code

The first assertion demonstrates information loss before any repair attempt. The second format keeps a separate flag, so the legitimate text NULL remains text. This is an example schema, not a standard that another application will automatically recognize. The producer and consumer must agree on the flag meaning and reject invalid flag values in production.

Choose the behavior your caller needs

Choose an explicit schema field, an escaped sentinel with a collision policy, or a supported quoting mode whose reader also understands its semantics. Python offers QUOTE_NOTNULL in newer versions, but compatibility with spreadsheets and other readers still needs verification. Do not replace every empty field with None when empty text is a valid business value. Preserve the original file while investigating a lossy export.

Scope and limits

This example avoids depending on a newer quoting constant. It does not establish how a particular spreadsheet imports CSV or restore distinctions already lost in the only available file.

Related: compare the adjacent data or execution boundary.

The embedded example was executed in an isolated local Python process on November 9, 2026. It is a reproducible boundary check, not a test of your full application.

Sources