# HG changeset patch # User Matt Harbison # Date 1723934603 14400 # Node ID 673b07a1db08d647ee4f106fee69fbd398c8d390 # Parent 07086b3ad50256744fb71150ae741d7b1e00468f typing: create an @overload of `phasecache` ctor to handle the copy case In `phasecache.copy()`, it calls `self.__class__(None, None, _load=False)`, but the constuctor is typed to take a non-None repository. For the `_load=False` case, all args are ignored (and the copy function itself populates the attrs on the new object), so this isn't an error. For the default `_load=True` case, it needs a non-None repository. This is the simplest way to handle that duality. The reason this wasn't being detected is because pytype is confused by the interface decorators on the `localrepository` class, and is inferring the whole class as `Any`. (See 3e9a660b074a or c1d7ac70980b) Therefore, the type hint of `localrepo.localrepository` here was also effectively `Any`, which disabled the type checking entirely. This is the first foray into using `typing_extensions` to unlock future typing features. I think this is safe and reasonable because 1) it is only imported in the type checking phase (so no need to vendor our own copy), and 2) pytype has its own copy of `typing_extensions` bundled with it, so no need to alter the test environment. When run with a version of python that supports the symbol(s) natively, `typing_extensions` simply re-exports from `typing`, so there shouldn't be any future headaches with this. diff -r 07086b3ad502 -r 673b07a1db08 mercurial/phases.py --- a/mercurial/phases.py Sat Aug 17 17:38:35 2024 -0400 +++ b/mercurial/phases.py Sat Aug 17 18:43:23 2024 -0400 @@ -116,6 +116,7 @@ Optional, Set, Tuple, + overload, ) from .i18n import _ @@ -138,6 +139,9 @@ PhaseSets = Dict[int, Set[int]] if typing.TYPE_CHECKING: + from typing_extensions import ( + Literal, # py3.8+ + ) from . import ( localrepo, ui as uimod, @@ -375,11 +379,31 @@ class phasecache: + if typing.TYPE_CHECKING: + + @overload + def __init__( + self, + repo: Any, + phasedefaults: Any, + _load: Literal[False], + ) -> None: + pass + + @overload + def __init__( + self, + repo: "localrepo.localrepository", + phasedefaults: Optional["Phasedefaults"], + _load: bool = True, + ) -> None: + pass + def __init__( self, - repo: "localrepo.localrepository", - phasedefaults: Optional["Phasedefaults"], - _load: bool = True, + repo, + phasedefaults, + _load=True, ): if _load: # Cheap trick to allow shallow-copy without copy module