Skip to content

Commit 222c8f8

Browse files
committed
Handle UnicodeDecodeError in file class when file contents are not
text (e.g. jpg). This shows up in python3 due to conversion from bytes to string. This allows binary file retrieval via the rest interface to complete.
1 parent f2efc66 commit 222c8f8

File tree

4 files changed

+49
-0
lines changed

4 files changed

+49
-0
lines changed

CHANGES.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ Fixed:
126126
C�dric Krier, John Rouillard)
127127
- issue2550144 - fix use of undefined icing macro in devel
128128
template. Replace with frame macro. (C�dric Krier)
129+
- handle UnicodeDecodeError in file class when file contents are
130+
not text (e.g. jpg). (John Rouillard)
129131

130132
2018-07-13 1.6.0
131133

roundup/backends/back_anydbm.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636

3737
from roundup.backends.indexer_common import get_indexer
3838

39+
from hashlib import md5
40+
3941
def db_exists(config):
4042
# check for the user db
4143
for db in 'nodes.user nodes.user.db'.split():
@@ -2195,6 +2197,15 @@ def get(self, nodeid, propname, default=_marker, cache=1):
21952197
# XXX by catching this we don't see an error in the log.
21962198
return 'ERROR reading file: %s%s\n%s\n%s'%(
21972199
self.classname, nodeid, poss_msg, strerror)
2200+
except UnicodeDecodeError as e:
2201+
# if content is not text (e.g. jpeg file) we get
2202+
# unicode error trying to convert to string in python 3.
2203+
# trap it and supply an error message. Include md5sum
2204+
# of content as this string is included in the etag
2205+
# calculation of the object.
2206+
return ('%s%s is not text, retrieve using '
2207+
'binary_content property. mdsum: %s')%(self.classname,
2208+
nodeid, md5(self.db.getfile(self.classname, nodeid, None)).hexdigest())
21982209
elif propname == 'binary_content':
21992210
return self.db.getfile(self.classname, nodeid, None)
22002211

roundup/backends/rdbms_common.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
from roundup.backends.back_anydbm import compile_expression
7272
from roundup.anypy.strings import b2s, bs2b, us2s, repr_export, eval_import
7373

74+
from hashlib import md5
7475

7576
# dummy value meaning "argument not passed"
7677
_marker = []
@@ -3079,6 +3080,15 @@ def get(self, nodeid, propname, default=_marker, cache=1):
30793080
# BUG: by catching this we donot see an error in the log.
30803081
return 'ERROR reading file: %s%s\n%s\n%s'%(
30813082
self.classname, nodeid, poss_msg, strerror)
3083+
except UnicodeDecodeError as e:
3084+
# if content is not text (e.g. jpeg file) we get
3085+
# unicode error trying to convert to string in python 3.
3086+
# trap it and supply an error message. Include md5sum
3087+
# of content as this string is included in the etag
3088+
# calculation of the object.
3089+
return ('%s%s is not text, retrieve using '
3090+
'binary_content property. mdsum: %s')%(self.classname,
3091+
nodeid, md5(self.db.getfile(self.classname, nodeid, None)).hexdigest())
30823092
elif propname == 'binary_content':
30833093
return self.db.getfile(self.classname, nodeid, None)
30843094

test/db_test_base.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,32 @@ def testStringUnicode(self):
304304
self.assertEqual(self.db.file.get(nid, 'content'), ustr)
305305
self.assertEqual(self.db.file.get(nid, 'binary_content'), s2b(ustr))
306306

307+
def testStringBinary(self):
308+
''' Create file with binary content that is not able
309+
to be interpreted as unicode. Try to cause file module
310+
trigger and handle UnicodeDecodeError
311+
and get valid output
312+
'''
313+
# test set & retrieve
314+
bstr = b'\x00\xF0\x34\x33' # random binary data
315+
316+
# test set & retrieve (this time for file contents)
317+
nid = self.db.file.create(content=bstr)
318+
print(nid)
319+
print(repr(self.db.file.get(nid, 'content')))
320+
print(repr(self.db.file.get(nid, 'binary_content')))
321+
p3val='file1 is not text, retrieve using binary_content property. mdsum: 0e1d1b47e4bd1beab3afc9b79f596c1d'
322+
323+
if sys.version_info[0] > 2:
324+
# python 3
325+
self.assertEqual(self.db.file.get(nid, 'content'), p3val)
326+
self.assertEqual(self.db.file.get(nid, 'binary_content'),
327+
bstr)
328+
else:
329+
# python 2
330+
self.assertEqual(self.db.file.get(nid, 'content'), bstr)
331+
self.assertEqual(self.db.file.get(nid, 'binary_content'), bstr)
332+
307333
# Link
308334
def testLinkChange(self):
309335
self.assertRaises(IndexError, self.db.issue.create, title="spam",

0 commit comments

Comments
 (0)