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.
--- 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