comparison mercurial/smartset.py @ 43076:2372284d9457

formatting: blacken the codebase This is using my patch to black (https://github.com/psf/black/pull/826) so we don't un-wrap collection literals. Done with: hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S # skip-blame mass-reformatting only # no-check-commit reformats foo_bar functions Differential Revision: https://phab.mercurial-scm.org/D6971
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:45:02 -0400
parents 6309128ff61f
children 687b865b95ad
comparison
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
11 encoding, 11 encoding,
12 error, 12 error,
13 pycompat, 13 pycompat,
14 util, 14 util,
15 ) 15 )
16 from .utils import ( 16 from .utils import stringutil
17 stringutil, 17
18 )
19 18
20 def _typename(o): 19 def _typename(o):
21 return pycompat.sysbytes(type(o).__name__).lstrip('_') 20 return pycompat.sysbytes(type(o).__name__).lstrip('_')
22 21
22
23 class abstractsmartset(object): 23 class abstractsmartset(object):
24
25 def __nonzero__(self): 24 def __nonzero__(self):
26 """True if the smartset is not empty""" 25 """True if the smartset is not empty"""
27 raise NotImplementedError() 26 raise NotImplementedError()
28 27
29 __bool__ = __nonzero__ 28 __bool__ = __nonzero__
123 def __sub__(self, other): 122 def __sub__(self, other):
124 """Returns a new object with the substraction of the two collections. 123 """Returns a new object with the substraction of the two collections.
125 124
126 This is part of the mandatory API for smartset.""" 125 This is part of the mandatory API for smartset."""
127 c = other.__contains__ 126 c = other.__contains__
128 return self.filter(lambda r: not c(r), condrepr=('<not %r>', other), 127 return self.filter(
129 cache=False) 128 lambda r: not c(r), condrepr=('<not %r>', other), cache=False
129 )
130 130
131 def filter(self, condition, condrepr=None, cache=True): 131 def filter(self, condition, condrepr=None, cache=True):
132 """Returns this smartset filtered by condition as a new smartset. 132 """Returns this smartset filtered by condition as a new smartset.
133 133
134 `condition` is a callable which takes a revision number and returns a 134 `condition` is a callable which takes a revision number and returns a
161 if y is None: 161 if y is None:
162 break 162 break
163 ys.append(y) 163 ys.append(y)
164 return baseset(ys, datarepr=('slice=%d:%d %r', start, stop, self)) 164 return baseset(ys, datarepr=('slice=%d:%d %r', start, stop, self))
165 165
166
166 class baseset(abstractsmartset): 167 class baseset(abstractsmartset):
167 """Basic data structure that represents a revset and contains the basic 168 """Basic data structure that represents a revset and contains the basic
168 operation that it should be able to perform. 169 operation that it should be able to perform.
169 170
170 Every method in this class should be implemented by any smartset class. 171 Every method in this class should be implemented by any smartset class.
220 >>> type(rs).__name__ 221 >>> type(rs).__name__
221 'baseset' 222 'baseset'
222 >>> rs._istopo 223 >>> rs._istopo
223 True 224 True
224 """ 225 """
226
225 def __init__(self, data=(), datarepr=None, istopo=False): 227 def __init__(self, data=(), datarepr=None, istopo=False):
226 """ 228 """
227 datarepr: a tuple of (format, obj, ...), a function or an object that 229 datarepr: a tuple of (format, obj, ...), a function or an object that
228 provides a printable representation of the given data. 230 provides a printable representation of the given data.
229 """ 231 """
340 return self._asclist[0] 342 return self._asclist[0]
341 return None 343 return None
342 344
343 def _fastsetop(self, other, op): 345 def _fastsetop(self, other, op):
344 # try to use native set operations as fast paths 346 # try to use native set operations as fast paths
345 if (type(other) is baseset and r'_set' in other.__dict__ and r'_set' in 347 if (
346 self.__dict__ and self._ascending is not None): 348 type(other) is baseset
347 s = baseset(data=getattr(self._set, op)(other._set), 349 and r'_set' in other.__dict__
348 istopo=self._istopo) 350 and r'_set' in self.__dict__
351 and self._ascending is not None
352 ):
353 s = baseset(
354 data=getattr(self._set, op)(other._set), istopo=self._istopo
355 )
349 s._ascending = self._ascending 356 s._ascending = self._ascending
350 else: 357 else:
351 s = getattr(super(baseset, self), op)(other) 358 s = getattr(super(baseset, self), op)(other)
352 return s 359 return s
353 360
381 if self._ascending is not None: 388 if self._ascending is not None:
382 l = self._asclist 389 l = self._asclist
383 s = pycompat.byterepr(l) 390 s = pycompat.byterepr(l)
384 return '<%s%s %s>' % (_typename(self), d, s) 391 return '<%s%s %s>' % (_typename(self), d, s)
385 392
393
386 class filteredset(abstractsmartset): 394 class filteredset(abstractsmartset):
387 """Duck type for baseset class which iterates lazily over the revisions in 395 """Duck type for baseset class which iterates lazily over the revisions in
388 the subset and contains a function which tests for membership in the 396 the subset and contains a function which tests for membership in the
389 revset 397 revset
390 """ 398 """
399
391 def __init__(self, subset, condition=lambda x: True, condrepr=None): 400 def __init__(self, subset, condition=lambda x: True, condrepr=None):
392 """ 401 """
393 condition: a function that decide whether a revision in the subset 402 condition: a function that decide whether a revision in the subset
394 belongs to the revset or not. 403 belongs to the revset or not.
395 condrepr: a tuple of (format, obj, ...), a function or an object that 404 condrepr: a tuple of (format, obj, ...), a function or an object that
425 return None 434 return None
426 return lambda: self._iterfilter(it()) 435 return lambda: self._iterfilter(it())
427 436
428 def __nonzero__(self): 437 def __nonzero__(self):
429 fast = None 438 fast = None
430 candidates = [self.fastasc if self.isascending() else None, 439 candidates = [
431 self.fastdesc if self.isdescending() else None, 440 self.fastasc if self.isascending() else None,
432 self.fastasc, 441 self.fastdesc if self.isdescending() else None,
433 self.fastdesc] 442 self.fastasc,
443 self.fastdesc,
444 ]
434 for candidate in candidates: 445 for candidate in candidates:
435 if candidate is not None: 446 if candidate is not None:
436 fast = candidate 447 fast = candidate
437 break 448 break
438 449
482 elif self.isdescending(): 493 elif self.isdescending():
483 it = self.fastasc 494 it = self.fastasc
484 if it is not None: 495 if it is not None:
485 for x in it(): 496 for x in it():
486 return x 497 return x
487 return None #empty case 498 return None # empty case
488 else: 499 else:
489 x = None 500 x = None
490 for x in self: 501 for x in self:
491 pass 502 pass
492 return x 503 return x
496 xs = [pycompat.byterepr(self._subset)] 507 xs = [pycompat.byterepr(self._subset)]
497 s = stringutil.buildrepr(self._condrepr) 508 s = stringutil.buildrepr(self._condrepr)
498 if s: 509 if s:
499 xs.append(s) 510 xs.append(s)
500 return '<%s %s>' % (_typename(self), ', '.join(xs)) 511 return '<%s %s>' % (_typename(self), ', '.join(xs))
512
501 513
502 def _iterordered(ascending, iter1, iter2): 514 def _iterordered(ascending, iter1, iter2):
503 """produce an ordered iteration from two iterators with the same order 515 """produce an ordered iteration from two iterators with the same order
504 516
505 The ascending is used to indicated the iteration direction. 517 The ascending is used to indicated the iteration direction.
533 # might have been equality and both are empty 545 # might have been equality and both are empty
534 yield val2 546 yield val2
535 for val in it: 547 for val in it:
536 yield val 548 yield val
537 549
550
538 class addset(abstractsmartset): 551 class addset(abstractsmartset):
539 """Represent the addition of two sets 552 """Represent the addition of two sets
540 553
541 Wrapper structure for lazily adding two structures without losing much 554 Wrapper structure for lazily adding two structures without losing much
542 performance on the __contains__ method 555 performance on the __contains__ method
604 >>> rs = addset(generatorset(xs), ys, ascending=False) 617 >>> rs = addset(generatorset(xs), ys, ascending=False)
605 >>> assert rs.fastdesc is None 618 >>> assert rs.fastdesc is None
606 >>> [x for x in rs] 619 >>> [x for x in rs]
607 [5, 4, 3, 2, 0] 620 [5, 4, 3, 2, 0]
608 """ 621 """
622
609 def __init__(self, revs1, revs2, ascending=None): 623 def __init__(self, revs1, revs2, ascending=None):
610 self._r1 = revs1 624 self._r1 = revs1
611 self._r2 = revs2 625 self._r2 = revs2
612 self._iter = None 626 self._iter = None
613 self._ascending = ascending 627 self._ascending = ascending
639 same time, yielding only one value at a time in the given order. 653 same time, yielding only one value at a time in the given order.
640 """ 654 """
641 if self._ascending is None: 655 if self._ascending is None:
642 if self._genlist: 656 if self._genlist:
643 return iter(self._genlist) 657 return iter(self._genlist)
658
644 def arbitraryordergen(): 659 def arbitraryordergen():
645 for r in self._r1: 660 for r in self._r1:
646 yield r 661 yield r
647 inr1 = self._r1.__contains__ 662 inr1 = self._r1.__contains__
648 for r in self._r2: 663 for r in self._r2:
649 if not inr1(r): 664 if not inr1(r):
650 yield r 665 yield r
666
651 return arbitraryordergen() 667 return arbitraryordergen()
652 # try to use our own fast iterator if it exists 668 # try to use our own fast iterator if it exists
653 self._trysetasclist() 669 self._trysetasclist()
654 if self._ascending: 670 if self._ascending:
655 attr = 'fastasc' 671 attr = 'fastasc'
745 @encoding.strmethod 761 @encoding.strmethod
746 def __repr__(self): 762 def __repr__(self):
747 d = {None: '', False: '-', True: '+'}[self._ascending] 763 d = {None: '', False: '-', True: '+'}[self._ascending]
748 return '<%s%s %r, %r>' % (_typename(self), d, self._r1, self._r2) 764 return '<%s%s %r, %r>' % (_typename(self), d, self._r1, self._r2)
749 765
766
750 class generatorset(abstractsmartset): 767 class generatorset(abstractsmartset):
751 """Wrap a generator for lazy iteration 768 """Wrap a generator for lazy iteration
752 769
753 Wrapper structure for generators that provides lazy membership and can 770 Wrapper structure for generators that provides lazy membership and can
754 be iterated more than once. 771 be iterated more than once.
758 >>> xs = generatorset([0, 1, 4], iterasc=True) 775 >>> xs = generatorset([0, 1, 4], iterasc=True)
759 >>> assert xs.last() == xs.last() 776 >>> assert xs.last() == xs.last()
760 >>> xs.last() # cached 777 >>> xs.last() # cached
761 4 778 4
762 """ 779 """
780
763 def __new__(cls, gen, iterasc=None): 781 def __new__(cls, gen, iterasc=None):
764 if iterasc is None: 782 if iterasc is None:
765 typ = cls 783 typ = cls
766 elif iterasc: 784 elif iterasc:
767 typ = _generatorsetasc 785 typ = _generatorsetasc
828 # 846 #
829 # Getting rid of it would provide an about 15% speed up on this 847 # Getting rid of it would provide an about 15% speed up on this
830 # iteration. 848 # iteration.
831 genlist = self._genlist 849 genlist = self._genlist
832 nextgen = self._consumegen() 850 nextgen = self._consumegen()
833 _len, _next = len, next # cache global lookup 851 _len, _next = len, next # cache global lookup
852
834 def gen(): 853 def gen():
835 i = 0 854 i = 0
836 while True: 855 while True:
837 if i < _len(genlist): 856 if i < _len(genlist):
838 yield genlist[i] 857 yield genlist[i]
840 try: 859 try:
841 yield _next(nextgen) 860 yield _next(nextgen)
842 except StopIteration: 861 except StopIteration:
843 return 862 return
844 i += 1 863 i += 1
864
845 return gen() 865 return gen()
846 866
847 def _consumegen(self): 867 def _consumegen(self):
848 cache = self._cache 868 cache = self._cache
849 genlist = self._genlist.append 869 genlist = self._genlist.append
909 @encoding.strmethod 929 @encoding.strmethod
910 def __repr__(self): 930 def __repr__(self):
911 d = {False: '-', True: '+'}[self._ascending] 931 d = {False: '-', True: '+'}[self._ascending]
912 return '<%s%s>' % (_typename(self), d) 932 return '<%s%s>' % (_typename(self), d)
913 933
934
914 class _generatorsetasc(generatorset): 935 class _generatorsetasc(generatorset):
915 """Special case of generatorset optimized for ascending generators.""" 936 """Special case of generatorset optimized for ascending generators."""
916 937
917 fastasc = generatorset._iterator 938 fastasc = generatorset._iterator
918 939
928 break 949 break
929 950
930 self._cache[x] = False 951 self._cache[x] = False
931 return False 952 return False
932 953
954
933 class _generatorsetdesc(generatorset): 955 class _generatorsetdesc(generatorset):
934 """Special case of generatorset optimized for descending generators.""" 956 """Special case of generatorset optimized for descending generators."""
935 957
936 fastdesc = generatorset._iterator 958 fastdesc = generatorset._iterator
937 959
946 if l < x: 968 if l < x:
947 break 969 break
948 970
949 self._cache[x] = False 971 self._cache[x] = False
950 return False 972 return False
973
951 974
952 def spanset(repo, start=0, end=None): 975 def spanset(repo, start=0, end=None):
953 """Create a spanset that represents a range of repository revisions 976 """Create a spanset that represents a range of repository revisions
954 977
955 start: first revision included the set (default to 0) 978 start: first revision included the set (default to 0)
962 ascending = start <= end 985 ascending = start <= end
963 if not ascending: 986 if not ascending:
964 start, end = end + 1, start + 1 987 start, end = end + 1, start + 1
965 return _spanset(start, end, ascending, repo.changelog.filteredrevs) 988 return _spanset(start, end, ascending, repo.changelog.filteredrevs)
966 989
990
967 class _spanset(abstractsmartset): 991 class _spanset(abstractsmartset):
968 """Duck type for baseset class which represents a range of revisions and 992 """Duck type for baseset class which represents a range of revisions and
969 can work lazily and without having all the range in memory 993 can work lazily and without having all the range in memory
970 994
971 Note that spanset(x, y) behave almost like xrange(x, y) except for two 995 Note that spanset(x, y) behave almost like xrange(x, y) except for two
972 notable points: 996 notable points:
973 - when x < y it will be automatically descending, 997 - when x < y it will be automatically descending,
974 - revision filtered with this repoview will be skipped. 998 - revision filtered with this repoview will be skipped.
975 999
976 """ 1000 """
1001
977 def __init__(self, start, end, ascending, hiddenrevs): 1002 def __init__(self, start, end, ascending, hiddenrevs):
978 self._start = start 1003 self._start = start
979 self._end = end 1004 self._end = end
980 self._ascending = ascending 1005 self._ascending = ascending
981 self._hiddenrevs = hiddenrevs 1006 self._hiddenrevs = hiddenrevs
1016 return self._iterfilter(iterrange) 1041 return self._iterfilter(iterrange)
1017 return iter(iterrange) 1042 return iter(iterrange)
1018 1043
1019 def __contains__(self, rev): 1044 def __contains__(self, rev):
1020 hidden = self._hiddenrevs 1045 hidden = self._hiddenrevs
1021 return ((self._start <= rev < self._end) 1046 return (self._start <= rev < self._end) and not (
1022 and not (hidden and rev in hidden)) 1047 hidden and rev in hidden
1048 )
1023 1049
1024 def __nonzero__(self): 1050 def __nonzero__(self):
1025 for r in self: 1051 for r in self:
1026 return True 1052 return True
1027 return False 1053 return False
1079 @encoding.strmethod 1105 @encoding.strmethod
1080 def __repr__(self): 1106 def __repr__(self):
1081 d = {False: '-', True: '+'}[self._ascending] 1107 d = {False: '-', True: '+'}[self._ascending]
1082 return '<%s%s %d:%d>' % (_typename(self), d, self._start, self._end) 1108 return '<%s%s %d:%d>' % (_typename(self), d, self._start, self._end)
1083 1109
1110
1084 class fullreposet(_spanset): 1111 class fullreposet(_spanset):
1085 """a set containing all revisions in the repo 1112 """a set containing all revisions in the repo
1086 1113
1087 This class exists to host special optimization and magic to handle virtual 1114 This class exists to host special optimization and magic to handle virtual
1088 revisions such as "null". 1115 revisions such as "null".
1089 """ 1116 """
1090 1117
1091 def __init__(self, repo): 1118 def __init__(self, repo):
1092 super(fullreposet, self).__init__(0, len(repo), True, 1119 super(fullreposet, self).__init__(
1093 repo.changelog.filteredrevs) 1120 0, len(repo), True, repo.changelog.filteredrevs
1121 )
1094 1122
1095 def __and__(self, other): 1123 def __and__(self, other):
1096 """As self contains the whole repo, all of the other set should also be 1124 """As self contains the whole repo, all of the other set should also be
1097 in self. Therefore `self & other = other`. 1125 in self. Therefore `self & other = other`.
1098 1126