Skip to content

Commit 40e6756

Browse files
committed
gh-156955: Cover line terminator quoting in the csv writer tests
Apply the reviewer's suggestion on GH-156956: exercise csv.writer with a field that embeds the line terminator, over terminators spanning the latin-1, BMP and non-BMP string kinds. Add two more tests for the paths the optimization introduced but nothing pinned: * test_write_lineterminator_quoting -- every character of a multi-character terminator forces quoting, not just the last, and characters bracketing the terminator in code point order do not. * test_write_empty_lineterminator -- an empty terminator has no characters, so it separates nothing and quotes nothing, including '\0'. All three pass against Modules/_csv.c as it stood before the optimization, so they pin existing csv.writer semantics rather than new behavior.
1 parent 3b2ec50 commit 40e6756

1 file changed

Lines changed: 29 additions & 2 deletions

File tree

Lib/test/test_csv.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,17 +260,44 @@ def test_write_escape(self):
260260
escapechar='\\', quoting=csv.QUOTE_MINIMAL)
261261

262262
def test_write_lineterminator(self):
263-
for lineterminator in '\r\n', '\n', '\r', '!@#', '\0':
263+
for lineterminator in ('\r\n', '\n', '\r', '!@#', '\0', '\x85',
264+
'\u2028', '\U0001f600'):
264265
with self.subTest(lineterminator=lineterminator):
265266
with StringIO() as sio:
266267
writer = csv.writer(sio, lineterminator=lineterminator)
267268
writer.writerow(['a', 'b'])
268269
writer.writerow([1, 2])
269270
writer.writerow(['\r', '\n'])
271+
writer.writerow([f'a{lineterminator[-1]}b', 'c'])
270272
self.assertEqual(sio.getvalue(),
271273
f'a,b{lineterminator}'
272274
f'1,2{lineterminator}'
273-
f'"\r","\n"{lineterminator}')
275+
f'"\r","\n"{lineterminator}'
276+
f'"a{lineterminator[-1]}b",c{lineterminator}')
277+
278+
def test_write_lineterminator_quoting(self):
279+
# Every character of the line terminator forces quoting, not just the
280+
# last one, and no other character does. Each terminator is paired
281+
# with characters that bracket it in code point order, to pin that
282+
# boundary.
283+
for lineterminator, plain in ('!@#', ' ?A'), ('\u2028', '\u2027\u2029'):
284+
with self.subTest(lineterminator=lineterminator):
285+
for c in lineterminator:
286+
self._write_test([f'a{c}b', 'c'], f'"a{c}b",c',
287+
lineterminator=lineterminator)
288+
self._write_test([f'a{plain}b', 'c'], f'a{plain}b,c',
289+
lineterminator=lineterminator)
290+
291+
def test_write_empty_lineterminator(self):
292+
# An empty line terminator separates nothing and, having no characters
293+
# of its own, forces no quoting -- not even of '\0', the lowest code
294+
# point. '\r' and '\n' are quoted whatever the terminator is.
295+
with StringIO() as sio:
296+
writer = csv.writer(sio, lineterminator='')
297+
writer.writerow(['a', 'b'])
298+
writer.writerow(['\0', 'c'])
299+
writer.writerow(['\r', '\n'])
300+
self.assertEqual(sio.getvalue(), 'a,b\0,c"\r","\n"')
274301

275302
def test_write_iterable(self):
276303
self._write_test(iter(['a', 1, 'p,q']), 'a,1,"p,q"')

0 commit comments

Comments
 (0)