@@ -356,22 +356,25 @@ def test_array(self):
356356 np .savetxt (c , a , fmt = fmt )
357357 c .seek (0 )
358358 assert_equal (c .readlines (),
359- [asbytes ((fmt + ' ' + fmt + ' \n ' ) % (1 , 2 )),
360- asbytes ((fmt + ' ' + fmt + ' \n ' ) % (3 , 4 ))])
359+ [asbytes ((fmt + ' ' + fmt + os . linesep ) % (1 , 2 )),
360+ asbytes ((fmt + ' ' + fmt + os . linesep ) % (3 , 4 ))])
361361
362362 a = np .array ([[1 , 2 ], [3 , 4 ]], int )
363363 c = BytesIO ()
364364 np .savetxt (c , a , fmt = '%d' )
365365 c .seek (0 )
366- assert_equal (c .readlines (), [b'1 2\n ' , b'3 4\n ' ])
366+ assert_equal (c .readlines (), [b'1 2' + os .linesep .encode (),
367+ b'3 4' + os .linesep .encode ()])
367368
368369 def test_1D (self ):
369370 a = np .array ([1 , 2 , 3 , 4 ], int )
370371 c = BytesIO ()
371372 np .savetxt (c , a , fmt = '%d' )
372373 c .seek (0 )
373374 lines = c .readlines ()
374- assert_equal (lines , [b'1\n ' , b'2\n ' , b'3\n ' , b'4\n ' ])
375+ newline = os .linesep .encode ()
376+ assert_equal (lines , [b'1' + newline , b'2' + newline , b'3' + newline ,
377+ b'4' + newline ])
375378
376379 def test_0D_3D (self ):
377380 c = BytesIO ()
@@ -383,7 +386,8 @@ def test_structured(self):
383386 c = BytesIO ()
384387 np .savetxt (c , a , fmt = '%d' )
385388 c .seek (0 )
386- assert_equal (c .readlines (), [b'1 2\n ' , b'3 4\n ' ])
389+ newline = os .linesep .encode ()
390+ assert_equal (c .readlines (), [b'1 2' + newline , b'3 4' + newline ])
387391
388392 def test_structured_padded (self ):
389393 # gh-13297
@@ -393,7 +397,8 @@ def test_structured_padded(self):
393397 c = BytesIO ()
394398 np .savetxt (c , a [['foo' , 'baz' ]], fmt = '%d' )
395399 c .seek (0 )
396- assert_equal (c .readlines (), [b'1 3\n ' , b'4 6\n ' ])
400+ newline = os .linesep .encode ()
401+ assert_equal (c .readlines (), [b'1 3' + newline , b'4 6' + newline ])
397402
398403 def test_multifield_view (self ):
399404 a = np .ones (1 , dtype = [('x' , 'i4' ), ('y' , 'i4' ), ('z' , 'f4' )])
@@ -409,68 +414,135 @@ def test_delimiter(self):
409414 c = BytesIO ()
410415 np .savetxt (c , a , delimiter = ',' , fmt = '%d' )
411416 c .seek (0 )
412- assert_equal (c .readlines (), [b'1,2\n ' , b'3,4\n ' ])
417+ newline = os .linesep .encode ()
418+ assert_equal (c .readlines (), [b'1,2' + newline , b'3,4' + newline ])
413419
414420 def test_format (self ):
415421 a = np .array ([(1 , 2 ), (3 , 4 )])
422+ newline = os .linesep .encode ()
416423 c = BytesIO ()
417424 # Sequence of formats
418425 np .savetxt (c , a , fmt = ['%02d' , '%3.1f' ])
419426 c .seek (0 )
420- assert_equal (c .readlines (), [b'01 2.0\n ' , b'03 4.0\n ' ])
427+ assert_equal (c .readlines (), [b'01 2.0' + newline , b'03 4.0' + newline ])
421428
422429 # A single multiformat string
423430 c = BytesIO ()
424431 np .savetxt (c , a , fmt = '%02d : %3.1f' )
425432 c .seek (0 )
426433 lines = c .readlines ()
427- assert_equal (lines , [b'01 : 2.0\n ' , b'03 : 4.0\n ' ])
434+ assert_equal (lines , [b'01 : 2.0' + newline , b'03 : 4.0' + newline ])
428435
429436 # Specify delimiter, should be overridden
430437 c = BytesIO ()
431438 np .savetxt (c , a , fmt = '%02d : %3.1f' , delimiter = ',' )
432439 c .seek (0 )
433440 lines = c .readlines ()
434- assert_equal (lines , [b'01 : 2.0\n ' , b'03 : 4.0\n ' ])
441+ assert_equal (lines , [b'01 : 2.0' + newline , b'03 : 4.0' + newline ])
435442
436443 # Bad fmt, should raise a ValueError
437444 c = BytesIO ()
438445 assert_raises (ValueError , np .savetxt , c , a , fmt = 99 )
439446
447+ def test_newline (self ):
448+ a = np .array ([(1 , 2 ), (3 , 4 )])
449+ c = BytesIO ()
450+
451+ # Universal newline, implicit and explicit
452+ newline = os .linesep .encode ()
453+ np .savetxt (c , a , fmt = '%d' )
454+ c .seek (0 )
455+ assert_equal (c .readlines (), [b'1 2' + newline , b'3 4' + newline ],
456+ err_msg = 'Universal newline, implicit' )
457+ c = BytesIO ()
458+ np .savetxt (c , a , fmt = '%d' , newline = None )
459+ c .seek (0 )
460+ assert_equal (c .readlines (), [b'1 2' + newline , b'3 4' + newline ],
461+ err_msg = 'Universal newline, explicit' )
462+
463+ # POSIX newline
464+ newline = '\n '
465+ c = BytesIO ()
466+ np .savetxt (c , a , fmt = '%d' , newline = newline )
467+ c .seek (0 )
468+ lines = c .readlines ()
469+ newline = newline .encode ()
470+ assert_equal (lines , [b'1 2' + newline , b'3 4' + newline ],
471+ err_msg = 'POSIX newline' )
472+
473+ # NT newline
474+ newline = '\r \n '
475+ c = BytesIO ()
476+ np .savetxt (c , a , fmt = '%d' , newline = newline )
477+ c .seek (0 )
478+ lines = c .readlines ()
479+ newline = newline .encode ()
480+ assert_equal (lines , [b'1 2' + newline , b'3 4' + newline ],
481+ err_msg = 'NT newline' )
482+
483+ # Tab "newline"
484+ newline = '\t '
485+ c = BytesIO ()
486+ np .savetxt (c , a , fmt = '%d' , newline = newline )
487+ c .seek (0 )
488+ lines = c .readlines ()
489+ newline = newline .encode ()
490+ assert_equal (lines , [b'1 2' + newline + b'3 4' + newline , ],
491+ err_msg = 'Tab newline' )
492+
440493 def test_header_footer (self ):
441494 # Test the functionality of the header and footer keyword argument.
442495
443496 c = BytesIO ()
444497 a = np .array ([(1 , 2 ), (3 , 4 )], dtype = int )
498+ a_txt = '1 2' + os .linesep + '3 4' + os .linesep
445499 test_header_footer = 'Test header / footer'
446500 # Test the header keyword argument
447501 np .savetxt (c , a , fmt = '%1d' , header = test_header_footer )
448502 c .seek (0 )
449503 assert_equal (c .read (),
450- asbytes ('# ' + test_header_footer + '\n 1 2\n 3 4\n ' ))
504+ asbytes ('# ' + test_header_footer + os .linesep
505+ + a_txt ))
451506 # Test the footer keyword argument
452507 c = BytesIO ()
453508 np .savetxt (c , a , fmt = '%1d' , footer = test_header_footer )
454509 c .seek (0 )
455510 assert_equal (c .read (),
456- asbytes ('1 2 \n 3 4 \n # ' + test_header_footer + ' \n ' ))
511+ asbytes (a_txt + ' # ' + test_header_footer + os . linesep ))
457512 # Test the commentstr keyword argument used on the header
458513 c = BytesIO ()
459514 commentstr = '% '
460515 np .savetxt (c , a , fmt = '%1d' ,
461516 header = test_header_footer , comments = commentstr )
462517 c .seek (0 )
463518 assert_equal (c .read (),
464- asbytes (commentstr + test_header_footer + '\n ' + '1 2\n 3 4\n ' ))
519+ asbytes (commentstr + test_header_footer + os .linesep
520+ + a_txt ))
465521 # Test the commentstr keyword argument used on the footer
466522 c = BytesIO ()
467523 commentstr = '% '
468524 np .savetxt (c , a , fmt = '%1d' ,
469525 footer = test_header_footer , comments = commentstr )
470526 c .seek (0 )
471527 assert_equal (c .read (),
472- asbytes ('1 2\n 3 4\n ' + commentstr + test_header_footer + '\n ' ))
528+ asbytes (a_txt + commentstr + test_header_footer
529+ + os .linesep ))
473530
531+ @pytest .mark .parametrize ("newline" , ['\n ' , '\r \n ' ])
532+ def test_newline_header_footer (self , newline ):
533+ c = BytesIO ()
534+ a = np .array ([(1 , 2 ), (3 , 4 )], dtype = int )
535+ a_txt = '1 2' + newline + '3 4' + newline
536+ test_header_footer = 'Test header / footer'
537+ # Test the header and footer keyword argument
538+ np .savetxt (c , a , fmt = '%1d' , newline = newline , header = test_header_footer ,
539+ footer = test_header_footer )
540+ c .seek (0 )
541+ assert_equal (c .read (),
542+ asbytes ('# ' + test_header_footer + newline
543+ + a_txt
544+ + '# ' + test_header_footer + newline ))
545+
474546 def test_file_roundtrip (self ):
475547 with temppath () as name :
476548 a = np .array ([(1 , 2 ), (3 , 4 )])
@@ -485,6 +557,7 @@ def test_complex_arrays(self):
485557 re = np .pi
486558 im = np .e
487559 a [:] = re + 1.0j * im
560+ newline = os .linesep .encode ()
488561
489562 # One format only
490563 c = BytesIO ()
@@ -493,8 +566,10 @@ def test_complex_arrays(self):
493566 lines = c .readlines ()
494567 assert_equal (
495568 lines ,
496- [b' ( +3.142e+00+ +2.718e+00j) ( +3.142e+00+ +2.718e+00j)\n ' ,
497- b' ( +3.142e+00+ +2.718e+00j) ( +3.142e+00+ +2.718e+00j)\n ' ])
569+ [b' ( +3.142e+00+ +2.718e+00j) ( +3.142e+00+ +2.718e+00j)'
570+ + newline ,
571+ b' ( +3.142e+00+ +2.718e+00j) ( +3.142e+00+ +2.718e+00j)'
572+ + newline ])
498573
499574 # One format for each real and imaginary part
500575 c = BytesIO ()
@@ -503,8 +578,10 @@ def test_complex_arrays(self):
503578 lines = c .readlines ()
504579 assert_equal (
505580 lines ,
506- [b' +3.142e+00 +2.718e+00 +3.142e+00 +2.718e+00\n ' ,
507- b' +3.142e+00 +2.718e+00 +3.142e+00 +2.718e+00\n ' ])
581+ [b' +3.142e+00 +2.718e+00 +3.142e+00 +2.718e+00'
582+ + newline ,
583+ b' +3.142e+00 +2.718e+00 +3.142e+00 +2.718e+00'
584+ + newline ])
508585
509586 # One format for each complex number
510587 c = BytesIO ()
@@ -513,8 +590,10 @@ def test_complex_arrays(self):
513590 lines = c .readlines ()
514591 assert_equal (
515592 lines ,
516- [b'(3.142e+00+2.718e+00j) (3.142e+00+2.718e+00j)\n ' ,
517- b'(3.142e+00+2.718e+00j) (3.142e+00+2.718e+00j)\n ' ])
593+ [b'(3.142e+00+2.718e+00j) (3.142e+00+2.718e+00j)'
594+ + newline ,
595+ b'(3.142e+00+2.718e+00j) (3.142e+00+2.718e+00j)'
596+ + newline ])
518597
519598 def test_complex_negative_exponent (self ):
520599 # Previous to 1.15, some formats generated x+-yj, gh 7895
@@ -524,14 +603,17 @@ def test_complex_negative_exponent(self):
524603 re = np .pi
525604 im = np .e
526605 a [:] = re - 1.0j * im
606+ newline = os .linesep .encode ()
527607 c = BytesIO ()
528608 np .savetxt (c , a , fmt = '%.3e' )
529609 c .seek (0 )
530610 lines = c .readlines ()
531611 assert_equal (
532612 lines ,
533- [b' (3.142e+00-2.718e+00j) (3.142e+00-2.718e+00j)\n ' ,
534- b' (3.142e+00-2.718e+00j) (3.142e+00-2.718e+00j)\n ' ])
613+ [b' (3.142e+00-2.718e+00j) (3.142e+00-2.718e+00j)'
614+ + newline ,
615+ b' (3.142e+00-2.718e+00j) (3.142e+00-2.718e+00j)'
616+ + newline ])
535617
536618
537619 def test_custom_writer (self ):
@@ -577,15 +659,15 @@ def test_unicode_bytestream(self):
577659 s = BytesIO ()
578660 np .savetxt (s , a , fmt = ['%s' ], encoding = 'UTF-8' )
579661 s .seek (0 )
580- assert_equal (s .read ().decode ('UTF-8' ), utf8 + ' \n ' )
662+ assert_equal (s .read ().decode ('UTF-8' ), utf8 + os . linesep )
581663
582664 def test_unicode_stringstream (self ):
583665 utf8 = b'\xcf \x96 ' .decode ('UTF-8' )
584666 a = np .array ([utf8 ], dtype = np .str_ )
585667 s = StringIO ()
586668 np .savetxt (s , a , fmt = ['%s' ], encoding = 'UTF-8' )
587669 s .seek (0 )
588- assert_equal (s .read (), utf8 + ' \n ' )
670+ assert_equal (s .read (), utf8 + os . linesep )
589671
590672 @pytest .mark .parametrize ("fmt" , ["%f" , b"%f" ])
591673 @pytest .mark .parametrize ("iotype" , [StringIO , BytesIO ])
@@ -596,9 +678,9 @@ def test_unicode_and_bytes_fmt(self, fmt, iotype):
596678 np .savetxt (s , a , fmt = fmt )
597679 s .seek (0 )
598680 if iotype is StringIO :
599- assert_equal (s .read (), "%f\n " % 1. )
681+ assert_equal (s .read (), "%f%s " % ( 1. , os . linesep ) )
600682 else :
601- assert_equal (s .read (), b"%f\n " % 1. )
683+ assert_equal (s .read (), b"%f%s " % ( 1. , os . linesep . encode ()) )
602684
603685 @pytest .mark .skipif (sys .platform == 'win32' , reason = "files>4GB may not work" )
604686 @pytest .mark .slow
0 commit comments