comparison roundup/hyperdb.py @ 224:ad2c98faec97

using isinstance(blah, Foo) now instead of isFooType
author Richard Jones <richard@users.sourceforge.net>
date Sun, 12 Aug 2001 06:32:36 +0000
parents 18134bffab37
children 1d1848c99abe
comparison
equal deleted inserted replaced
223:5ce5fc22ac6c 224:ad2c98faec97
13 # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 13 # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
14 # FOR A PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" 14 # FOR A PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS"
15 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, 15 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
16 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 16 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
17 # 17 #
18 # $Id: hyperdb.py,v 1.14 2001-08-07 00:24:42 richard Exp $ 18 # $Id: hyperdb.py,v 1.15 2001-08-12 06:32:36 richard Exp $
19 19
20 # standard python modules 20 # standard python modules
21 import cPickle, re, string 21 import cPickle, re, string
22 22
23 # roundup modules 23 # roundup modules
25 25
26 26
27 # 27 #
28 # Types 28 # Types
29 # 29 #
30 class BaseType: 30 class String:
31 isStringType = 0 31 """An object designating a String property."""
32 isDateType = 0
33 isIntervalType = 0
34 isLinkType = 0
35 isMultilinkType = 0
36
37 class String(BaseType):
38 def __init__(self):
39 """An object designating a String property."""
40 pass
41 def __repr__(self): 32 def __repr__(self):
42 return '<%s>'%self.__class__ 33 return '<%s>'%self.__class__
43 isStringType = 1 34
44 35 class Date:
45 class Date(BaseType, String): 36 """An object designating a Date property."""
46 isDateType = 1 37 def __repr__(self):
47 38 return '<%s>'%self.__class__
48 class Interval(BaseType, String): 39
49 isIntervalType = 1 40 class Interval:
50 41 """An object designating an Interval property."""
51 class Link(BaseType): 42 def __repr__(self):
43 return '<%s>'%self.__class__
44
45 class Link:
46 """An object designating a Link property that links to a
47 node in a specified class."""
52 def __init__(self, classname): 48 def __init__(self, classname):
53 """An object designating a Link property that links to
54 nodes in a specified class."""
55 self.classname = classname 49 self.classname = classname
56 def __repr__(self): 50 def __repr__(self):
57 return '<%s to "%s">'%(self.__class__, self.classname) 51 return '<%s to "%s">'%(self.__class__, self.classname)
58 isLinkType = 1 52
59 53 class Multilink:
60 class Multilink(BaseType, Link):
61 """An object designating a Multilink property that links 54 """An object designating a Multilink property that links
62 to nodes in a specified class. 55 to nodes in a specified class.
63 """ 56 """
64 isMultilinkType = 1 57 def __init__(self, classname):
58 self.classname = classname
59 def __repr__(self):
60 return '<%s to "%s">'%(self.__class__, self.classname)
65 61
66 class DatabaseError(ValueError): 62 class DatabaseError(ValueError):
67 pass 63 pass
68 64
69 65
141 prop = self.properties[key] 137 prop = self.properties[key]
142 except KeyError: 138 except KeyError:
143 raise KeyError, '"%s" has no property "%s"'%(self.classname, 139 raise KeyError, '"%s" has no property "%s"'%(self.classname,
144 key) 140 key)
145 141
146 if prop.isLinkType: 142 if isinstance(prop, Link):
147 if type(value) != type(''): 143 if type(value) != type(''):
148 raise ValueError, 'link value must be String' 144 raise ValueError, 'link value must be String'
149 # value = str(value)
150 link_class = self.properties[key].classname 145 link_class = self.properties[key].classname
151 # if it isn't a number, it's a key 146 # if it isn't a number, it's a key
152 if not num_re.match(value): 147 if not num_re.match(value):
153 try: 148 try:
154 value = self.db.classes[link_class].lookup(value) 149 value = self.db.classes[link_class].lookup(value)
161 156
162 # register the link with the newly linked node 157 # register the link with the newly linked node
163 self.db.addjournal(link_class, value, 'link', 158 self.db.addjournal(link_class, value, 'link',
164 (self.classname, newid, key)) 159 (self.classname, newid, key))
165 160
166 elif prop.isMultilinkType: 161 elif isinstance(prop, Multilink):
167 if type(value) != type([]): 162 if type(value) != type([]):
168 raise TypeError, 'new property "%s" not a list of ids'%key 163 raise TypeError, 'new property "%s" not a list of ids'%key
169 link_class = self.properties[key].classname 164 link_class = self.properties[key].classname
170 l = [] 165 l = []
171 for entry in value: 166 for entry in value:
188 raise IndexError, '%s has no node %s'%(link_class, id) 183 raise IndexError, '%s has no node %s'%(link_class, id)
189 # register the link with the newly linked node 184 # register the link with the newly linked node
190 self.db.addjournal(link_class, id, 'link', 185 self.db.addjournal(link_class, id, 'link',
191 (self.classname, newid, key)) 186 (self.classname, newid, key))
192 187
193 elif prop.isStringType: 188 elif isinstance(prop, String):
194 if type(value) != type(''): 189 if type(value) != type(''):
195 raise TypeError, 'new property "%s" not a string'%key 190 raise TypeError, 'new property "%s" not a string'%key
196 191
197 elif prop.isDateType: 192 elif isinstance(prop, Date):
198 if not hasattr(value, 'isDate'): 193 if not hasattr(value, 'isDate'):
199 raise TypeError, 'new property "%s" not a Date'% key 194 raise TypeError, 'new property "%s" not a Date'% key
200 195
201 elif prop.isIntervalType: 196 elif isinstance(prop, Interval):
202 if not hasattr(value, 'isInterval'): 197 if not hasattr(value, 'isInterval'):
203 raise TypeError, 'new property "%s" not an Interval'% key 198 raise TypeError, 'new property "%s" not an Interval'% key
204 199
205 for key, prop in self.properties.items(): 200 for key, prop in self.properties.items():
206 if propvalues.has_key(key): 201 if propvalues.has_key(key):
207 continue 202 continue
208 if prop.isMultilinkType: 203 if isinstance(prop, Multilink):
209 propvalues[key] = [] 204 propvalues[key] = []
210 else: 205 else:
211 propvalues[key] = None 206 propvalues[key] = None
212 207
213 # done 208 # done
222 IndexError is raised. 'propname' must be the name of a property 217 IndexError is raised. 'propname' must be the name of a property
223 of this class or a KeyError is raised. 218 of this class or a KeyError is raised.
224 """ 219 """
225 if propname == 'id': 220 if propname == 'id':
226 return nodeid 221 return nodeid
227 # nodeid = str(nodeid)
228 d = self.db.getnode(self.classname, nodeid) 222 d = self.db.getnode(self.classname, nodeid)
229 if not d.has_key(propname) and default is not _marker: 223 if not d.has_key(propname) and default is not _marker:
230 return default 224 return default
231 return d[propname] 225 return d[propname]
232 226
261 raise KeyError, '"id" is reserved' 255 raise KeyError, '"id" is reserved'
262 256
263 if self.db.journaltag is None: 257 if self.db.journaltag is None:
264 raise DatabaseError, 'Database open read-only' 258 raise DatabaseError, 'Database open read-only'
265 259
266 # nodeid = str(nodeid)
267 node = self.db.getnode(self.classname, nodeid) 260 node = self.db.getnode(self.classname, nodeid)
268 if node.has_key(self.db.RETIRED_FLAG): 261 if node.has_key(self.db.RETIRED_FLAG):
269 raise IndexError 262 raise IndexError
270 num_re = re.compile('^\d+$') 263 num_re = re.compile('^\d+$')
271 for key, value in propvalues.items(): 264 for key, value in propvalues.items():
280 else: 273 else:
281 raise ValueError, 'node with key "%s" exists'%value 274 raise ValueError, 'node with key "%s" exists'%value
282 275
283 prop = self.properties[key] 276 prop = self.properties[key]
284 277
285 if prop.isLinkType: 278 if isinstance(prop, Link):
286 # value = str(value)
287 link_class = self.properties[key].classname 279 link_class = self.properties[key].classname
288 # if it isn't a number, it's a key 280 # if it isn't a number, it's a key
289 if type(value) != type(''): 281 if type(value) != type(''):
290 raise ValueError, 'link value must be String' 282 raise ValueError, 'link value must be String'
291 if not num_re.match(value): 283 if not num_re.match(value):
306 # register the link with the newly linked node 298 # register the link with the newly linked node
307 if value is not None: 299 if value is not None:
308 self.db.addjournal(link_class, value, 'link', 300 self.db.addjournal(link_class, value, 'link',
309 (self.classname, nodeid, key)) 301 (self.classname, nodeid, key))
310 302
311 elif prop.isMultilinkType: 303 elif isinstance(prop, Multilink):
312 if type(value) != type([]): 304 if type(value) != type([]):
313 raise TypeError, 'new property "%s" not a list of ids'%key 305 raise TypeError, 'new property "%s" not a list of ids'%key
314 link_class = self.properties[key].classname 306 link_class = self.properties[key].classname
315 l = [] 307 l = []
316 for entry in value: 308 for entry in value:
346 # register the link with the newly linked node 338 # register the link with the newly linked node
347 self.db.addjournal(link_class, id, 'link', 339 self.db.addjournal(link_class, id, 'link',
348 (self.classname, nodeid, key)) 340 (self.classname, nodeid, key))
349 l.append(id) 341 l.append(id)
350 342
351 elif prop.isStringType: 343 elif isinstance(prop, String):
352 if value is not None and type(value) != type(''): 344 if value is not None and type(value) != type(''):
353 raise TypeError, 'new property "%s" not a string'%key 345 raise TypeError, 'new property "%s" not a string'%key
354 346
355 elif prop.isDateType: 347 elif isinstance(prop, Date):
356 if not hasattr(value, 'isDate'): 348 if not hasattr(value, 'isDate'):
357 raise TypeError, 'new property "%s" not a Date'% key 349 raise TypeError, 'new property "%s" not a Date'% key
358 350
359 elif prop.isIntervalType: 351 elif isinstance(prop, Interval):
360 if not hasattr(value, 'isInterval'): 352 if not hasattr(value, 'isInterval'):
361 raise TypeError, 'new property "%s" not an Interval'% key 353 raise TypeError, 'new property "%s" not an Interval'% key
362 354
363 node[key] = value 355 node[key] = value
364 356
372 and the node's id is never reused. 364 and the node's id is never reused.
373 365
374 Retired nodes are not returned by the find(), list(), or lookup() 366 Retired nodes are not returned by the find(), list(), or lookup()
375 methods, and other nodes may reuse the values of their key properties. 367 methods, and other nodes may reuse the values of their key properties.
376 """ 368 """
377 # nodeid = str(nodeid)
378 if self.db.journaltag is None: 369 if self.db.journaltag is None:
379 raise DatabaseError, 'Database open read-only' 370 raise DatabaseError, 'Database open read-only'
380 node = self.db.getnode(self.classname, nodeid) 371 node = self.db.getnode(self.classname, nodeid)
381 node[self.db.RETIRED_FLAG] = 1 372 node[self.db.RETIRED_FLAG] = 1
382 self.db.setnode(self.classname, nodeid, node) 373 self.db.setnode(self.classname, nodeid, node)
467 'nodeid' must be the id of an existing node in the class linked 458 'nodeid' must be the id of an existing node in the class linked
468 to by the given property, or an IndexError is raised. 459 to by the given property, or an IndexError is raised.
469 """ 460 """
470 propspec = propspec.items() 461 propspec = propspec.items()
471 for propname, nodeid in propspec: 462 for propname, nodeid in propspec:
472 # nodeid = str(nodeid)
473 # check the prop is OK 463 # check the prop is OK
474 prop = self.properties[propname] 464 prop = self.properties[propname]
475 if not prop.isLinkType and not prop.isMultilinkType: 465 if not isinstance(prop, Link) and not isinstance(prop, Multilink):
476 raise TypeError, "'%s' not a Link/Multilink property"%propname 466 raise TypeError, "'%s' not a Link/Multilink property"%propname
477 if not self.db.hasnode(prop.classname, nodeid): 467 if not self.db.hasnode(prop.classname, nodeid):
478 raise ValueError, '%s has no node %s'%(link_class, nodeid) 468 raise ValueError, '%s has no node %s'%(link_class, nodeid)
479 469
480 # ok, now do the find 470 # ok, now do the find
483 for id in self.db.getnodeids(self.classname, cldb): 473 for id in self.db.getnodeids(self.classname, cldb):
484 node = self.db.getnode(self.classname, id, cldb) 474 node = self.db.getnode(self.classname, id, cldb)
485 if node.has_key(self.db.RETIRED_FLAG): 475 if node.has_key(self.db.RETIRED_FLAG):
486 continue 476 continue
487 for propname, nodeid in propspec: 477 for propname, nodeid in propspec:
488 # nodeid = str(nodeid)
489 property = node[propname] 478 property = node[propname]
490 if prop.isLinkType and nodeid == property: 479 if isinstance(prop, Link) and nodeid == property:
491 l.append(id) 480 l.append(id)
492 elif prop.isMultilinkType and nodeid in property: 481 elif isinstance(prop, Multilink) and nodeid in property:
493 l.append(id) 482 l.append(id)
494 cldb.close() 483 cldb.close()
495 return l 484 return l
496 485
497 def stringFind(self, **requirements): 486 def stringFind(self, **requirements):
501 490
502 The return is a list of the id of all nodes that match. 491 The return is a list of the id of all nodes that match.
503 """ 492 """
504 for propname in requirements.keys(): 493 for propname in requirements.keys():
505 prop = self.properties[propname] 494 prop = self.properties[propname]
506 if not prop.isStringType: 495 if isinstance(not prop, String):
507 raise TypeError, "'%s' not a String property"%propname 496 raise TypeError, "'%s' not a String property"%propname
508 l = [] 497 l = []
509 cldb = self.db.getclassdb(self.classname) 498 cldb = self.db.getclassdb(self.classname)
510 for nodeid in self.db.getnodeids(self.classname, cldb): 499 for nodeid in self.db.getnodeids(self.classname, cldb):
511 node = self.db.getnode(self.classname, nodeid, cldb) 500 node = self.db.getnode(self.classname, nodeid, cldb)
544 # optimise filterspec 533 # optimise filterspec
545 l = [] 534 l = []
546 props = self.getprops() 535 props = self.getprops()
547 for k, v in filterspec.items(): 536 for k, v in filterspec.items():
548 propclass = props[k] 537 propclass = props[k]
549 if propclass.isLinkType: 538 if isinstance(propclass, Link):
550 if type(v) is not type([]): 539 if type(v) is not type([]):
551 v = [v] 540 v = [v]
552 # replace key values with node ids 541 # replace key values with node ids
553 u = [] 542 u = []
554 link_class = self.db.classes[propclass.classname] 543 link_class = self.db.classes[propclass.classname]
560 raise ValueError, 'new property "%s": %s not a %s'%( 549 raise ValueError, 'new property "%s": %s not a %s'%(
561 k, entry, self.properties[k].classname) 550 k, entry, self.properties[k].classname)
562 u.append(entry) 551 u.append(entry)
563 552
564 l.append((0, k, u)) 553 l.append((0, k, u))
565 elif propclass.isMultilinkType: 554 elif isinstance(propclass, Multilink):
566 if type(v) is not type([]): 555 if type(v) is not type([]):
567 v = [v] 556 v = [v]
568 # replace key values with node ids 557 # replace key values with node ids
569 u = [] 558 u = []
570 link_class = self.db.classes[propclass.classname] 559 link_class = self.db.classes[propclass.classname]
575 except: 564 except:
576 raise ValueError, 'new property "%s": %s not a %s'%( 565 raise ValueError, 'new property "%s": %s not a %s'%(
577 k, entry, self.properties[k].classname) 566 k, entry, self.properties[k].classname)
578 u.append(entry) 567 u.append(entry)
579 l.append((1, k, u)) 568 l.append((1, k, u))
580 elif propclass.isStringType: 569 elif isinstance(propclass, String):
581 if '*' in v or '?' in v: 570 if '*' in v or '?' in v:
582 # simple glob searching 571 # simple glob searching
583 v = v.replace('?', '.') 572 v = v.replace('?', '.')
584 v = v.replace('*', '.*?') 573 v = v.replace('*', '.*?')
585 v = re.compile(v) 574 v = re.compile(v)
678 667
679 # sorting is class-specific 668 # sorting is class-specific
680 propclass = properties[prop] 669 propclass = properties[prop]
681 670
682 # String and Date values are sorted in the natural way 671 # String and Date values are sorted in the natural way
683 if propclass.isStringType: 672 if isinstance(propclass, String):
684 # clean up the strings 673 # clean up the strings
685 if av and av[0] in string.uppercase: 674 if av and av[0] in string.uppercase:
686 av = an[prop] = av.lower() 675 av = an[prop] = av.lower()
687 if bv and bv[0] in string.uppercase: 676 if bv and bv[0] in string.uppercase:
688 bv = bn[prop] = bv.lower() 677 bv = bn[prop] = bv.lower()
689 if propclass.isStringType or propclass.isDateType: 678 if isinstance(propclass.isStringType or propclass, Date):
690 if dir == '+': 679 if dir == '+':
691 r = cmp(av, bv) 680 r = cmp(av, bv)
692 if r != 0: return r 681 if r != 0: return r
693 elif dir == '-': 682 elif dir == '-':
694 r = cmp(bv, av) 683 r = cmp(bv, av)
696 685
697 # Link properties are sorted according to the value of 686 # Link properties are sorted according to the value of
698 # the "order" property on the linked nodes if it is 687 # the "order" property on the linked nodes if it is
699 # present; or otherwise on the key string of the linked 688 # present; or otherwise on the key string of the linked
700 # nodes; or finally on the node ids. 689 # nodes; or finally on the node ids.
701 elif propclass.isLinkType: 690 elif isinstance(propclass, Link):
702 link = db.classes[propclass.classname] 691 link = db.classes[propclass.classname]
703 if av is None and bv is not None: return -1 692 if av is None and bv is not None: return -1
704 if av is not None and bv is None: return 1 693 if av is not None and bv is None: return 1
705 if av is None and bv is None: return 0 694 if av is None and bv is None: return 0
706 if link.getprops().has_key('order'): 695 if link.getprops().has_key('order'):
728 r = cmp(bv, av) 717 r = cmp(bv, av)
729 if r != 0: return r 718 if r != 0: return r
730 719
731 # Multilink properties are sorted according to how many 720 # Multilink properties are sorted according to how many
732 # links are present. 721 # links are present.
733 elif propclass.isMultilinkType: 722 elif isinstance(propclass, Multilink):
734 if dir == '+': 723 if dir == '+':
735 r = cmp(len(av), len(bv)) 724 r = cmp(len(av), len(bv))
736 if r != 0: return r 725 if r != 0: return r
737 elif dir == '-': 726 elif dir == '-':
738 r = cmp(len(bv), len(av)) 727 r = cmp(len(bv), len(av))
815 cl.create(name=option[i], order=i) 804 cl.create(name=option[i], order=i)
816 return hyperdb.Link(name) 805 return hyperdb.Link(name)
817 806
818 # 807 #
819 # $Log: not supported by cvs2svn $ 808 # $Log: not supported by cvs2svn $
809 # Revision 1.14 2001/08/07 00:24:42 richard
810 # stupid typo
811 #
820 # Revision 1.13 2001/08/07 00:15:51 richard 812 # Revision 1.13 2001/08/07 00:15:51 richard
821 # Added the copyright/license notice to (nearly) all files at request of 813 # Added the copyright/license notice to (nearly) all files at request of
822 # Bizar Software. 814 # Bizar Software.
823 # 815 #
824 # Revision 1.12 2001/08/02 06:38:17 richard 816 # Revision 1.12 2001/08/02 06:38:17 richard

Roundup Issue Tracker: http://roundup-tracker.org/