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 :

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