##// END OF EJS Templates
branchmap-v3: introduce a "stop_rev" argument to `headsrevs`...
branchmap-v3: introduce a "stop_rev" argument to `headsrevs` The `headsrevs` method of the revlog already have a `revs` argument to compute the headrevs of a limited set of heads. However, it disable the use of the native compiled code to compute the heads, which slows down the branchmap v3 code a lot. The branchmap v3 usage is actually quite constrained as we will always only ignores a part at the top of the graph. So we could be significantly faster. We start by making small change to the python side to improve the situation and introduce the new API. More collaboration with the native code are coming later. This massively speedup operation and close most of the remaining gaps between branchmap-v3 and branchmap-v2. especially on repository with many revs like mozilla-try. A small overhead remains mostly because the `headrevs` logic currently has some inefficiently. We will look into them from there. ### benchmark.name = hg.command.unbundle # bin-env-vars.hg.py-re2-module = default # benchmark.variants.issue6528 = disabled # benchmark.variants.resource-usage = default # benchmark.variants.reuse-external-delta-parent = yes # benchmark.variants.revs = any-1-extra-rev # benchmark.variants.source = unbundle # benchmark.variants.validate = default # benchmark.variants.verbosity = quiet ## data-env-vars.name = netbeans-2018-08-01-zstd-sparse-revlog # bin-env-vars.hg.flavor = default branch-v2: 0.233711 ~~~~~ branch-v3 before: 0.368769 (+57.79%, +0.14) branch-v3 after: 0.239857 (+2.63%, +0.01) # bin-env-vars.hg.flavor = rust branch-v2: 0.235230 ~~~~~ branch-v3 before: 0.372460 (+58.34%, +0.14) branch-v3 after: 0.240972 (+2.44%, +0.01) ## data-env-vars.name = netbeans-2018-08-01-ds2-pnm # bin-env-vars.hg.flavor = rust branch-v2: 0.255586 ~~~~~ branch-v3 before: 0.318907 (+24.78%, +0.06) branch-v3 after: 0.268560 (+5.08%, +0.01) ## data-env-vars.name = mozilla-central-2024-03-22-zstd-sparse-revlog # bin-env-vars.hg.flavor = default branch-v2: 0.339010 ~~~~~ branch-v3 before: 0.349752 (+3.17%, +0.01) branch-v3 after: 0.349389 (+3.06%, +0.01) # bin-env-vars.hg.flavor = rust branch-v2: 0.346525 ~~~~~ branch-v3 before: 0.354300 (+2.24%, +0.01) branch-v3 after: 0.355661 (+2.64%, +0.01) ## data-env-vars.name = mozilla-central-2024-03-22-ds2-pnm # bin-env-vars.hg.flavor = rust branch-v2: 0.380202 ~~~~~ branch-v3 before: 0.396293 (+4.23%, +0.02) branch-v3 after: 0.408851 (+7.54%, +0.03) ## data-env-vars.name = mozilla-unified-2024-03-22-zstd-sparse-revlog # bin-env-vars.hg.flavor = default branch-v2: 0.412165 ~~~~~ branch-v3 before: 0.424769 (+3.06%, +0.01) branch-v3 after: 0.427782 (+3.79%, +0.02) # bin-env-vars.hg.flavor = rust branch-v2: 0.412397 ~~~~~ branch-v3 before: 0.421796 (+2.28%, +0.01) branch-v3 after: 0.422354 (+2.41%, +0.01) ## data-env-vars.name = mozilla-unified-2024-03-22-ds2-pnm # bin-env-vars.hg.flavor = rust branch-v2: 0.429501 ~~~~~ branch-v3 before: 0.443849 (+3.34%, +0.01) branch-v3 after: 0.443197 (+3.19%, +0.01) ## data-env-vars.name = mozilla-try-2024-03-26-zstd-sparse-revlog # bin-env-vars.hg.flavor = default branch-v2: 3.403171 ~~~~~ branch-v3 before: 6.234055 (+83.18%, +2.83) branch-v3 after: 3.819477 (+12.23%, +0.42) # bin-env-vars.hg.flavor = rust branch-v2: 3.454876 ~~~~~ branch-v3 before: 6.307813 (+82.58%, +2.85) branch-v3 after: 3.590284 (+3.92%, +0.14) ## data-env-vars.name = mozilla-try-2024-03-26-ds2-pnm # bin-env-vars.hg.flavor = rust branch-v2: 3.465435 ~~~~~ branch-v3 before: 5.176076 (+49.36%, +1.71) branch-v3 after: 3.633278 (+4.84%, +0.17)

File last commit:

r52787:992fcf6b default
r52870:42a116f1 default
Show More
vfs.py
913 lines | 30.7 KiB | text/x-python | PythonLexer
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 # vfs.py - Mercurial 'vfs' classes
#
Raphaël Gomès
contributor: change mentions of mpm to olivia...
r47575 # Copyright Olivia Mackall <olivia@selenic.com>
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 #
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
Matt Harbison
typing: add `from __future__ import annotations` to most files...
r52756 from __future__ import annotations
Matt Harbison
vfs: use @abstractmethod instead of homebrewing abstract methods...
r52777 import abc
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 import contextlib
import os
import shutil
import stat
import threading
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 import typing
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 from typing import (
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 Any,
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 BinaryIO,
Callable,
Matt Harbison
typing: add a handful more annotations to `mercurial/vfs.py`...
r52787 Dict,
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 Iterable,
Iterator,
List,
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 MutableMapping,
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 Optional,
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 Tuple,
Type,
TypeVar,
Matt Harbison
typing: add a handful more annotations to `mercurial/vfs.py`...
r52787 Union,
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 )
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 from .i18n import _
from . import (
Augie Fackler
python3: wrap all uses of <exception>.strerror with strtolocal...
r34024 encoding,
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 error,
pathutil,
pycompat,
util,
)
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 if typing.TYPE_CHECKING:
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 from . import (
ui as uimod,
)
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 _Tbackgroundfilecloser = TypeVar(
'_Tbackgroundfilecloser', bound='backgroundfilecloser'
)
_Tclosewrapbase = TypeVar('_Tclosewrapbase', bound='closewrapbase')
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 _OnErrorFn = Callable[[Exception], Optional[object]]
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 def _avoidambig(path: bytes, oldstat: util.filestat) -> None:
FUJIWARA Katsunori
vfs: copy if EPERM to avoid file stat ambiguity forcibly at closing...
r33280 """Avoid file stat ambiguity forcibly
This function causes copying ``path`` file, if it is owned by
another (see issue5418 and issue5584 for detail).
"""
Augie Fackler
formatting: blacken the codebase...
r43346
FUJIWARA Katsunori
vfs: copy if EPERM to avoid file stat ambiguity forcibly at closing...
r33280 def checkandavoid():
newstat = util.filestat.frompath(path)
# return whether file stat ambiguity is (already) avoided
Augie Fackler
formatting: blacken the codebase...
r43346 return not newstat.isambig(oldstat) or newstat.avoidambig(path, oldstat)
FUJIWARA Katsunori
vfs: copy if EPERM to avoid file stat ambiguity forcibly at closing...
r33280 if not checkandavoid():
# simply copy to change owner of path to get privilege to
# advance mtime (see issue5418)
util.rename(util.mktempcopy(path), path)
checkandavoid()
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Harbison
vfs: use @abstractmethod instead of homebrewing abstract methods...
r52777 class abstractvfs(abc.ABC):
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 """Abstract base class; cannot be instantiated"""
vfs: always use / as file separator (issue6546)...
r48617 # default directory separator for vfs
#
# Other vfs code always use `/` and this works fine because python file API
# abstract the use of `/` and make it work transparently. For consistency
Matt Harbison
vfs: do minor copyediting on comments and doc strings...
r52779 # vfs will always use `/` when joining. This avoids some confusion in
vfs: always use / as file separator (issue6546)...
r48617 # encoded vfs (see issue6546)
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 _dir_sep: bytes = b'/'
vfs: always use / as file separator (issue6546)...
r48617
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 # TODO: type return, which is util.posixfile wrapped by a proxy
Matt Harbison
vfs: use @abstractmethod instead of homebrewing abstract methods...
r52777 @abc.abstractmethod
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 def __call__(self, path: bytes, mode: bytes = b'rb', **kwargs) -> Any:
Matt Harbison
vfs: use @abstractmethod instead of homebrewing abstract methods...
r52777 ...
Augie Fackler
vfs: add a NotImplementedError implementation of __call__...
r43768
Matt Harbison
vfs: use @abstractmethod instead of homebrewing abstract methods...
r52777 @abc.abstractmethod
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 def _auditpath(self, path: bytes, mode: bytes) -> None:
Matt Harbison
vfs: use @abstractmethod instead of homebrewing abstract methods...
r52777 ...
Boris Feld
vfs: add a `_auditpath` to abstract vfs...
r41123
Matt Harbison
vfs: use @abstractmethod instead of homebrewing abstract methods...
r52777 @abc.abstractmethod
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 def join(self, path: Optional[bytes], *insidef: bytes) -> bytes:
Matt Harbison
vfs: use @abstractmethod instead of homebrewing abstract methods...
r52777 ...
Augie Fackler
vfs: add NotImplementedError version of join...
r43769
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 def tryread(self, path: bytes) -> bytes:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 '''gracefully return an empty string for missing files'''
try:
return self.read(path)
Manuel Jacob
py3: catch FileNotFoundError instead of checking errno == ENOENT
r50201 except FileNotFoundError:
pass
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return b""
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 def tryreadlines(self, path: bytes, mode: bytes = b'rb') -> List[bytes]:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 '''gracefully return an empty array for missing files'''
try:
return self.readlines(path, mode=mode)
Manuel Jacob
py3: catch FileNotFoundError instead of checking errno == ENOENT
r50201 except FileNotFoundError:
pass
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return []
@util.propertycache
def open(self):
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """Open ``path`` file, which is relative to vfs root.
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
Newly created directories are marked as "not to be indexed by
the content indexing service", if ``notindexed`` is specified
for "write" mode access.
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return self.__call__
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 def read(self, path: bytes) -> bytes:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 with self(path, b'rb') as fp:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return fp.read()
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 def readlines(self, path: bytes, mode: bytes = b'rb') -> List[bytes]:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 with self(path, mode=mode) as fp:
return fp.readlines()
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 def write(
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 self, path: bytes, data: bytes, backgroundclose: bool = False, **kwargs
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 ) -> int:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 with self(path, b'wb', backgroundclose=backgroundclose, **kwargs) as fp:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return fp.write(data)
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 def writelines(
Matt Harbison
typing: correct pytype mistakes in `mercurial/vfs.py`...
r52784 self,
path: bytes,
data: Iterable[bytes],
mode: bytes = b'wb',
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 notindexed: bool = False,
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 ) -> None:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 with self(path, mode=mode, notindexed=notindexed) as fp:
return fp.writelines(data)
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 def append(self, path: bytes, data: bytes) -> int:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 with self(path, b'ab') as fp:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return fp.write(data)
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 def basename(self, path: bytes) -> bytes:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 """return base element of a path (as os.path.basename would do)
This exists to allow handling of strange encoding if needed."""
return os.path.basename(path)
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 def chmod(self, path: bytes, mode: int) -> None:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return os.chmod(self.join(path), mode)
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 def dirname(self, path: bytes) -> bytes:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 """return dirname element of a path (as os.path.dirname would do)
This exists to allow handling of strange encoding if needed."""
return os.path.dirname(path)
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 def exists(self, path: Optional[bytes] = None) -> bool:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return os.path.exists(self.join(path))
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 def fstat(self, fp: BinaryIO) -> os.stat_result:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return util.fstat(fp)
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 def isdir(self, path: Optional[bytes] = None) -> bool:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return os.path.isdir(self.join(path))
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 def isfile(self, path: Optional[bytes] = None) -> bool:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return os.path.isfile(self.join(path))
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 def islink(self, path: Optional[bytes] = None) -> bool:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return os.path.islink(self.join(path))
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 def isfileorlink(self, path: Optional[bytes] = None) -> bool:
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """return whether path is a regular file or a symlink
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 Unlike isfile, this doesn't follow symlinks."""
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 try:
st = self.lstat(path)
except OSError:
return False
mode = st.st_mode
return stat.S_ISREG(mode) or stat.S_ISLNK(mode)
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 def _join(self, *paths: bytes) -> bytes:
vfs: always use / as file separator (issue6546)...
r48617 root_idx = 0
for idx, p in enumerate(paths):
if os.path.isabs(p) or p.startswith(self._dir_sep):
root_idx = idx
if root_idx != 0:
paths = paths[root_idx:]
paths = [p for p in paths if p]
return self._dir_sep.join(paths)
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 def reljoin(self, *paths: bytes) -> bytes:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 """join various elements of a path together (as os.path.join would do)
The vfs base is not injected so that path stay relative. This exists
to allow handling of strange encoding if needed."""
vfs: always use / as file separator (issue6546)...
r48617 return self._join(*paths)
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 def split(self, path: bytes) -> Tuple[bytes, bytes]:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 """split top-most element of a path (as os.path.split would do)
This exists to allow handling of strange encoding if needed."""
return os.path.split(path)
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 def lexists(self, path: Optional[bytes] = None) -> bool:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return os.path.lexists(self.join(path))
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 def lstat(self, path: Optional[bytes] = None) -> os.stat_result:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return os.lstat(self.join(path))
mmap: add a `is_mmap_safe` method to vfs...
r52545 def is_mmap_safe(self, path: Optional[bytes] = None) -> bool:
"""return True if it is safe to read a file content as mmap
This focus on the file system aspect of such safety, the application
logic around that file is not taken into account, so caller need to
make sure the file won't be truncated in a way that will create SIGBUS
on access.
The initial motivation for this logic is that if mmap is used on NFS
and somebody deletes the mapped file (e.g. by renaming on top of it),
then you get SIGBUS, which can be pretty disruptive: we get core dump
reports, and the process terminates without writing to the blackbox.
Matt Harbison
vfs: do minor copyediting on comments and doc strings...
r52779 Instead, in this situation we prefer to read the file normally.
mmap: add a `is_mmap_safe` method to vfs...
r52545 The risk of ESTALE in the middle of the read remains, but it's
smaller because we read sooner and the error should be reported
just as any other error.
Note that python standard library does not offer the necessary function
to detect the file stem bits. So this detection rely on compiled bits
and is not available in pure python.
"""
# XXX Since we already assume a vfs to address a consistent file system
# in other location, we could determine the fstype once for the root
# and cache that value.
fstype = util.getfstype(self.join(path))
return fstype is not None and fstype != b'nfs'
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 def listdir(self, path: Optional[bytes] = None) -> List[bytes]:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return os.listdir(self.join(path))
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 def makedir(self, path: Optional[bytes] = None, notindexed=True) -> None:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return util.makedir(self.join(path), notindexed)
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 def makedirs(
self, path: Optional[bytes] = None, mode: Optional[int] = None
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 ) -> None:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return util.makedirs(self.join(path), mode)
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 def makelock(self, info: bytes, path: bytes) -> None:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return util.makelock(info, self.join(path))
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 def mkdir(self, path: Optional[bytes] = None) -> None:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return os.mkdir(self.join(path))
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 def mkstemp(
self,
suffix: bytes = b'',
prefix: bytes = b'tmp',
dir: Optional[bytes] = None,
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 ) -> Tuple[int, bytes]:
Augie Fackler
formatting: blacken the codebase...
r43346 fd, name = pycompat.mkstemp(
suffix=suffix, prefix=prefix, dir=self.join(dir)
)
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 dname, fname = util.split(name)
if dir:
return fd, os.path.join(dir, fname)
else:
return fd, fname
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 # TODO: This doesn't match osutil.listdir(). stat=False in pure;
# non-optional bool in cext. 'skip' is bool if we trust cext, or bytes
# going by how pure uses it. Also, cext returns a custom stat structure.
# from cext.osutil.pyi:
#
# path: bytes, st: bool, skip: Optional[bool]
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 def readdir(
self, path: Optional[bytes] = None, stat=None, skip=None
) -> Any:
Yuya Nishihara
osutil: proxy through util (and platform) modules (API)...
r32203 return util.listdir(self.join(path), stat, skip)
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 def readlock(self, path: bytes) -> bytes:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return util.readlock(self.join(path))
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 def rename(self, src: bytes, dst: bytes, checkambig: bool = False) -> None:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 """Rename from src to dst
checkambig argument is used with util.filestat, and is useful
only if destination file is guarded by any lock
(e.g. repo.lock or repo.wlock).
FUJIWARA Katsunori
vfs: add explanation about cost of checkambig=True in corner case
r33282
To avoid file stat ambiguity forcibly, checkambig=True involves
copying ``src`` file, if it is owned by another. Therefore, use
checkambig=True only in limited cases (see also issue5418 and
issue5584 for detail).
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 """
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self._auditpath(dst, b'w')
FUJIWARA Katsunori
vfs: create copy at renaming to avoid file stat ambiguity if needed...
r32748 srcpath = self.join(src)
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 dstpath = self.join(dst)
Matt Harbison
vfs: simplify the `abstractvfs.rename()` implementation...
r52778 oldstat = util.filestat.frompath(dstpath) if checkambig else None
util.rename(srcpath, dstpath)
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 if oldstat and oldstat.stat:
FUJIWARA Katsunori
vfs: replace avoiding ambiguity in abstractvfs.rename with _avoidambig...
r33281 _avoidambig(dstpath, oldstat)
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 def readlink(self, path: bytes) -> bytes:
Matt Harbison
py3: convert os.readlink() path to native strings on Windows...
r39940 return util.readlink(self.join(path))
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 def removedirs(self, path: Optional[bytes] = None) -> None:
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """Remove a leaf directory and all empty intermediate ones"""
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return util.removedirs(self.join(path))
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 def rmdir(self, path: Optional[bytes] = None) -> None:
Gregory Szorc
merge: use vfs methods for I/O...
r39500 """Remove an empty directory."""
return os.rmdir(self.join(path))
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 def rmtree(
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 self,
path: Optional[bytes] = None,
ignore_errors: bool = False,
forcibly: bool = False,
Matt Harbison
typing: add a trivial type hint to `mercurial/vfs.py`...
r52572 ) -> None:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 """Remove a directory tree recursively
If ``forcibly``, this tries to remove READ-ONLY files, too.
"""
if forcibly:
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 def onexc(function, path: bytes, excinfo):
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 if function is not os.remove:
raise
# read-only files cannot be unlinked under Windows
s = os.stat(path)
if (s.st_mode & stat.S_IWRITE) != 0:
raise
os.chmod(path, stat.S_IMODE(s.st_mode) | stat.S_IWRITE)
os.remove(path)
Augie Fackler
formatting: blacken the codebase...
r43346
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 else:
Mads Kiilerich
vfs: handle shutil.rmtree deprecation of onerror in Python 3.12...
r51647 onexc = None
try:
# pytype: disable=wrong-keyword-args
return shutil.rmtree(
self.join(path), ignore_errors=ignore_errors, onexc=onexc
)
# pytype: enable=wrong-keyword-args
except TypeError: # onexc was introduced in Python 3.12
return shutil.rmtree(
self.join(path), ignore_errors=ignore_errors, onerror=onexc
)
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 def setflags(self, path: bytes, l: bool, x: bool) -> None:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return util.setflags(self.join(path), l, x)
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 def stat(self, path: Optional[bytes] = None) -> os.stat_result:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return os.stat(self.join(path))
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 def unlink(self, path: Optional[bytes] = None) -> None:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return util.unlink(self.join(path))
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 def tryunlink(self, path: Optional[bytes] = None) -> bool:
Ryan McElroy
vfs: add tryunlink method...
r31542 """Attempt to remove a file, ignoring missing file errors."""
Georges Racinet
vfs: have tryunlink tell what it did...
r52323 return util.tryunlink(self.join(path))
Ryan McElroy
vfs: add tryunlink method...
r31542
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 def unlinkpath(
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 self,
path: Optional[bytes] = None,
ignoremissing: bool = False,
rmdir: bool = True,
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 ) -> None:
Augie Fackler
formatting: blacken the codebase...
r43346 return util.unlinkpath(
self.join(path), ignoremissing=ignoremissing, rmdir=rmdir
)
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 # TODO: could be Tuple[float, float] too.
def utime(
self, path: Optional[bytes] = None, t: Optional[Tuple[int, int]] = None
) -> None:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return os.utime(self.join(path), t)
Matt Harbison
typing: correct pytype mistakes in `mercurial/vfs.py`...
r52784 def walk(
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 self, path: Optional[bytes] = None, onerror: Optional[_OnErrorFn] = None
Matt Harbison
typing: correct pytype mistakes in `mercurial/vfs.py`...
r52784 ) -> Iterator[Tuple[bytes, List[bytes], List[bytes]]]:
Matt Harbison
vfs: do minor copyediting on comments and doc strings...
r52779 """Yield (dirpath, dirs, files) tuple for each directory under path
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
``dirpath`` is relative one from the root of this vfs. This
uses ``os.sep`` as path separator, even you specify POSIX
style ``path``.
"The root of this vfs" is represented as empty ``dirpath``.
"""
root = os.path.normpath(self.join(None))
# when dirpath == root, dirpath[prefixlen:] becomes empty
# because len(dirpath) < prefixlen.
prefixlen = len(pathutil.normasprefix(root))
for dirpath, dirs, files in os.walk(self.join(path), onerror=onerror):
yield (dirpath[prefixlen:], dirs, files)
@contextlib.contextmanager
Matt Harbison
typing: correct pytype mistakes in `mercurial/vfs.py`...
r52784 def backgroundclosing(
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 self, ui: uimod.ui, expectedcount: int = -1
Matt Harbison
typing: correct pytype mistakes in `mercurial/vfs.py`...
r52784 ) -> Iterator[Optional[backgroundfilecloser]]:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 """Allow files to be closed asynchronously.
When this context manager is active, ``backgroundclose`` can be passed
to ``__call__``/``open`` to result in the file possibly being closed
asynchronously, on a background thread.
"""
Wojciech Lis
workers: don't use backgroundfilecloser in threads...
r35426 # Sharing backgroundfilecloser between threads is complex and using
# multiple instances puts us at risk of running out of file descriptors
# only allow to use backgroundfilecloser when in main thread.
Matt Harbison
vfs: modernize the detection of the main thread...
r52776 if threading.current_thread() is not threading.main_thread():
Wojciech Lis
workers: don't use backgroundfilecloser in threads...
r35426 yield
return
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 vfs = getattr(self, 'vfs', self)
if getattr(vfs, '_backgroundfilecloser', None):
raise error.Abort(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b'can only have 1 active background file closer')
Augie Fackler
formatting: blacken the codebase...
r43346 )
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
with backgroundfilecloser(ui, expectedcount=expectedcount) as bfc:
try:
Augie Fackler
vfs: suppress some pytype errors around us using a private attribute...
r43783 vfs._backgroundfilecloser = (
bfc # pytype: disable=attribute-error
)
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 yield bfc
finally:
Augie Fackler
vfs: suppress some pytype errors around us using a private attribute...
r43783 vfs._backgroundfilecloser = (
None # pytype: disable=attribute-error
)
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 def register_file(self, path: bytes) -> None:
vfs: add a `register_file` method on the vfs class...
r48236 """generic hook point to lets fncache steer its stew"""
Augie Fackler
formatting: blacken the codebase...
r43346
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 class vfs(abstractvfs):
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """Operate files relative to a base directory
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
This class is used to hide the details of COW semantics and
remote file access from higher level code.
Yuya Nishihara
pathauditor: disable cache of audited paths by default (issue5628)...
r33722
'cacheaudited' should be enabled only if (a) vfs object is short-lived, or
(b) the base directory is managed by hg and considered sort-of append-only.
See pathutil.pathauditor() for details.
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Harbison
typing: add a handful more annotations to `mercurial/vfs.py`...
r52787 audit: Union[pathutil.pathauditor, Callable[[bytes, Optional[bytes]], Any]]
base: bytes
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 createmode: Optional[int]
Matt Harbison
typing: add a handful more annotations to `mercurial/vfs.py`...
r52787 options: Dict[bytes, Any]
_audit: bool
_trustnlink: Optional[bool]
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785
Augie Fackler
formatting: blacken the codebase...
r43346 def __init__(
self,
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 base: bytes,
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 audit: bool = True,
cacheaudited: bool = False,
expandpath: bool = False,
realpath: bool = False,
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 ) -> None:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 if expandpath:
base = util.expandpath(base)
if realpath:
base = os.path.realpath(base)
self.base = base
vfs: drop the 'mustaudit' API...
r33257 self._audit = audit
if audit:
Yuya Nishihara
pathauditor: disable cache of audited paths by default (issue5628)...
r33722 self.audit = pathutil.pathauditor(self.base, cached=cacheaudited)
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 else:
Augie Fackler
formatting: blacken the codebase...
r43346 self.audit = lambda path, mode=None: True
vfs: drop the 'mustaudit' API...
r33257 self.createmode = None
self._trustnlink = None
vfs: give all vfs an options attribute by default...
r43295 self.options = {}
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
@util.propertycache
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 def _cansymlink(self) -> bool:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return util.checklink(self.base)
@util.propertycache
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 def _chmod(self) -> bool:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return util.checkexec(self.base)
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 def _fixfilemode(self, name: bytes) -> None:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 if self.createmode is None or not self._chmod:
return
os.chmod(name, self.createmode & 0o666)
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 def _auditpath(self, path: bytes, mode: bytes) -> None:
Boris Feld
vfs: extract the audit path logic into a submethod...
r40785 if self._audit:
Boris Feld
vfs: makes all audited path relative...
r41122 if os.path.isabs(path) and path.startswith(self.base):
path = os.path.relpath(path, self.base)
Boris Feld
vfs: extract the audit path logic into a submethod...
r40785 r = util.checkosfilename(path)
if r:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.Abort(b"%s: %r" % (r, path))
Boris Feld
vfs: extract the audit path logic into a submethod...
r40785 self.audit(path, mode=mode)
Arseniy Alekseyev
merge: skip syntactic path checks in [_checkunknownfile]...
r50804 def isfileorlink_checkdir(
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 self,
dircache: MutableMapping[bytes, bool],
Matt Harbison
typing: make `vfs.isfileorlink_checkdir()` path arg required...
r52786 path: bytes,
Arseniy Alekseyev
merge: skip syntactic path checks in [_checkunknownfile]...
r50804 ) -> bool:
"""return True if the path is a regular file or a symlink and
the directories along the path are "normal", that is
Arseniy Alekseyev
pathauditor: make _checkfs_exists a static method...
r50807 not symlinks or nested hg repositories.
Ignores the `_audit` setting, and checks the directories regardless.
`dircache` is used to cache the directory checks.
"""
Arseniy Alekseyev
merge: skip syntactic path checks in [_checkunknownfile]...
r50804 try:
for prefix in pathutil.finddirs_rev_noroot(util.localpath(path)):
if prefix in dircache:
res = dircache[prefix]
else:
Arseniy Alekseyev
pathauditor: make _checkfs_exists a static method...
r50807 res = pathutil.pathauditor._checkfs_exists(
self.base, prefix, path
)
Arseniy Alekseyev
merge: skip syntactic path checks in [_checkunknownfile]...
r50804 dircache[prefix] = res
if not res:
return False
except (OSError, error.Abort):
return False
return self.isfileorlink(path)
Augie Fackler
formatting: blacken the codebase...
r43346 def __call__(
self,
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 path: bytes,
Matt Harbison
vfs: make the default opener mode binary...
r50469 mode: bytes = b"rb",
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 atomictemp: bool = False,
notindexed: bool = False,
backgroundclose: bool = False,
checkambig: bool = False,
auditpath: bool = True,
makeparentdirs: bool = True,
) -> Any: # TODO: should be BinaryIO if util.atomictempfile can be coersed
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """Open ``path`` file, which is relative to vfs root.
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
Yuya Nishihara
vfs: add option to not create parent directories implicitly...
r40827 By default, parent directories are created as needed. Newly created
directories are marked as "not to be indexed by the content indexing
service", if ``notindexed`` is specified for "write" mode access.
Set ``makeparentdirs=False`` to not create directories implicitly.
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
If ``backgroundclose`` is passed, the file may be closed asynchronously.
It can only be used if the ``self.backgroundclosing()`` context manager
is active. This should only be specified if the following criteria hold:
1. There is a potential for writing thousands of files. Unless you
are writing thousands of files, the performance benefits of
asynchronously closing files is not realized.
2. Files are opened exactly once for the ``backgroundclosing``
active duration and are therefore free of race conditions between
closing a file on a background thread and reopening it. (If the
file were opened multiple times, there could be unflushed data
because the original file handle hasn't been flushed/closed yet.)
Kyle Lippincott
vfs: fix typo in comment (remove extra "l")...
r45056 ``checkambig`` argument is passed to atomictempfile (valid
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 only for writing), and is useful only if target file is
guarded by any lock (e.g. repo.lock or repo.wlock).
FUJIWARA Katsunori
vfs: add explanation about cost of checkambig=True in corner case
r33282
To avoid file stat ambiguity forcibly, checkambig=True involves
copying ``path`` file opened in "append" mode (e.g. for
truncation), if it is owned by another. Therefore, use
combination of append mode and checkambig=True only in limited
cases (see also issue5418 and issue5584 for detail).
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """
vfs: simplify path audit disabling in stream clone...
r33255 if auditpath:
Boris Feld
vfs: extract the audit path logic into a submethod...
r40785 self._auditpath(path, mode)
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 f = self.join(path)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if b"b" not in mode:
mode += b"b" # for that other OS
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
nlink = -1
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if mode not in (b'r', b'rb'):
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 dirname, basename = util.split(f)
# If basename is empty, then the path is malformed because it points
# to a directory. Let the posixfile() call below raise IOError.
if basename:
if atomictemp:
Yuya Nishihara
vfs: add option to not create parent directories implicitly...
r40827 if makeparentdirs:
util.makedirs(dirname, self.createmode, notindexed)
Augie Fackler
formatting: blacken the codebase...
r43346 return util.atomictempfile(
f, mode, self.createmode, checkambig=checkambig
)
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 try:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if b'w' in mode:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 util.unlink(f)
nlink = 0
else:
# nlinks() may behave differently for files on Windows
# shares if the file is open.
with util.posixfile(f):
nlink = util.nlinks(f)
if nlink < 1:
Augie Fackler
formatting: blacken the codebase...
r43346 nlink = 2 # force mktempcopy (issue1922)
Manuel Jacob
py3: catch FileNotFoundError instead of checking errno == ENOENT
r50201 except FileNotFoundError:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 nlink = 0
Yuya Nishihara
vfs: add option to not create parent directories implicitly...
r40827 if makeparentdirs:
util.makedirs(dirname, self.createmode, notindexed)
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 if nlink > 0:
if self._trustnlink is None:
self._trustnlink = nlink > 1 or util.checknlink(f)
if nlink > 1 or not self._trustnlink:
util.rename(util.mktempcopy(f), f)
fp = util.posixfile(f, mode)
if nlink == 0:
self._fixfilemode(f)
if checkambig:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if mode in (b'r', b'rb'):
Augie Fackler
formatting: blacken the codebase...
r43346 raise error.Abort(
_(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'implementation error: mode %s is not'
b' valid for checkambig=True'
Augie Fackler
formatting: blacken the codebase...
r43346 )
% mode
)
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 fp = checkambigatclosing(fp)
Matt Harbison
vfs: modernize the detection of the main thread...
r52776 if (
backgroundclose
and threading.current_thread() is threading.main_thread()
Augie Fackler
formatting: blacken the codebase...
r43346 ):
Augie Fackler
vfs: more attribute suppressions...
r43785 if (
not self._backgroundfilecloser # pytype: disable=attribute-error
):
Augie Fackler
formatting: blacken the codebase...
r43346 raise error.Abort(
_(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'backgroundclose can only be used when a '
b'backgroundclosing context manager is active'
Augie Fackler
formatting: blacken the codebase...
r43346 )
)
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
Augie Fackler
vfs: more attribute suppressions...
r43785 fp = delayclosedfile(
fp,
self._backgroundfilecloser, # pytype: disable=attribute-error
)
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
return fp
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 def symlink(self, src: bytes, dst: bytes) -> None:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 self.audit(dst)
linkname = self.join(dst)
Ryan McElroy
vfs: use tryunlink
r31549 util.tryunlink(linkname)
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
util.makedirs(os.path.dirname(linkname), self.createmode)
if self._cansymlink:
try:
os.symlink(src, linkname)
except OSError as err:
Augie Fackler
formatting: blacken the codebase...
r43346 raise OSError(
err.errno,
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b'could not symlink to %r: %s')
Augie Fackler
formatting: blacken the codebase...
r43346 % (src, encoding.strtolocal(err.strerror)),
linkname,
)
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 else:
self.write(dst, src)
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 def join(self, path: Optional[bytes], *insidef: bytes) -> bytes:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 if path:
vfs: always use / as file separator (issue6546)...
r48617 parts = [self.base, path]
parts.extend(insidef)
return self._join(*parts)
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 else:
return self.base
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 opener: Type[vfs] = vfs
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Harbison
vfs: use @abstractmethod instead of homebrewing abstract methods...
r52777 class proxyvfs(abstractvfs, abc.ABC):
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 def __init__(self, vfs: vfs) -> None:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 self.vfs = vfs
vfsproxy: inherit the `createmode` attribute too...
r51347 @property
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 def createmode(self) -> Optional[int]:
vfsproxy: inherit the `createmode` attribute too...
r51347 return self.vfs.createmode
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 def _auditpath(self, path: bytes, mode: bytes) -> None:
Boris Feld
vfs: handle _auditpath in proxyvfs...
r41126 return self.vfs._auditpath(path, mode)
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 @property
Matt Harbison
typing: add a handful more annotations to `mercurial/vfs.py`...
r52787 def options(self) -> Dict[bytes, Any]:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return self.vfs.options
@options.setter
Matt Harbison
typing: add a handful more annotations to `mercurial/vfs.py`...
r52787 def options(self, value: Dict[bytes, Any]) -> None:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 self.vfs.options = value
proxy-vfs: also proxy the `audit` attribute...
r52486 @property
def audit(self):
return self.vfs.audit
Augie Fackler
formatting: blacken the codebase...
r43346
Boris Feld
vfs: fix proxyvfs inheritance...
r41125 class filtervfs(proxyvfs, abstractvfs):
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 '''Wrapper vfs for filtering filenames with a function.'''
Matt Harbison
typing: add a handful more annotations to `mercurial/vfs.py`...
r52787 def __init__(self, vfs: vfs, filter: Callable[[bytes], bytes]) -> None:
Yuya Nishihara
vfs: rename auditvfs to proxyvfs...
r33412 proxyvfs.__init__(self, vfs)
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 self._filter = filter
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 # TODO: The return type should be BinaryIO
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 def __call__(self, path: bytes, *args, **kwargs) -> Any:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return self.vfs(self._filter(path), *args, **kwargs)
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 def join(self, path: Optional[bytes], *insidef: bytes) -> bytes:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 if path:
return self.vfs.join(self._filter(self.vfs.reljoin(path, *insidef)))
else:
return self.vfs.join(path)
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 filteropener: Type[filtervfs] = filtervfs
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
Augie Fackler
formatting: blacken the codebase...
r43346
Boris Feld
vfs: fix proxyvfs inheritance...
r41125 class readonlyvfs(proxyvfs):
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 '''Wrapper vfs preventing any writing.'''
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 def __init__(self, vfs: vfs) -> None:
Yuya Nishihara
vfs: rename auditvfs to proxyvfs...
r33412 proxyvfs.__init__(self, vfs)
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 # TODO: The return type should be BinaryIO
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 def __call__(self, path: bytes, mode: bytes = b'rb', *args, **kw) -> Any:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if mode not in (b'r', b'rb'):
raise error.Abort(_(b'this vfs is read only'))
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return self.vfs(path, mode, *args, **kw)
Matt Harbison
typing: add basic type hints to vfs.py...
r50468 def join(self, path: Optional[bytes], *insidef: bytes) -> bytes:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return self.vfs.join(path, *insidef)
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Harbison
vfs: use @abstractmethod instead of homebrewing abstract methods...
r52777 class closewrapbase(abc.ABC):
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 """Base class of wrapper, which hooks closing
Matt Harbison
vfs: do minor copyediting on comments and doc strings...
r52779 Do not instantiate outside the vfs layer.
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 """
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 def __init__(self, fh) -> None:
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 object.__setattr__(self, '_origfh', fh)
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 def __getattr__(self, attr: str) -> Any:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return getattr(self._origfh, attr)
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 def __setattr__(self, attr: str, value: Any) -> None:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return setattr(self._origfh, attr, value)
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 def __delattr__(self, attr: str) -> None:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 return delattr(self._origfh, attr)
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 def __enter__(self: _Tclosewrapbase) -> _Tclosewrapbase:
Matt Harbison
vfs: ensure closewrapbase fh doesn't escape by entering context manager...
r40975 self._origfh.__enter__()
return self
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
Matt Harbison
vfs: use @abstractmethod instead of homebrewing abstract methods...
r52777 @abc.abstractmethod
Matt Harbison
typing: correct pytype mistakes in `mercurial/vfs.py`...
r52784 def __exit__(self, exc_type, exc_value, exc_tb) -> None:
Matt Harbison
vfs: use @abstractmethod instead of homebrewing abstract methods...
r52777 ...
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
Matt Harbison
vfs: use @abstractmethod instead of homebrewing abstract methods...
r52777 @abc.abstractmethod
Matt Harbison
typing: correct pytype mistakes in `mercurial/vfs.py`...
r52784 def close(self) -> None:
Matt Harbison
vfs: use @abstractmethod instead of homebrewing abstract methods...
r52777 ...
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
Augie Fackler
formatting: blacken the codebase...
r43346
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 class delayclosedfile(closewrapbase):
"""Proxy for a file object whose close is delayed.
Matt Harbison
vfs: do minor copyediting on comments and doc strings...
r52779 Do not instantiate outside the vfs layer.
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 """
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 def __init__(self, fh, closer) -> None:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 super(delayclosedfile, self).__init__(fh)
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 object.__setattr__(self, '_closer', closer)
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
Matt Harbison
typing: correct pytype mistakes in `mercurial/vfs.py`...
r52784 def __exit__(self, exc_type, exc_value, exc_tb) -> None:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 self._closer.close(self._origfh)
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 def close(self) -> None:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 self._closer.close(self._origfh)
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
py3: use class X: instead of class X(object):...
r49801 class backgroundfilecloser:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 """Coordinates background closing of file handles on multiple threads."""
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Harbison
typing: manually add type annotations to `mercurial/vfs.py`...
r52785 def __init__(self, ui: uimod.ui, expectedcount: int = -1) -> None:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 self._running = False
self._entered = False
self._threads = []
self._threadexception = None
# Only Windows/NTFS has slow file closing. So only enable by default
# on that platform. But allow to be enabled elsewhere for testing.
Jun Wu
codemod: use pycompat.iswindows...
r34646 defaultenabled = pycompat.iswindows
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 enabled = ui.configbool(b'worker', b'backgroundclose', defaultenabled)
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
if not enabled:
return
# There is overhead to starting and stopping the background threads.
# Don't do background processing unless the file count is large enough
# to justify it.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 minfilecount = ui.configint(b'worker', b'backgroundcloseminfilecount')
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 # FUTURE dynamically start background threads after minfilecount closes.
# (We don't currently have any callers that don't know their file count)
if expectedcount > 0 and expectedcount < minfilecount:
return
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 maxqueue = ui.configint(b'worker', b'backgroundclosemaxqueue')
threadcount = ui.configint(b'worker', b'backgroundclosethreadcount')
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
Augie Fackler
formatting: blacken the codebase...
r43346 ui.debug(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'starting %d threads for background file closing\n' % threadcount
Augie Fackler
formatting: blacken the codebase...
r43346 )
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
Gregory Szorc
pycompat: export queue module instead of symbols in module (API)...
r37863 self._queue = pycompat.queue.Queue(maxsize=maxqueue)
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 self._running = True
for i in range(threadcount):
Augie Fackler
vfs: another bytes-str confusion on thread name...
r43763 t = threading.Thread(target=self._worker, name='backgroundcloser')
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 self._threads.append(t)
t.start()
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 def __enter__(self: _Tbackgroundfilecloser) -> _Tbackgroundfilecloser:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 self._entered = True
return self
Matt Harbison
typing: correct pytype mistakes in `mercurial/vfs.py`...
r52784 def __exit__(self, exc_type, exc_value, exc_tb) -> None:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 self._running = False
# Wait for threads to finish closing so open files don't linger for
# longer than lifetime of context manager.
for t in self._threads:
t.join()
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 def _worker(self) -> None:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 """Main routine for worker thread."""
while True:
try:
fh = self._queue.get(block=True, timeout=0.100)
# Need to catch or the thread will terminate and
# we could orphan file descriptors.
try:
fh.close()
except Exception as e:
# Stash so can re-raise from main thread later.
self._threadexception = e
Gregory Szorc
pycompat: export queue module instead of symbols in module (API)...
r37863 except pycompat.queue.Empty:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 if not self._running:
break
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 def close(self, fh) -> None:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 """Schedule a file for closing."""
if not self._entered:
Augie Fackler
formatting: blacken the codebase...
r43346 raise error.Abort(
Martin von Zweigbergk
cleanup: join string literals that are already on one line...
r43387 _(b'can only call close() when context manager active')
Augie Fackler
formatting: blacken the codebase...
r43346 )
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
# If a background thread encountered an exception, raise now so we fail
Matt Harbison
vfs: do minor copyediting on comments and doc strings...
r52779 # fast. Otherwise, we may potentially go on for minutes until the error
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 # is acted on.
if self._threadexception:
e = self._threadexception
self._threadexception = None
raise e
# If we're not actively running, close synchronously.
if not self._running:
fh.close()
return
self._queue.put(fh, block=True, timeout=None)
Augie Fackler
formatting: blacken the codebase...
r43346
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 class checkambigatclosing(closewrapbase):
"""Proxy for a file object, to avoid ambiguity of file stat
See also util.filestat for detail about "ambiguity of file stat".
This proxy is useful only if the target file is guarded by any
lock (e.g. repo.lock or repo.wlock)
Matt Harbison
vfs: do minor copyediting on comments and doc strings...
r52779 Do not instantiate outside the vfs layer.
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 """
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 def __init__(self, fh) -> None:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 super(checkambigatclosing, self).__init__(fh)
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 object.__setattr__(self, '_oldstat', util.filestat.frompath(fh.name))
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 def _checkambig(self) -> None:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 oldstat = self._oldstat
if oldstat.stat:
FUJIWARA Katsunori
vfs: copy if EPERM to avoid file stat ambiguity forcibly at closing...
r33280 _avoidambig(self._origfh.name, oldstat)
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217
Matt Harbison
typing: correct pytype mistakes in `mercurial/vfs.py`...
r52784 def __exit__(self, exc_type, exc_value, exc_tb) -> None:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 self._origfh.__exit__(exc_type, exc_value, exc_tb)
self._checkambig()
Matt Harbison
typing: run `merge-pyi` on `mercurial/vfs.py`...
r52783 def close(self) -> None:
Pierre-Yves David
vfs: extract 'vfs' class and related code to a new 'vfs' module (API)...
r31217 self._origfh.close()
self._checkambig()