##// END OF EJS Templates
smartset: add some doctests...
Jun Wu -
r31019:74f77f1c default
parent child Browse files
Show More
@@ -1,986 +1,1031 b''
1 # smartset.py - data structure for revision set
1 # smartset.py - data structure for revision set
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 from . import (
10 from . import (
11 util,
11 util,
12 )
12 )
13
13
14 def _formatsetrepr(r):
14 def _formatsetrepr(r):
15 """Format an optional printable representation of a set
15 """Format an optional printable representation of a set
16
16
17 ======== =================================
17 ======== =================================
18 type(r) example
18 type(r) example
19 ======== =================================
19 ======== =================================
20 tuple ('<not %r>', other)
20 tuple ('<not %r>', other)
21 str '<branch closed>'
21 str '<branch closed>'
22 callable lambda: '<branch %r>' % sorted(b)
22 callable lambda: '<branch %r>' % sorted(b)
23 object other
23 object other
24 ======== =================================
24 ======== =================================
25 """
25 """
26 if r is None:
26 if r is None:
27 return ''
27 return ''
28 elif isinstance(r, tuple):
28 elif isinstance(r, tuple):
29 return r[0] % r[1:]
29 return r[0] % r[1:]
30 elif isinstance(r, str):
30 elif isinstance(r, str):
31 return r
31 return r
32 elif callable(r):
32 elif callable(r):
33 return r()
33 return r()
34 else:
34 else:
35 return repr(r)
35 return repr(r)
36
36
37 class abstractsmartset(object):
37 class abstractsmartset(object):
38
38
39 def __nonzero__(self):
39 def __nonzero__(self):
40 """True if the smartset is not empty"""
40 """True if the smartset is not empty"""
41 raise NotImplementedError()
41 raise NotImplementedError()
42
42
43 def __contains__(self, rev):
43 def __contains__(self, rev):
44 """provide fast membership testing"""
44 """provide fast membership testing"""
45 raise NotImplementedError()
45 raise NotImplementedError()
46
46
47 def __iter__(self):
47 def __iter__(self):
48 """iterate the set in the order it is supposed to be iterated"""
48 """iterate the set in the order it is supposed to be iterated"""
49 raise NotImplementedError()
49 raise NotImplementedError()
50
50
51 # Attributes containing a function to perform a fast iteration in a given
51 # Attributes containing a function to perform a fast iteration in a given
52 # direction. A smartset can have none, one, or both defined.
52 # direction. A smartset can have none, one, or both defined.
53 #
53 #
54 # Default value is None instead of a function returning None to avoid
54 # Default value is None instead of a function returning None to avoid
55 # initializing an iterator just for testing if a fast method exists.
55 # initializing an iterator just for testing if a fast method exists.
56 fastasc = None
56 fastasc = None
57 fastdesc = None
57 fastdesc = None
58
58
59 def isascending(self):
59 def isascending(self):
60 """True if the set will iterate in ascending order"""
60 """True if the set will iterate in ascending order"""
61 raise NotImplementedError()
61 raise NotImplementedError()
62
62
63 def isdescending(self):
63 def isdescending(self):
64 """True if the set will iterate in descending order"""
64 """True if the set will iterate in descending order"""
65 raise NotImplementedError()
65 raise NotImplementedError()
66
66
67 def istopo(self):
67 def istopo(self):
68 """True if the set will iterate in topographical order"""
68 """True if the set will iterate in topographical order"""
69 raise NotImplementedError()
69 raise NotImplementedError()
70
70
71 def min(self):
71 def min(self):
72 """return the minimum element in the set"""
72 """return the minimum element in the set"""
73 if self.fastasc is None:
73 if self.fastasc is None:
74 v = min(self)
74 v = min(self)
75 else:
75 else:
76 for v in self.fastasc():
76 for v in self.fastasc():
77 break
77 break
78 else:
78 else:
79 raise ValueError('arg is an empty sequence')
79 raise ValueError('arg is an empty sequence')
80 self.min = lambda: v
80 self.min = lambda: v
81 return v
81 return v
82
82
83 def max(self):
83 def max(self):
84 """return the maximum element in the set"""
84 """return the maximum element in the set"""
85 if self.fastdesc is None:
85 if self.fastdesc is None:
86 return max(self)
86 return max(self)
87 else:
87 else:
88 for v in self.fastdesc():
88 for v in self.fastdesc():
89 break
89 break
90 else:
90 else:
91 raise ValueError('arg is an empty sequence')
91 raise ValueError('arg is an empty sequence')
92 self.max = lambda: v
92 self.max = lambda: v
93 return v
93 return v
94
94
95 def first(self):
95 def first(self):
96 """return the first element in the set (user iteration perspective)
96 """return the first element in the set (user iteration perspective)
97
97
98 Return None if the set is empty"""
98 Return None if the set is empty"""
99 raise NotImplementedError()
99 raise NotImplementedError()
100
100
101 def last(self):
101 def last(self):
102 """return the last element in the set (user iteration perspective)
102 """return the last element in the set (user iteration perspective)
103
103
104 Return None if the set is empty"""
104 Return None if the set is empty"""
105 raise NotImplementedError()
105 raise NotImplementedError()
106
106
107 def __len__(self):
107 def __len__(self):
108 """return the length of the smartsets
108 """return the length of the smartsets
109
109
110 This can be expensive on smartset that could be lazy otherwise."""
110 This can be expensive on smartset that could be lazy otherwise."""
111 raise NotImplementedError()
111 raise NotImplementedError()
112
112
113 def reverse(self):
113 def reverse(self):
114 """reverse the expected iteration order"""
114 """reverse the expected iteration order"""
115 raise NotImplementedError()
115 raise NotImplementedError()
116
116
117 def sort(self, reverse=True):
117 def sort(self, reverse=True):
118 """get the set to iterate in an ascending or descending order"""
118 """get the set to iterate in an ascending or descending order"""
119 raise NotImplementedError()
119 raise NotImplementedError()
120
120
121 def __and__(self, other):
121 def __and__(self, other):
122 """Returns a new object with the intersection of the two collections.
122 """Returns a new object with the intersection of the two collections.
123
123
124 This is part of the mandatory API for smartset."""
124 This is part of the mandatory API for smartset."""
125 if isinstance(other, fullreposet):
125 if isinstance(other, fullreposet):
126 return self
126 return self
127 return self.filter(other.__contains__, condrepr=other, cache=False)
127 return self.filter(other.__contains__, condrepr=other, cache=False)
128
128
129 def __add__(self, other):
129 def __add__(self, other):
130 """Returns a new object with the union of the two collections.
130 """Returns a new object with the union of the two collections.
131
131
132 This is part of the mandatory API for smartset."""
132 This is part of the mandatory API for smartset."""
133 return addset(self, other)
133 return addset(self, other)
134
134
135 def __sub__(self, other):
135 def __sub__(self, other):
136 """Returns a new object with the substraction of the two collections.
136 """Returns a new object with the substraction of the two collections.
137
137
138 This is part of the mandatory API for smartset."""
138 This is part of the mandatory API for smartset."""
139 c = other.__contains__
139 c = other.__contains__
140 return self.filter(lambda r: not c(r), condrepr=('<not %r>', other),
140 return self.filter(lambda r: not c(r), condrepr=('<not %r>', other),
141 cache=False)
141 cache=False)
142
142
143 def filter(self, condition, condrepr=None, cache=True):
143 def filter(self, condition, condrepr=None, cache=True):
144 """Returns this smartset filtered by condition as a new smartset.
144 """Returns this smartset filtered by condition as a new smartset.
145
145
146 `condition` is a callable which takes a revision number and returns a
146 `condition` is a callable which takes a revision number and returns a
147 boolean. Optional `condrepr` provides a printable representation of
147 boolean. Optional `condrepr` provides a printable representation of
148 the given `condition`.
148 the given `condition`.
149
149
150 This is part of the mandatory API for smartset."""
150 This is part of the mandatory API for smartset."""
151 # builtin cannot be cached. but do not needs to
151 # builtin cannot be cached. but do not needs to
152 if cache and util.safehasattr(condition, 'func_code'):
152 if cache and util.safehasattr(condition, 'func_code'):
153 condition = util.cachefunc(condition)
153 condition = util.cachefunc(condition)
154 return filteredset(self, condition, condrepr)
154 return filteredset(self, condition, condrepr)
155
155
156 class baseset(abstractsmartset):
156 class baseset(abstractsmartset):
157 """Basic data structure that represents a revset and contains the basic
157 """Basic data structure that represents a revset and contains the basic
158 operation that it should be able to perform.
158 operation that it should be able to perform.
159
159
160 Every method in this class should be implemented by any smartset class.
160 Every method in this class should be implemented by any smartset class.
161
162 This class could be constructed by an (unordered) set, or an (ordered)
163 list-like object. If a set is provided, it'll be sorted lazily.
164
165 >>> x = [4, 0, 7, 6]
166 >>> y = [5, 6, 7, 3]
167
168 Construct by a set:
169 >>> xs = baseset(set(x))
170 >>> ys = baseset(set(y))
171 >>> [list(i) for i in [xs + ys, xs & ys, xs - ys]]
172 [[0, 4, 6, 7, 3, 5], [6, 7], [0, 4]]
173 >>> [type(i).__name__ for i in [xs + ys, xs & ys, xs - ys]]
174 ['addset', 'filteredset', 'filteredset']
175
176 Construct by a list-like:
177 >>> xs = baseset(x)
178 >>> ys = baseset(i for i in y)
179 >>> [list(i) for i in [xs + ys, xs & ys, xs - ys]]
180 [[4, 0, 7, 6, 5, 3], [7, 6], [4, 0]]
181 >>> [type(i).__name__ for i in [xs + ys, xs & ys, xs - ys]]
182 ['addset', 'filteredset', 'filteredset']
183
184 Populate "_set" fields in the lists so set optimization may be used:
185 >>> [1 in xs, 3 in ys]
186 [False, True]
187
188 Without sort(), results won't be changed:
189 >>> [list(i) for i in [xs + ys, xs & ys, xs - ys]]
190 [[4, 0, 7, 6, 5, 3], [7, 6], [4, 0]]
191 >>> [type(i).__name__ for i in [xs + ys, xs & ys, xs - ys]]
192 ['addset', 'filteredset', 'filteredset']
193
194 With sort():
195 >>> xs.sort(reverse=True)
196 >>> [list(i) for i in [xs + ys, xs & ys, xs - ys]]
197 [[7, 6, 4, 0, 5, 3], [7, 6], [4, 0]]
198 >>> [type(i).__name__ for i in [xs + ys, xs & ys, xs - ys]]
199 ['addset', 'filteredset', 'filteredset']
200
201 >>> ys.sort()
202 >>> [list(i) for i in [xs + ys, xs & ys, xs - ys]]
203 [[7, 6, 4, 0, 3, 5], [7, 6], [4, 0]]
204 >>> [type(i).__name__ for i in [xs + ys, xs & ys, xs - ys]]
205 ['addset', 'filteredset', 'filteredset']
161 """
206 """
162 def __init__(self, data=(), datarepr=None, istopo=False):
207 def __init__(self, data=(), datarepr=None, istopo=False):
163 """
208 """
164 datarepr: a tuple of (format, obj, ...), a function or an object that
209 datarepr: a tuple of (format, obj, ...), a function or an object that
165 provides a printable representation of the given data.
210 provides a printable representation of the given data.
166 """
211 """
167 self._ascending = None
212 self._ascending = None
168 self._istopo = istopo
213 self._istopo = istopo
169 if not isinstance(data, list):
214 if not isinstance(data, list):
170 if isinstance(data, set):
215 if isinstance(data, set):
171 self._set = data
216 self._set = data
172 # set has no order we pick one for stability purpose
217 # set has no order we pick one for stability purpose
173 self._ascending = True
218 self._ascending = True
174 # converting set to list has a cost, do it lazily
219 # converting set to list has a cost, do it lazily
175 data = None
220 data = None
176 else:
221 else:
177 data = list(data)
222 data = list(data)
178 if data is not None:
223 if data is not None:
179 self._list = data
224 self._list = data
180 self._datarepr = datarepr
225 self._datarepr = datarepr
181
226
182 @util.propertycache
227 @util.propertycache
183 def _set(self):
228 def _set(self):
184 return set(self._list)
229 return set(self._list)
185
230
186 @util.propertycache
231 @util.propertycache
187 def _asclist(self):
232 def _asclist(self):
188 asclist = self._list[:]
233 asclist = self._list[:]
189 asclist.sort()
234 asclist.sort()
190 return asclist
235 return asclist
191
236
192 @util.propertycache
237 @util.propertycache
193 def _list(self):
238 def _list(self):
194 # _list is only lazily constructed if we have _set
239 # _list is only lazily constructed if we have _set
195 assert '_set' in self.__dict__
240 assert '_set' in self.__dict__
196 return list(self._set)
241 return list(self._set)
197
242
198 def __iter__(self):
243 def __iter__(self):
199 if self._ascending is None:
244 if self._ascending is None:
200 return iter(self._list)
245 return iter(self._list)
201 elif self._ascending:
246 elif self._ascending:
202 return iter(self._asclist)
247 return iter(self._asclist)
203 else:
248 else:
204 return reversed(self._asclist)
249 return reversed(self._asclist)
205
250
206 def fastasc(self):
251 def fastasc(self):
207 return iter(self._asclist)
252 return iter(self._asclist)
208
253
209 def fastdesc(self):
254 def fastdesc(self):
210 return reversed(self._asclist)
255 return reversed(self._asclist)
211
256
212 @util.propertycache
257 @util.propertycache
213 def __contains__(self):
258 def __contains__(self):
214 return self._set.__contains__
259 return self._set.__contains__
215
260
216 def __nonzero__(self):
261 def __nonzero__(self):
217 return bool(len(self))
262 return bool(len(self))
218
263
219 def sort(self, reverse=False):
264 def sort(self, reverse=False):
220 self._ascending = not bool(reverse)
265 self._ascending = not bool(reverse)
221 self._istopo = False
266 self._istopo = False
222
267
223 def reverse(self):
268 def reverse(self):
224 if self._ascending is None:
269 if self._ascending is None:
225 self._list.reverse()
270 self._list.reverse()
226 else:
271 else:
227 self._ascending = not self._ascending
272 self._ascending = not self._ascending
228 self._istopo = False
273 self._istopo = False
229
274
230 def __len__(self):
275 def __len__(self):
231 if '_list' in self.__dict__:
276 if '_list' in self.__dict__:
232 return len(self._list)
277 return len(self._list)
233 else:
278 else:
234 return len(self._set)
279 return len(self._set)
235
280
236 def isascending(self):
281 def isascending(self):
237 """Returns True if the collection is ascending order, False if not.
282 """Returns True if the collection is ascending order, False if not.
238
283
239 This is part of the mandatory API for smartset."""
284 This is part of the mandatory API for smartset."""
240 if len(self) <= 1:
285 if len(self) <= 1:
241 return True
286 return True
242 return self._ascending is not None and self._ascending
287 return self._ascending is not None and self._ascending
243
288
244 def isdescending(self):
289 def isdescending(self):
245 """Returns True if the collection is descending order, False if not.
290 """Returns True if the collection is descending order, False if not.
246
291
247 This is part of the mandatory API for smartset."""
292 This is part of the mandatory API for smartset."""
248 if len(self) <= 1:
293 if len(self) <= 1:
249 return True
294 return True
250 return self._ascending is not None and not self._ascending
295 return self._ascending is not None and not self._ascending
251
296
252 def istopo(self):
297 def istopo(self):
253 """Is the collection is in topographical order or not.
298 """Is the collection is in topographical order or not.
254
299
255 This is part of the mandatory API for smartset."""
300 This is part of the mandatory API for smartset."""
256 if len(self) <= 1:
301 if len(self) <= 1:
257 return True
302 return True
258 return self._istopo
303 return self._istopo
259
304
260 def first(self):
305 def first(self):
261 if self:
306 if self:
262 if self._ascending is None:
307 if self._ascending is None:
263 return self._list[0]
308 return self._list[0]
264 elif self._ascending:
309 elif self._ascending:
265 return self._asclist[0]
310 return self._asclist[0]
266 else:
311 else:
267 return self._asclist[-1]
312 return self._asclist[-1]
268 return None
313 return None
269
314
270 def last(self):
315 def last(self):
271 if self:
316 if self:
272 if self._ascending is None:
317 if self._ascending is None:
273 return self._list[-1]
318 return self._list[-1]
274 elif self._ascending:
319 elif self._ascending:
275 return self._asclist[-1]
320 return self._asclist[-1]
276 else:
321 else:
277 return self._asclist[0]
322 return self._asclist[0]
278 return None
323 return None
279
324
280 def __repr__(self):
325 def __repr__(self):
281 d = {None: '', False: '-', True: '+'}[self._ascending]
326 d = {None: '', False: '-', True: '+'}[self._ascending]
282 s = _formatsetrepr(self._datarepr)
327 s = _formatsetrepr(self._datarepr)
283 if not s:
328 if not s:
284 l = self._list
329 l = self._list
285 # if _list has been built from a set, it might have a different
330 # if _list has been built from a set, it might have a different
286 # order from one python implementation to another.
331 # order from one python implementation to another.
287 # We fallback to the sorted version for a stable output.
332 # We fallback to the sorted version for a stable output.
288 if self._ascending is not None:
333 if self._ascending is not None:
289 l = self._asclist
334 l = self._asclist
290 s = repr(l)
335 s = repr(l)
291 return '<%s%s %s>' % (type(self).__name__, d, s)
336 return '<%s%s %s>' % (type(self).__name__, d, s)
292
337
293 class filteredset(abstractsmartset):
338 class filteredset(abstractsmartset):
294 """Duck type for baseset class which iterates lazily over the revisions in
339 """Duck type for baseset class which iterates lazily over the revisions in
295 the subset and contains a function which tests for membership in the
340 the subset and contains a function which tests for membership in the
296 revset
341 revset
297 """
342 """
298 def __init__(self, subset, condition=lambda x: True, condrepr=None):
343 def __init__(self, subset, condition=lambda x: True, condrepr=None):
299 """
344 """
300 condition: a function that decide whether a revision in the subset
345 condition: a function that decide whether a revision in the subset
301 belongs to the revset or not.
346 belongs to the revset or not.
302 condrepr: a tuple of (format, obj, ...), a function or an object that
347 condrepr: a tuple of (format, obj, ...), a function or an object that
303 provides a printable representation of the given condition.
348 provides a printable representation of the given condition.
304 """
349 """
305 self._subset = subset
350 self._subset = subset
306 self._condition = condition
351 self._condition = condition
307 self._condrepr = condrepr
352 self._condrepr = condrepr
308
353
309 def __contains__(self, x):
354 def __contains__(self, x):
310 return x in self._subset and self._condition(x)
355 return x in self._subset and self._condition(x)
311
356
312 def __iter__(self):
357 def __iter__(self):
313 return self._iterfilter(self._subset)
358 return self._iterfilter(self._subset)
314
359
315 def _iterfilter(self, it):
360 def _iterfilter(self, it):
316 cond = self._condition
361 cond = self._condition
317 for x in it:
362 for x in it:
318 if cond(x):
363 if cond(x):
319 yield x
364 yield x
320
365
321 @property
366 @property
322 def fastasc(self):
367 def fastasc(self):
323 it = self._subset.fastasc
368 it = self._subset.fastasc
324 if it is None:
369 if it is None:
325 return None
370 return None
326 return lambda: self._iterfilter(it())
371 return lambda: self._iterfilter(it())
327
372
328 @property
373 @property
329 def fastdesc(self):
374 def fastdesc(self):
330 it = self._subset.fastdesc
375 it = self._subset.fastdesc
331 if it is None:
376 if it is None:
332 return None
377 return None
333 return lambda: self._iterfilter(it())
378 return lambda: self._iterfilter(it())
334
379
335 def __nonzero__(self):
380 def __nonzero__(self):
336 fast = None
381 fast = None
337 candidates = [self.fastasc if self.isascending() else None,
382 candidates = [self.fastasc if self.isascending() else None,
338 self.fastdesc if self.isdescending() else None,
383 self.fastdesc if self.isdescending() else None,
339 self.fastasc,
384 self.fastasc,
340 self.fastdesc]
385 self.fastdesc]
341 for candidate in candidates:
386 for candidate in candidates:
342 if candidate is not None:
387 if candidate is not None:
343 fast = candidate
388 fast = candidate
344 break
389 break
345
390
346 if fast is not None:
391 if fast is not None:
347 it = fast()
392 it = fast()
348 else:
393 else:
349 it = self
394 it = self
350
395
351 for r in it:
396 for r in it:
352 return True
397 return True
353 return False
398 return False
354
399
355 def __len__(self):
400 def __len__(self):
356 # Basic implementation to be changed in future patches.
401 # Basic implementation to be changed in future patches.
357 # until this gets improved, we use generator expression
402 # until this gets improved, we use generator expression
358 # here, since list comprehensions are free to call __len__ again
403 # here, since list comprehensions are free to call __len__ again
359 # causing infinite recursion
404 # causing infinite recursion
360 l = baseset(r for r in self)
405 l = baseset(r for r in self)
361 return len(l)
406 return len(l)
362
407
363 def sort(self, reverse=False):
408 def sort(self, reverse=False):
364 self._subset.sort(reverse=reverse)
409 self._subset.sort(reverse=reverse)
365
410
366 def reverse(self):
411 def reverse(self):
367 self._subset.reverse()
412 self._subset.reverse()
368
413
369 def isascending(self):
414 def isascending(self):
370 return self._subset.isascending()
415 return self._subset.isascending()
371
416
372 def isdescending(self):
417 def isdescending(self):
373 return self._subset.isdescending()
418 return self._subset.isdescending()
374
419
375 def istopo(self):
420 def istopo(self):
376 return self._subset.istopo()
421 return self._subset.istopo()
377
422
378 def first(self):
423 def first(self):
379 for x in self:
424 for x in self:
380 return x
425 return x
381 return None
426 return None
382
427
383 def last(self):
428 def last(self):
384 it = None
429 it = None
385 if self.isascending():
430 if self.isascending():
386 it = self.fastdesc
431 it = self.fastdesc
387 elif self.isdescending():
432 elif self.isdescending():
388 it = self.fastasc
433 it = self.fastasc
389 if it is not None:
434 if it is not None:
390 for x in it():
435 for x in it():
391 return x
436 return x
392 return None #empty case
437 return None #empty case
393 else:
438 else:
394 x = None
439 x = None
395 for x in self:
440 for x in self:
396 pass
441 pass
397 return x
442 return x
398
443
399 def __repr__(self):
444 def __repr__(self):
400 xs = [repr(self._subset)]
445 xs = [repr(self._subset)]
401 s = _formatsetrepr(self._condrepr)
446 s = _formatsetrepr(self._condrepr)
402 if s:
447 if s:
403 xs.append(s)
448 xs.append(s)
404 return '<%s %s>' % (type(self).__name__, ', '.join(xs))
449 return '<%s %s>' % (type(self).__name__, ', '.join(xs))
405
450
406 def _iterordered(ascending, iter1, iter2):
451 def _iterordered(ascending, iter1, iter2):
407 """produce an ordered iteration from two iterators with the same order
452 """produce an ordered iteration from two iterators with the same order
408
453
409 The ascending is used to indicated the iteration direction.
454 The ascending is used to indicated the iteration direction.
410 """
455 """
411 choice = max
456 choice = max
412 if ascending:
457 if ascending:
413 choice = min
458 choice = min
414
459
415 val1 = None
460 val1 = None
416 val2 = None
461 val2 = None
417 try:
462 try:
418 # Consume both iterators in an ordered way until one is empty
463 # Consume both iterators in an ordered way until one is empty
419 while True:
464 while True:
420 if val1 is None:
465 if val1 is None:
421 val1 = next(iter1)
466 val1 = next(iter1)
422 if val2 is None:
467 if val2 is None:
423 val2 = next(iter2)
468 val2 = next(iter2)
424 n = choice(val1, val2)
469 n = choice(val1, val2)
425 yield n
470 yield n
426 if val1 == n:
471 if val1 == n:
427 val1 = None
472 val1 = None
428 if val2 == n:
473 if val2 == n:
429 val2 = None
474 val2 = None
430 except StopIteration:
475 except StopIteration:
431 # Flush any remaining values and consume the other one
476 # Flush any remaining values and consume the other one
432 it = iter2
477 it = iter2
433 if val1 is not None:
478 if val1 is not None:
434 yield val1
479 yield val1
435 it = iter1
480 it = iter1
436 elif val2 is not None:
481 elif val2 is not None:
437 # might have been equality and both are empty
482 # might have been equality and both are empty
438 yield val2
483 yield val2
439 for val in it:
484 for val in it:
440 yield val
485 yield val
441
486
442 class addset(abstractsmartset):
487 class addset(abstractsmartset):
443 """Represent the addition of two sets
488 """Represent the addition of two sets
444
489
445 Wrapper structure for lazily adding two structures without losing much
490 Wrapper structure for lazily adding two structures without losing much
446 performance on the __contains__ method
491 performance on the __contains__ method
447
492
448 If the ascending attribute is set, that means the two structures are
493 If the ascending attribute is set, that means the two structures are
449 ordered in either an ascending or descending way. Therefore, we can add
494 ordered in either an ascending or descending way. Therefore, we can add
450 them maintaining the order by iterating over both at the same time
495 them maintaining the order by iterating over both at the same time
451
496
452 >>> xs = baseset([0, 3, 2])
497 >>> xs = baseset([0, 3, 2])
453 >>> ys = baseset([5, 2, 4])
498 >>> ys = baseset([5, 2, 4])
454
499
455 >>> rs = addset(xs, ys)
500 >>> rs = addset(xs, ys)
456 >>> bool(rs), 0 in rs, 1 in rs, 5 in rs, rs.first(), rs.last()
501 >>> bool(rs), 0 in rs, 1 in rs, 5 in rs, rs.first(), rs.last()
457 (True, True, False, True, 0, 4)
502 (True, True, False, True, 0, 4)
458 >>> rs = addset(xs, baseset([]))
503 >>> rs = addset(xs, baseset([]))
459 >>> bool(rs), 0 in rs, 1 in rs, rs.first(), rs.last()
504 >>> bool(rs), 0 in rs, 1 in rs, rs.first(), rs.last()
460 (True, True, False, 0, 2)
505 (True, True, False, 0, 2)
461 >>> rs = addset(baseset([]), baseset([]))
506 >>> rs = addset(baseset([]), baseset([]))
462 >>> bool(rs), 0 in rs, rs.first(), rs.last()
507 >>> bool(rs), 0 in rs, rs.first(), rs.last()
463 (False, False, None, None)
508 (False, False, None, None)
464
509
465 iterate unsorted:
510 iterate unsorted:
466 >>> rs = addset(xs, ys)
511 >>> rs = addset(xs, ys)
467 >>> # (use generator because pypy could call len())
512 >>> # (use generator because pypy could call len())
468 >>> list(x for x in rs) # without _genlist
513 >>> list(x for x in rs) # without _genlist
469 [0, 3, 2, 5, 4]
514 [0, 3, 2, 5, 4]
470 >>> assert not rs._genlist
515 >>> assert not rs._genlist
471 >>> len(rs)
516 >>> len(rs)
472 5
517 5
473 >>> [x for x in rs] # with _genlist
518 >>> [x for x in rs] # with _genlist
474 [0, 3, 2, 5, 4]
519 [0, 3, 2, 5, 4]
475 >>> assert rs._genlist
520 >>> assert rs._genlist
476
521
477 iterate ascending:
522 iterate ascending:
478 >>> rs = addset(xs, ys, ascending=True)
523 >>> rs = addset(xs, ys, ascending=True)
479 >>> # (use generator because pypy could call len())
524 >>> # (use generator because pypy could call len())
480 >>> list(x for x in rs), list(x for x in rs.fastasc()) # without _asclist
525 >>> list(x for x in rs), list(x for x in rs.fastasc()) # without _asclist
481 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
526 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
482 >>> assert not rs._asclist
527 >>> assert not rs._asclist
483 >>> len(rs)
528 >>> len(rs)
484 5
529 5
485 >>> [x for x in rs], [x for x in rs.fastasc()]
530 >>> [x for x in rs], [x for x in rs.fastasc()]
486 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
531 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
487 >>> assert rs._asclist
532 >>> assert rs._asclist
488
533
489 iterate descending:
534 iterate descending:
490 >>> rs = addset(xs, ys, ascending=False)
535 >>> rs = addset(xs, ys, ascending=False)
491 >>> # (use generator because pypy could call len())
536 >>> # (use generator because pypy could call len())
492 >>> list(x for x in rs), list(x for x in rs.fastdesc()) # without _asclist
537 >>> list(x for x in rs), list(x for x in rs.fastdesc()) # without _asclist
493 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
538 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
494 >>> assert not rs._asclist
539 >>> assert not rs._asclist
495 >>> len(rs)
540 >>> len(rs)
496 5
541 5
497 >>> [x for x in rs], [x for x in rs.fastdesc()]
542 >>> [x for x in rs], [x for x in rs.fastdesc()]
498 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
543 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
499 >>> assert rs._asclist
544 >>> assert rs._asclist
500
545
501 iterate ascending without fastasc:
546 iterate ascending without fastasc:
502 >>> rs = addset(xs, generatorset(ys), ascending=True)
547 >>> rs = addset(xs, generatorset(ys), ascending=True)
503 >>> assert rs.fastasc is None
548 >>> assert rs.fastasc is None
504 >>> [x for x in rs]
549 >>> [x for x in rs]
505 [0, 2, 3, 4, 5]
550 [0, 2, 3, 4, 5]
506
551
507 iterate descending without fastdesc:
552 iterate descending without fastdesc:
508 >>> rs = addset(generatorset(xs), ys, ascending=False)
553 >>> rs = addset(generatorset(xs), ys, ascending=False)
509 >>> assert rs.fastdesc is None
554 >>> assert rs.fastdesc is None
510 >>> [x for x in rs]
555 >>> [x for x in rs]
511 [5, 4, 3, 2, 0]
556 [5, 4, 3, 2, 0]
512 """
557 """
513 def __init__(self, revs1, revs2, ascending=None):
558 def __init__(self, revs1, revs2, ascending=None):
514 self._r1 = revs1
559 self._r1 = revs1
515 self._r2 = revs2
560 self._r2 = revs2
516 self._iter = None
561 self._iter = None
517 self._ascending = ascending
562 self._ascending = ascending
518 self._genlist = None
563 self._genlist = None
519 self._asclist = None
564 self._asclist = None
520
565
521 def __len__(self):
566 def __len__(self):
522 return len(self._list)
567 return len(self._list)
523
568
524 def __nonzero__(self):
569 def __nonzero__(self):
525 return bool(self._r1) or bool(self._r2)
570 return bool(self._r1) or bool(self._r2)
526
571
527 @util.propertycache
572 @util.propertycache
528 def _list(self):
573 def _list(self):
529 if not self._genlist:
574 if not self._genlist:
530 self._genlist = baseset(iter(self))
575 self._genlist = baseset(iter(self))
531 return self._genlist
576 return self._genlist
532
577
533 def __iter__(self):
578 def __iter__(self):
534 """Iterate over both collections without repeating elements
579 """Iterate over both collections without repeating elements
535
580
536 If the ascending attribute is not set, iterate over the first one and
581 If the ascending attribute is not set, iterate over the first one and
537 then over the second one checking for membership on the first one so we
582 then over the second one checking for membership on the first one so we
538 dont yield any duplicates.
583 dont yield any duplicates.
539
584
540 If the ascending attribute is set, iterate over both collections at the
585 If the ascending attribute is set, iterate over both collections at the
541 same time, yielding only one value at a time in the given order.
586 same time, yielding only one value at a time in the given order.
542 """
587 """
543 if self._ascending is None:
588 if self._ascending is None:
544 if self._genlist:
589 if self._genlist:
545 return iter(self._genlist)
590 return iter(self._genlist)
546 def arbitraryordergen():
591 def arbitraryordergen():
547 for r in self._r1:
592 for r in self._r1:
548 yield r
593 yield r
549 inr1 = self._r1.__contains__
594 inr1 = self._r1.__contains__
550 for r in self._r2:
595 for r in self._r2:
551 if not inr1(r):
596 if not inr1(r):
552 yield r
597 yield r
553 return arbitraryordergen()
598 return arbitraryordergen()
554 # try to use our own fast iterator if it exists
599 # try to use our own fast iterator if it exists
555 self._trysetasclist()
600 self._trysetasclist()
556 if self._ascending:
601 if self._ascending:
557 attr = 'fastasc'
602 attr = 'fastasc'
558 else:
603 else:
559 attr = 'fastdesc'
604 attr = 'fastdesc'
560 it = getattr(self, attr)
605 it = getattr(self, attr)
561 if it is not None:
606 if it is not None:
562 return it()
607 return it()
563 # maybe half of the component supports fast
608 # maybe half of the component supports fast
564 # get iterator for _r1
609 # get iterator for _r1
565 iter1 = getattr(self._r1, attr)
610 iter1 = getattr(self._r1, attr)
566 if iter1 is None:
611 if iter1 is None:
567 # let's avoid side effect (not sure it matters)
612 # let's avoid side effect (not sure it matters)
568 iter1 = iter(sorted(self._r1, reverse=not self._ascending))
613 iter1 = iter(sorted(self._r1, reverse=not self._ascending))
569 else:
614 else:
570 iter1 = iter1()
615 iter1 = iter1()
571 # get iterator for _r2
616 # get iterator for _r2
572 iter2 = getattr(self._r2, attr)
617 iter2 = getattr(self._r2, attr)
573 if iter2 is None:
618 if iter2 is None:
574 # let's avoid side effect (not sure it matters)
619 # let's avoid side effect (not sure it matters)
575 iter2 = iter(sorted(self._r2, reverse=not self._ascending))
620 iter2 = iter(sorted(self._r2, reverse=not self._ascending))
576 else:
621 else:
577 iter2 = iter2()
622 iter2 = iter2()
578 return _iterordered(self._ascending, iter1, iter2)
623 return _iterordered(self._ascending, iter1, iter2)
579
624
580 def _trysetasclist(self):
625 def _trysetasclist(self):
581 """populate the _asclist attribute if possible and necessary"""
626 """populate the _asclist attribute if possible and necessary"""
582 if self._genlist is not None and self._asclist is None:
627 if self._genlist is not None and self._asclist is None:
583 self._asclist = sorted(self._genlist)
628 self._asclist = sorted(self._genlist)
584
629
585 @property
630 @property
586 def fastasc(self):
631 def fastasc(self):
587 self._trysetasclist()
632 self._trysetasclist()
588 if self._asclist is not None:
633 if self._asclist is not None:
589 return self._asclist.__iter__
634 return self._asclist.__iter__
590 iter1 = self._r1.fastasc
635 iter1 = self._r1.fastasc
591 iter2 = self._r2.fastasc
636 iter2 = self._r2.fastasc
592 if None in (iter1, iter2):
637 if None in (iter1, iter2):
593 return None
638 return None
594 return lambda: _iterordered(True, iter1(), iter2())
639 return lambda: _iterordered(True, iter1(), iter2())
595
640
596 @property
641 @property
597 def fastdesc(self):
642 def fastdesc(self):
598 self._trysetasclist()
643 self._trysetasclist()
599 if self._asclist is not None:
644 if self._asclist is not None:
600 return self._asclist.__reversed__
645 return self._asclist.__reversed__
601 iter1 = self._r1.fastdesc
646 iter1 = self._r1.fastdesc
602 iter2 = self._r2.fastdesc
647 iter2 = self._r2.fastdesc
603 if None in (iter1, iter2):
648 if None in (iter1, iter2):
604 return None
649 return None
605 return lambda: _iterordered(False, iter1(), iter2())
650 return lambda: _iterordered(False, iter1(), iter2())
606
651
607 def __contains__(self, x):
652 def __contains__(self, x):
608 return x in self._r1 or x in self._r2
653 return x in self._r1 or x in self._r2
609
654
610 def sort(self, reverse=False):
655 def sort(self, reverse=False):
611 """Sort the added set
656 """Sort the added set
612
657
613 For this we use the cached list with all the generated values and if we
658 For this we use the cached list with all the generated values and if we
614 know they are ascending or descending we can sort them in a smart way.
659 know they are ascending or descending we can sort them in a smart way.
615 """
660 """
616 self._ascending = not reverse
661 self._ascending = not reverse
617
662
618 def isascending(self):
663 def isascending(self):
619 return self._ascending is not None and self._ascending
664 return self._ascending is not None and self._ascending
620
665
621 def isdescending(self):
666 def isdescending(self):
622 return self._ascending is not None and not self._ascending
667 return self._ascending is not None and not self._ascending
623
668
624 def istopo(self):
669 def istopo(self):
625 # not worth the trouble asserting if the two sets combined are still
670 # not worth the trouble asserting if the two sets combined are still
626 # in topographical order. Use the sort() predicate to explicitly sort
671 # in topographical order. Use the sort() predicate to explicitly sort
627 # again instead.
672 # again instead.
628 return False
673 return False
629
674
630 def reverse(self):
675 def reverse(self):
631 if self._ascending is None:
676 if self._ascending is None:
632 self._list.reverse()
677 self._list.reverse()
633 else:
678 else:
634 self._ascending = not self._ascending
679 self._ascending = not self._ascending
635
680
636 def first(self):
681 def first(self):
637 for x in self:
682 for x in self:
638 return x
683 return x
639 return None
684 return None
640
685
641 def last(self):
686 def last(self):
642 self.reverse()
687 self.reverse()
643 val = self.first()
688 val = self.first()
644 self.reverse()
689 self.reverse()
645 return val
690 return val
646
691
647 def __repr__(self):
692 def __repr__(self):
648 d = {None: '', False: '-', True: '+'}[self._ascending]
693 d = {None: '', False: '-', True: '+'}[self._ascending]
649 return '<%s%s %r, %r>' % (type(self).__name__, d, self._r1, self._r2)
694 return '<%s%s %r, %r>' % (type(self).__name__, d, self._r1, self._r2)
650
695
651 class generatorset(abstractsmartset):
696 class generatorset(abstractsmartset):
652 """Wrap a generator for lazy iteration
697 """Wrap a generator for lazy iteration
653
698
654 Wrapper structure for generators that provides lazy membership and can
699 Wrapper structure for generators that provides lazy membership and can
655 be iterated more than once.
700 be iterated more than once.
656 When asked for membership it generates values until either it finds the
701 When asked for membership it generates values until either it finds the
657 requested one or has gone through all the elements in the generator
702 requested one or has gone through all the elements in the generator
658 """
703 """
659 def __init__(self, gen, iterasc=None):
704 def __init__(self, gen, iterasc=None):
660 """
705 """
661 gen: a generator producing the values for the generatorset.
706 gen: a generator producing the values for the generatorset.
662 """
707 """
663 self._gen = gen
708 self._gen = gen
664 self._asclist = None
709 self._asclist = None
665 self._cache = {}
710 self._cache = {}
666 self._genlist = []
711 self._genlist = []
667 self._finished = False
712 self._finished = False
668 self._ascending = True
713 self._ascending = True
669 if iterasc is not None:
714 if iterasc is not None:
670 if iterasc:
715 if iterasc:
671 self.fastasc = self._iterator
716 self.fastasc = self._iterator
672 self.__contains__ = self._asccontains
717 self.__contains__ = self._asccontains
673 else:
718 else:
674 self.fastdesc = self._iterator
719 self.fastdesc = self._iterator
675 self.__contains__ = self._desccontains
720 self.__contains__ = self._desccontains
676
721
677 def __nonzero__(self):
722 def __nonzero__(self):
678 # Do not use 'for r in self' because it will enforce the iteration
723 # Do not use 'for r in self' because it will enforce the iteration
679 # order (default ascending), possibly unrolling a whole descending
724 # order (default ascending), possibly unrolling a whole descending
680 # iterator.
725 # iterator.
681 if self._genlist:
726 if self._genlist:
682 return True
727 return True
683 for r in self._consumegen():
728 for r in self._consumegen():
684 return True
729 return True
685 return False
730 return False
686
731
687 def __contains__(self, x):
732 def __contains__(self, x):
688 if x in self._cache:
733 if x in self._cache:
689 return self._cache[x]
734 return self._cache[x]
690
735
691 # Use new values only, as existing values would be cached.
736 # Use new values only, as existing values would be cached.
692 for l in self._consumegen():
737 for l in self._consumegen():
693 if l == x:
738 if l == x:
694 return True
739 return True
695
740
696 self._cache[x] = False
741 self._cache[x] = False
697 return False
742 return False
698
743
699 def _asccontains(self, x):
744 def _asccontains(self, x):
700 """version of contains optimised for ascending generator"""
745 """version of contains optimised for ascending generator"""
701 if x in self._cache:
746 if x in self._cache:
702 return self._cache[x]
747 return self._cache[x]
703
748
704 # Use new values only, as existing values would be cached.
749 # Use new values only, as existing values would be cached.
705 for l in self._consumegen():
750 for l in self._consumegen():
706 if l == x:
751 if l == x:
707 return True
752 return True
708 if l > x:
753 if l > x:
709 break
754 break
710
755
711 self._cache[x] = False
756 self._cache[x] = False
712 return False
757 return False
713
758
714 def _desccontains(self, x):
759 def _desccontains(self, x):
715 """version of contains optimised for descending generator"""
760 """version of contains optimised for descending generator"""
716 if x in self._cache:
761 if x in self._cache:
717 return self._cache[x]
762 return self._cache[x]
718
763
719 # Use new values only, as existing values would be cached.
764 # Use new values only, as existing values would be cached.
720 for l in self._consumegen():
765 for l in self._consumegen():
721 if l == x:
766 if l == x:
722 return True
767 return True
723 if l < x:
768 if l < x:
724 break
769 break
725
770
726 self._cache[x] = False
771 self._cache[x] = False
727 return False
772 return False
728
773
729 def __iter__(self):
774 def __iter__(self):
730 if self._ascending:
775 if self._ascending:
731 it = self.fastasc
776 it = self.fastasc
732 else:
777 else:
733 it = self.fastdesc
778 it = self.fastdesc
734 if it is not None:
779 if it is not None:
735 return it()
780 return it()
736 # we need to consume the iterator
781 # we need to consume the iterator
737 for x in self._consumegen():
782 for x in self._consumegen():
738 pass
783 pass
739 # recall the same code
784 # recall the same code
740 return iter(self)
785 return iter(self)
741
786
742 def _iterator(self):
787 def _iterator(self):
743 if self._finished:
788 if self._finished:
744 return iter(self._genlist)
789 return iter(self._genlist)
745
790
746 # We have to use this complex iteration strategy to allow multiple
791 # We have to use this complex iteration strategy to allow multiple
747 # iterations at the same time. We need to be able to catch revision
792 # iterations at the same time. We need to be able to catch revision
748 # removed from _consumegen and added to genlist in another instance.
793 # removed from _consumegen and added to genlist in another instance.
749 #
794 #
750 # Getting rid of it would provide an about 15% speed up on this
795 # Getting rid of it would provide an about 15% speed up on this
751 # iteration.
796 # iteration.
752 genlist = self._genlist
797 genlist = self._genlist
753 nextrev = self._consumegen().next
798 nextrev = self._consumegen().next
754 _len = len # cache global lookup
799 _len = len # cache global lookup
755 def gen():
800 def gen():
756 i = 0
801 i = 0
757 while True:
802 while True:
758 if i < _len(genlist):
803 if i < _len(genlist):
759 yield genlist[i]
804 yield genlist[i]
760 else:
805 else:
761 yield nextrev()
806 yield nextrev()
762 i += 1
807 i += 1
763 return gen()
808 return gen()
764
809
765 def _consumegen(self):
810 def _consumegen(self):
766 cache = self._cache
811 cache = self._cache
767 genlist = self._genlist.append
812 genlist = self._genlist.append
768 for item in self._gen:
813 for item in self._gen:
769 cache[item] = True
814 cache[item] = True
770 genlist(item)
815 genlist(item)
771 yield item
816 yield item
772 if not self._finished:
817 if not self._finished:
773 self._finished = True
818 self._finished = True
774 asc = self._genlist[:]
819 asc = self._genlist[:]
775 asc.sort()
820 asc.sort()
776 self._asclist = asc
821 self._asclist = asc
777 self.fastasc = asc.__iter__
822 self.fastasc = asc.__iter__
778 self.fastdesc = asc.__reversed__
823 self.fastdesc = asc.__reversed__
779
824
780 def __len__(self):
825 def __len__(self):
781 for x in self._consumegen():
826 for x in self._consumegen():
782 pass
827 pass
783 return len(self._genlist)
828 return len(self._genlist)
784
829
785 def sort(self, reverse=False):
830 def sort(self, reverse=False):
786 self._ascending = not reverse
831 self._ascending = not reverse
787
832
788 def reverse(self):
833 def reverse(self):
789 self._ascending = not self._ascending
834 self._ascending = not self._ascending
790
835
791 def isascending(self):
836 def isascending(self):
792 return self._ascending
837 return self._ascending
793
838
794 def isdescending(self):
839 def isdescending(self):
795 return not self._ascending
840 return not self._ascending
796
841
797 def istopo(self):
842 def istopo(self):
798 # not worth the trouble asserting if the two sets combined are still
843 # not worth the trouble asserting if the two sets combined are still
799 # in topographical order. Use the sort() predicate to explicitly sort
844 # in topographical order. Use the sort() predicate to explicitly sort
800 # again instead.
845 # again instead.
801 return False
846 return False
802
847
803 def first(self):
848 def first(self):
804 if self._ascending:
849 if self._ascending:
805 it = self.fastasc
850 it = self.fastasc
806 else:
851 else:
807 it = self.fastdesc
852 it = self.fastdesc
808 if it is None:
853 if it is None:
809 # we need to consume all and try again
854 # we need to consume all and try again
810 for x in self._consumegen():
855 for x in self._consumegen():
811 pass
856 pass
812 return self.first()
857 return self.first()
813 return next(it(), None)
858 return next(it(), None)
814
859
815 def last(self):
860 def last(self):
816 if self._ascending:
861 if self._ascending:
817 it = self.fastdesc
862 it = self.fastdesc
818 else:
863 else:
819 it = self.fastasc
864 it = self.fastasc
820 if it is None:
865 if it is None:
821 # we need to consume all and try again
866 # we need to consume all and try again
822 for x in self._consumegen():
867 for x in self._consumegen():
823 pass
868 pass
824 return self.first()
869 return self.first()
825 return next(it(), None)
870 return next(it(), None)
826
871
827 def __repr__(self):
872 def __repr__(self):
828 d = {False: '-', True: '+'}[self._ascending]
873 d = {False: '-', True: '+'}[self._ascending]
829 return '<%s%s>' % (type(self).__name__, d)
874 return '<%s%s>' % (type(self).__name__, d)
830
875
831 class spanset(abstractsmartset):
876 class spanset(abstractsmartset):
832 """Duck type for baseset class which represents a range of revisions and
877 """Duck type for baseset class which represents a range of revisions and
833 can work lazily and without having all the range in memory
878 can work lazily and without having all the range in memory
834
879
835 Note that spanset(x, y) behave almost like xrange(x, y) except for two
880 Note that spanset(x, y) behave almost like xrange(x, y) except for two
836 notable points:
881 notable points:
837 - when x < y it will be automatically descending,
882 - when x < y it will be automatically descending,
838 - revision filtered with this repoview will be skipped.
883 - revision filtered with this repoview will be skipped.
839
884
840 """
885 """
841 def __init__(self, repo, start=0, end=None):
886 def __init__(self, repo, start=0, end=None):
842 """
887 """
843 start: first revision included the set
888 start: first revision included the set
844 (default to 0)
889 (default to 0)
845 end: first revision excluded (last+1)
890 end: first revision excluded (last+1)
846 (default to len(repo)
891 (default to len(repo)
847
892
848 Spanset will be descending if `end` < `start`.
893 Spanset will be descending if `end` < `start`.
849 """
894 """
850 if end is None:
895 if end is None:
851 end = len(repo)
896 end = len(repo)
852 self._ascending = start <= end
897 self._ascending = start <= end
853 if not self._ascending:
898 if not self._ascending:
854 start, end = end + 1, start +1
899 start, end = end + 1, start +1
855 self._start = start
900 self._start = start
856 self._end = end
901 self._end = end
857 self._hiddenrevs = repo.changelog.filteredrevs
902 self._hiddenrevs = repo.changelog.filteredrevs
858
903
859 def sort(self, reverse=False):
904 def sort(self, reverse=False):
860 self._ascending = not reverse
905 self._ascending = not reverse
861
906
862 def reverse(self):
907 def reverse(self):
863 self._ascending = not self._ascending
908 self._ascending = not self._ascending
864
909
865 def istopo(self):
910 def istopo(self):
866 # not worth the trouble asserting if the two sets combined are still
911 # not worth the trouble asserting if the two sets combined are still
867 # in topographical order. Use the sort() predicate to explicitly sort
912 # in topographical order. Use the sort() predicate to explicitly sort
868 # again instead.
913 # again instead.
869 return False
914 return False
870
915
871 def _iterfilter(self, iterrange):
916 def _iterfilter(self, iterrange):
872 s = self._hiddenrevs
917 s = self._hiddenrevs
873 for r in iterrange:
918 for r in iterrange:
874 if r not in s:
919 if r not in s:
875 yield r
920 yield r
876
921
877 def __iter__(self):
922 def __iter__(self):
878 if self._ascending:
923 if self._ascending:
879 return self.fastasc()
924 return self.fastasc()
880 else:
925 else:
881 return self.fastdesc()
926 return self.fastdesc()
882
927
883 def fastasc(self):
928 def fastasc(self):
884 iterrange = xrange(self._start, self._end)
929 iterrange = xrange(self._start, self._end)
885 if self._hiddenrevs:
930 if self._hiddenrevs:
886 return self._iterfilter(iterrange)
931 return self._iterfilter(iterrange)
887 return iter(iterrange)
932 return iter(iterrange)
888
933
889 def fastdesc(self):
934 def fastdesc(self):
890 iterrange = xrange(self._end - 1, self._start - 1, -1)
935 iterrange = xrange(self._end - 1, self._start - 1, -1)
891 if self._hiddenrevs:
936 if self._hiddenrevs:
892 return self._iterfilter(iterrange)
937 return self._iterfilter(iterrange)
893 return iter(iterrange)
938 return iter(iterrange)
894
939
895 def __contains__(self, rev):
940 def __contains__(self, rev):
896 hidden = self._hiddenrevs
941 hidden = self._hiddenrevs
897 return ((self._start <= rev < self._end)
942 return ((self._start <= rev < self._end)
898 and not (hidden and rev in hidden))
943 and not (hidden and rev in hidden))
899
944
900 def __nonzero__(self):
945 def __nonzero__(self):
901 for r in self:
946 for r in self:
902 return True
947 return True
903 return False
948 return False
904
949
905 def __len__(self):
950 def __len__(self):
906 if not self._hiddenrevs:
951 if not self._hiddenrevs:
907 return abs(self._end - self._start)
952 return abs(self._end - self._start)
908 else:
953 else:
909 count = 0
954 count = 0
910 start = self._start
955 start = self._start
911 end = self._end
956 end = self._end
912 for rev in self._hiddenrevs:
957 for rev in self._hiddenrevs:
913 if (end < rev <= start) or (start <= rev < end):
958 if (end < rev <= start) or (start <= rev < end):
914 count += 1
959 count += 1
915 return abs(self._end - self._start) - count
960 return abs(self._end - self._start) - count
916
961
917 def isascending(self):
962 def isascending(self):
918 return self._ascending
963 return self._ascending
919
964
920 def isdescending(self):
965 def isdescending(self):
921 return not self._ascending
966 return not self._ascending
922
967
923 def first(self):
968 def first(self):
924 if self._ascending:
969 if self._ascending:
925 it = self.fastasc
970 it = self.fastasc
926 else:
971 else:
927 it = self.fastdesc
972 it = self.fastdesc
928 for x in it():
973 for x in it():
929 return x
974 return x
930 return None
975 return None
931
976
932 def last(self):
977 def last(self):
933 if self._ascending:
978 if self._ascending:
934 it = self.fastdesc
979 it = self.fastdesc
935 else:
980 else:
936 it = self.fastasc
981 it = self.fastasc
937 for x in it():
982 for x in it():
938 return x
983 return x
939 return None
984 return None
940
985
941 def __repr__(self):
986 def __repr__(self):
942 d = {False: '-', True: '+'}[self._ascending]
987 d = {False: '-', True: '+'}[self._ascending]
943 return '<%s%s %d:%d>' % (type(self).__name__, d,
988 return '<%s%s %d:%d>' % (type(self).__name__, d,
944 self._start, self._end - 1)
989 self._start, self._end - 1)
945
990
946 class fullreposet(spanset):
991 class fullreposet(spanset):
947 """a set containing all revisions in the repo
992 """a set containing all revisions in the repo
948
993
949 This class exists to host special optimization and magic to handle virtual
994 This class exists to host special optimization and magic to handle virtual
950 revisions such as "null".
995 revisions such as "null".
951 """
996 """
952
997
953 def __init__(self, repo):
998 def __init__(self, repo):
954 super(fullreposet, self).__init__(repo)
999 super(fullreposet, self).__init__(repo)
955
1000
956 def __and__(self, other):
1001 def __and__(self, other):
957 """As self contains the whole repo, all of the other set should also be
1002 """As self contains the whole repo, all of the other set should also be
958 in self. Therefore `self & other = other`.
1003 in self. Therefore `self & other = other`.
959
1004
960 This boldly assumes the other contains valid revs only.
1005 This boldly assumes the other contains valid revs only.
961 """
1006 """
962 # other not a smartset, make is so
1007 # other not a smartset, make is so
963 if not util.safehasattr(other, 'isascending'):
1008 if not util.safehasattr(other, 'isascending'):
964 # filter out hidden revision
1009 # filter out hidden revision
965 # (this boldly assumes all smartset are pure)
1010 # (this boldly assumes all smartset are pure)
966 #
1011 #
967 # `other` was used with "&", let's assume this is a set like
1012 # `other` was used with "&", let's assume this is a set like
968 # object.
1013 # object.
969 other = baseset(other - self._hiddenrevs)
1014 other = baseset(other - self._hiddenrevs)
970
1015
971 other.sort(reverse=self.isdescending())
1016 other.sort(reverse=self.isdescending())
972 return other
1017 return other
973
1018
974 def prettyformat(revs):
1019 def prettyformat(revs):
975 lines = []
1020 lines = []
976 rs = repr(revs)
1021 rs = repr(revs)
977 p = 0
1022 p = 0
978 while p < len(rs):
1023 while p < len(rs):
979 q = rs.find('<', p + 1)
1024 q = rs.find('<', p + 1)
980 if q < 0:
1025 if q < 0:
981 q = len(rs)
1026 q = len(rs)
982 l = rs.count('<', 0, p) - rs.count('>', 0, p)
1027 l = rs.count('<', 0, p) - rs.count('>', 0, p)
983 assert l >= 0
1028 assert l >= 0
984 lines.append((l, rs[p:q].rstrip()))
1029 lines.append((l, rs[p:q].rstrip()))
985 p = q
1030 p = q
986 return '\n'.join(' ' * l + s for l, s in lines)
1031 return '\n'.join(' ' * l + s for l, s in lines)
General Comments 0
You need to be logged in to leave comments. Login now