Skip to content

Commit dae2190

Browse files
committed
Test fixes
1 parent e645ca5 commit dae2190

File tree

3 files changed

+34
-33
lines changed

3 files changed

+34
-33
lines changed

src/stdlib/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
all = ["csv", "re"]

src/stdlib/csv/_csv.py

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,7 @@
55
This module provides a CSV parser and writer.
66
"""
77

8-
from typing import (
9-
Any,
10-
Dict,
11-
Iterable,
12-
List,
13-
Optional,
14-
Union,
15-
TextIO,
16-
Sequence,
17-
Type,
18-
)
8+
from typing import Any, Dict, Iterable, List, Optional, Sequence, TextIO, Union
199

2010
# Quoting styles
2111
QUOTE_MINIMAL = 0
@@ -470,6 +460,7 @@ def reader(
470460
quotechar = d.quotechar
471461
quoting = d.quoting
472462
skipinitialspace = d.skipinitialspace
463+
lineterminator = d.lineterminator
473464
# strict = d.strict # TODO: Use strict mode
474465

475466
if not csvfile:
@@ -488,8 +479,8 @@ def reader(
488479
raise Error(f"field larger than field limit ({_field_size_limit})")
489480

490481
row_str = row_str_orig.rstrip(
491-
"\r\n"
492-
) # Reader should not depend on lineterminator from dialect
482+
lineterminator
483+
) # Reader should use dialect's lineterminator
493484

494485
fields: List[str] = []
495486
current_field: str = ""
@@ -579,9 +570,7 @@ def reader(
579570
pass
580571
else:
581572
if d.strict:
582-
raise Error(
583-
f"'{delimiter}' expected after '{quotechar}' at char {idx}, found '{char}'"
584-
)
573+
raise Error(f"delimiter expected after '{quotechar}'")
585574
# If not strict, CPython CSV often appends this char to the field or starts a new unquoted field.
586575
# This behavior is complex. For simplicity, we'll be strict or error-prone here.
587576
# Let's assume for now it's an error if strict, or append to field if not (though might be wrong for some cases)
@@ -604,7 +593,7 @@ def reader(
604593
if d.strict or not (
605594
escapechar and row_str.endswith(escapechar)
606595
): # CPython behavior for unclosed quote
607-
raise Error("unexpected end of data - unclosed quote")
596+
raise Error("unclosed quote")
608597
if state == ESCAPE:
609598
raise Error("unexpected end of data - incomplete escape sequence")
610599

@@ -670,7 +659,10 @@ def writerow(self, row: _Row) -> None:
670659
elif quoting == QUOTE_NONNUMERIC:
671660
if quotechar is None:
672661
raise Error("quotechar must be set for QUOTE_NONNUMERIC")
673-
if not isinstance(field_obj, (int, float)):
662+
# Check for boolean first since isinstance(bool, int) is True
663+
if isinstance(field_obj, bool) or not isinstance(
664+
field_obj, (int, float)
665+
):
674666
needs_quoting = True
675667
else:
676668
if quotechar and (
@@ -702,20 +694,21 @@ def writerow(self, row: _Row) -> None:
702694
continue
703695

704696
if needs_quoting and quotechar:
705-
escaped_field = ""
697+
escaped_field = field_str # Start with the original field
706698
if doublequote:
707-
escaped_field = field_str.replace(quotechar, quotechar * 2)
699+
escaped_field = escaped_field.replace(quotechar, quotechar * 2)
708700
elif escapechar:
709-
escaped_field = field_str.replace(escapechar, escapechar * 2)
701+
escaped_field = escaped_field.replace(escapechar, escapechar * 2)
710702
escaped_field = escaped_field.replace(
711703
quotechar, escapechar + quotechar
712704
)
713705
else:
714706
# This case means quotechar is in field, needs_quoting is true,
715707
# but no mechanism (doublequote=F, escapechar=None) to escape it.
716-
raise Error(
717-
"quotechar found in field, but no escape mechanism (doublequote=False, escapechar=None)"
718-
)
708+
if quotechar in field_str:
709+
raise Error(
710+
"quotechar found in field, but no escape mechanism (doublequote=False, escapechar=None)"
711+
)
719712

720713
processed_fields.append(quotechar + escaped_field + quotechar)
721714
else:

tests/test_csv.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import io
2-
import pytest
3-
import sys
42
import os
3+
import sys
4+
5+
import pytest
6+
57
from stdlib import csv
68

79
# Add src directory to PYTHONPATH to allow direct import of stdlib
@@ -230,7 +232,8 @@ def test_embedded_newlines_in_quoted_fields(self):
230232
# Let's assume strict=True for this test.
231233
with pytest.raises(csv.Error, match="unclosed quote"):
232234
list(csv.reader(io.StringIO('a,"b\nc",d'), strict=True))
233-
# If not strict, it might yield `[['a', 'b']]` or `[['a', '"b']]` for `a,"b\n`. The current reader's unclosed quote error isn't bypassed by non-strict mode.
235+
# If not strict, it might yield `[['a', 'b']]` or `[['a', '"b']]` for `a,"b\n`.
236+
# The current reader's unclosed quote error isn't bypassed by non-strict mode.
234237

235238
def test_empty_lines_and_whitespace_lines(self):
236239
data = "\r\n \r\nval1,val2\r\n\r\n" # Empty line, whitespace line, data, empty line
@@ -283,6 +286,7 @@ def test_reader_error_unclosed_quote(self):
283286
with pytest.raises(csv.Error, match="unclosed quote"):
284287
list(csv.reader(sio)) # Test with default strictness
285288

289+
sio.seek(0) # Reset position for second test
286290
with pytest.raises(csv.Error, match="unclosed quote"):
287291
list(csv.reader(sio, strict=True))
288292

@@ -297,13 +301,16 @@ def test_reader_error_unexpected_chars_after_quotes_strict(self):
297301
# So it always raises an error, but message might differ or behavior could be refined for non-strict.
298302
# For now, let's assume strict=True in the dialect for this test.
299303
with pytest.raises(
300-
csv.Error, match="'b' found after quoted field"
301-
): # Or similar, based on exact error msg
304+
csv.Error, match="delimiter expected after"
305+
): # Our error message pattern
302306
list(csv.reader(sio, strict=True))
303307

304308
# Test default strictness (False) - still expect error from current code
309+
sio2 = io.StringIO(
310+
data
311+
) # Need a fresh StringIO since the first one was consumed
305312
with pytest.raises(csv.Error, match="malformed CSV row"):
306-
list(csv.reader(sio))
313+
list(csv.reader(sio2))
307314

308315
def test_field_size_limit_reader(self):
309316
original_limit = csv.field_size_limit()
@@ -372,7 +379,7 @@ def test_writerows(self):
372379
# Assuming the goal is to fix the E501 on the line that was *originally* here at 322.
373380
# The current `read_files` shows the problematic line.
374381
# Shortened comment. Note: This assertion itself is debated in the test.
375-
assert sio.getvalue() == 'a,b\r\n1,2\r\n"x",""\r\n'
382+
assert sio.getvalue() == "a,b\r\n1,2\r\nx,\r\n"
376383
# Correction for writerows output:
377384
# If x is simple string, and "" is empty string due to None:
378385
# 'a,b\r\n1,2\r\nx,\r\n' (If empty string doesn't get quoted by default)
@@ -431,7 +438,7 @@ def test_quoting_nonnumeric_writer(self):
431438
sio3 = io.StringIO() # Numeric that contains delimiter
432439
w3 = csv.writer(sio3, quoting=csv.QUOTE_NONNUMERIC, delimiter=".")
433440
w3.writerow([1, 2.3]) # 2.3 -> "2.3" which contains '.', so it will be quoted
434-
assert sio3.getvalue() == '1,"2.3"\r\n'
441+
assert sio3.getvalue() == '1."2.3"\r\n'
435442

436443
def test_quoting_none_writer_with_escapechar(self):
437444
sio = io.StringIO()
@@ -572,7 +579,7 @@ def test_dialect_properties_validation(self):
572579
):
573580
csv.Dialect(delimiter="long")
574581
with pytest.raises(TypeError, match="doublequote must be a boolean"):
575-
csv.Dialect(doublequote=True) # Changed "true" to True
582+
csv.Dialect(doublequote="true") # Invalid type - should be boolean
576583
# ... other validation checks in Dialect.__init__ can be tested similarly
577584

578585
def test_predefined_dialects_exist(self):

0 commit comments

Comments
 (0)