changeset 51721:ed28085827ec

typing: explicitly type some `mercurial.util` eol code to avoid @overload Unlike the previous commit, this makes a material difference in the generated stub file- the `pycompat.identity()` aliases generated an @overload like this: @overload def fromnativeeol(a: _T0) -> _T0: ... ... which might fail to detect a bad argument, like str. This drops the @overload for the 3 related methods, so there's a single definition for each. The `typelib.BinaryIO_Proxy` is used for subclassing (the same as was done in 8147abc05794), so that it is a `BinaryIO` type during type checking, but still inherits `object` at runtime. That way, we don't need to implement unused abstract methods.
author Matt Harbison <matt_harbison@yahoo.com>
date Fri, 19 Jul 2024 16:49:46 -0400
parents e618a1756b08
children 43adbe03079b
files mercurial/util.py
diffstat 1 files changed, 25 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/util.py	Fri Jul 19 16:38:53 2024 -0400
+++ b/mercurial/util.py	Fri Jul 19 16:49:46 2024 -0400
@@ -32,10 +32,13 @@
 import sys
 import time
 import traceback
+import typing
 import warnings
 
 from typing import (
     Any,
+    BinaryIO,
+    Callable,
     Iterable,
     Iterator,
     List,
@@ -55,6 +58,7 @@
     i18n,
     policy,
     pycompat,
+    typelib,
     urllibcompat,
 )
 from .utils import (
@@ -2907,20 +2911,20 @@
 )
 
 
-class transformingwriter:
+class transformingwriter(typelib.BinaryIO_Proxy):
     """Writable file wrapper to transform data by function"""
 
-    def __init__(self, fp, encode):
+    def __init__(self, fp: BinaryIO, encode: Callable[[bytes], bytes]) -> None:
         self._fp = fp
         self._encode = encode
 
-    def close(self):
+    def close(self) -> None:
         self._fp.close()
 
-    def flush(self):
+    def flush(self) -> None:
         self._fp.flush()
 
-    def write(self, data):
+    def write(self, data: bytes) -> int:
         return self._fp.write(self._encode(data))
 
 
@@ -2938,7 +2942,7 @@
     return _eolre.sub(b'\r\n', s)
 
 
-def _crlfwriter(fp):
+def _crlfwriter(fp: typelib.BinaryIO_Proxy) -> typelib.BinaryIO_Proxy:
     return transformingwriter(fp, tocrlf)
 
 
@@ -2951,6 +2955,21 @@
     fromnativeeol = pycompat.identity
     nativeeolwriter = pycompat.identity
 
+if typing.TYPE_CHECKING:
+    # Replace the various overloads that come along with aliasing other methods
+    # with the narrow definition that we care about in the type checking phase
+    # only.  This ensures that both Windows and POSIX see only the definition
+    # that is actually available.
+
+    def tonativeeol(s: bytes) -> bytes:
+        raise NotImplementedError
+
+    def fromnativeeol(s: bytes) -> bytes:
+        raise NotImplementedError
+
+    def nativeeolwriter(fp: typelib.BinaryIO_Proxy) -> typelib.BinaryIO_Proxy:
+        raise NotImplementedError
+
 
 # TODO delete since workaround variant for Python 2 no longer needed.
 def iterfile(fp):