Skip to content

Commit 9bb6744

Browse files
committed
rework the write file and chunk helpers to do intelligent 'mode' selection based on input data type (unicode or bytes) as a follow-up to PR48. Thoroughly exercise the helpers using all four combinations of format and mode in unit tests to make sure we cover the cases previously handled by explicit byte and unicode conversion with force utf8 and force native str helpers when running in python3 on 'experimental' branch.
git-svn-id: svn+ssh://svn.code.sf.net/p/migrid/code/trunk@6040 b75ad72c-e7d7-11dd-a971-7dbc132099af
1 parent d538083 commit 9bb6744

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)