hgext/git/manifest.py
changeset 52492 48cdbd4d5443
parent 52491 c855943e334b
child 52494 db6efd74cf14
--- a/hgext/git/manifest.py	Thu Oct 24 22:24:46 2024 -0400
+++ b/hgext/git/manifest.py	Wed Oct 23 15:56:48 2024 -0400
@@ -1,5 +1,13 @@
 from __future__ import annotations
 
+import typing
+
+from typing import (
+    Any,
+    Iterator,
+    Set,
+)
+
 from mercurial import (
     match as matchmod,
     pathutil,
@@ -12,6 +20,10 @@
 )
 from . import gitutil
 
+if typing.TYPE_CHECKING:
+    from typing import (
+        ByteString,  # TODO: change to Buffer for 3.14
+    )
 
 pygit2 = gitutil.get_pygit2()
 
@@ -71,16 +83,16 @@
             raise ValueError('unsupported mode %s' % oct(ent.filemode))
         return ent.id.raw, flags
 
-    def __getitem__(self, path):
+    def __getitem__(self, path: bytes) -> bytes:
         return self._resolve_entry(path)[0]
 
-    def find(self, path):
+    def find(self, path: bytes) -> tuple[bytes, bytes]:
         return self._resolve_entry(path)
 
-    def __len__(self):
+    def __len__(self) -> int:
         return len(list(self.walk(matchmod.always())))
 
-    def __nonzero__(self):
+    def __nonzero__(self) -> bool:
         try:
             next(iter(self))
             return True
@@ -89,30 +101,30 @@
 
     __bool__ = __nonzero__
 
-    def __contains__(self, path):
+    def __contains__(self, path: bytes) -> bool:
         try:
             self._resolve_entry(path)
             return True
         except KeyError:
             return False
 
-    def iterkeys(self):
+    def iterkeys(self) -> Iterator[bytes]:
         return self.walk(matchmod.always())
 
-    def keys(self):
+    def keys(self) -> list[bytes]:
         return list(self.iterkeys())
 
-    def __iter__(self):
+    def __iter__(self) -> Iterator[bytes]:
         return self.iterkeys()
 
-    def __setitem__(self, path, node):
+    def __setitem__(self, path: bytes, node: bytes) -> None:
         self._pending_changes[path] = node, self.flags(path)
 
-    def __delitem__(self, path):
+    def __delitem__(self, path: bytes) -> None:
         # TODO: should probably KeyError for already-deleted  files?
         self._pending_changes[path] = None
 
-    def filesnotin(self, other, match=None):
+    def filesnotin(self, other, match=None) -> Set[bytes]:
         if match is not None:
             match = matchmod.badmatch(match, lambda path, msg: None)
             sm2 = set(other.walk(match))
@@ -123,10 +135,18 @@
     def _dirs(self):
         return pathutil.dirs(self)
 
-    def hasdir(self, dir):
+    def hasdir(self, dir: bytes) -> bool:
         return dir in self._dirs
 
-    def diff(self, other, match=lambda x: True, clean=False):
+    def diff(
+        self,
+        other: Any,  # TODO: 'manifestdict' or (better) equivalent interface
+        match: Any = lambda x: True,  # TODO: Optional[matchmod.basematcher] = None,
+        clean: bool = False,
+    ) -> dict[
+        bytes,
+        tuple[tuple[bytes | None, bytes], tuple[bytes | None, bytes]] | None,
+    ]:
         """Finds changes between the current manifest and m2.
 
         The result is returned as a dict with filename as key and
@@ -200,42 +220,43 @@
 
         return result
 
-    def setflag(self, path, flag):
+    def setflag(self, path: bytes, flag: bytes) -> None:
         node, unused_flag = self._resolve_entry(path)
         self._pending_changes[path] = node, flag
 
-    def get(self, path, default=None):
+    def get(self, path: bytes, default=None) -> bytes | None:
         try:
             return self._resolve_entry(path)[0]
         except KeyError:
             return default
 
-    def flags(self, path):
+    def flags(self, path: bytes) -> bytes:
         try:
             return self._resolve_entry(path)[1]
         except KeyError:
             return b''
 
-    def copy(self):
+    def copy(self) -> 'gittreemanifest':
         return gittreemanifest(
             self._git_repo, self._tree, dict(self._pending_changes)
         )
 
-    def items(self):
+    def items(self) -> Iterator[tuple[bytes, bytes]]:
         for f in self:
             # TODO: build a proper iterator version of this
             yield f, self[f]
 
-    def iteritems(self):
+    def iteritems(self) -> Iterator[tuple[bytes, bytes]]:
         return self.items()
 
-    def iterentries(self):
+    def iterentries(self) -> Iterator[tuple[bytes, bytes, bytes]]:
         for f in self:
             # TODO: build a proper iterator version of this
             yield f, *self._resolve_entry(f)
 
-    def text(self):
-        assert False  # TODO can this method move out of the manifest iface?
+    def text(self) -> ByteString:
+        # TODO can this method move out of the manifest iface?
+        raise NotImplementedError
 
     def _walkonetree(self, tree, match, subdir):
         for te in tree:
@@ -249,7 +270,7 @@
             elif match(realname):
                 yield pycompat.fsencode(realname)
 
-    def walk(self, match):
+    def walk(self, match: matchmod.basematcher) -> Iterator[bytes]:
         # TODO: this is a very lazy way to merge in the pending
         # changes. There is absolutely room for optimization here by
         # being clever about walking over the sets...