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

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