typing: create an @overload of `phasecache` ctor to handle the copy case
authorMatt Harbison <matt_harbison@yahoo.com>
Sat, 17 Aug 2024 18:43:23 -0400
changeset 51811 673b07a1db08
parent 51810 07086b3ad502
child 51812 68ec9743ef04
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.
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