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