Fix Python CSV Field Limits: Set a Character Cap and Test Boundaries
A CSV export gains a long-text column, and a previously working script fails with _csv.Error: field larger than field limit (131072). The field may genuinely be large, or broken quoting may have absorbed several lines into one field. Choosing an enormous limit can hide the latter problem.
This beginner-to-intermediate tutorial reproduces the failure and builds a read-only tool with an explicit character cap and nonzero failure exit codes. It fits Linux exports and automation and requires only the Python standard library.
1. Method: inspect the field before choosing a cap
- Query the parser’s current limit and reproduce the failure.
- Check the export format and field meaning before choosing an acceptable cap.
- Parse strictly, test boundaries and broken quoting, and stop downstream work on failure.
The csv.field_size_limit() reference documents querying and changing the parser limit. Query your interpreter rather than treating the number in an error as a universal default.
This example uses CPython. Its _csv implementation checks field length before appending another Unicode character, so the cap applies to decoded field characters, rather than UTF-8 file bytes. Eight “汉” characters occupy 24 UTF-8 bytes but have a field length of eight. Surrounding CSV quotes are not field content.
2. Setup and fixtures
Use Python 3.9 or newer and bash or zsh. These examples were tested on Linux with CPython 3.11.4. They require no scheduler or system Python changes.
- Create an isolated demo directory. Choose a new name when repeating the experiment, and keep subsequent commands in this directory:
python3 --version
mkdir csv-field-demo
cd csv-field-demo
python3 - <<'PY_DATA'
import csv
from pathlib import Path
fixtures = {
"long.csv": [["id", "note"], ["1", "x" * 200_000]],
"edge.csv": [["id", "note"], ["1", "汉" * 8]],
"over.csv": [["id", "note"], ["1", "汉" * 9]],
"multiline.csv": [["id", "note"], ["1", "first\nsecond"]],
}
for name, rows in fixtures.items():
with open(name, "w", encoding="utf-8", newline="") as target:
csv.writer(target).writerows(rows)
Path("broken.csv").write_text('id,note\n1,"unfinished\n', encoding="utf-8")
Path("invalid.csv").write_bytes(b"id,note\n1,\xff\n")
PY_DATA
- Query the limit, then explicitly set 131072 to reproduce the error independently of the current default:
python3 -c 'import csv; print("current limit:", csv.field_size_limit())'
python3 - <<'PY_REPRO'
import csv
previous = csv.field_size_limit(131_072)
try:
with open("long.csv", encoding="utf-8", newline="") as source:
try:
for row in csv.reader(source):
pass
except csv.Error as exc:
print(f"Expected failure: {exc}")
else:
raise AssertionError("expected a field-limit error")
finally:
csv.field_size_limit(previous)
PY_REPRO
The final output should be Expected failure: field larger than field limit (131072). The second field in long.csv contains 200000 characters; broken.csv deliberately lacks a closing quote.
3. Complete implementation: cap fields and restore the setting
Save this code as inspect_csv.py in the current directory:
"""Inspect comma-separated UTF-8 CSV with an explicit field character limit."""
import argparse
import csv
import json
from pathlib import Path
import sys
def field_limit(value):
number = int(value)
if not 1 <= number <= 10_000_000:
raise argparse.ArgumentTypeError("must be between 1 and 10000000")
return number
def inspect_csv(path, limit):
previous = csv.field_size_limit(limit)
try:
records = 0
largest = 0
with open(path, encoding="utf-8-sig", newline="") as source:
reader = csv.reader(source, strict=True)
try:
for row in reader:
records += 1
largest = max(largest, max(map(len, row), default=0))
except csv.Error as exc:
raise ValueError(
f"physical line {reader.line_num}: {exc}"
) from exc
return {"records": records, "max_field_chars": largest, "limit": limit}
finally:
csv.field_size_limit(previous)
def main():
parser = argparse.ArgumentParser(description=__doc__, allow_abbrev=False)
parser.add_argument("file", type=Path)
parser.add_argument("--max-field-chars", type=field_limit, required=True,
help="maximum decoded characters per field (1..10000000)")
args = parser.parse_args()
try:
report = inspect_csv(args.file, args.max_field_chars)
except (OSError, UnicodeError, ValueError) as exc:
print(f"ERROR: {args.file}: {exc}", file=sys.stderr)
return 1
print(json.dumps(report))
return 0
if __name__ == "__main__":
raise SystemExit(main())
--max-field-chars must be between 1 and 10000000. This is the tool’s chosen operating range, rather than a CSV standard limit. The argparse type conversion interface rejects invalid arguments.
The file uses utf-8-sig decoding, accepting UTF-8 with or without a BOM. Identify and convert other encodings using the CSV encoding troubleshooting guide. We use newline="" and strict=True to catch parsing errors such as broken quoting; see the CSV format parameter reference. Strict mode does not validate headers, column counts, or business fields.
reader.line_num marks the physical line reached, which differs from record numbering when fields span lines; see the reader line-number reference. Use the position to inspect nearby input, rather than assuming it is the broken field’s starting line.
The limit is a module setting shared within the interpreter. finally restores it after success or failure; this does not isolate concurrent threads. Use separate processes for concurrent tasks with different caps.
4. Usage and expected results
- Show help, then inspect the long field, Chinese character boundary, and multiline field:
python3 inspect_csv.py --help
python3 inspect_csv.py long.csv --max-field-chars 262144
python3 inspect_csv.py edge.csv --max-field-chars 8
python3 inspect_csv.py multiline.csv --max-field-chars 12
The three checks print, in order:
{"records": 2, "max_field_chars": 200000, "limit": 262144}
{"records": 2, "max_field_chars": 8, "limit": 8}
{"records": 2, "max_field_chars": 12, "limit": 12}
records includes the header; max_field_chars is the largest successfully parsed field. Embedded newlines count as content: first\nsecond has a length of 12 here.
- Integrate using these exit codes:
| Exit code | Meaning |
|---|---|
| 0 | The entire file parsed; stdout contains one JSON line. |
| 1 | Oversized field, bad CSV, decoding error, or file access failure; errors go only to stderr. |
| 2 | Invalid command-line arguments. |
JSON appears only after reaching the end, so partial statistics cannot look like success. An empty file succeeds with zero records. If the task requires a header and fixed column count, also apply CSV structure validation.
5. Test boundaries and failure paths
This self-check verifies that the exact cap succeeds and one extra character fails. It also checks that inputs stay unchanged and the module setting is restored:
python3 - <<'PY_CHECK'
import csv
import json
from pathlib import Path
import subprocess
import sys
from inspect_csv import inspect_csv
cases = [
(["edge.csv", "--max-field-chars", "8"], 0, 8),
(["over.csv", "--max-field-chars", "8"], 1, None),
(["long.csv", "--max-field-chars", "131072"], 1, None),
(["long.csv", "--max-field-chars", "262144"], 0, 200000),
(["multiline.csv", "--max-field-chars", "12"], 0, 12),
(["broken.csv", "--max-field-chars", "64"], 1, None),
(["invalid.csv", "--max-field-chars", "64"], 1, None),
(["missing.csv", "--max-field-chars", "64"], 1, None),
(["edge.csv", "--max-field-chars", "0"], 2, None),
(["edge.csv", "--max-field-chars", "10000001"], 2, None),
]
before = {p: p.read_bytes() for p in Path(".").glob("*.csv")}
for arguments, code, largest in cases:
result = subprocess.run(
[sys.executable, "inspect_csv.py", *arguments],
capture_output=True, text=True, check=False,
)
assert result.returncode == code, (arguments, result)
if code == 0:
assert json.loads(result.stdout) == {
"records": 2, "max_field_chars": largest,
"limit": int(arguments[-1]),
}, result
assert result.stderr == "", result
else:
assert result.stdout == "" and result.stderr, result
if arguments[0] == "over.csv":
assert "physical line 2: field larger than field limit (8)" in result.stderr
if arguments[0] == "broken.csv":
assert "unexpected end of data" in result.stderr
original = csv.field_size_limit()
for name in ("edge.csv", "over.csv"):
try:
inspect_csv(name, 8)
except ValueError:
assert name == "over.csv"
assert csv.field_size_limit() == original
assert all(p.read_bytes() == content for p, content in before.items())
print("OK: 10 CLI cases; limit restored; inputs unchanged")
PY_CHECK
Expected output:
OK: 10 CLI cases; limit restored; inputs unchanged
Subprocess checks inspect real exit codes, allowing expected failures without aborting the test. Handle nonzero status in production too; file existence alone is insufficient.
6. Troubleshooting: a higher limit may not fix the cause
| Symptom | Action |
|---|---|
| The field genuinely exceeds the current cap | Confirm its maximum reasonable length, then retry a finite value such as --max-field-chars 262144. |
| A larger cap still produces quoting or EOF errors | Fix truncation or escaping in the exporter instead of increasing the cap again. |
UnicodeDecodeError |
Identify the source encoding and convert strictly; do not discard content with errors="ignore". |
| A whole line becomes one field | Check the delimiter; this tool accepts comma-separated input without automatic guessing. |
| An oversized argument returns 2 | Choose an in-range cap for the task; larger legitimate fields need a separate tool and memory-budget review. |
A field cap is neither a total file-size limit nor a process memory limit. Parsing still holds the current field and record, and text input may allocate a long physical line first. A row containing many short fields can also consume substantial memory. This is not complete resource isolation.
After checking the file, follow streaming large CSV files to process records individually. This tool reads input without truncating fields, repairing quotes, or writing converted CSV.
7. Summary
Distinguish genuine long fields from damaged exports before choosing an explicit character cap with csv.field_size_limit(). Verify exact boundaries, oversized fields, broken quotes, decoding failures, and exit codes before handing success to downstream work. Restoring the module setting reduces interference between sequential calls; concurrent tasks still need separate processes.
- 原文作者:春江暮客
- 原文链接:https://www.bobobk.com/en/python-csv-field-size-limit.html
- 版权声明:本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。
相关文章
- Read CSV Files Inside tar.gz with Python: Summaries Without Extraction
- Fix Python UnicodeEncodeError in Linux Jobs: Inspect stdout and Select UTF-8
- Fix Python BrokenPipeError with head and Verify Pipeline Exit Codes
- Compare CSV Snapshots by ID with Python: Added, Removed, and Changed Records
- Split Large CSV Files with Python: Headers, Multiline Fields, and Completion Checks