Mercurial > p > roundup > code
comparison roundup/hyperdb.py @ 6967:8473039648da
inital flake8 cleanups
Replaced some calls to re.compile with module level variable.
Renamed some variables.
| author | John Rouillard <rouilj@ieee.org> |
|---|---|
| date | Tue, 13 Sep 2022 17:42:32 -0400 |
| parents | 8ab98de22df0 |
| children | ca90f7270cd4 9b4bd9bc9bdc |
comparison
equal
deleted
inserted
replaced
| 6966:8733aa2a8e40 | 6967:8473039648da |
|---|---|
| 19 """Hyperdatabase implementation, especially field types. | 19 """Hyperdatabase implementation, especially field types. |
| 20 """ | 20 """ |
| 21 __docformat__ = 'restructuredtext' | 21 __docformat__ = 'restructuredtext' |
| 22 | 22 |
| 23 # standard python modules | 23 # standard python modules |
| 24 import os, re, shutil, sys, weakref | 24 import logging |
| 25 import os | |
| 26 import re | |
| 27 import shutil | |
| 28 import sys | |
| 25 import traceback | 29 import traceback |
| 26 import logging | 30 import weakref |
| 27 | 31 |
| 28 # roundup modules | 32 # roundup modules |
| 29 from . import date, password | 33 from . import date, password |
| 30 from .support import ensureParentsExist, PrioList | 34 from .support import ensureParentsExist, PrioList |
| 31 from roundup.mlink_expr import Expression | 35 from roundup.mlink_expr import Expression |
| 56 | 60 |
| 57 def get_default_value(self): | 61 def get_default_value(self): |
| 58 """The default value when creating a new instance of this property.""" | 62 """The default value when creating a new instance of this property.""" |
| 59 return self.__default_value | 63 return self.__default_value |
| 60 | 64 |
| 61 def register (self, cls, propname): | 65 def register(self, cls, propname): |
| 62 """Register myself to the class of which we are a property | 66 """Register myself to the class of which we are a property |
| 63 the given propname is the name we have in our class. | 67 the given propname is the name we have in our class. |
| 64 """ | 68 """ |
| 65 assert not getattr(self, 'cls', None) | 69 assert not getattr(self, 'cls', None) |
| 66 self.name = propname | 70 self.name = propname |
| 67 self.cls = cls | 71 self.cls = cls |
| 68 | 72 |
| 69 def sort_repr(self, cls, val, name): | 73 def sort_repr(self, cls, val, name): |
| 70 """Representation used for sorting. This should be a python | 74 """Representation used for sorting. This should be a python |
| 71 built-in type, otherwise sorting will take ages. Note that | 75 built-in type, otherwise sorting will take ages. Note that |
| 72 individual backends may chose to use something different for | 76 individual backends may chose to use something different for |
| 279 do_journal, | 283 do_journal, |
| 280 required=required, | 284 required=required, |
| 281 default_value=[], quiet=quiet, | 285 default_value=[], quiet=quiet, |
| 282 try_id_parsing=try_id_parsing, | 286 try_id_parsing=try_id_parsing, |
| 283 rev_multilink=rev_multilink) | 287 rev_multilink=rev_multilink) |
| 284 self.rev_property = rev_property | 288 self.rev_property = rev_property |
| 285 self.rev_classname = None | 289 self.rev_classname = None |
| 286 self.rev_propname = None | 290 self.rev_propname = None |
| 287 self.table_name = None # computed in 'register' below | 291 self.table_name = None # computed in 'register' below |
| 288 self.linkid_name = 'linkid' | 292 self.linkid_name = 'linkid' |
| 289 self.nodeid_name = 'nodeid' | 293 self.nodeid_name = 'nodeid' |
| 290 if self.rev_property: | 294 if self.rev_property: |
| 291 # Do not allow updates if this is a reverse multilink | 295 # Do not allow updates if this is a reverse multilink |
| 292 self.computed = True | 296 self.computed = True |
| 293 self.rev_classname = rev_property.cls.classname | 297 self.rev_classname = rev_property.cls.classname |
| 294 self.rev_propname = rev_property.name | 298 self.rev_propname = rev_property.name |
| 295 if isinstance(self.rev_property, Link): | 299 if isinstance(self.rev_property, Link): |
| 296 self.table_name = '_' + self.rev_classname | 300 self.table_name = '_' + self.rev_classname |
| 297 self.linkid_name = 'id' | 301 self.linkid_name = 'id' |
| 298 self.nodeid_name = '_' + self.rev_propname | 302 self.nodeid_name = '_' + self.rev_propname |
| 299 else: | 303 else: |
| 300 self.table_name = self.rev_classname + '_' + self.rev_propname | 304 self.table_name = self.rev_classname + '_' + self.rev_propname |
| 301 self.linkid_name = 'nodeid' | 305 self.linkid_name = 'nodeid' |
| 302 self.nodeid_name = 'linkid' | 306 self.nodeid_name = 'linkid' |
| 303 | 307 |
| 304 def from_raw(self, value, db, klass, propname, itemid, **kw): | 308 def from_raw(self, value, db, klass, propname, itemid, **kw): |
| 305 if not value: | 309 if not value: |
| 324 newvalue = [] | 328 newvalue = [] |
| 325 for item in value: | 329 for item in value: |
| 326 item = item.strip() | 330 item = item.strip() |
| 327 | 331 |
| 328 # skip blanks | 332 # skip blanks |
| 329 if not item: continue | 333 if not item: continue # noqa: E701 |
| 330 | 334 |
| 331 # handle +/- | 335 # handle +/- |
| 332 remove = 0 | 336 remove = 0 |
| 333 if item.startswith('-'): | 337 if item.startswith('-'): |
| 334 remove = 1 | 338 remove = 1 |
| 436 # | 440 # |
| 437 class DesignatorError(ValueError): | 441 class DesignatorError(ValueError): |
| 438 pass | 442 pass |
| 439 | 443 |
| 440 | 444 |
| 445 dre = re.compile(r'^([A-Za-z](?:[A-Za-z_0-9]*[A-Za-z_]+)?)(\d+)$') | |
| 446 | |
| 447 | |
| 441 def splitDesignator(designator, | 448 def splitDesignator(designator, |
| 442 dre=re.compile(r'^([A-Za-z](?:[A-Za-z_0-9]*[A-Za-z_]+)?)(\d+)$')): | 449 dre=dre): |
| 443 """ Take a foo123 and return ('foo', 123) | 450 """ Take a foo123 and return ('foo', 123) |
| 444 """ | 451 """ |
| 445 m = dre.match(designator) | 452 m = dre.match(designator) |
| 446 if m is None: | 453 if m is None: |
| 447 raise DesignatorError(_('"%s" not a node designator') % designator) | 454 raise DesignatorError(_('"%s" not a node designator') % designator) |
| 518 """ | 525 """ |
| 519 if name in self.propdict: | 526 if name in self.propdict: |
| 520 pt = self.propdict[name] | 527 pt = self.propdict[name] |
| 521 pt.need_for[need_for] = True | 528 pt.need_for[need_for] = True |
| 522 # For now we do not recursively retrieve Link properties | 529 # For now we do not recursively retrieve Link properties |
| 523 #if retr and isinstance(pt.propclass, Link): | 530 # if retr and isinstance(pt.propclass, Link): |
| 524 # pt.append_retr_props() | 531 # pt.append_retr_props() |
| 525 return pt | 532 return pt |
| 526 propclass = self.props[name] | 533 propclass = self.props[name] |
| 527 cls = None | 534 cls = None |
| 528 props = None | 535 props = None |
| 538 else: | 545 else: |
| 539 child.need_child_retired = True | 546 child.need_child_retired = True |
| 540 self.children.append(child) | 547 self.children.append(child) |
| 541 self.propdict[name] = child | 548 self.propdict[name] = child |
| 542 # For now we do not recursively retrieve Link properties | 549 # For now we do not recursively retrieve Link properties |
| 543 #if retr and isinstance(child.propclass, Link): | 550 # if retr and isinstance(child.propclass, Link): |
| 544 # child.append_retr_props() | 551 # child.append_retr_props() |
| 545 return child | 552 return child |
| 546 | 553 |
| 547 def append_retr_props(self): | 554 def append_retr_props(self): |
| 548 """Append properties for retrieval.""" | 555 """Append properties for retrieval.""" |
| 588 for p in self.children: | 595 for p in self.children: |
| 589 if 'search' in p.need_for: | 596 if 'search' in p.need_for: |
| 590 x = [c for c in p.children if 'search' in c.need_for] | 597 x = [c for c in p.children if 'search' in c.need_for] |
| 591 if x: | 598 if x: |
| 592 p.search(sort=False) | 599 p.search(sort=False) |
| 593 if getattr(p.propclass,'rev_property',None): | 600 if getattr(p.propclass, 'rev_property', None): |
| 594 pn = p.propclass.rev_property.name | 601 pn = p.propclass.rev_property.name |
| 595 cl = p.propclass.rev_property.cls | 602 cl = p.propclass.rev_property.cls |
| 596 if not isinstance(p.val, type([])): | 603 if not isinstance(p.val, type([])): |
| 597 p.val = [p.val] | 604 p.val = [p.val] |
| 598 nval = [int(i) for i in p.val] | 605 nval = [int(i) for i in p.val] |
| 610 else: | 617 else: |
| 611 s2.add(node[pn]) | 618 s2.add(node[pn]) |
| 612 items |= s1.difference(s2) | 619 items |= s1.difference(s2) |
| 613 if isinstance(p.propclass.rev_property, Link): | 620 if isinstance(p.propclass.rev_property, Link): |
| 614 items |= set(cl.get(x, pn) for x in pval | 621 items |= set(cl.get(x, pn) for x in pval |
| 615 if not cl.is_retired(x)) | 622 if not cl.is_retired(x)) |
| 616 else: | 623 else: |
| 617 items |= set().union(*(cl.get(x, pn) for x in pval | 624 items |= set().union(*(cl.get(x, pn) for x in pval |
| 618 if not cl.is_retired(x))) | 625 if not cl.is_retired(x))) |
| 619 else: | 626 else: |
| 620 # Expression: materialize rev multilinks and run | 627 # Expression: materialize rev multilinks and run |
| 621 # expression on them | 628 # expression on them |
| 622 expr = Expression(nval) | 629 expr = Expression(nval) |
| 623 by_id = {} | 630 by_id = {} |
| 654 subst.append(v) | 661 subst.append(v) |
| 655 if exact: | 662 if exact: |
| 656 exact_match_spec[p.name] = exact | 663 exact_match_spec[p.name] = exact |
| 657 if subst: | 664 if subst: |
| 658 filterspec[p.name] = subst | 665 filterspec[p.name] = subst |
| 659 elif not exact: # don't set if we have exact criteria | 666 elif not exact: # don't set if we have exact criteria |
| 660 if p.has_result: | 667 if p.has_result: |
| 661 # A subquery already has found nothing. So | 668 # A subquery already has found nothing. So |
| 662 # it doesn't make sense to search further. | 669 # it doesn't make sense to search further. |
| 663 self.set_val([], force=True) | 670 self.set_val([], force=True) |
| 664 return self.val | 671 return self.val |
| 665 else: | 672 else: |
| 666 filterspec[p.name] = ['-1'] # no match was found | 673 filterspec[p.name] = ['-1'] # no match was found |
| 667 else: | 674 else: |
| 668 assert not isinstance(p.val, Exact_Match) | 675 assert not isinstance(p.val, Exact_Match) |
| 669 filterspec[p.name] = p.val | 676 filterspec[p.name] = p.val |
| 670 self.set_val(self.cls._filter(search_matches, filterspec, sort and self, | 677 self.set_val(self.cls._filter(search_matches, filterspec, sort and self, |
| 671 retired=retired, | 678 retired=retired, |
| 808 is_expression = \ | 815 is_expression = \ |
| 809 self.val and min(int(i) for i in self.val) < -1 | 816 self.val and min(int(i) for i in self.val) < -1 |
| 810 if is_expression: | 817 if is_expression: |
| 811 # Tag on the ORed values with an AND | 818 # Tag on the ORed values with an AND |
| 812 l = val | 819 l = val |
| 813 for i in range(len(val)-1): | 820 for _i in range(len(val)-1): |
| 814 l.append('-4') | 821 l.append('-4') |
| 815 l.append('-3') | 822 l.append('-3') |
| 816 self.val = self.val + l | 823 self.val = self.val + l |
| 817 else: | 824 else: |
| 818 vals.intersection_update(val) | 825 vals.intersection_update(val) |
| 956 # This will change properties if a back-multilink happens to | 963 # This will change properties if a back-multilink happens to |
| 957 # have the same class, so we need to iterate over a list made | 964 # have the same class, so we need to iterate over a list made |
| 958 # from .keys() | 965 # from .keys() |
| 959 for p in list(cl.properties.keys()): | 966 for p in list(cl.properties.keys()): |
| 960 prop = cl.properties[p] | 967 prop = cl.properties[p] |
| 961 if not isinstance (prop, (Link, Multilink)): | 968 if not isinstance(prop, (Link, Multilink)): |
| 962 continue | 969 continue |
| 963 if prop.rev_multilink: | 970 if prop.rev_multilink: |
| 964 linkcls = self.getclass(prop.classname) | 971 linkcls = self.getclass(prop.classname) |
| 965 if prop.rev_multilink in linkcls.properties: | 972 if prop.rev_multilink in linkcls.properties: |
| 966 if not done: | 973 if not done: |
| 967 raise ValueError( | 974 raise ValueError( |
| 968 "%s already a property of class %s"% | 975 "%s already a property of class %s" % |
| 969 (prop.rev_multilink, linkcls.classname)) | 976 (prop.rev_multilink, linkcls.classname)) |
| 970 else: | 977 else: |
| 971 linkcls.properties[prop.rev_multilink] = Multilink( | 978 linkcls.properties[prop.rev_multilink] = Multilink( |
| 972 cl.classname, rev_property=prop) | 979 cl.classname, rev_property=prop) |
| 973 self.post_init_done = True | 980 self.post_init_done = True |
| 1102 | 1109 |
| 1103 This method must be called at the end of processing. | 1110 This method must be called at the end of processing. |
| 1104 | 1111 |
| 1105 """ | 1112 """ |
| 1106 raise NotImplementedError | 1113 raise NotImplementedError |
| 1114 | |
| 1107 | 1115 |
| 1108 def iter_roles(roles): | 1116 def iter_roles(roles): |
| 1109 ''' handle the text processing of turning the roles list | 1117 ''' handle the text processing of turning the roles list |
| 1110 into something python can use more easily | 1118 into something python can use more easily |
| 1111 ''' | 1119 ''' |
| 1171 """Slightly more useful representation | 1179 """Slightly more useful representation |
| 1172 Note that an error message can be raised at a point | 1180 Note that an error message can be raised at a point |
| 1173 where self.classname isn't known yet if the error | 1181 where self.classname isn't known yet if the error |
| 1174 occurs during schema parsing. | 1182 occurs during schema parsing. |
| 1175 """ | 1183 """ |
| 1176 cn = getattr (self, 'classname', 'Unknown') | 1184 cn = getattr(self, 'classname', 'Unknown') |
| 1177 return '<hyperdb.Class "%s">' % cn | 1185 return '<hyperdb.Class "%s">' % cn |
| 1178 | 1186 |
| 1179 # Editing nodes: | 1187 # Editing nodes: |
| 1180 | 1188 |
| 1181 def create(self, **propvalues): | 1189 def create(self, **propvalues): |
| 1562 """For some backends this implements the non-transitive | 1570 """For some backends this implements the non-transitive |
| 1563 search, for more information see the filter method. | 1571 search, for more information see the filter method. |
| 1564 """ | 1572 """ |
| 1565 raise NotImplementedError | 1573 raise NotImplementedError |
| 1566 | 1574 |
| 1567 def _proptree(self, filterspec, exact_match_spec={}, sortattr=[], | 1575 def _proptree(self, filterspec, exact_match_spec=None, sortattr=None, |
| 1568 retr=False): | 1576 retr=False): |
| 1569 """Build a tree of all transitive properties in the given | 1577 """Build a tree of all transitive properties in the given |
| 1570 exact_match_spec/filterspec. | 1578 exact_match_spec/filterspec. |
| 1571 If we retrieve (retr is True) linked items we don't follow | 1579 If we retrieve (retr is True) linked items we don't follow |
| 1572 across multilinks or links. | 1580 across multilinks or links. |
| 1573 """ | 1581 """ |
| 1574 if filterspec is None: | 1582 if filterspec is None: |
| 1575 filterspec = {} | 1583 filterspec = {} |
| 1584 if exact_match_spec is None: | |
| 1585 exact_match_spec = {} | |
| 1586 if sortattr is None: | |
| 1587 sortattr = [] | |
| 1576 | 1588 |
| 1577 proptree = Proptree(self.db, self, '', self.getprops(), retr=retr) | 1589 proptree = Proptree(self.db, self, '', self.getprops(), retr=retr) |
| 1578 for exact, spec in enumerate((filterspec, exact_match_spec)): | 1590 for exact, spec in enumerate((filterspec, exact_match_spec)): |
| 1579 for key, v in spec.items(): | 1591 for key, v in spec.items(): |
| 1580 keys = key.split('.') | 1592 keys = key.split('.') |
| 1649 """Build a single list of sort attributes in the correct order | 1661 """Build a single list of sort attributes in the correct order |
| 1650 with sanity checks (no duplicate properties) included. Always | 1662 with sanity checks (no duplicate properties) included. Always |
| 1651 sort last by id -- if id is not already in sortattr. | 1663 sort last by id -- if id is not already in sortattr. |
| 1652 """ | 1664 """ |
| 1653 if sort is None: | 1665 if sort is None: |
| 1654 sort = [(None,None)] | 1666 sort = [(None, None)] |
| 1655 if group is None: | 1667 if group is None: |
| 1656 group = [(None, None)] | 1668 group = [(None, None)] |
| 1657 | 1669 |
| 1658 seen = {} | 1670 seen = {} |
| 1659 sortattr = [] | 1671 sortattr = [] |
| 1781 If the "protected" flag is true, we include protected properties - | 1793 If the "protected" flag is true, we include protected properties - |
| 1782 those which may not be modified. | 1794 those which may not be modified. |
| 1783 """ | 1795 """ |
| 1784 raise NotImplementedError | 1796 raise NotImplementedError |
| 1785 | 1797 |
| 1786 def get_required_props(self, propnames=[]): | 1798 def get_required_props(self, propnames=None): |
| 1787 """Return a dict of property names mapping to property objects. | 1799 """Return a dict of property names mapping to property objects. |
| 1788 All properties that have the "required" flag set will be | 1800 All properties that have the "required" flag set will be |
| 1789 returned in addition to all properties in the propnames | 1801 returned in addition to all properties in the propnames |
| 1790 parameter. | 1802 parameter. |
| 1791 """ | 1803 """ |
| 1804 if propnames is None: | |
| 1805 propnames = [] | |
| 1792 props = self.getprops(protected=False) | 1806 props = self.getprops(protected=False) |
| 1793 pdict = dict([(p, props[p]) for p in propnames]) | 1807 pdict = dict([(p, props[p]) for p in propnames]) |
| 1794 pdict.update([(k, v) for k, v in props.items() if v.required]) | 1808 pdict.update([(k, v) for k, v in props.items() if v.required]) |
| 1795 return pdict | 1809 return pdict |
| 1796 | 1810 |
| 1863 each id, this way the memory footprint is a lot smaller than the | 1877 each id, this way the memory footprint is a lot smaller than the |
| 1864 initial implementation which stored everything in a big hash by | 1878 initial implementation which stored everything in a big hash by |
| 1865 id and then proceeded to import journals for each id.""" | 1879 id and then proceeded to import journals for each id.""" |
| 1866 properties = self.getprops() | 1880 properties = self.getprops() |
| 1867 a = [] | 1881 a = [] |
| 1868 for l in entries: | 1882 for entry in entries: |
| 1869 # first element in sorted list is the (numeric) id | 1883 # first element in sorted list is the (numeric) id |
| 1870 # in python2.4 and up we would use sorted with a key... | 1884 # in python2.4 and up we would use sorted with a key... |
| 1871 a.append((int(l[0].strip("'")), l)) | 1885 a.append((int(entry[0].strip("'")), entry)) |
| 1872 a.sort() | 1886 a.sort() |
| 1873 | 1887 |
| 1874 last = 0 | 1888 last = 0 |
| 1875 r = [] | 1889 r = [] |
| 1876 for n, l in a: | 1890 for n, l in a: |
| 1935 class HyperdbValueError(ValueError): | 1949 class HyperdbValueError(ValueError): |
| 1936 """ Error converting a raw value into a Hyperdb value """ | 1950 """ Error converting a raw value into a Hyperdb value """ |
| 1937 pass | 1951 pass |
| 1938 | 1952 |
| 1939 | 1953 |
| 1940 def convertLinkValue(db, propname, prop, value, idre=re.compile(r'^\d+$')): | 1954 id_regex = re.compile(r'^\d+$') |
| 1955 | |
| 1956 | |
| 1957 def convertLinkValue(db, propname, prop, value, idre=id_regex): | |
| 1941 """ Convert the link value (may be id or key value) to an id value. """ | 1958 """ Convert the link value (may be id or key value) to an id value. """ |
| 1942 linkcl = db.classes[prop.classname] | 1959 linkcl = db.classes[prop.classname] |
| 1943 if not idre or not idre.match(value): | 1960 if not idre or not idre.match(value): |
| 1944 if linkcl.getkey(): | 1961 if linkcl.getkey(): |
| 1945 try: | 1962 try: |
| 2070 | 2087 |
| 2071 def keys(self, protected=1): | 2088 def keys(self, protected=1): |
| 2072 return list(self.cl.getprops(protected=protected).keys()) | 2089 return list(self.cl.getprops(protected=protected).keys()) |
| 2073 | 2090 |
| 2074 def values(self, protected=1): | 2091 def values(self, protected=1): |
| 2075 l = [] | 2092 value_list = [] |
| 2076 for name in self.cl.getprops(protected=protected).keys(): | 2093 for name in self.cl.getprops(protected=protected).keys(): |
| 2077 l.append(self.cl.get(self.nodeid, name)) | 2094 value_list.append(self.cl.get(self.nodeid, name)) |
| 2078 return l | 2095 return value_list |
| 2079 | 2096 |
| 2080 def items(self, protected=1): | 2097 def items(self, protected=1): |
| 2081 l = [] | 2098 item_list = [] |
| 2082 for name in self.cl.getprops(protected=protected).keys(): | 2099 for name in self.cl.getprops(protected=protected).keys(): |
| 2083 l.append((name, self.cl.get(self.nodeid, name))) | 2100 item_list.append((name, self.cl.get(self.nodeid, name))) |
| 2084 return l | 2101 return item_list |
| 2085 | 2102 |
| 2086 def has_key(self, name): | 2103 def has_key(self, name): |
| 2087 return name in self.cl.getprops() | 2104 return name in self.cl.getprops() |
| 2088 | 2105 |
| 2089 def get(self, name, default=None): | 2106 def get(self, name, default=None): |
| 2130 """ | 2147 """ |
| 2131 cl = Class(db, name, name=String(), order=String()) | 2148 cl = Class(db, name, name=String(), order=String()) |
| 2132 for i in range(len(options)): | 2149 for i in range(len(options)): |
| 2133 cl.create(name=options[i], order=i) | 2150 cl.create(name=options[i], order=i) |
| 2134 return Link(name) | 2151 return Link(name) |
| 2135 |
