Mercurial > p > roundup > code
comparison roundup/hyperdb.py @ 3384:6d6d7e331c54 maint-0.8
merge from HEAD
| author | Richard Jones <richard@users.sourceforge.net> |
|---|---|
| date | Tue, 12 Jul 2005 01:43:17 +0000 |
| parents | ed97e2a85576 |
| children | 876cda688636 |
comparison
equal
deleted
inserted
replaced
| 3381:e012231982e9 | 3384:6d6d7e331c54 |
|---|---|
| 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.107.2.1 2005-03-03 22:12:35 richard Exp $ | 18 # $Id: hyperdb.py,v 1.107.2.2 2005-07-12 01:43:17 richard Exp $ |
| 19 | 19 |
| 20 """Hyperdatabase implementation, especially field types. | 20 """Hyperdatabase implementation, especially field types. |
| 21 """ | 21 """ |
| 22 __docformat__ = 'restructuredtext' | 22 __docformat__ = 'restructuredtext' |
| 23 | 23 |
| 35 def __init__(self, indexme='no'): | 35 def __init__(self, indexme='no'): |
| 36 self.indexme = indexme == 'yes' | 36 self.indexme = indexme == 'yes' |
| 37 def __repr__(self): | 37 def __repr__(self): |
| 38 ' more useful for dumps ' | 38 ' more useful for dumps ' |
| 39 return '<%s>'%self.__class__ | 39 return '<%s>'%self.__class__ |
| 40 def from_raw(self, value, **kw): | |
| 41 """fix the CRLF/CR -> LF stuff""" | |
| 42 return fixNewlines(value) | |
| 40 | 43 |
| 41 class Password: | 44 class Password: |
| 42 """An object designating a Password property.""" | 45 """An object designating a Password property.""" |
| 43 def __repr__(self): | 46 def __repr__(self): |
| 44 ' more useful for dumps ' | 47 ' more useful for dumps ' |
| 45 return '<%s>'%self.__class__ | 48 return '<%s>'%self.__class__ |
| 49 def from_raw(self, value, **kw): | |
| 50 m = password.Passowrd.pwre.match(value) | |
| 51 if m: | |
| 52 # password is being given to us encrypted | |
| 53 p = password.Password() | |
| 54 p.scheme = m.group(1) | |
| 55 if p.scheme not in 'SHA crypt plaintext'.split(): | |
| 56 raise HyperdbValueError, 'property %s: unknown encryption '\ | |
| 57 'scheme %r'%(propname, p.scheme) | |
| 58 p.password = m.group(2) | |
| 59 value = p | |
| 60 else: | |
| 61 try: | |
| 62 value = password.Password(value) | |
| 63 except password.PasswordValueError, message: | |
| 64 raise HyperdbValueError, 'property %s: %s'%(propname, message) | |
| 65 return value | |
| 46 | 66 |
| 47 class Date: | 67 class Date: |
| 48 """An object designating a Date property.""" | 68 """An object designating a Date property.""" |
| 69 def __init__(self, offset = None): | |
| 70 self._offset = offset | |
| 49 def __repr__(self): | 71 def __repr__(self): |
| 50 ' more useful for dumps ' | 72 ' more useful for dumps ' |
| 51 return '<%s>'%self.__class__ | 73 return '<%s>'%self.__class__ |
| 74 def offset (self, db) : | |
| 75 if self._offset is not None : | |
| 76 return self._offset | |
| 77 return db.getUserTimezone () | |
| 78 def from_raw(self, value, db, **kw): | |
| 79 try: | |
| 80 value = date.Date(value).local(-self.offset(db)) | |
| 81 except ValueError, message: | |
| 82 raise HyperdbValueError, 'property %s: %r is an invalid '\ | |
| 83 'date (%s)'%(propname, value, message) | |
| 84 return value | |
| 85 def range_from_raw (self, value, db): | |
| 86 """return Range value from given raw value with offset correction""" | |
| 87 return date.Range(value, date.Date, offset=self.offset (db)) | |
| 52 | 88 |
| 53 class Interval: | 89 class Interval: |
| 54 """An object designating an Interval property.""" | 90 """An object designating an Interval property.""" |
| 55 def __repr__(self): | 91 def __repr__(self): |
| 56 ' more useful for dumps ' | 92 ' more useful for dumps ' |
| 57 return '<%s>'%self.__class__ | 93 return '<%s>'%self.__class__ |
| 94 def from_raw(self, value, **kw): | |
| 95 try: | |
| 96 value = date.Interval(value) | |
| 97 except ValueError, message: | |
| 98 raise HyperdbValueError, 'property %s: %r is an invalid '\ | |
| 99 'date interval (%s)'%(propname, value, message) | |
| 100 return value | |
| 58 | 101 |
| 59 class Link: | 102 class Link: |
| 60 """An object designating a Link property that links to a | 103 """An object designating a Link property that links to a |
| 61 node in a specified class.""" | 104 node in a specified class.""" |
| 62 def __init__(self, classname, do_journal='yes'): | 105 def __init__(self, classname, do_journal='yes'): |
| 65 self.classname = classname | 108 self.classname = classname |
| 66 self.do_journal = do_journal == 'yes' | 109 self.do_journal = do_journal == 'yes' |
| 67 def __repr__(self): | 110 def __repr__(self): |
| 68 ' more useful for dumps ' | 111 ' more useful for dumps ' |
| 69 return '<%s to "%s">'%(self.__class__, self.classname) | 112 return '<%s to "%s">'%(self.__class__, self.classname) |
| 113 def from_raw(self, value, db, propname, **kw): | |
| 114 if value == '-1' or not value: | |
| 115 value = None | |
| 116 else: | |
| 117 value = convertLinkValue(db, propname, self, value) | |
| 118 return value | |
| 70 | 119 |
| 71 class Multilink: | 120 class Multilink: |
| 72 """An object designating a Multilink property that links | 121 """An object designating a Multilink property that links |
| 73 to nodes in a specified class. | 122 to nodes in a specified class. |
| 74 | 123 |
| 83 self.classname = classname | 132 self.classname = classname |
| 84 self.do_journal = do_journal == 'yes' | 133 self.do_journal = do_journal == 'yes' |
| 85 def __repr__(self): | 134 def __repr__(self): |
| 86 ' more useful for dumps ' | 135 ' more useful for dumps ' |
| 87 return '<%s to "%s">'%(self.__class__, self.classname) | 136 return '<%s to "%s">'%(self.__class__, self.classname) |
| 137 def from_raw(self, value, db, klass, propname, itemid, **kw): | |
| 138 # get the current item value if it's not a new item | |
| 139 if itemid and not itemid.startswith('-'): | |
| 140 curvalue = klass.get(itemid, propname) | |
| 141 else: | |
| 142 curvalue = [] | |
| 143 | |
| 144 # if the value is a comma-separated string then split it now | |
| 145 if isinstance(value, type('')): | |
| 146 value = value.split(',') | |
| 147 | |
| 148 # handle each add/remove in turn | |
| 149 # keep an extra list for all items that are | |
| 150 # definitely in the new list (in case of e.g. | |
| 151 # <propname>=A,+B, which should replace the old | |
| 152 # list with A,B) | |
| 153 set = 1 | |
| 154 newvalue = [] | |
| 155 for item in value: | |
| 156 item = item.strip() | |
| 157 | |
| 158 # skip blanks | |
| 159 if not item: continue | |
| 160 | |
| 161 # handle +/- | |
| 162 remove = 0 | |
| 163 if item.startswith('-'): | |
| 164 remove = 1 | |
| 165 item = item[1:] | |
| 166 set = 0 | |
| 167 elif item.startswith('+'): | |
| 168 item = item[1:] | |
| 169 set = 0 | |
| 170 | |
| 171 # look up the value | |
| 172 itemid = convertLinkValue(db, propname, proptype, item) | |
| 173 | |
| 174 # perform the add/remove | |
| 175 if remove: | |
| 176 try: | |
| 177 curvalue.remove(itemid) | |
| 178 except ValueError: | |
| 179 raise HyperdbValueError, 'property %s: %r is not ' \ | |
| 180 'currently an element'%(propname, item) | |
| 181 else: | |
| 182 newvalue.append(itemid) | |
| 183 if itemid not in curvalue: | |
| 184 curvalue.append(itemid) | |
| 185 | |
| 186 # that's it, set the new Multilink property value, | |
| 187 # or overwrite it completely | |
| 188 if set: | |
| 189 value = newvalue | |
| 190 else: | |
| 191 value = curvalue | |
| 192 | |
| 193 # TODO: one day, we'll switch to numeric ids and this will be | |
| 194 # unnecessary :( | |
| 195 value = [int(x) for x in value] | |
| 196 value.sort() | |
| 197 value = [str(x) for x in value] | |
| 198 return value | |
| 88 | 199 |
| 89 class Boolean: | 200 class Boolean: |
| 90 """An object designating a boolean property""" | 201 """An object designating a boolean property""" |
| 91 def __repr__(self): | 202 def __repr__(self): |
| 92 'more useful for dumps' | 203 'more useful for dumps' |
| 93 return '<%s>' % self.__class__ | 204 return '<%s>' % self.__class__ |
| 205 def from_raw(self, value, **kw): | |
| 206 value = value.strip() | |
| 207 # checked is a common HTML checkbox value | |
| 208 value = value.lower() in ('checked', 'yes', 'true', 'on', '1') | |
| 209 return value | |
| 94 | 210 |
| 95 class Number: | 211 class Number: |
| 96 """An object designating a numeric property""" | 212 """An object designating a numeric property""" |
| 97 def __repr__(self): | 213 def __repr__(self): |
| 98 'more useful for dumps' | 214 'more useful for dumps' |
| 99 return '<%s>' % self.__class__ | 215 return '<%s>' % self.__class__ |
| 216 def from_raw(self, value, **kw): | |
| 217 value = value.strip() | |
| 218 try: | |
| 219 value = float(value) | |
| 220 except ValueError: | |
| 221 raise HyperdbValueError, 'property %s: %r is not a number'%( | |
| 222 propname, value) | |
| 223 return value | |
| 100 # | 224 # |
| 101 # Support for splitting designators | 225 # Support for splitting designators |
| 102 # | 226 # |
| 103 class DesignatorError(ValueError): | 227 class DesignatorError(ValueError): |
| 104 pass | 228 pass |
| 595 endings. Our solution is to convert all line endings to LF. | 719 endings. Our solution is to convert all line endings to LF. |
| 596 """ | 720 """ |
| 597 text = text.replace('\r\n', '\n') | 721 text = text.replace('\r\n', '\n') |
| 598 return text.replace('\r', '\n') | 722 return text.replace('\r', '\n') |
| 599 | 723 |
| 600 def rawToHyperdb(db, klass, itemid, propname, value, | 724 def rawToHyperdb(db, klass, itemid, propname, value, **kw): |
| 601 pwre=re.compile(r'{(\w+)}(.+)')): | |
| 602 ''' Convert the raw (user-input) value to a hyperdb-storable value. The | 725 ''' Convert the raw (user-input) value to a hyperdb-storable value. The |
| 603 value is for the "propname" property on itemid (may be None for a | 726 value is for the "propname" property on itemid (may be None for a |
| 604 new item) of "klass" in "db". | 727 new item) of "klass" in "db". |
| 605 | 728 |
| 606 The value is usually a string, but in the case of multilink inputs | 729 The value is usually a string, but in the case of multilink inputs |
| 618 klass.classname) | 741 klass.classname) |
| 619 | 742 |
| 620 # if we got a string, strip it now | 743 # if we got a string, strip it now |
| 621 if isinstance(value, type('')): | 744 if isinstance(value, type('')): |
| 622 value = value.strip() | 745 value = value.strip() |
| 623 | |
| 624 # convert the input value to a real property value | 746 # convert the input value to a real property value |
| 625 if isinstance(proptype, String): | 747 value = proptype.from_raw \ |
| 626 # fix the CRLF/CR -> LF stuff | 748 ( value |
| 627 value = fixNewlines(value) | 749 , db = db |
| 628 if isinstance(proptype, Password): | 750 , klass = klass |
| 629 m = pwre.match(value) | 751 , propname = propname |
| 630 if m: | 752 , itemid = itemid |
| 631 # password is being given to us encrypted | 753 , **kw |
| 632 p = password.Password() | 754 ) |
| 633 p.scheme = m.group(1) | |
| 634 if p.scheme not in 'SHA crypt plaintext'.split(): | |
| 635 raise HyperdbValueError, 'property %s: unknown encryption '\ | |
| 636 'scheme %r'%(propname, p.scheme) | |
| 637 p.password = m.group(2) | |
| 638 value = p | |
| 639 else: | |
| 640 try: | |
| 641 value = password.Password(value) | |
| 642 except password.PasswordValueError, message: | |
| 643 raise HyperdbValueError, 'property %s: %s'%(propname, message) | |
| 644 elif isinstance(proptype, Date): | |
| 645 try: | |
| 646 tz = db.getUserTimezone() | |
| 647 value = date.Date(value).local(-tz) | |
| 648 except ValueError, message: | |
| 649 raise HyperdbValueError, 'property %s: %r is an invalid '\ | |
| 650 'date (%s)'%(propname, value, message) | |
| 651 elif isinstance(proptype, Interval): | |
| 652 try: | |
| 653 value = date.Interval(value) | |
| 654 except ValueError, message: | |
| 655 raise HyperdbValueError, 'property %s: %r is an invalid '\ | |
| 656 'date interval (%s)'%(propname, value, message) | |
| 657 elif isinstance(proptype, Link): | |
| 658 if value == '-1' or not value: | |
| 659 value = None | |
| 660 else: | |
| 661 value = convertLinkValue(db, propname, proptype, value) | |
| 662 | |
| 663 elif isinstance(proptype, Multilink): | |
| 664 # get the current item value if it's not a new item | |
| 665 if itemid and not itemid.startswith('-'): | |
| 666 curvalue = klass.get(itemid, propname) | |
| 667 else: | |
| 668 curvalue = [] | |
| 669 | |
| 670 # if the value is a comma-separated string then split it now | |
| 671 if isinstance(value, type('')): | |
| 672 value = value.split(',') | |
| 673 | |
| 674 # handle each add/remove in turn | |
| 675 # keep an extra list for all items that are | |
| 676 # definitely in the new list (in case of e.g. | |
| 677 # <propname>=A,+B, which should replace the old | |
| 678 # list with A,B) | |
| 679 set = 1 | |
| 680 newvalue = [] | |
| 681 for item in value: | |
| 682 item = item.strip() | |
| 683 | |
| 684 # skip blanks | |
| 685 if not item: continue | |
| 686 | |
| 687 # handle +/- | |
| 688 remove = 0 | |
| 689 if item.startswith('-'): | |
| 690 remove = 1 | |
| 691 item = item[1:] | |
| 692 set = 0 | |
| 693 elif item.startswith('+'): | |
| 694 item = item[1:] | |
| 695 set = 0 | |
| 696 | |
| 697 # look up the value | |
| 698 itemid = convertLinkValue(db, propname, proptype, item) | |
| 699 | |
| 700 # perform the add/remove | |
| 701 if remove: | |
| 702 try: | |
| 703 curvalue.remove(itemid) | |
| 704 except ValueError: | |
| 705 raise HyperdbValueError, 'property %s: %r is not ' \ | |
| 706 'currently an element'%(propname, item) | |
| 707 else: | |
| 708 newvalue.append(itemid) | |
| 709 if itemid not in curvalue: | |
| 710 curvalue.append(itemid) | |
| 711 | |
| 712 # that's it, set the new Multilink property value, | |
| 713 # or overwrite it completely | |
| 714 if set: | |
| 715 value = newvalue | |
| 716 else: | |
| 717 value = curvalue | |
| 718 | |
| 719 # TODO: one day, we'll switch to numeric ids and this will be | |
| 720 # unnecessary :( | |
| 721 value = [int(x) for x in value] | |
| 722 value.sort() | |
| 723 value = [str(x) for x in value] | |
| 724 elif isinstance(proptype, Boolean): | |
| 725 value = value.strip() | |
| 726 # checked is a common HTML checkbox value | |
| 727 value = value.lower() in ('checked', 'yes', 'true', 'on', '1') | |
| 728 elif isinstance(proptype, Number): | |
| 729 value = value.strip() | |
| 730 try: | |
| 731 value = float(value) | |
| 732 except ValueError: | |
| 733 raise HyperdbValueError, 'property %s: %r is not a number'%( | |
| 734 propname, value) | |
| 735 return value | 755 return value |
| 736 | 756 |
| 737 class FileClass: | 757 class FileClass: |
| 738 ''' A class that requires the "content" property and stores it on | 758 ''' A class that requires the "content" property and stores it on |
| 739 disk. | 759 disk. |
