comparison mercurial/phases.py @ 46644:77e129be10de

typing: add some type annotations to mercurial/phases.py Some of these were helpful in typing other modules, and then I typed the easy-ish ones. Black forces the long `Phasedefaults` definition to be wrapped, which pytype seems OK with (as shown with `reveal_type()`), but it does seem to confuse PyCharm a bit. Differential Revision: https://phab.mercurial-scm.org/D10126
author Matt Harbison <matt_harbison@yahoo.com>
date Sat, 06 Mar 2021 18:51:33 -0500
parents 5d65e04b6a80
children d55b71393907
comparison
equal deleted inserted replaced
46643:eef13b940887 46644:77e129be10de
125 smartset, 125 smartset,
126 txnutil, 126 txnutil,
127 util, 127 util,
128 ) 128 )
129 129
130 if pycompat.TYPE_CHECKING:
131 from typing import (
132 Any,
133 Callable,
134 Dict,
135 Iterable,
136 List,
137 Optional,
138 Set,
139 Tuple,
140 )
141 from . import (
142 localrepo,
143 ui as uimod,
144 )
145
146 Phaseroots = Dict[int, Set[bytes]]
147 Phasedefaults = List[
148 Callable[[localrepo.localrepository, Phaseroots], Phaseroots]
149 ]
150
151
130 _fphasesentry = struct.Struct(b'>i20s') 152 _fphasesentry = struct.Struct(b'>i20s')
131 153
132 # record phase index 154 # record phase index
133 public, draft, secret = range(3) 155 public, draft, secret = range(3) # type: int
134 archived = 32 # non-continuous for compatibility 156 archived = 32 # non-continuous for compatibility
135 internal = 96 # non-continuous for compatibility 157 internal = 96 # non-continuous for compatibility
136 allphases = (public, draft, secret, archived, internal) 158 allphases = (public, draft, secret, archived, internal)
137 trackedphases = (draft, secret, archived, internal) 159 trackedphases = (draft, secret, archived, internal)
138 # record phase names 160 # record phase names
152 remotehiddenphases = (secret, archived, internal) 174 remotehiddenphases = (secret, archived, internal)
153 localhiddenphases = (internal, archived) 175 localhiddenphases = (internal, archived)
154 176
155 177
156 def supportinternal(repo): 178 def supportinternal(repo):
179 # type: (localrepo.localrepository) -> bool
157 """True if the internal phase can be used on a repository""" 180 """True if the internal phase can be used on a repository"""
158 return requirements.INTERNAL_PHASE_REQUIREMENT in repo.requirements 181 return requirements.INTERNAL_PHASE_REQUIREMENT in repo.requirements
159 182
160 183
161 def _readroots(repo, phasedefaults=None): 184 def _readroots(repo, phasedefaults=None):
185 # type: (localrepo.localrepository, Optional[Phasedefaults]) -> Tuple[Phaseroots, bool]
162 """Read phase roots from disk 186 """Read phase roots from disk
163 187
164 phasedefaults is a list of fn(repo, roots) callable, which are 188 phasedefaults is a list of fn(repo, roots) callable, which are
165 executed if the phase roots file does not exist. When phases are 189 executed if the phase roots file does not exist. When phases are
166 being initialized on an existing repository, this could be used to 190 being initialized on an existing repository, this could be used to
189 dirty = True 213 dirty = True
190 return roots, dirty 214 return roots, dirty
191 215
192 216
193 def binaryencode(phasemapping): 217 def binaryencode(phasemapping):
218 # type: (Dict[int, List[bytes]]) -> bytes
194 """encode a 'phase -> nodes' mapping into a binary stream 219 """encode a 'phase -> nodes' mapping into a binary stream
195 220
196 The revision lists are encoded as (phase, root) pairs. 221 The revision lists are encoded as (phase, root) pairs.
197 """ 222 """
198 binarydata = [] 223 binarydata = []
201 binarydata.append(_fphasesentry.pack(phase, head)) 226 binarydata.append(_fphasesentry.pack(phase, head))
202 return b''.join(binarydata) 227 return b''.join(binarydata)
203 228
204 229
205 def binarydecode(stream): 230 def binarydecode(stream):
231 # type: (...) -> Dict[int, List[bytes]]
206 """decode a binary stream into a 'phase -> nodes' mapping 232 """decode a binary stream into a 'phase -> nodes' mapping
207 233
208 The (phase, root) pairs are turned back into a dictionary with 234 The (phase, root) pairs are turned back into a dictionary with
209 the phase as index and the aggregated roots of that phase as value.""" 235 the phase as index and the aggregated roots of that phase as value."""
210 headsbyphase = {i: [] for i in allphases} 236 headsbyphase = {i: [] for i in allphases}
319 data.insert(low + 1, (pycompat.xrange(rev, rev + 1), t)) 345 data.insert(low + 1, (pycompat.xrange(rev, rev + 1), t))
320 346
321 347
322 class phasecache(object): 348 class phasecache(object):
323 def __init__(self, repo, phasedefaults, _load=True): 349 def __init__(self, repo, phasedefaults, _load=True):
350 # type: (localrepo.localrepository, Optional[Phasedefaults], bool) -> None
324 if _load: 351 if _load:
325 # Cheap trick to allow shallow-copy without copy module 352 # Cheap trick to allow shallow-copy without copy module
326 self.phaseroots, self.dirty = _readroots(repo, phasedefaults) 353 self.phaseroots, self.dirty = _readroots(repo, phasedefaults)
327 self._loadedrevslen = 0 354 self._loadedrevslen = 0
328 self._phasesets = None 355 self._phasesets = None
329 self.filterunknown(repo) 356 self.filterunknown(repo)
330 self.opener = repo.svfs 357 self.opener = repo.svfs
331 358
332 def hasnonpublicphases(self, repo): 359 def hasnonpublicphases(self, repo):
360 # type: (localrepo.localrepository) -> bool
333 """detect if there are revisions with non-public phase""" 361 """detect if there are revisions with non-public phase"""
334 repo = repo.unfiltered() 362 repo = repo.unfiltered()
335 cl = repo.changelog 363 cl = repo.changelog
336 if len(cl) >= self._loadedrevslen: 364 if len(cl) >= self._loadedrevslen:
337 self.invalidate() 365 self.invalidate()
341 for phase, revs in pycompat.iteritems(self.phaseroots) 369 for phase, revs in pycompat.iteritems(self.phaseroots)
342 if phase != public 370 if phase != public
343 ) 371 )
344 372
345 def nonpublicphaseroots(self, repo): 373 def nonpublicphaseroots(self, repo):
374 # type: (localrepo.localrepository) -> Set[bytes]
346 """returns the roots of all non-public phases 375 """returns the roots of all non-public phases
347 376
348 The roots are not minimized, so if the secret revisions are 377 The roots are not minimized, so if the secret revisions are
349 descendants of draft revisions, their roots will still be present. 378 descendants of draft revisions, their roots will still be present.
350 """ 379 """
360 if phase != public 389 if phase != public
361 ] 390 ]
362 ) 391 )
363 392
364 def getrevset(self, repo, phases, subset=None): 393 def getrevset(self, repo, phases, subset=None):
394 # type: (localrepo.localrepository, Iterable[int], Optional[Any]) -> Any
395 # TODO: finish typing this
365 """return a smartset for the given phases""" 396 """return a smartset for the given phases"""
366 self.loadphaserevs(repo) # ensure phase's sets are loaded 397 self.loadphaserevs(repo) # ensure phase's sets are loaded
367 phases = set(phases) 398 phases = set(phases)
368 publicphase = public in phases 399 publicphase = public in phases
369 400
455 lowerroots.update(ps) 486 lowerroots.update(ps)
456 self._phasesets[phase] = ps 487 self._phasesets[phase] = ps
457 self._loadedrevslen = len(cl) 488 self._loadedrevslen = len(cl)
458 489
459 def loadphaserevs(self, repo): 490 def loadphaserevs(self, repo):
491 # type: (localrepo.localrepository) -> None
460 """ensure phase information is loaded in the object""" 492 """ensure phase information is loaded in the object"""
461 if self._phasesets is None: 493 if self._phasesets is None:
462 try: 494 try:
463 res = self._getphaserevsnative(repo) 495 res = self._getphaserevsnative(repo)
464 self._loadedrevslen, self._phasesets = res 496 self._loadedrevslen, self._phasesets = res
468 def invalidate(self): 500 def invalidate(self):
469 self._loadedrevslen = 0 501 self._loadedrevslen = 0
470 self._phasesets = None 502 self._phasesets = None
471 503
472 def phase(self, repo, rev): 504 def phase(self, repo, rev):
505 # type: (localrepo.localrepository, int) -> int
473 # We need a repo argument here to be able to build _phasesets 506 # We need a repo argument here to be able to build _phasesets
474 # if necessary. The repository instance is not stored in 507 # if necessary. The repository instance is not stored in
475 # phasecache to avoid reference cycles. The changelog instance 508 # phasecache to avoid reference cycles. The changelog instance
476 # is not stored because it is a filecache() property and can 509 # is not stored because it is a filecache() property and can
477 # be replaced without us being notified. 510 # be replaced without us being notified.
650 ) 683 )
651 return True 684 return True
652 return False 685 return False
653 686
654 def filterunknown(self, repo): 687 def filterunknown(self, repo):
688 # type: (localrepo.localrepository) -> None
655 """remove unknown nodes from the phase boundary 689 """remove unknown nodes from the phase boundary
656 690
657 Nothing is lost as unknown nodes only hold data for their descendants. 691 Nothing is lost as unknown nodes only hold data for their descendants.
658 """ 692 """
659 filtered = False 693 filtered = False
727 phcache.registernew(repo, tr, targetphase, revs) 761 phcache.registernew(repo, tr, targetphase, revs)
728 repo._phasecache.replace(phcache) 762 repo._phasecache.replace(phcache)
729 763
730 764
731 def listphases(repo): 765 def listphases(repo):
766 # type: (localrepo.localrepository) -> Dict[bytes, bytes]
732 """List phases root for serialization over pushkey""" 767 """List phases root for serialization over pushkey"""
733 # Use ordered dictionary so behavior is deterministic. 768 # Use ordered dictionary so behavior is deterministic.
734 keys = util.sortdict() 769 keys = util.sortdict()
735 value = b'%i' % draft 770 value = b'%i' % draft
736 cl = repo.unfiltered().changelog 771 cl = repo.unfiltered().changelog
758 keys[b'publishing'] = b'True' 793 keys[b'publishing'] = b'True'
759 return keys 794 return keys
760 795
761 796
762 def pushphase(repo, nhex, oldphasestr, newphasestr): 797 def pushphase(repo, nhex, oldphasestr, newphasestr):
798 # type: (localrepo.localrepository, bytes, bytes, bytes) -> bool
763 """List phases root for serialization over pushkey""" 799 """List phases root for serialization over pushkey"""
764 repo = repo.unfiltered() 800 repo = repo.unfiltered()
765 with repo.lock(): 801 with repo.lock():
766 currentphase = repo[nhex].phase() 802 currentphase = repo[nhex].phase()
767 newphase = abs(int(newphasestr)) # let's avoid negative index surprise 803 newphase = abs(int(newphasestr)) # let's avoid negative index surprise
907 943
908 return pycompat.maplist(cl.node, sorted(new_heads)) 944 return pycompat.maplist(cl.node, sorted(new_heads))
909 945
910 946
911 def newcommitphase(ui): 947 def newcommitphase(ui):
948 # type: (uimod.ui) -> int
912 """helper to get the target phase of new commit 949 """helper to get the target phase of new commit
913 950
914 Handle all possible values for the phases.new-commit options. 951 Handle all possible values for the phases.new-commit options.
915 952
916 """ 953 """
922 _(b"phases.new-commit: not a valid phase name ('%s')") % v 959 _(b"phases.new-commit: not a valid phase name ('%s')") % v
923 ) 960 )
924 961
925 962
926 def hassecret(repo): 963 def hassecret(repo):
964 # type: (localrepo.localrepository) -> bool
927 """utility function that check if a repo have any secret changeset.""" 965 """utility function that check if a repo have any secret changeset."""
928 return bool(repo._phasecache.phaseroots[secret]) 966 return bool(repo._phasecache.phaseroots[secret])
929 967
930 968
931 def preparehookargs(node, old, new): 969 def preparehookargs(node, old, new):
970 # type: (bytes, Optional[int], Optional[int]) -> Dict[bytes, bytes]
932 if old is None: 971 if old is None:
933 old = b'' 972 old = b''
934 else: 973 else:
935 old = phasenames[old] 974 old = phasenames[old]
936 return {b'node': node, b'oldphase': old, b'phase': phasenames[new]} 975 return {b'node': node, b'oldphase': old, b'phase': phasenames[new]}