# Date 1679620732 14400
# Thu Mar 23 21:18:52 2023 -0400
# Branch pytype
# Node ID 15860a12ca4dea203790d2ae533ca050dea16df9
# Parent 3683c8a297580398f24016c2bd4a77a1b6e4357a
# EXP-Topic pytype_stubs
typing: factor `pycompat.maplist(hglib.tounicode, ...)` into a typesafe utility
This allows running pytype after hg 0ab92dabea6e, which added typehints to
`pycompat.maplist()` to ensure the item sequence is compatible with the
conversion function. Whether it's a bug in the typehint spec, or in the
implementation of both pytype and PyCharm, both combine the overloads of
`hglib.tounicode()` to express it as `(Optional[bytes | str]) -> Optional[str]`.
So even when the argument at the call site is known to *not* contain None, they
both think the result is a `List[Optional[str]]`, and that breaks places that
aren't declared with `Optional`. I also fiddled with `Literal[]` + py3.8, but
that didn't seem to help either.
I opted to add the utility function and cast inside instead of casting at all of
the call sites because casting simply disables the checking. That means future
refactoring could change the type of the argument, and it wouldn't be caught.
With this, the precondition can still be tested.
diff --git a/tortoisehg/hgqt/bookmark.py b/tortoisehg/hgqt/bookmark.py
--- a/tortoisehg/hgqt/bookmark.py
+++ b/tortoisehg/hgqt/bookmark.py
@@ -9,6 +9,10 @@
import re
+from typing import (
+ List,
+)
+
from .qtcore import (
QPoint,
Qt,
@@ -138,8 +142,8 @@
def repo(self):
return self._repoagent.rawRepo()
- def _allBookmarks(self):
- return pycompat.maplist(hglib.tounicode, self.repo._bookmarks)
+ def _allBookmarks(self) -> List[str]:
+ return hglib.to_unicode_list(self.repo._bookmarks)
@pyqtSlot()
def refresh(self):
diff --git a/tortoisehg/hgqt/hgconfig.py b/tortoisehg/hgqt/hgconfig.py
--- a/tortoisehg/hgqt/hgconfig.py
+++ b/tortoisehg/hgqt/hgconfig.py
@@ -79,7 +79,7 @@
default = pycompat.maplist(hglib.fromunicode, default)
data = self._ui.configlist(hglib.fromunicode(section), hglib.fromunicode(name),
default=default)
- return pycompat.maplist(hglib.tounicode, data)
+ return hglib.to_unicode_list(data)
def configStringItems(self, section):
# type: (Text) -> List[Tuple[Text, Text]]
diff --git a/tortoisehg/hgqt/mq.py b/tortoisehg/hgqt/mq.py
--- a/tortoisehg/hgqt/mq.py
+++ b/tortoisehg/hgqt/mq.py
@@ -10,6 +10,10 @@
import os
import re
+from typing import (
+ List,
+)
+
from .qtcore import (
QAbstractListModel,
QByteArray,
@@ -416,7 +420,7 @@
self._repoagent = repoagent
self._repoagent.repositoryChanged.connect(self._updateCache)
self._series = []
- self._seriesguards = []
+ self._seriesguards: List[List[bytes]] = []
self._statusmap = {} # patch: applied/guarded/unguarded
self._buildCache()
@@ -508,11 +512,10 @@
return ''
return hglib.tounicode(self._series[index.row()])
- def patchGuards(self, index):
+ def patchGuards(self, index: QModelIndex) -> List[str]:
if not index.isValid():
return []
- return pycompat.maplist(hglib.tounicode,
- self._seriesguards[index.row()])
+ return hglib.to_unicode_list(self._seriesguards[index.row()])
def isApplied(self, index):
if not index.isValid():
diff --git a/tortoisehg/hgqt/qfold.py b/tortoisehg/hgqt/qfold.py
--- a/tortoisehg/hgqt/qfold.py
+++ b/tortoisehg/hgqt/qfold.py
@@ -6,7 +6,9 @@
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
-
+from typing import (
+ List,
+)
from .qtcore import (
QSettings,
@@ -151,8 +153,8 @@
return {'keep': self.keepchk.isChecked(),
'message': self.msgte.text()}
- def patches(self):
- return pycompat.maplist(hglib.tounicode, self.ulw.getPatchList())
+ def patches(self) -> List[str]:
+ return hglib.to_unicode_list(self.ulw.getPatchList())
def accept(self):
self._writesettings()
diff --git a/tortoisehg/hgqt/repotreemodel.py b/tortoisehg/hgqt/repotreemodel.py
--- a/tortoisehg/hgqt/repotreemodel.py
+++ b/tortoisehg/hgqt/repotreemodel.py
@@ -10,6 +10,7 @@
import os
from typing import (
+ List,
Optional,
)
@@ -378,7 +379,7 @@
return self._indexFromItem(self._activeRepoItem, column)
# TODO: rename loadSubrepos() and appendSubrepos() to scanRepo() ?
- def loadSubrepos(self, index):
+ def loadSubrepos(self, index: QModelIndex) -> List[str]:
"""Scan subrepos of the repo; returns list of invalid paths"""
item = index.internalPointer()
if (not isinstance(item, repotreeitem.RepoItem)
@@ -400,7 +401,7 @@
item._sharedpath = tmpitem._sharedpath
item._valid = tmpitem._valid
self._emitItemDataChanged(item)
- return pycompat.maplist(hglib.tounicode, invalidpaths)
+ return hglib.to_unicode_list(invalidpaths)
def updateCommonPaths(self, showShortPaths=None):
if showShortPaths is not None:
diff --git a/tortoisehg/util/hglib.py b/tortoisehg/util/hglib.py
--- a/tortoisehg/util/hglib.py
+++ b/tortoisehg/util/hglib.py
@@ -15,6 +15,13 @@
import sys
import time
+from typing import (
+ cast,
+ Iterable,
+ List,
+ Union,
+)
+
from hgext import mq as mqmod
from mercurial import (
cmdutil,
@@ -65,12 +72,9 @@
Any,
Callable,
Dict,
- Iterable,
- List,
Optional,
Text,
Tuple,
- Union,
overload,
)
from mercurial import (
@@ -136,6 +140,9 @@
pass
return s.decode(_fallbackencoding, 'replace')
+def to_unicode_list(seq: Iterable[Union[bytes, str]]) -> List[str]:
+ return cast(List[str], pycompat.maplist(tounicode, seq))
+
if TYPE_CHECKING:
@overload
def fromunicode(s, errors='strict'):