comparison mercurial/vfs.py @ 51887:ad83e4f9b40e

typing: correct pytype mistakes in `mercurial/vfs.py` With the previous changes in this series (prior to merging the *.pyi file), this wasn't too bad- the only definitively wrong things were the `data` argument to `writelines()`, and the return type on `backgroundclosing()` (both of these errors were dropped in the previous commit; for some reason pytype doesn't like `contextlib._GeneratorContextManager`, even though that's what it determined it is): File "/mnt/c/Users/Matt/hg/mercurial/vfs.py", line 411, in abstractvfs: Bad return type 'contextlib._GeneratorContextManager' for generator function abstractvfs.backgroundclosing [bad-yield-annotation] Expected Generator, Iterable or Iterator PyCharm thinks this is `Generator[backgroundfilecloser], Any, None]`, which can be reduced to `Iterator[backgroundfilecloser]`, but pytype flagged the line that calls `yield` without an argument unless it's also `Optional`. PyCharm is happy either way. For some reason, `Iterable` didn't work for pytype: File "/mnt/c/Users/Matt/hg/mercurial/vfs.py", line 390, in abstractvfs: Function contextlib.contextmanager was called with the wrong arguments [wrong-arg-types] Expected: (func: Callable[[Any], Iterator]) Actually passed: (func: Callable[[Any, Any, Any], Iterable[Optional[Any]]]) Attributes of protocol Iterator[_T_co] are not implemented on Iterable[Optional[Any]]: __next__
author Matt Harbison <matt_harbison@yahoo.com>
date Fri, 20 Sep 2024 16:36:28 -0400
parents 38720073aa34
children fa9e8a6521c1
comparison
equal deleted inserted replaced
51886:38720073aa34 51887:ad83e4f9b40e
124 ) -> int: 124 ) -> int:
125 with self(path, b'wb', backgroundclose=backgroundclose, **kwargs) as fp: 125 with self(path, b'wb', backgroundclose=backgroundclose, **kwargs) as fp:
126 return fp.write(data) 126 return fp.write(data)
127 127
128 def writelines( 128 def writelines(
129 self, path: bytes, data: bytes, mode: bytes = b'wb', notindexed=False 129 self,
130 path: bytes,
131 data: Iterable[bytes],
132 mode: bytes = b'wb',
133 notindexed=False,
130 ) -> None: 134 ) -> None:
131 with self(path, mode=mode, notindexed=notindexed) as fp: 135 with self(path, mode=mode, notindexed=notindexed) as fp:
132 return fp.writelines(data) 136 return fp.writelines(data)
133 137
134 def append(self, path: bytes, data: bytes) -> int: 138 def append(self, path: bytes, data: bytes) -> int:
360 ) 364 )
361 365
362 def utime(self, path: Optional[bytes] = None, t=None) -> None: 366 def utime(self, path: Optional[bytes] = None, t=None) -> None:
363 return os.utime(self.join(path), t) 367 return os.utime(self.join(path), t)
364 368
365 def walk(self, path: Optional[bytes] = None, onerror=None): 369 def walk(
370 self, path: Optional[bytes] = None, onerror=None
371 ) -> Iterator[Tuple[bytes, List[bytes], List[bytes]]]:
366 """Yield (dirpath, dirs, files) tuple for each directory under path 372 """Yield (dirpath, dirs, files) tuple for each directory under path
367 373
368 ``dirpath`` is relative one from the root of this vfs. This 374 ``dirpath`` is relative one from the root of this vfs. This
369 uses ``os.sep`` as path separator, even you specify POSIX 375 uses ``os.sep`` as path separator, even you specify POSIX
370 style ``path``. 376 style ``path``.
377 prefixlen = len(pathutil.normasprefix(root)) 383 prefixlen = len(pathutil.normasprefix(root))
378 for dirpath, dirs, files in os.walk(self.join(path), onerror=onerror): 384 for dirpath, dirs, files in os.walk(self.join(path), onerror=onerror):
379 yield (dirpath[prefixlen:], dirs, files) 385 yield (dirpath[prefixlen:], dirs, files)
380 386
381 @contextlib.contextmanager 387 @contextlib.contextmanager
382 def backgroundclosing(self, ui, expectedcount=-1): 388 def backgroundclosing(
389 self, ui, expectedcount=-1
390 ) -> Iterator[Optional[backgroundfilecloser]]:
383 """Allow files to be closed asynchronously. 391 """Allow files to be closed asynchronously.
384 392
385 When this context manager is active, ``backgroundclose`` can be passed 393 When this context manager is active, ``backgroundclose`` can be passed
386 to ``__call__``/``open`` to result in the file possibly being closed 394 to ``__call__``/``open`` to result in the file possibly being closed
387 asynchronously, on a background thread. 395 asynchronously, on a background thread.
721 def __enter__(self: _Tclosewrapbase) -> _Tclosewrapbase: 729 def __enter__(self: _Tclosewrapbase) -> _Tclosewrapbase:
722 self._origfh.__enter__() 730 self._origfh.__enter__()
723 return self 731 return self
724 732
725 @abc.abstractmethod 733 @abc.abstractmethod
726 def __exit__(self, exc_type, exc_value, exc_tb): 734 def __exit__(self, exc_type, exc_value, exc_tb) -> None:
727 ... 735 ...
728 736
729 @abc.abstractmethod 737 @abc.abstractmethod
730 def close(self): 738 def close(self) -> None:
731 ... 739 ...
732 740
733 741
734 class delayclosedfile(closewrapbase): 742 class delayclosedfile(closewrapbase):
735 """Proxy for a file object whose close is delayed. 743 """Proxy for a file object whose close is delayed.
739 747
740 def __init__(self, fh, closer) -> None: 748 def __init__(self, fh, closer) -> None:
741 super(delayclosedfile, self).__init__(fh) 749 super(delayclosedfile, self).__init__(fh)
742 object.__setattr__(self, '_closer', closer) 750 object.__setattr__(self, '_closer', closer)
743 751
744 def __exit__(self, exc_type, exc_value, exc_tb): 752 def __exit__(self, exc_type, exc_value, exc_tb) -> None:
745 self._closer.close(self._origfh) 753 self._closer.close(self._origfh)
746 754
747 def close(self) -> None: 755 def close(self) -> None:
748 self._closer.close(self._origfh) 756 self._closer.close(self._origfh)
749 757
791 799
792 def __enter__(self: _Tbackgroundfilecloser) -> _Tbackgroundfilecloser: 800 def __enter__(self: _Tbackgroundfilecloser) -> _Tbackgroundfilecloser:
793 self._entered = True 801 self._entered = True
794 return self 802 return self
795 803
796 def __exit__(self, exc_type, exc_value, exc_tb) -> None: # TODO 804 def __exit__(self, exc_type, exc_value, exc_tb) -> None:
797 self._running = False 805 self._running = False
798 806
799 # Wait for threads to finish closing so open files don't linger for 807 # Wait for threads to finish closing so open files don't linger for
800 # longer than lifetime of context manager. 808 # longer than lifetime of context manager.
801 for t in self._threads: 809 for t in self._threads:
858 def _checkambig(self) -> None: 866 def _checkambig(self) -> None:
859 oldstat = self._oldstat 867 oldstat = self._oldstat
860 if oldstat.stat: 868 if oldstat.stat:
861 _avoidambig(self._origfh.name, oldstat) 869 _avoidambig(self._origfh.name, oldstat)
862 870
863 def __exit__(self, exc_type, exc_value, exc_tb) -> None: # TODO 871 def __exit__(self, exc_type, exc_value, exc_tb) -> None:
864 self._origfh.__exit__(exc_type, exc_value, exc_tb) 872 self._origfh.__exit__(exc_type, exc_value, exc_tb)
865 self._checkambig() 873 self._checkambig()
866 874
867 def close(self) -> None: 875 def close(self) -> None:
868 self._origfh.close() 876 self._origfh.close()