# HG changeset patch # User Matt Harbison # Date 1669419587 18000 # Node ID 8147abc05794d862e2776d4992e0f6a2abc0c6d3 # Parent e1953a34c1109ddaf80e32266d79cec8791fd804 pytype: stop excluding mercurial/ui.py ui.extractchoices() is perhaps making assumptions that it shouldn't about the pattern always matching, but presumably we have test coverage for that. PyCharm flags the updated classes with a warning "Class xxx must implement all abstract methods", and suggests adding `abc.ABC` to the superclasses. I'm not sure why, unless it doesn't recognize the `__getattr__()` delegation pattern. Additionally, we can't unconditionally subclass `typing.BinaryIO` because that defeats the `__getattr__` delegation to the wrapped object at runtime. Instead, it has to only subclass during the type checking phase[1]. In any event, this fixes: File "/mnt/c/Users/Matt/hg/mercurial/ui.py", line 1518, in _runpager: Function subprocess.Popen.__new__ was called with the wrong arguments [wrong-arg-types] Expected: (cls, args, bufsize, executable, stdin, stdout: Optional[Union[IO, int]] = ..., ...) Actually passed: (cls, args, bufsize, stdin, stdout: Union[mercurial.utils.procutil.WriteAllWrapper, mercurial.windows.winstdout], ...) File "/mnt/c/Users/Matt/hg/mercurial/ui.py", line 1798, in extractchoices: No attribute 'group' on None [attribute-error] In Optional[Match[bytes]] File "/mnt/c/Users/Matt/hg/mercurial/ui.py", line 1799, in extractchoices: No attribute 'group' on None [attribute-error] In Optional[Match[bytes]] [1] https://stackoverflow.com/q/71365594 diff -r e1953a34c110 -r 8147abc05794 contrib/check-pytype.sh --- a/contrib/check-pytype.sh Wed Dec 07 20:12:23 2022 +0100 +++ b/contrib/check-pytype.sh Fri Nov 25 18:39:47 2022 -0500 @@ -31,7 +31,6 @@ # mercurial/pure/parsers.py # [attribute-error] # mercurial/repoview.py # [attribute-error] # mercurial/testing/storage.py # tons of [attribute-error] -# mercurial/ui.py # [attribute-error], [wrong-arg-types] # mercurial/unionrepo.py # ui, svfs, unfiltered [attribute-error] # mercurial/win32.py # [not-callable] # mercurial/wireprotoframing.py # [unsupported-operands], [attribute-error], [import-error] @@ -64,7 +63,6 @@ -x mercurial/repoview.py \ -x mercurial/testing/storage.py \ -x mercurial/thirdparty \ - -x mercurial/ui.py \ -x mercurial/unionrepo.py \ -x mercurial/win32.py \ -x mercurial/wireprotoframing.py \ diff -r e1953a34c110 -r 8147abc05794 mercurial/typelib.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/typelib.py Fri Nov 25 18:39:47 2022 -0500 @@ -0,0 +1,28 @@ +# typelib.py - type hint aliases and support +# +# Copyright 2022 Matt Harbison +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +import typing + +# Note: this is slightly different from pycompat.TYPE_CHECKING, as using +# pycompat causes the BinaryIO_Proxy type to be resolved to ``object`` when +# used as the base class during a pytype run. +TYPE_CHECKING = typing.TYPE_CHECKING + + +# The BinaryIO class provides empty methods, which at runtime means that +# ``__getattr__`` on the proxy classes won't get called for the methods that +# should delegate to the internal object. So to avoid runtime changes because +# of the required typing inheritance, just use BinaryIO when typechecking, and +# ``object`` otherwise. +if TYPE_CHECKING: + from typing import ( + BinaryIO, + ) + + BinaryIO_Proxy = BinaryIO +else: + BinaryIO_Proxy = object diff -r e1953a34c110 -r 8147abc05794 mercurial/ui.py --- a/mercurial/ui.py Wed Dec 07 20:12:23 2022 +0100 +++ b/mercurial/ui.py Fri Nov 25 18:39:47 2022 -0500 @@ -1795,6 +1795,9 @@ # choices containing spaces, ASCII, or basically anything # except an ampersand followed by a character. m = re.match(br'(?s)(.+?)\$\$([^$]*&[^ $].*)', prompt) + + assert m is not None # help pytype + msg = m.group(1) choices = [p.strip(b' ') for p in m.group(2).split(b'$$')] diff -r e1953a34c110 -r 8147abc05794 mercurial/utils/procutil.py --- a/mercurial/utils/procutil.py Wed Dec 07 20:12:23 2022 +0100 +++ b/mercurial/utils/procutil.py Fri Nov 25 18:39:47 2022 -0500 @@ -18,6 +18,10 @@ import threading import time +from typing import ( + BinaryIO, +) + from ..i18n import _ from ..pycompat import ( getattr, @@ -29,6 +33,7 @@ error, policy, pycompat, + typelib, ) # Import like this to keep import-checker happy @@ -118,8 +123,8 @@ return stream -class WriteAllWrapper: - def __init__(self, orig): +class WriteAllWrapper(typelib.BinaryIO_Proxy): + def __init__(self, orig: BinaryIO): self.orig = orig def __getattr__(self, attr): diff -r e1953a34c110 -r 8147abc05794 mercurial/windows.py --- a/mercurial/windows.py Wed Dec 07 20:12:23 2022 +0100 +++ b/mercurial/windows.py Fri Nov 25 18:39:47 2022 -0500 @@ -16,6 +16,10 @@ import sys import winreg # pytype: disable=import-error +from typing import ( + BinaryIO, +) + from .i18n import _ from .pycompat import getattr from . import ( @@ -23,6 +27,7 @@ error, policy, pycompat, + typelib, win32, ) @@ -208,7 +213,7 @@ return encoding.unitolocal(pw) -class winstdout: +class winstdout(typelib.BinaryIO_Proxy): """Some files on Windows misbehave. When writing to a broken pipe, EINVAL instead of EPIPE may be raised. @@ -217,7 +222,7 @@ error may happen. Python 3 already works around that. """ - def __init__(self, fp): + def __init__(self, fp: BinaryIO): self.fp = fp def __getattr__(self, key):