Skip to content

Commit c0bd358

Browse files
committed
gh-153634: Raise csv.Error, not ValueError, from csv.Sniffer for an inconsistent guessed dialect
csv.Sniffer.sniff could return a dialect whose delimiter equaled its quote character. Using that dialect (directly, or via csv.Sniffer.has_header, which builds a reader from the sniffed dialect) leaked a raw ValueError from the _csv extension instead of the module's own csv.Error. sniff now validates its guessed dialect before returning it and re-raises the _csv ValueError as csv.Error, so both sniff and has_header report the module exception type. The direct csv.reader and csv.writer paths and the Dialect._validate TypeError/ValueError behavior are unchanged.
1 parent 832bc0b commit c0bd358

3 files changed

Lines changed: 22 additions & 0 deletions

File tree

Lib/csv.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,14 @@ class dialect(Dialect):
264264
dialect.quotechar = quotechar or '"'
265265
dialect.skipinitialspace = skipinitialspace
266266

267+
# A guess can be inconsistent (for example delimiter == quotechar).
268+
# Building a reader validates it; re-raise the _csv ValueError as a
269+
# csv.Error so callers see the module's own exception type.
270+
try:
271+
reader(StringIO(), dialect)
272+
except ValueError as e:
273+
raise Error(str(e)) from None
274+
267275
return dialect
268276

269277

Lib/test/test_csv.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1463,6 +1463,16 @@ def test_sniff(self):
14631463
self.assertEqual(dialect.quotechar, "'")
14641464
self.assertIs(dialect.skipinitialspace, False)
14651465

1466+
def test_sniff_delimiter_equals_quotechar(self):
1467+
# A sample that makes sniff() guess a dialect whose delimiter equals
1468+
# its quotechar is invalid; that must surface as csv.Error, not as a
1469+
# raw ValueError leaking from the _csv extension.
1470+
sniffer = csv.Sniffer()
1471+
for sample in ('"', '""', "''"):
1472+
with self.subTest(sample=sample):
1473+
self.assertRaises(csv.Error, sniffer.sniff, sample)
1474+
self.assertRaises(csv.Error, sniffer.has_header, sample)
1475+
14661476
def test_delimiters(self):
14671477
sniffer = csv.Sniffer()
14681478
dialect = sniffer.sniff(self.sample3)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
:meth:`csv.Sniffer.sniff` now raises :exc:`csv.Error` for an inconsistent
2+
guessed dialect, for example one whose delimiter equals its quote character,
3+
instead of returning it. :meth:`csv.Sniffer.has_header` now raises
4+
:exc:`csv.Error` instead of leaking a :exc:`ValueError`.

0 commit comments

Comments
 (0)