##// END OF EJS Templates
typing: manually add type annotations to `mercurial/vfs.py`...
Matt Harbison -
r52785:fa9e8a65 default
parent child Browse files
Show More
@@ -17,9 +17,12 b' import typing'
17
17
18 from typing import (
18 from typing import (
19 Any,
19 Any,
20 BinaryIO,
21 Callable,
20 Iterable,
22 Iterable,
21 Iterator,
23 Iterator,
22 List,
24 List,
25 MutableMapping,
23 Optional,
26 Optional,
24 Tuple,
27 Tuple,
25 Type,
28 Type,
@@ -36,13 +39,18 b' from . import ('
36 )
39 )
37
40
38 if typing.TYPE_CHECKING:
41 if typing.TYPE_CHECKING:
42 from . import (
43 ui as uimod,
44 )
45
39 _Tbackgroundfilecloser = TypeVar(
46 _Tbackgroundfilecloser = TypeVar(
40 '_Tbackgroundfilecloser', bound='backgroundfilecloser'
47 '_Tbackgroundfilecloser', bound='backgroundfilecloser'
41 )
48 )
42 _Tclosewrapbase = TypeVar('_Tclosewrapbase', bound='closewrapbase')
49 _Tclosewrapbase = TypeVar('_Tclosewrapbase', bound='closewrapbase')
50 _OnErrorFn = Callable[[Exception], Optional[object]]
43
51
44
52
45 def _avoidambig(path: bytes, oldstat) -> None:
53 def _avoidambig(path: bytes, oldstat: util.filestat) -> None:
46 """Avoid file stat ambiguity forcibly
54 """Avoid file stat ambiguity forcibly
47
55
48 This function causes copying ``path`` file, if it is owned by
56 This function causes copying ``path`` file, if it is owned by
@@ -78,7 +86,7 b' class abstractvfs(abc.ABC):'
78 ...
86 ...
79
87
80 @abc.abstractmethod
88 @abc.abstractmethod
81 def _auditpath(self, path: bytes, mode: bytes) -> Any:
89 def _auditpath(self, path: bytes, mode: bytes) -> None:
82 ...
90 ...
83
91
84 @abc.abstractmethod
92 @abc.abstractmethod
@@ -93,7 +101,7 b' class abstractvfs(abc.ABC):'
93 pass
101 pass
94 return b""
102 return b""
95
103
96 def tryreadlines(self, path: bytes, mode: bytes = b'rb') -> Any:
104 def tryreadlines(self, path: bytes, mode: bytes = b'rb') -> List[bytes]:
97 '''gracefully return an empty array for missing files'''
105 '''gracefully return an empty array for missing files'''
98 try:
106 try:
99 return self.readlines(path, mode=mode)
107 return self.readlines(path, mode=mode)
@@ -115,12 +123,12 b' class abstractvfs(abc.ABC):'
115 with self(path, b'rb') as fp:
123 with self(path, b'rb') as fp:
116 return fp.read()
124 return fp.read()
117
125
118 def readlines(self, path: bytes, mode: bytes = b'rb') -> Any:
126 def readlines(self, path: bytes, mode: bytes = b'rb') -> List[bytes]:
119 with self(path, mode=mode) as fp:
127 with self(path, mode=mode) as fp:
120 return fp.readlines()
128 return fp.readlines()
121
129
122 def write(
130 def write(
123 self, path: bytes, data: bytes, backgroundclose=False, **kwargs
131 self, path: bytes, data: bytes, backgroundclose: bool = False, **kwargs
124 ) -> int:
132 ) -> int:
125 with self(path, b'wb', backgroundclose=backgroundclose, **kwargs) as fp:
133 with self(path, b'wb', backgroundclose=backgroundclose, **kwargs) as fp:
126 return fp.write(data)
134 return fp.write(data)
@@ -130,7 +138,7 b' class abstractvfs(abc.ABC):'
130 path: bytes,
138 path: bytes,
131 data: Iterable[bytes],
139 data: Iterable[bytes],
132 mode: bytes = b'wb',
140 mode: bytes = b'wb',
133 notindexed=False,
141 notindexed: bool = False,
134 ) -> None:
142 ) -> None:
135 with self(path, mode=mode, notindexed=notindexed) as fp:
143 with self(path, mode=mode, notindexed=notindexed) as fp:
136 return fp.writelines(data)
144 return fp.writelines(data)
@@ -157,7 +165,7 b' class abstractvfs(abc.ABC):'
157 def exists(self, path: Optional[bytes] = None) -> bool:
165 def exists(self, path: Optional[bytes] = None) -> bool:
158 return os.path.exists(self.join(path))
166 return os.path.exists(self.join(path))
159
167
160 def fstat(self, fp) -> os.stat_result:
168 def fstat(self, fp: BinaryIO) -> os.stat_result:
161 return util.fstat(fp)
169 return util.fstat(fp)
162
170
163 def isdir(self, path: Optional[bytes] = None) -> bool:
171 def isdir(self, path: Optional[bytes] = None) -> bool:
@@ -249,7 +257,7 b' class abstractvfs(abc.ABC):'
249 ) -> None:
257 ) -> None:
250 return util.makedirs(self.join(path), mode)
258 return util.makedirs(self.join(path), mode)
251
259
252 def makelock(self, info, path: bytes) -> None:
260 def makelock(self, info: bytes, path: bytes) -> None:
253 return util.makelock(info, self.join(path))
261 return util.makelock(info, self.join(path))
254
262
255 def mkdir(self, path: Optional[bytes] = None) -> None:
263 def mkdir(self, path: Optional[bytes] = None) -> None:
@@ -270,6 +278,12 b' class abstractvfs(abc.ABC):'
270 else:
278 else:
271 return fd, fname
279 return fd, fname
272
280
281 # TODO: This doesn't match osutil.listdir(). stat=False in pure;
282 # non-optional bool in cext. 'skip' is bool if we trust cext, or bytes
283 # going by how pure uses it. Also, cext returns a custom stat structure.
284 # from cext.osutil.pyi:
285 #
286 # path: bytes, st: bool, skip: Optional[bool]
273 def readdir(
287 def readdir(
274 self, path: Optional[bytes] = None, stat=None, skip=None
288 self, path: Optional[bytes] = None, stat=None, skip=None
275 ) -> Any:
289 ) -> Any:
@@ -278,7 +292,7 b' class abstractvfs(abc.ABC):'
278 def readlock(self, path: bytes) -> bytes:
292 def readlock(self, path: bytes) -> bytes:
279 return util.readlock(self.join(path))
293 return util.readlock(self.join(path))
280
294
281 def rename(self, src: bytes, dst: bytes, checkambig=False) -> None:
295 def rename(self, src: bytes, dst: bytes, checkambig: bool = False) -> None:
282 """Rename from src to dst
296 """Rename from src to dst
283
297
284 checkambig argument is used with util.filestat, and is useful
298 checkambig argument is used with util.filestat, and is useful
@@ -312,7 +326,10 b' class abstractvfs(abc.ABC):'
312 return os.rmdir(self.join(path))
326 return os.rmdir(self.join(path))
313
327
314 def rmtree(
328 def rmtree(
315 self, path: Optional[bytes] = None, ignore_errors=False, forcibly=False
329 self,
330 path: Optional[bytes] = None,
331 ignore_errors: bool = False,
332 forcibly: bool = False,
316 ) -> None:
333 ) -> None:
317 """Remove a directory tree recursively
334 """Remove a directory tree recursively
318
335
@@ -320,7 +337,7 b' class abstractvfs(abc.ABC):'
320 """
337 """
321 if forcibly:
338 if forcibly:
322
339
323 def onexc(function, path, excinfo):
340 def onexc(function, path: bytes, excinfo):
324 if function is not os.remove:
341 if function is not os.remove:
325 raise
342 raise
326 # read-only files cannot be unlinked under Windows
343 # read-only files cannot be unlinked under Windows
@@ -357,17 +374,23 b' class abstractvfs(abc.ABC):'
357 return util.tryunlink(self.join(path))
374 return util.tryunlink(self.join(path))
358
375
359 def unlinkpath(
376 def unlinkpath(
360 self, path: Optional[bytes] = None, ignoremissing=False, rmdir=True
377 self,
378 path: Optional[bytes] = None,
379 ignoremissing: bool = False,
380 rmdir: bool = True,
361 ) -> None:
381 ) -> None:
362 return util.unlinkpath(
382 return util.unlinkpath(
363 self.join(path), ignoremissing=ignoremissing, rmdir=rmdir
383 self.join(path), ignoremissing=ignoremissing, rmdir=rmdir
364 )
384 )
365
385
366 def utime(self, path: Optional[bytes] = None, t=None) -> None:
386 # TODO: could be Tuple[float, float] too.
387 def utime(
388 self, path: Optional[bytes] = None, t: Optional[Tuple[int, int]] = None
389 ) -> None:
367 return os.utime(self.join(path), t)
390 return os.utime(self.join(path), t)
368
391
369 def walk(
392 def walk(
370 self, path: Optional[bytes] = None, onerror=None
393 self, path: Optional[bytes] = None, onerror: Optional[_OnErrorFn] = None
371 ) -> Iterator[Tuple[bytes, List[bytes], List[bytes]]]:
394 ) -> Iterator[Tuple[bytes, List[bytes], List[bytes]]]:
372 """Yield (dirpath, dirs, files) tuple for each directory under path
395 """Yield (dirpath, dirs, files) tuple for each directory under path
373
396
@@ -386,7 +409,7 b' class abstractvfs(abc.ABC):'
386
409
387 @contextlib.contextmanager
410 @contextlib.contextmanager
388 def backgroundclosing(
411 def backgroundclosing(
389 self, ui, expectedcount=-1
412 self, ui: uimod.ui, expectedcount: int = -1
390 ) -> Iterator[Optional[backgroundfilecloser]]:
413 ) -> Iterator[Optional[backgroundfilecloser]]:
391 """Allow files to be closed asynchronously.
414 """Allow files to be closed asynchronously.
392
415
@@ -417,7 +440,7 b' class abstractvfs(abc.ABC):'
417 None # pytype: disable=attribute-error
440 None # pytype: disable=attribute-error
418 )
441 )
419
442
420 def register_file(self, path) -> None:
443 def register_file(self, path: bytes) -> None:
421 """generic hook point to lets fncache steer its stew"""
444 """generic hook point to lets fncache steer its stew"""
422
445
423
446
@@ -432,13 +455,15 b' class vfs(abstractvfs):'
432 See pathutil.pathauditor() for details.
455 See pathutil.pathauditor() for details.
433 """
456 """
434
457
458 createmode: Optional[int]
459
435 def __init__(
460 def __init__(
436 self,
461 self,
437 base: bytes,
462 base: bytes,
438 audit=True,
463 audit: bool = True,
439 cacheaudited=False,
464 cacheaudited: bool = False,
440 expandpath=False,
465 expandpath: bool = False,
441 realpath=False,
466 realpath: bool = False,
442 ) -> None:
467 ) -> None:
443 if expandpath:
468 if expandpath:
444 base = util.expandpath(base)
469 base = util.expandpath(base)
@@ -459,15 +484,15 b' class vfs(abstractvfs):'
459 return util.checklink(self.base)
484 return util.checklink(self.base)
460
485
461 @util.propertycache
486 @util.propertycache
462 def _chmod(self):
487 def _chmod(self) -> bool:
463 return util.checkexec(self.base)
488 return util.checkexec(self.base)
464
489
465 def _fixfilemode(self, name) -> None:
490 def _fixfilemode(self, name: bytes) -> None:
466 if self.createmode is None or not self._chmod:
491 if self.createmode is None or not self._chmod:
467 return
492 return
468 os.chmod(name, self.createmode & 0o666)
493 os.chmod(name, self.createmode & 0o666)
469
494
470 def _auditpath(self, path, mode) -> None:
495 def _auditpath(self, path: bytes, mode: bytes) -> None:
471 if self._audit:
496 if self._audit:
472 if os.path.isabs(path) and path.startswith(self.base):
497 if os.path.isabs(path) and path.startswith(self.base):
473 path = os.path.relpath(path, self.base)
498 path = os.path.relpath(path, self.base)
@@ -477,7 +502,9 b' class vfs(abstractvfs):'
477 self.audit(path, mode=mode)
502 self.audit(path, mode=mode)
478
503
479 def isfileorlink_checkdir(
504 def isfileorlink_checkdir(
480 self, dircache, path: Optional[bytes] = None
505 self,
506 dircache: MutableMapping[bytes, bool],
507 path: Optional[bytes] = None,
481 ) -> bool:
508 ) -> bool:
482 """return True if the path is a regular file or a symlink and
509 """return True if the path is a regular file or a symlink and
483 the directories along the path are "normal", that is
510 the directories along the path are "normal", that is
@@ -486,6 +513,8 b' class vfs(abstractvfs):'
486 Ignores the `_audit` setting, and checks the directories regardless.
513 Ignores the `_audit` setting, and checks the directories regardless.
487 `dircache` is used to cache the directory checks.
514 `dircache` is used to cache the directory checks.
488 """
515 """
516 # TODO: Should be a None check on 'path', or shouldn't default to None
517 # because of the immediate call to util.localpath().
489 try:
518 try:
490 for prefix in pathutil.finddirs_rev_noroot(util.localpath(path)):
519 for prefix in pathutil.finddirs_rev_noroot(util.localpath(path)):
491 if prefix in dircache:
520 if prefix in dircache:
@@ -505,13 +534,13 b' class vfs(abstractvfs):'
505 self,
534 self,
506 path: bytes,
535 path: bytes,
507 mode: bytes = b"rb",
536 mode: bytes = b"rb",
508 atomictemp=False,
537 atomictemp: bool = False,
509 notindexed=False,
538 notindexed: bool = False,
510 backgroundclose=False,
539 backgroundclose: bool = False,
511 checkambig=False,
540 checkambig: bool = False,
512 auditpath=True,
541 auditpath: bool = True,
513 makeparentdirs=True,
542 makeparentdirs: bool = True,
514 ) -> Any:
543 ) -> Any: # TODO: should be BinaryIO if util.atomictempfile can be coersed
515 """Open ``path`` file, which is relative to vfs root.
544 """Open ``path`` file, which is relative to vfs root.
516
545
517 By default, parent directories are created as needed. Newly created
546 By default, parent directories are created as needed. Newly created
@@ -650,14 +679,14 b' opener: Type[vfs] = vfs'
650
679
651
680
652 class proxyvfs(abstractvfs, abc.ABC):
681 class proxyvfs(abstractvfs, abc.ABC):
653 def __init__(self, vfs: "vfs"):
682 def __init__(self, vfs: vfs) -> None:
654 self.vfs = vfs
683 self.vfs = vfs
655
684
656 @property
685 @property
657 def createmode(self):
686 def createmode(self) -> Optional[int]:
658 return self.vfs.createmode
687 return self.vfs.createmode
659
688
660 def _auditpath(self, path, mode) -> None:
689 def _auditpath(self, path: bytes, mode: bytes) -> None:
661 return self.vfs._auditpath(path, mode)
690 return self.vfs._auditpath(path, mode)
662
691
663 @property
692 @property
@@ -676,10 +705,11 b' class proxyvfs(abstractvfs, abc.ABC):'
676 class filtervfs(proxyvfs, abstractvfs):
705 class filtervfs(proxyvfs, abstractvfs):
677 '''Wrapper vfs for filtering filenames with a function.'''
706 '''Wrapper vfs for filtering filenames with a function.'''
678
707
679 def __init__(self, vfs: "vfs", filter):
708 def __init__(self, vfs: vfs, filter) -> None:
680 proxyvfs.__init__(self, vfs)
709 proxyvfs.__init__(self, vfs)
681 self._filter = filter
710 self._filter = filter
682
711
712 # TODO: The return type should be BinaryIO
683 def __call__(self, path: bytes, *args, **kwargs) -> Any:
713 def __call__(self, path: bytes, *args, **kwargs) -> Any:
684 return self.vfs(self._filter(path), *args, **kwargs)
714 return self.vfs(self._filter(path), *args, **kwargs)
685
715
@@ -696,9 +726,10 b' filteropener: Type[filtervfs] = filtervf'
696 class readonlyvfs(proxyvfs):
726 class readonlyvfs(proxyvfs):
697 '''Wrapper vfs preventing any writing.'''
727 '''Wrapper vfs preventing any writing.'''
698
728
699 def __init__(self, vfs: "vfs"):
729 def __init__(self, vfs: vfs) -> None:
700 proxyvfs.__init__(self, vfs)
730 proxyvfs.__init__(self, vfs)
701
731
732 # TODO: The return type should be BinaryIO
702 def __call__(self, path: bytes, mode: bytes = b'rb', *args, **kw) -> Any:
733 def __call__(self, path: bytes, mode: bytes = b'rb', *args, **kw) -> Any:
703 if mode not in (b'r', b'rb'):
734 if mode not in (b'r', b'rb'):
704 raise error.Abort(_(b'this vfs is read only'))
735 raise error.Abort(_(b'this vfs is read only'))
@@ -717,13 +748,13 b' class closewrapbase(abc.ABC):'
717 def __init__(self, fh) -> None:
748 def __init__(self, fh) -> None:
718 object.__setattr__(self, '_origfh', fh)
749 object.__setattr__(self, '_origfh', fh)
719
750
720 def __getattr__(self, attr) -> Any:
751 def __getattr__(self, attr: str) -> Any:
721 return getattr(self._origfh, attr)
752 return getattr(self._origfh, attr)
722
753
723 def __setattr__(self, attr, value) -> None:
754 def __setattr__(self, attr: str, value: Any) -> None:
724 return setattr(self._origfh, attr, value)
755 return setattr(self._origfh, attr, value)
725
756
726 def __delattr__(self, attr) -> None:
757 def __delattr__(self, attr: str) -> None:
727 return delattr(self._origfh, attr)
758 return delattr(self._origfh, attr)
728
759
729 def __enter__(self: _Tclosewrapbase) -> _Tclosewrapbase:
760 def __enter__(self: _Tclosewrapbase) -> _Tclosewrapbase:
@@ -759,7 +790,7 b' class delayclosedfile(closewrapbase):'
759 class backgroundfilecloser:
790 class backgroundfilecloser:
760 """Coordinates background closing of file handles on multiple threads."""
791 """Coordinates background closing of file handles on multiple threads."""
761
792
762 def __init__(self, ui, expectedcount=-1) -> None:
793 def __init__(self, ui: uimod.ui, expectedcount: int = -1) -> None:
763 self._running = False
794 self._running = False
764 self._entered = False
795 self._entered = False
765 self._threads = []
796 self._threads = []
General Comments 0
You need to be logged in to leave comments. Login now