Skip to content

Commit fc3456f

Browse files
committed
Merge remote-tracking branch 'origin/master' into edge
2 parents 771d326 + 9bb6744 commit fc3456f

File tree

2 files changed

+77
-7
lines changed

2 files changed

+77
-7
lines changed

mig/shared/fileio.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,21 @@
7070
exit(1)
7171

7272

73+
def _auto_adjust_mode(data, mode):
74+
"""Select suitable file open mode based on string type of data. I.e. whether
75+
to use binary or text mode depending on data in bytes or unicode format.
76+
"""
77+
78+
# NOTE: detect byte/unicode writes and handle explicitly in a portable way
79+
if isinstance(data, bytes):
80+
if 'b' not in mode:
81+
mode = "%sb" % mode # appended to avoid mode ordering error on PY2
82+
else:
83+
if 'b' in mode:
84+
mode = mode.strip('b')
85+
return mode
86+
87+
7388
def _write_chunk(path, chunk, offset, logger=None, mode='r+b',
7489
make_parent=True, create_file=True):
7590
"""Internal helper to wrap writing of chunks with offset to path.
@@ -104,8 +119,8 @@ def _write_chunk(path, chunk, offset, logger=None, mode='r+b',
104119

105120
try:
106121
with open(path, mode) as filehandle:
122+
# Make sure we can write at requested position, filling if needed
107123
if offset > 0:
108-
# Make sure we can write at requested position, filling if needed
109124
try:
110125
filehandle.seek(offset)
111126
except:
@@ -131,9 +146,7 @@ def write_chunk(path, chunk, offset, logger, mode='r+b'):
131146
if not logger:
132147
logger = null_logger("dummy")
133148

134-
# NOTE: detect byte writes and handle explicitly in a portable way
135-
if isinstance(chunk, bytes) and 'b' not in mode:
136-
mode = "%sb" % mode # appended to avoid mode ordering error on PY2
149+
mode = _auto_adjust_mode(chunk, mode)
137150

138151
return _write_chunk(path, chunk, offset, logger, mode)
139152

@@ -147,9 +160,7 @@ def write_file(content, path, logger, mode='w', make_parent=True, umask=None):
147160
if umask is not None:
148161
old_umask = os.umask(umask)
149162

150-
# NOTE: detect byte writes and handle explicitly in a portable way
151-
if isinstance(content, bytes) and 'b' not in mode:
152-
mode = "%sb" % mode # appended to avoid mode ordering error on PY2
163+
mode = _auto_adjust_mode(content, mode)
153164

154165
retval = _write_chunk(path, content, offset=0, logger=logger, mode=mode,
155166
make_parent=make_parent, create_file=False)

tests/test_mig_shared_fileio.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
DUMMY_BYTES = binascii.unhexlify('DEADBEEF') # 4 bytes
1414
DUMMY_BYTES_LENGTH = 4
15+
DUMMY_UNICODE = u'UniCode123'
16+
DUMMY_UNICODE_LENGTH = len(DUMMY_UNICODE)
1517
DUMMY_FILE_WRITECHUNK = 'fileio/write_chunk'
1618
DUMMY_FILE_WRITEFILE = 'fileio/write_file'
1719

@@ -65,6 +67,33 @@ def test_store_bytes_at_offset(self):
6567
"expected a hole was left")
6668
self.assertEqual(content[3:], DUMMY_BYTES)
6769

70+
def test_store_bytes_in_text_mode(self):
71+
fileio.write_chunk(self.tmp_path, DUMMY_BYTES, 0, self.logger,
72+
mode="r+")
73+
74+
with open(self.tmp_path, 'rb') as file:
75+
content = file.read(1024)
76+
self.assertEqual(len(content), DUMMY_BYTES_LENGTH)
77+
self.assertEqual(content[:], DUMMY_BYTES)
78+
79+
def test_store_unicode(self):
80+
fileio.write_chunk(self.tmp_path, DUMMY_UNICODE, 0, self.logger,
81+
mode='r+')
82+
83+
with open(self.tmp_path, 'r') as file:
84+
content = file.read(1024)
85+
self.assertEqual(len(content), DUMMY_UNICODE_LENGTH)
86+
self.assertEqual(content[:], DUMMY_UNICODE)
87+
88+
def test_store_unicode_in_binary_mode(self):
89+
fileio.write_chunk(self.tmp_path, DUMMY_UNICODE, 0, self.logger,
90+
mode='r+b')
91+
92+
with open(self.tmp_path, 'r') as file:
93+
content = file.read(1024)
94+
self.assertEqual(len(content), DUMMY_UNICODE_LENGTH)
95+
self.assertEqual(content[:], DUMMY_UNICODE)
96+
6897

6998
class MigSharedFileio__write_file(MigTestCase):
7099
def setUp(self):
@@ -103,6 +132,36 @@ def test_store_bytes(self):
103132
self.assertEqual(len(content), DUMMY_BYTES_LENGTH)
104133
self.assertEqual(content[:], DUMMY_BYTES)
105134

135+
def test_store_bytes_in_text_mode(self):
136+
did_succeed = fileio.write_file(DUMMY_BYTES, self.tmp_path, self.logger,
137+
mode="w")
138+
self.assertTrue(did_succeed)
139+
140+
with open(self.tmp_path, 'rb') as file:
141+
content = file.read(1024)
142+
self.assertEqual(len(content), DUMMY_BYTES_LENGTH)
143+
self.assertEqual(content[:], DUMMY_BYTES)
144+
145+
def test_store_unicode(self):
146+
did_succeed = fileio.write_file(DUMMY_UNICODE, self.tmp_path,
147+
self.logger, mode='w')
148+
self.assertTrue(did_succeed)
149+
150+
with open(self.tmp_path, 'r') as file:
151+
content = file.read(1024)
152+
self.assertEqual(len(content), DUMMY_UNICODE_LENGTH)
153+
self.assertEqual(content[:], DUMMY_UNICODE)
154+
155+
def test_store_unicode_in_binary_mode(self):
156+
did_succeed = fileio.write_file(DUMMY_UNICODE, self.tmp_path,
157+
self.logger, mode='wb')
158+
self.assertTrue(did_succeed)
159+
160+
with open(self.tmp_path, 'r') as file:
161+
content = file.read(1024)
162+
self.assertEqual(len(content), DUMMY_UNICODE_LENGTH)
163+
self.assertEqual(content[:], DUMMY_UNICODE)
164+
106165

107166
if __name__ == '__main__':
108167
testmain()

0 commit comments

Comments
 (0)