Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correctly pass newline parameter to built-in open function #478

Merged
merged 11 commits into from
May 13, 2020
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Unreleased

- Correctly pass `newline` parameter to built-in `open` function (PR [#478](https://github.com/RaRe-Technologies/smart_open/pull/478), [@burkovae](https://github.com/burkovae))

# 2.0.0, 27 April 2020, "Python 3"

- **This version supports Python 3 only** (3.5+).
Expand Down
6 changes: 4 additions & 2 deletions smart_open/smart_open_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ def open(
buffering=buffering,
encoding=encoding,
errors=errors,
newline=newline,
)
if fobj is not None:
return fobj
Expand Down Expand Up @@ -316,6 +317,7 @@ def _shortcut_open(
buffering=-1,
encoding=None,
errors=None,
newline=None,
):
"""Try to open the URI using the standard library io.open function.
Expand All @@ -331,7 +333,6 @@ def _shortcut_open(
:param str uri: A string indicating what to open.
:param str mode: The mode to pass to the open function.
:param dict kw:
:returns: The opened file
:rtype: file
"""
Expand All @@ -348,10 +349,11 @@ def _shortcut_open(
return None

open_kwargs = {}

if encoding is not None:
open_kwargs['encoding'] = encoding
mode = mode.replace('b', '')
if newline is not None:
open_kwargs['newline'] = newline

#
# binary mode of the builtin/stdlib open function doesn't take an errors argument
Expand Down
25 changes: 24 additions & 1 deletion smart_open/tests/test_smart_open.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#

import bz2
import csv
import contextlib
import io
import unittest
Expand Down Expand Up @@ -911,7 +912,6 @@ def test_s3_read_moto(self):

self.assertEqual(content[14:], smart_open_object.read()) # read the rest

@unittest.skip('seek functionality for S3 currently disabled because of Issue #152')
@mock_s3
def test_s3_seek_moto(self):
"""Does seeking in S3 files work correctly?"""
Expand Down Expand Up @@ -1076,6 +1076,29 @@ def test_append_binary_absolute_path(self):
mock_open.assert_called_with("/some/file.txt", "wb+", buffering=-1)
fout.write(self.as_bytes)

def test_newline(self):
with mock.patch(_BUILTIN_OPEN, mock.Mock(return_value=self.bytesio)) as mock_open:
smart_open.smart_open("/some/file.txt", "wb+", newline='\n')
mock_open.assert_called_with("/some/file.txt", "wb+", buffering=-1, newline='\n')

def test_newline_csv(self):
#
# See https://github.com/RaRe-Technologies/smart_open/issues/477
#
rows = [{'name': 'alice', 'color': 'aqua'}, {'name': 'bob', 'color': 'blue'}]
expected = 'name,color\nalice,aqua\nbob,blue\n'

with named_temporary_file(mode='w') as tmp:
with smart_open.open(tmp.name, 'w+', newline='\n') as fout:
out = csv.DictWriter(fout, fieldnames=['name', 'color'])
out.writeheader()
out.writerows(rows)

with open(tmp.name, 'r') as fin:
content = fin.read()

assert content == expected

@mock.patch('boto3.Session')
def test_s3_mode_mock(self, mock_session):
"""Are s3:// open modes passed correctly?"""
Expand Down