Skip to content

Commit 439eb02

Browse files
author
Richard Jones
committed
Fixed serialisation problem...
...by moving the serialisation step out of the hyperdb.Class (get, set) into the hyperdb.Database. Also fixed htmltemplate after the showid changes I made yesterday. Unit tests for all of the above written.
1 parent 1702aa2 commit 439eb02

File tree

7 files changed

+135
-42
lines changed

7 files changed

+135
-42
lines changed

CHANGES.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
This file contains the changes to the Roundup system over time. The entries
22
are given with the most recent entry first.
33

4-
0.4.2
4+
2002-04-?? 0.4.2
55
Feature:
66
. link() htmltemplate function now has a "showid" option for links and
77
multilinks. When true, it only displays the linked node id as the anchor
@@ -18,6 +18,7 @@ Feature:
1818

1919
Fixed:
2020
. stop sending blank (whitespace-only) notes
21+
. cleanup of serialisation for database storage
2122

2223

2324
2002-03-25 - 0.4.1

roundup/backends/back_anydbm.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1717
#
18-
#$Id: back_anydbm.py,v 1.30 2002-02-27 03:40:59 richard Exp $
18+
#$Id: back_anydbm.py,v 1.31 2002-04-03 05:54:31 richard Exp $
1919
'''
2020
This module defines a backend that saves the hyperdatabase in a database
2121
chosen by anydbm. It is guaranteed to always be available in python
@@ -177,6 +177,7 @@ def setnode(self, classname, nodeid, node):
177177
if hyperdb.DEBUG:
178178
print 'setnode', (self, classname, nodeid, node)
179179
self.dirtynodes.setdefault(classname, {})[nodeid] = 1
180+
180181
# can't set without having already loaded the node
181182
self.cache[classname][nodeid] = node
182183
self.savenode(classname, nodeid, node)
@@ -204,9 +205,17 @@ def getnode(self, classname, nodeid, db=None, cache=1):
204205
db = self.getclassdb(classname)
205206
if not db.has_key(nodeid):
206207
raise IndexError, "no such %s %s"%(classname, nodeid)
208+
209+
# decode
207210
res = marshal.loads(db[nodeid])
211+
212+
# reverse the serialisation
213+
res = self.unserialise(classname, res)
214+
215+
# store off in the cache
208216
if cache:
209217
cache[nodeid] = res
218+
210219
return res
211220

212221
def hasnode(self, classname, nodeid, db=None):
@@ -380,11 +389,17 @@ def _doSaveNode(self, classname, nodeid, node):
380389
db = self.databases[db_name] = self.getclassdb(classname, 'c')
381390

382391
# now save the marshalled data
383-
db[nodeid] = marshal.dumps(node)
392+
db[nodeid] = marshal.dumps(self.serialise(classname, node))
384393

385394
def _doSaveJournal(self, classname, nodeid, action, params):
395+
# serialise first
396+
if action in ('set', 'create'):
397+
params = self.serialise(classname, params)
398+
399+
# create the journal entry
386400
entry = (nodeid, date.Date().get_tuple(), self.journaltag, action,
387401
params)
402+
388403
if hyperdb.DEBUG:
389404
print '_doSaveJournal', entry
390405

@@ -397,11 +412,13 @@ def _doSaveJournal(self, classname, nodeid, action, params):
397412

398413
# now insert the journal entry
399414
if db.has_key(nodeid):
415+
# append to existing
400416
s = db[nodeid]
401417
l = marshal.loads(s)
402418
l.append(entry)
403419
else:
404420
l = [entry]
421+
405422
db[nodeid] = marshal.dumps(l)
406423

407424
def _doStoreFile(self, name, **databases):
@@ -425,6 +442,9 @@ def rollback(self):
425442

426443
#
427444
#$Log: not supported by cvs2svn $
445+
#Revision 1.30 2002/02/27 03:40:59 richard
446+
#Ran it through pychecker, made fixes
447+
#
428448
#Revision 1.29 2002/02/25 14:34:31 grubert
429449
# . use blobfiles in back_anydbm which is used in back_bsddb.
430450
# change test_db as dirlist does not work for subdirectories.

roundup/backends/back_bsddb.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1717
#
18-
#$Id: back_bsddb.py,v 1.16 2002-02-27 03:40:59 richard Exp $
18+
#$Id: back_bsddb.py,v 1.17 2002-04-03 05:54:31 richard Exp $
1919
'''
2020
This module defines a backend that saves the hyperdatabase in BSDDB.
2121
'''
@@ -95,20 +95,33 @@ def getjournal(self, classname, nodeid):
9595
return res
9696

9797
def _doSaveJournal(self, classname, nodeid, action, params):
98+
# serialise first
99+
if action in ('set', 'create'):
100+
params = self.serialise(classname, params)
101+
98102
entry = (nodeid, date.Date().get_tuple(), self.journaltag, action,
99103
params)
104+
105+
if hyperdb.DEBUG:
106+
print '_doSaveJournal', entry
107+
100108
db = bsddb.btopen(os.path.join(self.dir, 'journals.%s'%classname), 'c')
109+
101110
if db.has_key(nodeid):
102111
s = db[nodeid]
103112
l = marshal.loads(s)
104113
l.append(entry)
105114
else:
106115
l = [entry]
116+
107117
db[nodeid] = marshal.dumps(l)
108118
db.close()
109119

110120
#
111121
#$Log: not supported by cvs2svn $
122+
#Revision 1.16 2002/02/27 03:40:59 richard
123+
#Ran it through pychecker, made fixes
124+
#
112125
#Revision 1.15 2002/02/16 09:15:33 richard
113126
#forgot to patch bsddb backend too
114127
#

roundup/htmltemplate.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1717
#
18-
# $Id: htmltemplate.py,v 1.85 2002-04-02 01:40:58 richard Exp $
18+
# $Id: htmltemplate.py,v 1.86 2002-04-03 05:54:31 richard Exp $
1919

2020
__doc__ = """
2121
Template engine.
@@ -357,10 +357,11 @@ def do_link(self, property=None, is_download=0, showid=0):
357357
linkvalue = cgi.escape(linkcl.get(value, k))
358358
if showid:
359359
label = value
360-
title = ' title="%s"'%linkvalue
361-
# note ... this should be urllib.quote(linkcl.get(value, k))
360+
title = ' title="%s"'%linkvalue
361+
# note ... this should be urllib.quote(linkcl.get(value, k))
362362
else:
363363
label = linkvalue
364+
title = ''
364365
if is_download:
365366
return '<a href="%s%s/%s"%s>%s</a>'%(linkname, value,
366367
linkvalue, title, label)
@@ -376,9 +377,10 @@ def do_link(self, property=None, is_download=0, showid=0):
376377
if showid:
377378
label = value
378379
title = ' title="%s"'%linkvalue
379-
# note ... this should be urllib.quote(linkcl.get(value, k))
380+
# note ... this should be urllib.quote(linkcl.get(value, k))
380381
else:
381382
label = linkvalue
383+
title = ''
382384
if is_download:
383385
l.append('<a href="%s%s/%s"%s>%s</a>'%(linkname, value,
384386
linkvalue, title, label))
@@ -1126,6 +1128,12 @@ def render(self, form):
11261128

11271129
#
11281130
# $Log: not supported by cvs2svn $
1131+
# Revision 1.85 2002/04/02 01:40:58 richard
1132+
# . link() htmltemplate function now has a "showid" option for links and
1133+
# multilinks. When true, it only displays the linked node id as the anchor
1134+
# text. The link value is displayed as a tooltip using the title anchor
1135+
# attribute.
1136+
#
11291137
# Revision 1.84 2002/03/29 19:41:48 rochecompaan
11301138
# . Fixed display of mutlilink properties when using the template
11311139
# functions, menu and plain.

roundup/hyperdb.py

Lines changed: 47 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1717
#
18-
# $Id: hyperdb.py,v 1.59 2002-03-12 22:52:26 richard Exp $
18+
# $Id: hyperdb.py,v 1.60 2002-04-03 05:54:31 richard Exp $
1919

2020
__doc__ = """
2121
Hyperdatabase implementation, especially field types.
@@ -171,11 +171,51 @@ def addnode(self, classname, nodeid, node):
171171
'''
172172
raise NotImplementedError
173173

174+
def serialise(self, classname, node):
175+
'''Copy the node contents, converting non-marshallable data into
176+
marshallable data.
177+
'''
178+
if DEBUG: print 'serialise', classname, node
179+
properties = self.getclass(classname).getprops()
180+
d = {}
181+
for k, v in node.items():
182+
prop = properties[k]
183+
184+
if isinstance(prop, Password):
185+
d[k] = str(v)
186+
elif isinstance(prop, Date) and v is not None:
187+
d[k] = v.get_tuple()
188+
elif isinstance(prop, Interval) and v is not None:
189+
d[k] = v.get_tuple()
190+
else:
191+
d[k] = v
192+
return d
193+
174194
def setnode(self, classname, nodeid, node):
175195
'''Change the specified node.
176196
'''
177197
raise NotImplementedError
178198

199+
def unserialise(self, classname, node):
200+
'''Decode the marshalled node data
201+
'''
202+
if DEBUG: print 'unserialise', classname, node
203+
properties = self.getclass(classname).getprops()
204+
d = {}
205+
for k, v in node.items():
206+
prop = properties[k]
207+
if isinstance(prop, Date) and v is not None:
208+
d[k] = date.Date(v)
209+
elif isinstance(prop, Interval) and v is not None:
210+
d[k] = date.Interval(v)
211+
elif isinstance(prop, Password):
212+
p = password.Password()
213+
p.unpack(v)
214+
d[k] = p
215+
else:
216+
d[k] = v
217+
return d
218+
179219
def getnode(self, classname, nodeid, db=None, cache=1):
180220
'''Get a node from the database.
181221
'''
@@ -396,17 +436,6 @@ def create(self, **propvalues):
396436
# TODO: None isn't right here, I think...
397437
propvalues[key] = None
398438

399-
# convert all data to strings
400-
for key, prop in self.properties.items():
401-
if isinstance(prop, Date):
402-
if propvalues[key] is not None:
403-
propvalues[key] = propvalues[key].get_tuple()
404-
elif isinstance(prop, Interval):
405-
if propvalues[key] is not None:
406-
propvalues[key] = propvalues[key].get_tuple()
407-
elif isinstance(prop, Password):
408-
propvalues[key] = str(propvalues[key])
409-
410439
# done
411440
self.db.addnode(self.classname, newid, propvalues)
412441
self.db.addjournal(self.classname, newid, 'create', propvalues)
@@ -443,20 +472,6 @@ def get(self, nodeid, propname, default=_marker, cache=1):
443472
else:
444473
return default
445474

446-
# possibly convert the marshalled data to instances
447-
if isinstance(prop, Date):
448-
if d[propname] is None:
449-
return None
450-
return date.Date(d[propname])
451-
elif isinstance(prop, Interval):
452-
if d[propname] is None:
453-
return None
454-
return date.Interval(d[propname])
455-
elif isinstance(prop, Password):
456-
p = password.Password()
457-
p.unpack(d[propname])
458-
return p
459-
460475
return d[propname]
461476

462477
# XXX not in spec
@@ -605,17 +620,17 @@ class or a KeyError is raised.
605620
elif isinstance(prop, Password):
606621
if not isinstance(value, password.Password):
607622
raise TypeError, 'new property "%s" not a Password'% key
608-
propvalues[key] = value = str(value)
623+
propvalues[key] = value
609624

610625
elif value is not None and isinstance(prop, Date):
611626
if not isinstance(value, date.Date):
612627
raise TypeError, 'new property "%s" not a Date'% key
613-
propvalues[key] = value = value.get_tuple()
628+
propvalues[key] = value
614629

615630
elif value is not None and isinstance(prop, Interval):
616631
if not isinstance(value, date.Interval):
617632
raise TypeError, 'new property "%s" not an Interval'% key
618-
propvalues[key] = value = value.get_tuple()
633+
propvalues[key] = value
619634

620635
node[key] = value
621636

@@ -1097,6 +1112,9 @@ def Choice(name, db, *options):
10971112

10981113
#
10991114
# $Log: not supported by cvs2svn $
1115+
# Revision 1.59 2002/03/12 22:52:26 richard
1116+
# more pychecker warnings removed
1117+
#
11001118
# Revision 1.58 2002/02/27 03:23:16 richard
11011119
# Ran it through pychecker, made fixes
11021120
#

test/test_db.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@
1515
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1717
#
18-
# $Id: test_db.py,v 1.19 2002-02-25 14:34:31 grubert Exp $
18+
# $Id: test_db.py,v 1.20 2002-04-03 05:54:31 richard Exp $
1919

2020
import unittest, os, shutil
2121

2222
from roundup.hyperdb import String, Password, Link, Multilink, Date, \
2323
Interval, Class, DatabaseError
2424
from roundup.roundupdb import FileClass
25-
from roundup import date
25+
from roundup import date, password
2626

2727
def setupSchema(db, create):
2828
status = Class(db, "status", name=String())
@@ -84,7 +84,11 @@ def testChanges(self):
8484

8585
a = self.db.issue.get('5', "deadline")
8686
self.db.issue.set('5', deadline=date.Date())
87-
self.assertNotEqual(a, self.db.issue.get('5', "deadline"))
87+
b = self.db.issue.get('5', "deadline")
88+
self.db.commit()
89+
self.assertNotEqual(a, b)
90+
self.assertNotEqual(b, date.Date('1970-1-1 00:00:00'))
91+
self.db.issue.set('5', deadline=date.Date())
8892

8993
a = self.db.issue.get('5', "foo")
9094
self.db.issue.set('5', foo=date.Interval('-1d'))
@@ -98,6 +102,17 @@ def testChanges(self):
98102
self.db.status.history('1')
99103
self.db.status.history('2')
100104

105+
def testSerialisation(self):
106+
self.db.issue.create(title="spam", status='1',
107+
deadline=date.Date(), foo=date.Interval('-1d'))
108+
self.db.commit()
109+
assert isinstance(self.db.issue.get('1', 'deadline'), date.Date)
110+
assert isinstance(self.db.issue.get('1', 'foo'), date.Interval)
111+
self.db.user.create(username="fozzy",
112+
password=password.Password('t. bear'))
113+
self.db.commit()
114+
assert isinstance(self.db.user.get('1', 'password'), password.Password)
115+
101116
def testTransactions(self):
102117
# remember the number of items we started
103118
num_issues = len(self.db.issue.list())
@@ -324,7 +339,8 @@ def setUp(self):
324339

325340

326341
def suite():
327-
l = [unittest.makeSuite(anydbmDBTestCase, 'test'),
342+
l = [
343+
unittest.makeSuite(anydbmDBTestCase, 'test'),
328344
unittest.makeSuite(anydbmReadOnlyDBTestCase, 'test')
329345
]
330346

@@ -346,6 +362,11 @@ def suite():
346362

347363
#
348364
# $Log: not supported by cvs2svn $
365+
# Revision 1.19 2002/02/25 14:34:31 grubert
366+
# . use blobfiles in back_anydbm which is used in back_bsddb.
367+
# change test_db as dirlist does not work for subdirectories.
368+
# ATTENTION: blobfiles now creates subdirectories for files.
369+
#
349370
# Revision 1.18 2002/01/22 07:21:13 richard
350371
# . fixed back_bsddb so it passed the journal tests
351372
#

0 commit comments

Comments
 (0)