|
1 | | -# $Id: hyperdb.py,v 1.4 2001-07-19 06:27:07 anthonybaxter Exp $ |
| 1 | +# $Id: hyperdb.py,v 1.5 2001-07-20 07:35:55 richard Exp $ |
2 | 2 |
|
3 | | -import bsddb, os, cPickle, re, string |
| 3 | +# standard python modules |
| 4 | +import cPickle, re, string |
4 | 5 |
|
| 6 | +# roundup modules |
5 | 7 | import date |
| 8 | + |
| 9 | + |
| 10 | +RETIRED_FLAG = '__hyperdb_retired' |
| 11 | + |
| 12 | +# |
| 13 | +# Here's where we figure which db to use.... |
| 14 | +# |
| 15 | +import hyperdb_bsddb |
| 16 | +Database = hyperdb_bsddb.Database |
| 17 | +hyperdb_bsddb.RETIRED_FLAG = RETIRED_FLAG |
| 18 | + |
| 19 | + |
6 | 20 | # |
7 | 21 | # Types |
8 | 22 | # |
@@ -45,165 +59,10 @@ class Multilink(BaseType, Link): |
45 | 59 | class DatabaseError(ValueError): |
46 | 60 | pass |
47 | 61 |
|
| 62 | + |
48 | 63 | # |
49 | | -# Now the database |
| 64 | +# The base Class class |
50 | 65 | # |
51 | | -RETIRED_FLAG = '__hyperdb_retired' |
52 | | -class Database: |
53 | | - """A database for storing records containing flexible data types.""" |
54 | | - |
55 | | - def __init__(self, storagelocator, journaltag=None): |
56 | | - """Open a hyperdatabase given a specifier to some storage. |
57 | | -
|
58 | | - The meaning of 'storagelocator' depends on the particular |
59 | | - implementation of the hyperdatabase. It could be a file name, |
60 | | - a directory path, a socket descriptor for a connection to a |
61 | | - database over the network, etc. |
62 | | -
|
63 | | - The 'journaltag' is a token that will be attached to the journal |
64 | | - entries for any edits done on the database. If 'journaltag' is |
65 | | - None, the database is opened in read-only mode: the Class.create(), |
66 | | - Class.set(), and Class.retire() methods are disabled. |
67 | | - """ |
68 | | - self.dir, self.journaltag = storagelocator, journaltag |
69 | | - self.classes = {} |
70 | | - |
71 | | - # |
72 | | - # Classes |
73 | | - # |
74 | | - def __getattr__(self, classname): |
75 | | - """A convenient way of calling self.getclass(classname).""" |
76 | | - return self.classes[classname] |
77 | | - |
78 | | - def addclass(self, cl): |
79 | | - cn = cl.classname |
80 | | - if self.classes.has_key(cn): |
81 | | - raise ValueError, cn |
82 | | - self.classes[cn] = cl |
83 | | - |
84 | | - def getclasses(self): |
85 | | - """Return a list of the names of all existing classes.""" |
86 | | - l = self.classes.keys() |
87 | | - l.sort() |
88 | | - return l |
89 | | - |
90 | | - def getclass(self, classname): |
91 | | - """Get the Class object representing a particular class. |
92 | | -
|
93 | | - If 'classname' is not a valid class name, a KeyError is raised. |
94 | | - """ |
95 | | - return self.classes[classname] |
96 | | - |
97 | | - # |
98 | | - # Class DBs |
99 | | - # |
100 | | - def clear(self): |
101 | | - for cn in self.classes.keys(): |
102 | | - db = os.path.join(self.dir, 'nodes.%s'%cn) |
103 | | - bsddb.btopen(db, 'n') |
104 | | - db = os.path.join(self.dir, 'journals.%s'%cn) |
105 | | - bsddb.btopen(db, 'n') |
106 | | - |
107 | | - def getclassdb(self, classname, mode='r'): |
108 | | - ''' grab a connection to the class db that will be used for |
109 | | - multiple actions |
110 | | - ''' |
111 | | - path = os.path.join(os.getcwd(), self.dir, 'nodes.%s'%classname) |
112 | | - return bsddb.btopen(path, mode) |
113 | | - |
114 | | - def addnode(self, classname, nodeid, node): |
115 | | - ''' add the specified node to its class's db |
116 | | - ''' |
117 | | - db = self.getclassdb(classname, 'c') |
118 | | - db[nodeid] = cPickle.dumps(node, 1) |
119 | | - db.close() |
120 | | - setnode = addnode |
121 | | - |
122 | | - def getnode(self, classname, nodeid, cldb=None): |
123 | | - ''' add the specified node to its class's db |
124 | | - ''' |
125 | | - db = cldb or self.getclassdb(classname) |
126 | | - if not db.has_key(nodeid): |
127 | | - raise IndexError, nodeid |
128 | | - res = cPickle.loads(db[nodeid]) |
129 | | - if not cldb: db.close() |
130 | | - return res |
131 | | - |
132 | | - def hasnode(self, classname, nodeid, cldb=None): |
133 | | - ''' add the specified node to its class's db |
134 | | - ''' |
135 | | - db = cldb or self.getclassdb(classname) |
136 | | - res = db.has_key(nodeid) |
137 | | - if not cldb: db.close() |
138 | | - return res |
139 | | - |
140 | | - def countnodes(self, classname, cldb=None): |
141 | | - db = cldb or self.getclassdb(classname) |
142 | | - return len(db.keys()) |
143 | | - if not cldb: db.close() |
144 | | - return res |
145 | | - |
146 | | - def getnodeids(self, classname, cldb=None): |
147 | | - db = cldb or self.getclassdb(classname) |
148 | | - res = db.keys() |
149 | | - if not cldb: db.close() |
150 | | - return res |
151 | | - |
152 | | - # |
153 | | - # Journal |
154 | | - # |
155 | | - def addjournal(self, classname, nodeid, action, params): |
156 | | - ''' Journal the Action |
157 | | - 'action' may be: |
158 | | -
|
159 | | - 'create' or 'set' -- 'params' is a dictionary of property values |
160 | | - 'link' or 'unlink' -- 'params' is (classname, nodeid, propname) |
161 | | - 'retire' -- 'params' is None |
162 | | - ''' |
163 | | - entry = (nodeid, date.Date(), self.journaltag, action, params) |
164 | | - db = bsddb.btopen(os.path.join(self.dir, 'journals.%s'%classname), 'c') |
165 | | - if db.has_key(nodeid): |
166 | | - s = db[nodeid] |
167 | | - l = cPickle.loads(db[nodeid]) |
168 | | - l.append(entry) |
169 | | - else: |
170 | | - l = [entry] |
171 | | - db[nodeid] = cPickle.dumps(l) |
172 | | - db.close() |
173 | | - |
174 | | - def getjournal(self, classname, nodeid): |
175 | | - ''' get the journal for id |
176 | | - ''' |
177 | | - db = bsddb.btopen(os.path.join(self.dir, 'journals.%s'%classname), 'r') |
178 | | - res = cPickle.loads(db[nodeid]) |
179 | | - db.close() |
180 | | - return res |
181 | | - |
182 | | - def close(self): |
183 | | - ''' Close the Database - we must release the circular refs so that |
184 | | - we can be del'ed and the underlying bsddb connections closed |
185 | | - cleanly. |
186 | | - ''' |
187 | | - self.classes = None |
188 | | - |
189 | | - |
190 | | - # |
191 | | - # Basic transaction support |
192 | | - # |
193 | | - # TODO: well, write these methods (and then use them in other code) |
194 | | - def register_action(self): |
195 | | - ''' Register an action to the transaction undo log |
196 | | - ''' |
197 | | - |
198 | | - def commit(self): |
199 | | - ''' Commit the current transaction, start a new one |
200 | | - ''' |
201 | | - |
202 | | - def rollback(self): |
203 | | - ''' Reverse all actions from the current transaction |
204 | | - ''' |
205 | | - |
206 | | - |
207 | 66 | class Class: |
208 | 67 | """The handle to a particular class of nodes in a hyperdatabase.""" |
209 | 68 |
|
@@ -876,53 +735,6 @@ def Choice(name, *options): |
876 | 735 | cl.create(name=option[i], order=i) |
877 | 736 | return hyperdb.Link(name) |
878 | 737 |
|
879 | | - |
880 | | -if __name__ == '__main__': |
881 | | - import pprint |
882 | | - db = Database("test_db", "richard") |
883 | | - status = Class(db, "status", name=String()) |
884 | | - status.setkey("name") |
885 | | - print db.status.create(name="unread") |
886 | | - print db.status.create(name="in-progress") |
887 | | - print db.status.create(name="testing") |
888 | | - print db.status.create(name="resolved") |
889 | | - print db.status.count() |
890 | | - print db.status.list() |
891 | | - print db.status.lookup("in-progress") |
892 | | - db.status.retire(3) |
893 | | - print db.status.list() |
894 | | - issue = Class(db, "issue", title=String(), status=Link("status")) |
895 | | - db.issue.create(title="spam", status=1) |
896 | | - db.issue.create(title="eggs", status=2) |
897 | | - db.issue.create(title="ham", status=4) |
898 | | - db.issue.create(title="arguments", status=2) |
899 | | - db.issue.create(title="abuse", status=1) |
900 | | - user = Class(db, "user", username=String(), password=String()) |
901 | | - user.setkey("username") |
902 | | - db.issue.addprop(fixer=Link("user")) |
903 | | - print db.issue.getprops() |
904 | | -#{"title": <hyperdb.String>, "status": <hyperdb.Link to "status">, |
905 | | -#"user": <hyperdb.Link to "user">} |
906 | | - db.issue.set(5, status=2) |
907 | | - print db.issue.get(5, "status") |
908 | | - print db.status.get(2, "name") |
909 | | - print db.issue.get(5, "title") |
910 | | - print db.issue.find(status = db.status.lookup("in-progress")) |
911 | | - print db.issue.history(5) |
912 | | -# [(<Date 2000-06-28.19:09:43>, "ping", "create", {"title": "abuse", "status": 1}), |
913 | | -# (<Date 2000-06-28.19:11:04>, "ping", "set", {"status": 2})] |
914 | | - print db.status.history(1) |
915 | | -# [(<Date 2000-06-28.19:09:43>, "ping", "link", ("issue", 5, "status")), |
916 | | -# (<Date 2000-06-28.19:11:04>, "ping", "unlink", ("issue", 5, "status"))] |
917 | | - print db.status.history(2) |
918 | | -# [(<Date 2000-06-28.19:11:04>, "ping", "link", ("issue", 5, "status"))] |
919 | | - |
920 | | - # TODO: set up some filter tests |
921 | | - |
922 | 738 | # |
923 | 739 | # $Log: not supported by cvs2svn $ |
924 | | -# Revision 1.3 2001/07/19 05:52:22 anthonybaxter |
925 | | -# Added CVS keywords Id and Log to all python files. |
926 | | -# |
927 | | -# |
928 | 740 |
|
0 commit comments