csv¶
csv.reader() - Read CSV data from file-like object.¶
csv.writer() - Write CSV data to file-like object.¶
csv.DictReader() - Read CSV with first row as field names.¶
csv.DictWriter() - Write CSV from dictionaries.¶
CSV dialects - Predefined formatting styles.¶
CSV quoting behavior - Control when fields are quoted.¶
CSV escaping behavior - Handle special characters.¶
csv.Sniffer - Detect CSV dialect from sample.¶
Edge cases and special scenarios.¶
Basic: Read simple CSV data.¶
def test_reader_basic(self):
"""Basic: Read simple CSV data."""
data = 'a,b,c\n1,2,3\n4,5,6'
reader = csv.reader(io.StringIO(data))
rows = list(reader)
assert rows == [['a', 'b', 'c'], ['1', '2', '3'], ['4', '5', '6']]
Verification: ✅ Tested in CI
Feature: Iterate over CSV rows.¶
def test_reader_iterate(self):
"""Feature: Iterate over CSV rows."""
data = 'name,age\nAlice,30\nBob,25'
reader = csv.reader(io.StringIO(data))
rows = []
for row in reader:
rows.append(row)
assert len(rows) == 3
assert rows[0] == ['name', 'age']
Verification: ✅ Tested in CI
Edge: Empty fields are preserved.¶
def test_reader_empty_fields(self):
"""Edge: Empty fields are preserved."""
data = 'a,,c\n1,2,'
reader = csv.reader(io.StringIO(data))
rows = list(reader)
assert rows == [['a', '', 'c'], ['1', '2', '']]
Verification: ✅ Tested in CI
Feature: Quoted fields preserve commas.¶
def test_reader_quoted_fields(self):
"""Feature: Quoted fields preserve commas."""
data = 'a,"b,c",d\n1,"2,3",4'
reader = csv.reader(io.StringIO(data))
rows = list(reader)
assert rows == [['a', 'b,c', 'd'], ['1', '2,3', '4']]
Verification: ✅ Tested in CI
Edge: Newlines in quoted fields are preserved.¶
def test_reader_newline_in_quoted_field(self):
"""Edge: Newlines in quoted fields are preserved."""
data = 'a,"b\nc",d\n1,2,3'
reader = csv.reader(io.StringIO(data))
rows = list(reader)
assert rows[0] == ['a', 'b\nc', 'd']
Verification: ✅ Tested in CI
Feature: Custom delimiter support.¶
def test_reader_custom_delimiter(self):
"""Feature: Custom delimiter support."""
data = 'a;b;c\n1;2;3'
reader = csv.reader(io.StringIO(data), delimiter=';')
rows = list(reader)
assert rows == [['a', 'b', 'c'], ['1', '2', '3']]
Verification: ✅ Tested in CI
Edge: Empty lines are yielded as empty lists.¶
def test_reader_empty_lines_skipped(self):
"""Edge: Empty lines are yielded as empty lists."""
data = 'a,b,c\n\n1,2,3'
reader = csv.reader(io.StringIO(data))
rows = list(reader)
assert len(rows) == 3
assert rows[0] == ['a', 'b', 'c']
Verification: ✅ Tested in CI
Basic: Write simple CSV data.¶
def test_writer_basic(self):
"""Basic: Write simple CSV data."""
output = io.StringIO()
writer = csv.writer(output)
writer.writerow(['a', 'b', 'c'])
writer.writerow(['1', '2', '3'])
result = output.getvalue()
assert result == 'a,b,c\r\n1,2,3\r\n'
Verification: ✅ Tested in CI
Feature: Write multiple rows at once.¶
def test_writer_writerows(self):
"""Feature: Write multiple rows at once."""
output = io.StringIO()
writer = csv.writer(output)
writer.writerows([['a', 'b'], ['1', '2'], ['3', '4']])
result = output.getvalue()
assert 'a,b' in result
assert '1,2' in result
Verification: ✅ Tested in CI
Feature: Fields with commas are quoted automatically.¶
def test_writer_quoting_commas(self):
"""Feature: Fields with commas are quoted automatically."""
output = io.StringIO()
writer = csv.writer(output)
writer.writerow(['a', 'b,c', 'd'])
result = output.getvalue()
assert '"b,c"' in result
Verification: ✅ Tested in CI
Feature: Custom delimiter support.¶
def test_writer_custom_delimiter(self):
"""Feature: Custom delimiter support."""
output = io.StringIO()
writer = csv.writer(output, delimiter=';')
writer.writerow(['a', 'b', 'c'])
result = output.getvalue()
assert result == 'a;b;c\r\n'
Verification: ✅ Tested in CI
Edge: Empty fields are written correctly.¶
def test_writer_empty_field(self):
"""Edge: Empty fields are written correctly."""
output = io.StringIO()
writer = csv.writer(output)
writer.writerow(['a', '', 'c'])
result = output.getvalue()
assert result == 'a,,c\r\n'
Verification: ✅ Tested in CI
Property: Numeric values are converted to strings.¶
def test_writer_numeric_values(self):
"""Property: Numeric values are converted to strings."""
output = io.StringIO()
writer = csv.writer(output)
writer.writerow([1, 2, 3])
result = output.getvalue()
assert result == '1,2,3\r\n'
Verification: ✅ Tested in CI
Basic: Read CSV as dictionaries.¶
def test_dictreader_basic(self):
"""Basic: Read CSV as dictionaries."""
data = 'name,age,city\nAlice,30,NYC\nBob,25,LA'
reader = csv.DictReader(io.StringIO(data))
rows = list(reader)
assert len(rows) == 2
assert rows[0] == {'name': 'Alice', 'age': '30', 'city': 'NYC'}
assert rows[1] == {'name': 'Bob', 'age': '25', 'city': 'LA'}
Verification: ✅ Tested in CI
Property: fieldnames accessible after reading.¶
def test_dictreader_fieldnames_property(self):
"""Property: fieldnames accessible after reading."""
data = 'name,age\nAlice,30'
reader = csv.DictReader(io.StringIO(data))
list(reader)
assert reader.fieldnames == ['name', 'age']
Verification: ✅ Tested in CI
Feature: Specify custom field names.¶
def test_dictreader_custom_fieldnames(self):
"""Feature: Specify custom field names."""
data = 'Alice,30\nBob,25'
reader = csv.DictReader(io.StringIO(data), fieldnames=['name', 'age'])
rows = list(reader)
assert rows[0] == {'name': 'Alice', 'age': '30'}
Verification: ✅ Tested in CI
Edge: Missing fields get None values.¶
def test_dictreader_missing_fields(self):
"""Edge: Missing fields get None values."""
data = 'name,age,city\nAlice,30\nBob,25,LA'
reader = csv.DictReader(io.StringIO(data))
rows = list(reader)
assert rows[0] == {'name': 'Alice', 'age': '30', 'city': None}
Verification: ✅ Tested in CI
Edge: Extra fields go into restkey.¶
def test_dictreader_extra_fields(self):
"""Edge: Extra fields go into restkey."""
data = 'name,age\nAlice,30,extra'
reader = csv.DictReader(io.StringIO(data))
rows = list(reader)
assert rows[0]['name'] == 'Alice'
assert rows[0]['age'] == '30'
Verification: ✅ Tested in CI
Property: DictReader is iterable.¶
def test_dictreader_iterate(self):
"""Property: DictReader is iterable."""
data = 'name,age\nAlice,30\nBob,25'
reader = csv.DictReader(io.StringIO(data))
count = 0
for row in reader:
assert isinstance(row, dict)
count += 1
assert count == 2
Verification: ✅ Tested in CI
Basic: Write dictionaries as CSV.¶
def test_dictwriter_basic(self):
"""Basic: Write dictionaries as CSV."""
output = io.StringIO()
fieldnames = ['name', 'age']
writer = csv.DictWriter(output, fieldnames=fieldnames)
writer.writeheader()
writer.writerow({'name': 'Alice', 'age': 30})
writer.writerow({'name': 'Bob', 'age': 25})
result = output.getvalue()
assert 'name,age' in result
assert 'Alice,30' in result
assert 'Bob,25' in result
Verification: ✅ Tested in CI
Feature: writeheader() writes field names.¶
def test_dictwriter_writeheader(self):
"""Feature: writeheader() writes field names."""
output = io.StringIO()
writer = csv.DictWriter(output, fieldnames=['a', 'b', 'c'])
writer.writeheader()
result = output.getvalue()
assert result == 'a,b,c\r\n'
Verification: ✅ Tested in CI
Edge: Missing fields in dict use empty string.¶
def test_dictwriter_missing_field(self):
"""Edge: Missing fields in dict use empty string."""
output = io.StringIO()
writer = csv.DictWriter(output, fieldnames=['name', 'age', 'city'])
writer.writeheader()
writer.writerow({'name': 'Alice', 'age': 30})
result = output.getvalue()
assert 'Alice,30,' in result
Verification: ✅ Tested in CI
Error: Extra fields in dict raise ValueError by default.¶
def test_dictwriter_extra_field_raises(self):
"""Error: Extra fields in dict raise ValueError by default."""
output = io.StringIO()
writer = csv.DictWriter(output, fieldnames=['name', 'age'])
with pytest.raises(ValueError):
writer.writerow({'name': 'Alice', 'age': 30, 'city': 'NYC'})
Verification: ✅ Tested in CI
Feature: extrasaction='ignore' skips extra fields.¶
def test_dictwriter_extrasaction_ignore(self):
"""Feature: extrasaction='ignore' skips extra fields."""
output = io.StringIO()
writer = csv.DictWriter(output, fieldnames=['name', 'age'], extrasaction='ignore')
writer.writerow({'name': 'Alice', 'age': 30, 'city': 'NYC'})
result = output.getvalue()
assert result == 'Alice,30\r\n'
Verification: ✅ Tested in CI
Feature: writerows() writes multiple dicts.¶
def test_dictwriter_writerows(self):
"""Feature: writerows() writes multiple dicts."""
output = io.StringIO()
writer = csv.DictWriter(output, fieldnames=['a', 'b'])
writer.writerows([{'a': 1, 'b': 2}, {'a': 3, 'b': 4}])
result = output.getvalue()
assert '1,2' in result
assert '3,4' in result
Verification: ✅ Tested in CI
Feature: excel dialect is default.¶
def test_excel_dialect(self):
"""Feature: excel dialect is default."""
output = io.StringIO()
writer = csv.writer(output, dialect='excel')
writer.writerow(['a', 'b'])
result = output.getvalue()
assert 'a,b' in result
Verification: ✅ Tested in CI
Feature: excel-tab dialect uses tabs.¶
def test_excel_tab_dialect(self):
"""Feature: excel-tab dialect uses tabs."""
output = io.StringIO()
writer = csv.writer(output, dialect='excel-tab')
writer.writerow(['a', 'b', 'c'])
result = output.getvalue()
assert 'a\tb\tc' in result
Verification: ✅ Tested in CI
Feature: unix dialect uses LF line terminator.¶
def test_unix_dialect(self):
"""Feature: unix dialect uses LF line terminator."""
output = io.StringIO()
writer = csv.writer(output, dialect='unix')
writer.writerow(['a', 'b'])
result = output.getvalue()
assert result == '"a","b"\n'
Verification: ✅ Tested in CI
Property: csv.list_dialects() returns available dialects.¶
def test_list_dialects(self):
"""Property: csv.list_dialects() returns available dialects."""
dialects = csv.list_dialects()
assert 'excel' in dialects
assert 'unix' in dialects
Verification: ✅ Tested in CI
Property: QUOTE_MINIMAL is default (quote only when needed).¶
def test_quote_minimal_default(self):
"""Property: QUOTE_MINIMAL is default (quote only when needed)."""
output = io.StringIO()
writer = csv.writer(output, quoting=csv.QUOTE_MINIMAL)
writer.writerow(['a', 'b,c', 'd'])
result = output.getvalue()
assert 'a,' in result or 'a"' not in result
assert '"b,c"' in result
Verification: ✅ Tested in CI
Feature: QUOTE_ALL quotes every field.¶
def test_quote_all(self):
"""Feature: QUOTE_ALL quotes every field."""
output = io.StringIO()
writer = csv.writer(output, quoting=csv.QUOTE_ALL)
writer.writerow(['a', 'b', 'c'])
result = output.getvalue()
assert '"a","b","c"' in result
Verification: ✅ Tested in CI
Feature: QUOTE_NONNUMERIC quotes non-numeric fields.¶
def test_quote_nonnumeric(self):
"""Feature: QUOTE_NONNUMERIC quotes non-numeric fields."""
output = io.StringIO()
writer = csv.writer(output, quoting=csv.QUOTE_NONNUMERIC)
writer.writerow(['a', 1, 2.5])
result = output.getvalue()
assert '"a"' in result
assert result.count('"') == 2
Verification: ✅ Tested in CI
Edge: QUOTE_NONE never quotes (escapes instead).¶
def test_quote_none(self):
"""Edge: QUOTE_NONE never quotes (escapes instead)."""
output = io.StringIO()
writer = csv.writer(output, quoting=csv.QUOTE_NONE, escapechar='\\')
writer.writerow(['a', 'b', 'c'])
result = output.getvalue()
assert '"' not in result
Verification: ✅ Tested in CI
Feature: Quotes in quoted fields are doubled.¶
def test_escape_quotes(self):
"""Feature: Quotes in quoted fields are doubled."""
output = io.StringIO()
writer = csv.writer(output)
writer.writerow(['a', 'b"c', 'd'])
result = output.getvalue()
assert 'b""c' in result
Verification: ✅ Tested in CI
Feature: Custom escape character.¶
def test_custom_escapechar(self):
"""Feature: Custom escape character."""
output = io.StringIO()
writer = csv.writer(output, quoting=csv.QUOTE_NONE, escapechar='\\')
writer.writerow(['a', 'b,c'])
result = output.getvalue()
assert 'b\\,c' in result
Verification: ✅ Tested in CI
Edge: doublequote=False uses escapechar.¶
def test_doublequote_disabled(self):
"""Edge: doublequote=False uses escapechar."""
output = io.StringIO()
writer = csv.writer(output, doublequote=False, escapechar='\\')
writer.writerow(['a', 'b"c'])
result = output.getvalue()
assert 'b\\"c' in result or 'b\\"c' in result
Verification: ✅ Tested in CI
Feature: Sniffer detects delimiter.¶
def test_sniffer_detect_delimiter(self):
"""Feature: Sniffer detects delimiter."""
sample = 'a;b;c\n1;2;3\n'
sniffer = csv.Sniffer()
dialect = sniffer.sniff(sample)
assert dialect.delimiter == ';'
Verification: ✅ Tested in CI
Feature: Sniffer detects if first row is header.¶
def test_sniffer_has_header(self):
"""Feature: Sniffer detects if first row is header."""
sample = 'name,age,city\nAlice,30,NYC\nBob,25,LA\n'
sniffer = csv.Sniffer()
has_header = sniffer.has_header(sample)
assert has_header is True
Verification: ✅ Tested in CI
Edge: Sniffer detects when no header present.¶
def test_sniffer_no_header(self):
"""Edge: Sniffer detects when no header present."""
sample = '1,2,3\n4,5,6\n7,8,9\n'
sniffer = csv.Sniffer()
has_header = sniffer.has_header(sample)
assert has_header is False
Verification: ✅ Tested in CI
Property: Write → Read roundtrip preserves data.¶
def test_roundtrip_preservation(self):
"""Property: Write → Read roundtrip preserves data."""
original = [['a', 'b', 'c'], ['1', '2', '3'], ['4', '5', '6']]
output = io.StringIO()
writer = csv.writer(output)
writer.writerows(original)
output.seek(0)
reader = csv.reader(output)
result = list(reader)
assert result == original
Verification: ✅ Tested in CI
Edge: Unicode characters are handled correctly.¶
def test_unicode_content(self):
"""Edge: Unicode characters are handled correctly."""
output = io.StringIO()
writer = csv.writer(output)
writer.writerow(['Hello', '世界', 'Привет'])
result = output.getvalue()
assert '世界' in result
assert 'Привет' in result
Verification: ✅ Tested in CI
Edge: Very long fields are handled.¶
def test_very_long_field(self):
"""Edge: Very long fields are handled."""
output = io.StringIO()
writer = csv.writer(output)
long_field = 'a' * 10000
writer.writerow(['short', long_field, 'end'])
result = output.getvalue()
assert long_field in result
Verification: ✅ Tested in CI
Edge: Single column CSV works correctly.¶
def test_single_column(self):
"""Edge: Single column CSV works correctly."""
data = 'a\nb\nc'
reader = csv.reader(io.StringIO(data))
rows = list(reader)
assert rows == [['a'], ['b'], ['c']]
Verification: ✅ Tested in CI
Edge: Empty CSV returns empty list.¶
def test_empty_csv(self):
"""Edge: Empty CSV returns empty list."""
data = ''
reader = csv.reader(io.StringIO(data))
rows = list(reader)
assert rows == []
Verification: ✅ Tested in CI
Edge: Trailing delimiter creates empty field.¶
def test_trailing_delimiter(self):
"""Edge: Trailing delimiter creates empty field."""
data = 'a,b,c,\n1,2,3,'
reader = csv.reader(io.StringIO(data))
rows = list(reader)
assert rows[0] == ['a', 'b', 'c', '']
assert rows[1] == ['1', '2', '3', '']
Verification: ✅ Tested in CI