Mercurial > p > roundup > code
comparison roundup/support.py @ 3682:193f316dbbe9
More transitive-property support.
- Implemented transitive properties in sort and group specs. Sort/group
specs can now be lists of specs.
- All regression tests except for one metakit backend test related to
metakit having no representation of NULL pass
- Fixed more PEP 8 whitespace peeves (and probably introduced some new
ones :-)
- Moved Proptree from support.py to hyperdb.py due to circular import
- Moved some proptree-specific methods from Class to Proptree
- Added a test for sorting by ids -> should be numeric sort (which now
really works for all backends)
- Added "required" attribute to all property classes in hyperdb (e.g.,
String, Link,...), see Feature Requests [SF#539081]
-> factored common stuff to _Type. Note that I also converted to a
new-style class when I was at it. Bad: The repr changes for new-style
classes which made some SQL backends break (!) because the repr of
Multilink is used in the schema storage. Fixed the repr to be
independent of the class type.
- Added get_required_props to Class. Todo: should also automagically
make the key property required...
- Add a sort_repr method to property classes. This defines the
sort-order. Individual backends may use diffent routines if the
outcome is the same. This one has a special case for id properties to
make the sorting numeric. Using these methods isn't mandatory in
backends as long as the sort-order is correct.
- Multilink sorting takes orderprop into account. It used to sort by
ids. You can restore the old behaviour by specifying id as the
orderprop of the Multilink if you really need that.
- If somebody specified a Link or Multilink as orderprop, we sort by
labelprop of that class -- not transitively by orderprop. I've
resited the tempation to implement recursive orderprop here: There
could even be loops if several classes specify a Link or Multilink as
the orderprop...
- Fixed a bug in Metakit-Backend: When sorting by Links, the backend
would do a natural join to the Link class. It would rename the "id"
attribute before joining but *not* all the other attributes of the
joined class. So in one test-case we had a name-clash with
priority.name and status.name when sorting *and* grouping by these
attributes. Depending on the order of joining this would produce a
name-clash with broken sort-results (and broken display if the
original class has an attribute that clashes). I'm now doing the
sorting of Links in the generic filter method for the metakit backend.
I've left the dead code in the metakit-backend since correctly
implementing this in the backend will probably be more efficient.
- updated doc/design.html with the new docstring of filter.
| author | Ralf Schlatterbeck <schlatterbeck@users.sourceforge.net> |
|---|---|
| date | Mon, 21 Aug 2006 12:19:48 +0000 |
| parents | f35ece8f8ff7 |
| children | 0a05c4d9a221 |
comparison
equal
deleted
inserted
replaced
| 3681:b9301ae1c34d | 3682:193f316dbbe9 |
|---|---|
| 3 """ | 3 """ |
| 4 | 4 |
| 5 __docformat__ = 'restructuredtext' | 5 __docformat__ = 'restructuredtext' |
| 6 | 6 |
| 7 import os, time, sys, re | 7 import os, time, sys, re |
| 8 | |
| 9 from sets import Set | |
| 10 | 8 |
| 11 class TruthDict: | 9 class TruthDict: |
| 12 '''Returns True for valid keys, False for others. | 10 '''Returns True for valid keys, False for others. |
| 13 ''' | 11 ''' |
| 14 def __init__(self, keys): | 12 def __init__(self, keys): |
| 117 s = '%s %2d%%'%(self.info, self.num * 100. / self.total) | 115 s = '%s %2d%%'%(self.info, self.num * 100. / self.total) |
| 118 else: | 116 else: |
| 119 s = '%s %d done'%(self.info, self.num) | 117 s = '%s %d done'%(self.info, self.num) |
| 120 sys.stdout.write(s + ' '*(75-len(s)) + '\r') | 118 sys.stdout.write(s + ' '*(75-len(s)) + '\r') |
| 121 sys.stdout.flush() | 119 sys.stdout.flush() |
| 122 | |
| 123 class Proptree(object): | |
| 124 ''' Simple tree data structure for optimizing searching of properties | |
| 125 ''' | |
| 126 | |
| 127 def __init__(self, db, cls, name, props, parent = None): | |
| 128 self.db = db | |
| 129 self.name = name | |
| 130 self.props = props | |
| 131 self.parent = parent | |
| 132 self._val = None | |
| 133 self.has_values = False | |
| 134 self.cls = cls | |
| 135 self.classname = None | |
| 136 self.uniqname = None | |
| 137 self.children = [] | |
| 138 self.propnames = {} | |
| 139 if parent: | |
| 140 self.root = parent.root | |
| 141 self.prcls = self.parent.props [name] | |
| 142 else: | |
| 143 self.root = self | |
| 144 self.seqno = 1 | |
| 145 self.id = self.root.seqno | |
| 146 self.root.seqno += 1 | |
| 147 if self.cls: | |
| 148 self.classname = self.cls.classname | |
| 149 self.uniqname = '%s%s' % (self.cls.classname, self.id) | |
| 150 if not self.parent: | |
| 151 self.uniqname = self.cls.classname | |
| 152 | |
| 153 def append(self, name): | |
| 154 """Append a property to self.children. Will create a new | |
| 155 propclass for the child. | |
| 156 """ | |
| 157 import hyperdb | |
| 158 if name in self.propnames: | |
| 159 return self.propnames [name] | |
| 160 propclass = self.props [name] | |
| 161 cls = None | |
| 162 props = None | |
| 163 if isinstance(propclass, (hyperdb.Link, hyperdb.Multilink)): | |
| 164 cls = self.db.getclass(propclass.classname) | |
| 165 props = cls.getprops() | |
| 166 child = self.__class__(self.db, cls, name, props, parent = self) | |
| 167 self.children.append(child) | |
| 168 self.propnames [name] = child | |
| 169 return child | |
| 170 | |
| 171 def _set_val(self, val): | |
| 172 """Check if self._val is already defined. If yes, we compute the | |
| 173 intersection of the old and the new value(s) | |
| 174 """ | |
| 175 if self.has_values: | |
| 176 v = self._val | |
| 177 if not isinstance(self._val, type([])): | |
| 178 v = [self._val] | |
| 179 vals = Set(v) | |
| 180 vals.intersection_update(val) | |
| 181 self._val = [v for v in vals] | |
| 182 else: | |
| 183 self._val = val | |
| 184 self.has_values = True | |
| 185 | |
| 186 val = property(lambda self: self._val, _set_val) | |
| 187 | |
| 188 def __iter__(self): | |
| 189 """ Yield nodes in depth-first order -- visited nodes first """ | |
| 190 for p in self.children: | |
| 191 yield p | |
| 192 for c in p: | |
| 193 yield c | |
| 194 | 120 |
| 195 LEFT = 'left' | 121 LEFT = 'left' |
| 196 LEFTN = 'left no strip' | 122 LEFTN = 'left no strip' |
| 197 RIGHT = 'right' | 123 RIGHT = 'right' |
| 198 CENTER = 'center' | 124 CENTER = 'center' |
| 283 | 209 |
| 284 def wrap(text, width=75, alignment=LEFTN): | 210 def wrap(text, width=75, alignment=LEFTN): |
| 285 return format_columns(((width, alignment),), [text], | 211 return format_columns(((width, alignment),), [text], |
| 286 collapse_whitespace=False) | 212 collapse_whitespace=False) |
| 287 | 213 |
| 214 # Python2.3 backwards-compatibility-hack. Should be removed (and clients | |
| 215 # fixed to use built-in reversed/sorted) when we abandon support for | |
| 216 # python2.3 | |
| 217 try: | |
| 218 reversed = reversed | |
| 219 except NameError: | |
| 220 def reversed(x): | |
| 221 x = list(x) | |
| 222 x.reverse() | |
| 223 return x | |
| 224 | |
| 225 try: | |
| 226 sorted = sorted | |
| 227 except NameError: | |
| 228 def sorted(iter, cmp=None, key=None, reverse=False): | |
| 229 if key: | |
| 230 l = [] | |
| 231 cnt = 0 # cnt preserves original sort-order | |
| 232 inc = [1, -1][bool(reverse)] # count down on reverse | |
| 233 for x in iter: | |
| 234 l.append ((key(x), cnt, x)) | |
| 235 cnt += inc | |
| 236 else: | |
| 237 l = list(iter) | |
| 238 if cmp: | |
| 239 l.sort(cmp = cmp) | |
| 240 else: | |
| 241 l.sort() | |
| 242 if reverse: | |
| 243 l.reverse() | |
| 244 if key: | |
| 245 return [x[-1] for x in l] | |
| 246 return l | |
| 247 | |
| 288 # vim: set et sts=4 sw=4 : | 248 # vim: set et sts=4 sw=4 : |
