Skip to content

Commit fb17c37

Browse files
committed
blobfiles now always stores/returns bytes
any conversation is done in the backend layer added a special "binary_content" property to read the data as bytes changed history generation to read property data on demand
1 parent 47da89f commit fb17c37

File tree

6 files changed

+60
-42
lines changed

6 files changed

+60
-42
lines changed

roundup/anypy/strings.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,16 @@ def s2b(s):
2727
else:
2828
return s
2929

30+
def bs2b(s):
31+
"""Convert a string object or UTF-8 encoded bytes to UTF-8 encoded bytes."""
32+
if _py3:
33+
if isinstance(s, bytes):
34+
return s
35+
else:
36+
return s.encode('utf-8')
37+
else:
38+
return s
39+
3040
def s2u(s, errors='strict'):
3141
"""Convert a string object to a Unicode string."""
3242
if _py3:

roundup/backends/back_anydbm.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
import os, marshal, re, weakref, string, copy, time, shutil, logging
2626

2727
from roundup.anypy.dbm_ import anydbm, whichdb
28-
from roundup.anypy.strings import b2s
28+
from roundup.anypy.strings import b2s, bs2b
2929

3030
from roundup import hyperdb, date, password, roundupdb, security, support
3131
from roundup.backends import locking
@@ -2168,7 +2168,7 @@ def create(self, **propvalues):
21682168
newid = self.create_inner(**propvalues)
21692169

21702170
# store off the content as a file
2171-
self.db.storefile(self.classname, newid, None, content)
2171+
self.db.storefile(self.classname, newid, None, bs2b(content))
21722172

21732173
# fire reactors
21742174
self.fireReactors('create', newid, None)
@@ -2183,11 +2183,14 @@ def get(self, nodeid, propname, default=_marker, cache=1):
21832183
poss_msg = 'Possibly an access right configuration problem.'
21842184
if propname == 'content':
21852185
try:
2186-
return self.db.getfile(self.classname, nodeid, None)
2186+
return b2s(self.db.getfile(self.classname, nodeid, None))
21872187
except IOError as strerror:
21882188
# XXX by catching this we don't see an error in the log.
21892189
return 'ERROR reading file: %s%s\n%s\n%s'%(
21902190
self.classname, nodeid, poss_msg, strerror)
2191+
elif propname == 'binary_content':
2192+
return self.db.getfile(self.classname, nodeid, None)
2193+
21912194
if default is not _marker:
21922195
return Class.get(self, nodeid, propname, default)
21932196
else:
@@ -2220,7 +2223,7 @@ def set(self, itemid, **propvalues):
22202223
# do content?
22212224
if content:
22222225
# store and possibly index
2223-
self.db.storefile(self.classname, itemid, None, content)
2226+
self.db.storefile(self.classname, itemid, None, bs2b(content))
22242227
if self.properties['content'].indexme:
22252228
mime_type = self.get(itemid, 'type', self.default_mime_type)
22262229
self.db.indexer.add_text((self.classname, itemid, 'content'),

roundup/backends/blobfiles.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@
2222

2323
import os
2424

25-
from roundup.anypy.strings import s2b
26-
2725
def files_in_dir(dir):
2826
if not os.path.exists(dir):
2927
return 0
@@ -334,8 +332,6 @@ def storefile(self, classname, nodeid, property, content):
334332
# in multi-tracker (i.e. multi-umask) or modpython scenarios
335333
# the umask may have changed since last we set it.
336334
os.umask(self.umask)
337-
if isinstance(content, str):
338-
content = s2b(content)
339335
open(name, 'wb').write(content)
340336

341337
def getfile(self, classname, nodeid, property):

roundup/backends/rdbms_common.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
from roundup.date import Range
7070

7171
from roundup.backends.back_anydbm import compile_expression
72-
from roundup.anypy.strings import us2s
72+
from roundup.anypy.strings import b2s, bs2b, us2s
7373

7474

7575
# dummy value meaning "argument not passed"
@@ -3055,7 +3055,7 @@ def create(self, **propvalues):
30553055
content, mime_type)
30563056

30573057
# store off the content as a file
3058-
self.db.storefile(self.classname, newid, None, content)
3058+
self.db.storefile(self.classname, newid, None, bs2b(content))
30593059

30603060
# fire reactors
30613061
self.fireReactors('create', newid, None)
@@ -3070,11 +3070,14 @@ def get(self, nodeid, propname, default=_marker, cache=1):
30703070
poss_msg = 'Possibly a access right configuration problem.'
30713071
if propname == 'content':
30723072
try:
3073-
return self.db.getfile(self.classname, nodeid, None)
3073+
return b2s(self.db.getfile(self.classname, nodeid, None))
30743074
except IOError as strerror:
30753075
# BUG: by catching this we donot see an error in the log.
30763076
return 'ERROR reading file: %s%s\n%s\n%s'%(
30773077
self.classname, nodeid, poss_msg, strerror)
3078+
elif propname == 'binary_content':
3079+
return self.db.getfile(self.classname, nodeid, None)
3080+
30783081
if default is not _marker:
30793082
return Class.get(self, nodeid, propname, default)
30803083
else:
@@ -3098,7 +3101,7 @@ def set(self, itemid, **propvalues):
30983101
# do content?
30993102
if content:
31003103
# store and possibly index
3101-
self.db.storefile(self.classname, itemid, None, content)
3104+
self.db.storefile(self.classname, itemid, None, bs2b(content))
31023105
if self.properties['content'].indexme:
31033106
mime_type = self.get(itemid, 'type', self.default_mime_type)
31043107
self.db.indexer.add_text((self.classname, itemid, 'content'),

roundup/cgi/templating.py

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -938,28 +938,6 @@ def history(self, direction='descending', dre=re.compile('^\d+$'),
938938
if not self.is_view_ok():
939939
return self._('[hidden]')
940940

941-
# pre-load the history with the current state
942-
current = {}
943-
for prop_n in self._props.keys():
944-
prop = self[prop_n]
945-
if not isinstance(prop, HTMLProperty):
946-
continue
947-
current[prop_n] = prop.plain(escape=1)
948-
# make link if hrefable
949-
if (prop_n in self._props and
950-
isinstance(self._props[prop_n], hyperdb.Link)):
951-
classname = self._props[prop_n].classname
952-
try:
953-
template = self._client.selectTemplate(classname, 'item')
954-
if template.startswith('_generic.'):
955-
raise NoTemplate('not really...')
956-
except NoTemplate:
957-
pass
958-
else:
959-
id = self._klass.get(self._nodeid, prop_n, None)
960-
current[prop_n] = '<a rel="nofollow" href="%s%s">%s</a>'%(
961-
classname, id, current[prop_n])
962-
963941
# get the journal, sort and reverse
964942
history = self._klass.history(self._nodeid, skipquiet=(not showall))
965943
history.sort(key=lambda a: a[:3])
@@ -971,6 +949,7 @@ def history(self, direction='descending', dre=re.compile('^\d+$'),
971949

972950
timezone = self._db.getUserTimezone()
973951
l = []
952+
current = {}
974953
comments = {}
975954
for id, evt_date, user, action, args in history:
976955
date_s = str(evt_date.local(timezone)).replace("."," ")
@@ -999,6 +978,28 @@ def history(self, direction='descending', dre=re.compile('^\d+$'),
999978
% (self._(k), str(args[k])))
1000979
continue
1001980

981+
# load the current state for the property (if we
982+
# haven't already)
983+
if k not in current:
984+
val = self[k]
985+
if not isinstance(val, HTMLProperty):
986+
current[k] = None
987+
else:
988+
current[k] = val.plain(escape=1)
989+
# make link if hrefable
990+
if (isinstance(prop, hyperdb.Link)):
991+
classname = prop.classname
992+
try:
993+
template = self._client.selectTemplate(classname, 'item')
994+
if template.startswith('_generic.'):
995+
raise NoTemplate('not really...')
996+
except NoTemplate:
997+
pass
998+
else:
999+
linkid = self._klass.get(self._nodeid, k, None)
1000+
current[k] = '<a rel="nofollow" href="%s%s">%s</a>'%(
1001+
classname, linkid, current[k])
1002+
10021003
if args[k] and (isinstance(prop, hyperdb.Multilink) or
10031004
isinstance(prop, hyperdb.Link)):
10041005
# figure what the link class is
@@ -1084,7 +1085,7 @@ def history(self, direction='descending', dre=re.compile('^\d+$'),
10841085
else:
10851086
old = label;
10861087
cell.append('%s: %s' % (self._(k), old))
1087-
if k in current:
1088+
if k in current and current[k] is not None:
10881089
cell[-1] += ' -> %s'%current[k]
10891090
current[k] = old
10901091

@@ -1095,49 +1096,49 @@ def history(self, direction='descending', dre=re.compile('^\d+$'),
10951096
d = date.Date(args[k],
10961097
translator=self._client).local(timezone)
10971098
cell.append('%s: %s'%(self._(k), str(d)))
1098-
if k in current:
1099+
if k in current and current[k] is not None:
10991100
cell[-1] += ' -> %s' % current[k]
11001101
current[k] = str(d)
11011102

11021103
elif isinstance(prop, hyperdb.Interval) and args[k]:
11031104
val = str(date.Interval(args[k],
11041105
translator=self._client))
11051106
cell.append('%s: %s'%(self._(k), val))
1106-
if k in current:
1107+
if k in current and current[k] is not None:
11071108
cell[-1] += ' -> %s'%current[k]
11081109
current[k] = val
11091110

11101111
elif isinstance(prop, hyperdb.String) and args[k]:
11111112
val = cgi.escape(args[k])
11121113
cell.append('%s: %s'%(self._(k), val))
1113-
if k in current:
1114+
if k in current and current[k] is not None:
11141115
cell[-1] += ' -> %s'%current[k]
11151116
current[k] = val
11161117

11171118
elif isinstance(prop, hyperdb.Boolean) and args[k] is not None:
11181119
val = args[k] and ''"Yes" or ''"No"
11191120
cell.append('%s: %s'%(self._(k), val))
1120-
if k in current:
1121+
if k in current and current[k] is not None:
11211122
cell[-1] += ' -> %s'%current[k]
11221123
current[k] = val
11231124

11241125
elif isinstance(prop, hyperdb.Password) and args[k] is not None:
11251126
val = args[k].dummystr()
11261127
cell.append('%s: %s'%(self._(k), val))
1127-
if k in current:
1128+
if k in current and current[k] is not None:
11281129
cell[-1] += ' -> %s'%current[k]
11291130
current[k] = val
11301131

11311132
elif not args[k]:
1132-
if k in current:
1133+
if k in current and current[k] is not None:
11331134
cell.append('%s: %s'%(self._(k), current[k]))
11341135
current[k] = '(no value)'
11351136
else:
11361137
cell.append(self._('%s: (no value)')%self._(k))
11371138

11381139
else:
11391140
cell.append('%s: %s'%(self._(k), str(args[k])))
1140-
if k in current:
1141+
if k in current and current[k] is not None:
11411142
cell[-1] += ' -> %s'%current[k]
11421143
current[k] = str(args[k])
11431144

test/db_test_base.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,11 @@ def testStringUnicode(self):
298298
self.db.commit()
299299
self.assertEqual(self.db.issue.get(nid, 'title'), ustr2)
300300

301+
# test set & retrieve (this time for file contents)
302+
nid = self.db.file.create(content=ustr)
303+
self.assertEqual(self.db.file.get(nid, 'content'), ustr)
304+
self.assertEqual(self.db.file.get(nid, 'binary_content'), s2b(ustr))
305+
301306
# Link
302307
def testLinkChange(self):
303308
self.assertRaises(IndexError, self.db.issue.create, title="spam",

0 commit comments

Comments
 (0)