mercurial/pathutil.py
author Arseniy Alekseyev <aalekseyev@janestreet.com>
Fri, 06 Jan 2023 16:27:31 +0000
changeset 49888 445b4d819e9a
parent 49887 44deb5a164dc
child 49890 1b701d425c37
permissions -rw-r--r--
pathauditor: no need to normcase the paths The only thing normed paths are used is the key of the caching sets, so the only change of behavior will be that the checks will be repeated for paths that differ by case. If anything, it seems correct for the check to be repeated, in case that actually affects semantics, but the main reasoning is simplifying the code and making it a bit faster. It looks like the code originally comes from commit [081e795c60e0]: it looks like that commit tried to get rid of the existing norming, but presumably did this overly cautiously, preserving it for the cache keys, even though it was pointless even then.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
44865
233ee525dcef grep: reduce the cost of pathauditor checks when grepping working copy
Valentin Gatien-Baron <valentin.gatienbaron@gmail.com>
parents: 44626
diff changeset
     1
import contextlib
25964
d740df4e96cf pathutil: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
     2
import errno
d740df4e96cf pathutil: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
     3
import os
d740df4e96cf pathutil: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
     4
import posixpath
d740df4e96cf pathutil: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
     5
import stat
d740df4e96cf pathutil: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
     6
d740df4e96cf pathutil: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
     7
from .i18n import _
d740df4e96cf pathutil: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
     8
from . import (
d740df4e96cf pathutil: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
     9
    encoding,
26587
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 25964
diff changeset
    10
    error,
43523
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
    11
    policy,
30614
cfe66dcf45c0 py3: replace os.sep with pycompat.ossep (part 2 of 4)
Pulkit Goyal <7895pulkit@gmail.com>
parents: 30332
diff changeset
    12
    pycompat,
25964
d740df4e96cf pathutil: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
    13
    util,
d740df4e96cf pathutil: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
    14
)
20033
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
    15
46646
5d483e3bb60e typing: add some type annotations to mercurial/pathutil.py
Matt Harbison <matt_harbison@yahoo.com>
parents: 45942
diff changeset
    16
if pycompat.TYPE_CHECKING:
5d483e3bb60e typing: add some type annotations to mercurial/pathutil.py
Matt Harbison <matt_harbison@yahoo.com>
parents: 45942
diff changeset
    17
    from typing import (
5d483e3bb60e typing: add some type annotations to mercurial/pathutil.py
Matt Harbison <matt_harbison@yahoo.com>
parents: 45942
diff changeset
    18
        Any,
5d483e3bb60e typing: add some type annotations to mercurial/pathutil.py
Matt Harbison <matt_harbison@yahoo.com>
parents: 45942
diff changeset
    19
        Callable,
5d483e3bb60e typing: add some type annotations to mercurial/pathutil.py
Matt Harbison <matt_harbison@yahoo.com>
parents: 45942
diff changeset
    20
        Iterator,
5d483e3bb60e typing: add some type annotations to mercurial/pathutil.py
Matt Harbison <matt_harbison@yahoo.com>
parents: 45942
diff changeset
    21
        Optional,
5d483e3bb60e typing: add some type annotations to mercurial/pathutil.py
Matt Harbison <matt_harbison@yahoo.com>
parents: 45942
diff changeset
    22
    )
5d483e3bb60e typing: add some type annotations to mercurial/pathutil.py
Matt Harbison <matt_harbison@yahoo.com>
parents: 45942
diff changeset
    23
5d483e3bb60e typing: add some type annotations to mercurial/pathutil.py
Matt Harbison <matt_harbison@yahoo.com>
parents: 45942
diff changeset
    24
43523
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
    25
rustdirs = policy.importrust('dirstate', 'Dirs')
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
    26
parsers = policy.importmod('parsers')
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
    27
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 38592
diff changeset
    28
23598
c02a05cc6f5e pathauditor: check for codepoints ignored on OS X
Augie Fackler <raf@durin42.com>
parents: 21568
diff changeset
    29
def _lowerclean(s):
46646
5d483e3bb60e typing: add some type annotations to mercurial/pathutil.py
Matt Harbison <matt_harbison@yahoo.com>
parents: 45942
diff changeset
    30
    # type: (bytes) -> bytes
23598
c02a05cc6f5e pathauditor: check for codepoints ignored on OS X
Augie Fackler <raf@durin42.com>
parents: 21568
diff changeset
    31
    return encoding.hfsignoreclean(s.lower())
c02a05cc6f5e pathauditor: check for codepoints ignored on OS X
Augie Fackler <raf@durin42.com>
parents: 21568
diff changeset
    32
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 38592
diff changeset
    33
48946
642e31cb55f0 py3: use class X: instead of class X(object):
Gregory Szorc <gregory.szorc@gmail.com>
parents: 48913
diff changeset
    34
class pathauditor:
45942
89a2afe31e82 formating: upgrade to black 20.8b1
Augie Fackler <raf@durin42.com>
parents: 44865
diff changeset
    35
    """ensure that a filesystem path contains no banned components.
20033
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
    36
    the following properties of a path are checked:
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
    37
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
    38
    - ends with a directory separator
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
    39
    - under top-level .hg
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
    40
    - starts at the root of a windows drive
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
    41
    - contains ".."
27232
79a86a95f325 pathauditor: add a way to skip file system check
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 27231
diff changeset
    42
79a86a95f325 pathauditor: add a way to skip file system check
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 27231
diff changeset
    43
    More check are also done about the file system states:
20033
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
    44
    - traverses a symlink (e.g. a/symlink_here/b)
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
    45
    - inside a nested repository (a callback can be used to approve
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
    46
      some nested repositories, e.g., subrepositories)
27232
79a86a95f325 pathauditor: add a way to skip file system check
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 27231
diff changeset
    47
79a86a95f325 pathauditor: add a way to skip file system check
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 27231
diff changeset
    48
    The file system checks are only done when 'realfs' is set to True (the
79a86a95f325 pathauditor: add a way to skip file system check
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 27231
diff changeset
    49
    default). They should be disable then we are auditing path for operation on
79a86a95f325 pathauditor: add a way to skip file system check
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 27231
diff changeset
    50
    stored history.
33649
377e8ddaebef pathauditor: disable cache of audited paths by default (issue5628)
Yuya Nishihara <yuya@tcha.org>
parents: 33435
diff changeset
    51
377e8ddaebef pathauditor: disable cache of audited paths by default (issue5628)
Yuya Nishihara <yuya@tcha.org>
parents: 33435
diff changeset
    52
    If 'cached' is set to True, audited paths and sub-directories are cached.
377e8ddaebef pathauditor: disable cache of audited paths by default (issue5628)
Yuya Nishihara <yuya@tcha.org>
parents: 33435
diff changeset
    53
    Be careful to not keep the cache of unmanaged directories for long because
377e8ddaebef pathauditor: disable cache of audited paths by default (issue5628)
Yuya Nishihara <yuya@tcha.org>
parents: 33435
diff changeset
    54
    audited paths may be replaced with symlinks.
45942
89a2afe31e82 formating: upgrade to black 20.8b1
Augie Fackler <raf@durin42.com>
parents: 44865
diff changeset
    55
    """
20033
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
    56
33649
377e8ddaebef pathauditor: disable cache of audited paths by default (issue5628)
Yuya Nishihara <yuya@tcha.org>
parents: 33435
diff changeset
    57
    def __init__(self, root, callback=None, realfs=True, cached=False):
20033
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
    58
        self.audited = set()
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
    59
        self.auditeddir = set()
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
    60
        self.root = root
27232
79a86a95f325 pathauditor: add a way to skip file system check
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 27231
diff changeset
    61
        self._realfs = realfs
33649
377e8ddaebef pathauditor: disable cache of audited paths by default (issue5628)
Yuya Nishihara <yuya@tcha.org>
parents: 33435
diff changeset
    62
        self._cached = cached
20033
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
    63
        self.callback = callback
29889
6f447b9ec263 util: rename checkcase() to fscasesensitive() (API)
Martin von Zweigbergk <martinvonz@google.com>
parents: 28087
diff changeset
    64
        if os.path.lexists(root) and not util.fscasesensitive(root):
20033
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
    65
            self.normcase = util.normcase
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
    66
        else:
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
    67
            self.normcase = lambda x: x
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
    68
33435
456626e9c3d1 vfs: allow to pass more argument to audit
Boris Feld <boris.feld@octobus.net>
parents: 30614
diff changeset
    69
    def __call__(self, path, mode=None):
46646
5d483e3bb60e typing: add some type annotations to mercurial/pathutil.py
Matt Harbison <matt_harbison@yahoo.com>
parents: 45942
diff changeset
    70
        # type: (bytes, Optional[Any]) -> None
45942
89a2afe31e82 formating: upgrade to black 20.8b1
Augie Fackler <raf@durin42.com>
parents: 44865
diff changeset
    71
        """Check the relative path.
89a2afe31e82 formating: upgrade to black 20.8b1
Augie Fackler <raf@durin42.com>
parents: 44865
diff changeset
    72
        path may contain a pattern (e.g. foodir/**.txt)"""
20033
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
    73
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
    74
        path = util.localpath(path)
49888
445b4d819e9a pathauditor: no need to normcase the paths
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 49887
diff changeset
    75
        if path in self.audited:
20033
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
    76
            return
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
    77
        # AIX ignores "/" at end of path, others raise EISDIR.
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
    78
        if util.endswithsep(path):
48369
35f1ecd84bd0 errors: use detailed exit code in pathauditor
Martin von Zweigbergk <martinvonz@google.com>
parents: 47944
diff changeset
    79
            raise error.InputError(
35f1ecd84bd0 errors: use detailed exit code in pathauditor
Martin von Zweigbergk <martinvonz@google.com>
parents: 47944
diff changeset
    80
                _(b"path ends in directory separator: %s") % path
35f1ecd84bd0 errors: use detailed exit code in pathauditor
Martin von Zweigbergk <martinvonz@google.com>
parents: 47944
diff changeset
    81
            )
20033
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
    82
        parts = util.splitpath(path)
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 38592
diff changeset
    83
        if (
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 38592
diff changeset
    84
            os.path.splitdrive(path)[0]
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
    85
            or _lowerclean(parts[0]) in (b'.hg', b'.hg.', b'')
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 38592
diff changeset
    86
            or pycompat.ospardir in parts
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 38592
diff changeset
    87
        ):
48369
35f1ecd84bd0 errors: use detailed exit code in pathauditor
Martin von Zweigbergk <martinvonz@google.com>
parents: 47944
diff changeset
    88
            raise error.InputError(
35f1ecd84bd0 errors: use detailed exit code in pathauditor
Martin von Zweigbergk <martinvonz@google.com>
parents: 47944
diff changeset
    89
                _(b"path contains illegal component: %s") % path
35f1ecd84bd0 errors: use detailed exit code in pathauditor
Martin von Zweigbergk <martinvonz@google.com>
parents: 47944
diff changeset
    90
            )
23599
6dad422ecc5a pathauditor: check for Windows shortname aliases
Matt Mackall <mpm@selenic.com>
parents: 23598
diff changeset
    91
        # Windows shortname aliases
49887
44deb5a164dc pathutil: slightly faster path audit in the common case
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 48946
diff changeset
    92
        if b"~" in path:
44deb5a164dc pathutil: slightly faster path audit in the common case
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 48946
diff changeset
    93
            for p in parts:
44deb5a164dc pathutil: slightly faster path audit in the common case
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 48946
diff changeset
    94
                if b"~" in p:
44deb5a164dc pathutil: slightly faster path audit in the common case
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 48946
diff changeset
    95
                    first, last = p.split(b"~", 1)
44deb5a164dc pathutil: slightly faster path audit in the common case
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 48946
diff changeset
    96
                    if last.isdigit() and first.upper() in [b"HG", b"HG8B6C"]:
44deb5a164dc pathutil: slightly faster path audit in the common case
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 48946
diff changeset
    97
                        raise error.InputError(
44deb5a164dc pathutil: slightly faster path audit in the common case
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 48946
diff changeset
    98
                            _(b"path contains illegal component: %s") % path
44deb5a164dc pathutil: slightly faster path audit in the common case
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 48946
diff changeset
    99
                        )
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   100
        if b'.hg' in _lowerclean(path):
44180
d84420232492 pathauditor: drop a redundant call to bytes.lower()
Martin von Zweigbergk <martinvonz@google.com>
parents: 43633
diff changeset
   101
            lparts = [_lowerclean(p) for p in parts]
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   102
            for p in b'.hg', b'.hg.':
20033
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   103
                if p in lparts[1:]:
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   104
                    pos = lparts.index(p)
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   105
                    base = os.path.join(*parts[:pos])
48369
35f1ecd84bd0 errors: use detailed exit code in pathauditor
Martin von Zweigbergk <martinvonz@google.com>
parents: 47944
diff changeset
   106
                    raise error.InputError(
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   107
                        _(b"path '%s' is inside nested repo %r")
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 38592
diff changeset
   108
                        % (path, pycompat.bytestr(base))
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 38592
diff changeset
   109
                    )
20033
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   110
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   111
        parts.pop()
28087
0b7ce0b16d8a pathauditor: change parts verification order to be root first
Durham Goode <durham@fb.com>
parents: 27235
diff changeset
   112
        # It's important that we check the path parts starting from the root.
44358
d52e3826cd4b pathutil: resurrect comment about path auditing order
Yuya Nishihara <yuya@tcha.org>
parents: 44195
diff changeset
   113
        # We don't want to add "foo/bar/baz" to auditeddir before checking if
d52e3826cd4b pathutil: resurrect comment about path auditing order
Yuya Nishihara <yuya@tcha.org>
parents: 44195
diff changeset
   114
        # there's a "foo/.hg" directory. This also means we won't accidentally
d52e3826cd4b pathutil: resurrect comment about path auditing order
Yuya Nishihara <yuya@tcha.org>
parents: 44195
diff changeset
   115
        # traverse a symlink into some other filesystem (which is potentially
d52e3826cd4b pathutil: resurrect comment about path auditing order
Yuya Nishihara <yuya@tcha.org>
parents: 44195
diff changeset
   116
        # expensive to access).
28087
0b7ce0b16d8a pathauditor: change parts verification order to be root first
Durham Goode <durham@fb.com>
parents: 27235
diff changeset
   117
        for i in range(len(parts)):
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 38592
diff changeset
   118
            prefix = pycompat.ossep.join(parts[: i + 1])
49888
445b4d819e9a pathauditor: no need to normcase the paths
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 49887
diff changeset
   119
            if prefix in self.auditeddir:
28087
0b7ce0b16d8a pathauditor: change parts verification order to be root first
Durham Goode <durham@fb.com>
parents: 27235
diff changeset
   120
                continue
27232
79a86a95f325 pathauditor: add a way to skip file system check
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 27231
diff changeset
   121
            if self._realfs:
79a86a95f325 pathauditor: add a way to skip file system check
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 27231
diff changeset
   122
                self._checkfs(prefix, path)
44195
51c86c6167c1 pathutil: mark parent directories as audited as we go
Martin von Zweigbergk <martinvonz@google.com>
parents: 44180
diff changeset
   123
            if self._cached:
49888
445b4d819e9a pathauditor: no need to normcase the paths
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 49887
diff changeset
   124
                self.auditeddir.add(prefix)
20033
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   125
33649
377e8ddaebef pathauditor: disable cache of audited paths by default (issue5628)
Yuya Nishihara <yuya@tcha.org>
parents: 33435
diff changeset
   126
        if self._cached:
49888
445b4d819e9a pathauditor: no need to normcase the paths
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 49887
diff changeset
   127
            self.audited.add(path)
20033
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   128
27231
6d29ce250a3d pathauditor: move file system specific check in their own function
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26587
diff changeset
   129
    def _checkfs(self, prefix, path):
46646
5d483e3bb60e typing: add some type annotations to mercurial/pathutil.py
Matt Harbison <matt_harbison@yahoo.com>
parents: 45942
diff changeset
   130
        # type: (bytes, bytes) -> None
27231
6d29ce250a3d pathauditor: move file system specific check in their own function
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26587
diff changeset
   131
        """raise exception if a file system backed check fails"""
6d29ce250a3d pathauditor: move file system specific check in their own function
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26587
diff changeset
   132
        curpath = os.path.join(self.root, prefix)
6d29ce250a3d pathauditor: move file system specific check in their own function
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26587
diff changeset
   133
        try:
6d29ce250a3d pathauditor: move file system specific check in their own function
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26587
diff changeset
   134
            st = os.lstat(curpath)
6d29ce250a3d pathauditor: move file system specific check in their own function
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26587
diff changeset
   135
        except OSError as err:
6d29ce250a3d pathauditor: move file system specific check in their own function
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26587
diff changeset
   136
            # EINVAL can be raised as invalid path syntax under win32.
6d29ce250a3d pathauditor: move file system specific check in their own function
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26587
diff changeset
   137
            # They must be ignored for patterns can be checked too.
6d29ce250a3d pathauditor: move file system specific check in their own function
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26587
diff changeset
   138
            if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
6d29ce250a3d pathauditor: move file system specific check in their own function
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26587
diff changeset
   139
                raise
6d29ce250a3d pathauditor: move file system specific check in their own function
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26587
diff changeset
   140
        else:
6d29ce250a3d pathauditor: move file system specific check in their own function
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26587
diff changeset
   141
            if stat.S_ISLNK(st.st_mode):
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   142
                msg = _(b'path %r traverses symbolic link %r') % (
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 38592
diff changeset
   143
                    pycompat.bytestr(path),
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 38592
diff changeset
   144
                    pycompat.bytestr(prefix),
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 38592
diff changeset
   145
                )
27235
054cd38a2f19 pathutil: use temporary variables instead of complicated wrapping
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 27232
diff changeset
   146
                raise error.Abort(msg)
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 38592
diff changeset
   147
            elif stat.S_ISDIR(st.st_mode) and os.path.isdir(
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   148
                os.path.join(curpath, b'.hg')
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 38592
diff changeset
   149
            ):
27231
6d29ce250a3d pathauditor: move file system specific check in their own function
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26587
diff changeset
   150
                if not self.callback or not self.callback(curpath):
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   151
                    msg = _(b"path '%s' is inside nested repo %r")
36649
d3b893ec5f08 py3: fix formatting of path-auditing errors
Yuya Nishihara <yuya@tcha.org>
parents: 36647
diff changeset
   152
                    raise error.Abort(msg % (path, pycompat.bytestr(prefix)))
27231
6d29ce250a3d pathauditor: move file system specific check in their own function
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 26587
diff changeset
   153
20033
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   154
    def check(self, path):
46646
5d483e3bb60e typing: add some type annotations to mercurial/pathutil.py
Matt Harbison <matt_harbison@yahoo.com>
parents: 45942
diff changeset
   155
        # type: (bytes) -> bool
20033
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   156
        try:
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   157
            self(path)
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   158
            return True
26587
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 25964
diff changeset
   159
        except (OSError, error.Abort):
20033
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   160
            return False
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   161
44865
233ee525dcef grep: reduce the cost of pathauditor checks when grepping working copy
Valentin Gatien-Baron <valentin.gatienbaron@gmail.com>
parents: 44626
diff changeset
   162
    @contextlib.contextmanager
233ee525dcef grep: reduce the cost of pathauditor checks when grepping working copy
Valentin Gatien-Baron <valentin.gatienbaron@gmail.com>
parents: 44626
diff changeset
   163
    def cached(self):
233ee525dcef grep: reduce the cost of pathauditor checks when grepping working copy
Valentin Gatien-Baron <valentin.gatienbaron@gmail.com>
parents: 44626
diff changeset
   164
        if self._cached:
233ee525dcef grep: reduce the cost of pathauditor checks when grepping working copy
Valentin Gatien-Baron <valentin.gatienbaron@gmail.com>
parents: 44626
diff changeset
   165
            yield
233ee525dcef grep: reduce the cost of pathauditor checks when grepping working copy
Valentin Gatien-Baron <valentin.gatienbaron@gmail.com>
parents: 44626
diff changeset
   166
        else:
233ee525dcef grep: reduce the cost of pathauditor checks when grepping working copy
Valentin Gatien-Baron <valentin.gatienbaron@gmail.com>
parents: 44626
diff changeset
   167
            try:
233ee525dcef grep: reduce the cost of pathauditor checks when grepping working copy
Valentin Gatien-Baron <valentin.gatienbaron@gmail.com>
parents: 44626
diff changeset
   168
                self._cached = True
233ee525dcef grep: reduce the cost of pathauditor checks when grepping working copy
Valentin Gatien-Baron <valentin.gatienbaron@gmail.com>
parents: 44626
diff changeset
   169
                yield
233ee525dcef grep: reduce the cost of pathauditor checks when grepping working copy
Valentin Gatien-Baron <valentin.gatienbaron@gmail.com>
parents: 44626
diff changeset
   170
            finally:
233ee525dcef grep: reduce the cost of pathauditor checks when grepping working copy
Valentin Gatien-Baron <valentin.gatienbaron@gmail.com>
parents: 44626
diff changeset
   171
                self.audited.clear()
233ee525dcef grep: reduce the cost of pathauditor checks when grepping working copy
Valentin Gatien-Baron <valentin.gatienbaron@gmail.com>
parents: 44626
diff changeset
   172
                self.auditeddir.clear()
233ee525dcef grep: reduce the cost of pathauditor checks when grepping working copy
Valentin Gatien-Baron <valentin.gatienbaron@gmail.com>
parents: 44626
diff changeset
   173
                self._cached = False
233ee525dcef grep: reduce the cost of pathauditor checks when grepping working copy
Valentin Gatien-Baron <valentin.gatienbaron@gmail.com>
parents: 44626
diff changeset
   174
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 38592
diff changeset
   175
20033
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   176
def canonpath(root, cwd, myname, auditor=None):
46646
5d483e3bb60e typing: add some type annotations to mercurial/pathutil.py
Matt Harbison <matt_harbison@yahoo.com>
parents: 45942
diff changeset
   177
    # type: (bytes, bytes, bytes, Optional[pathauditor]) -> bytes
45942
89a2afe31e82 formating: upgrade to black 20.8b1
Augie Fackler <raf@durin42.com>
parents: 44865
diff changeset
   178
    """return the canonical path of myname, given cwd and root
34980
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   179
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   180
    >>> def check(root, cwd, myname):
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   181
    ...     a = pathauditor(root, realfs=False)
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   182
    ...     try:
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   183
    ...         return canonpath(root, cwd, myname, a)
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   184
    ...     except error.Abort:
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   185
    ...         return 'aborted'
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   186
    >>> def unixonly(root, cwd, myname, expected='aborted'):
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   187
    ...     if pycompat.iswindows:
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   188
    ...         return expected
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   189
    ...     return check(root, cwd, myname)
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   190
    >>> def winonly(root, cwd, myname, expected='aborted'):
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   191
    ...     if not pycompat.iswindows:
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   192
    ...         return expected
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   193
    ...     return check(root, cwd, myname)
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   194
    >>> winonly(b'd:\\\\repo', b'c:\\\\dir', b'filename')
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   195
    'aborted'
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   196
    >>> winonly(b'c:\\\\repo', b'c:\\\\dir', b'filename')
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   197
    'aborted'
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   198
    >>> winonly(b'c:\\\\repo', b'c:\\\\', b'filename')
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   199
    'aborted'
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   200
    >>> winonly(b'c:\\\\repo', b'c:\\\\', b'repo\\\\filename',
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   201
    ...         b'filename')
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   202
    'filename'
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   203
    >>> winonly(b'c:\\\\repo', b'c:\\\\repo', b'filename', b'filename')
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   204
    'filename'
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   205
    >>> winonly(b'c:\\\\repo', b'c:\\\\repo\\\\subdir', b'filename',
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   206
    ...         b'subdir/filename')
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   207
    'subdir/filename'
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   208
    >>> unixonly(b'/repo', b'/dir', b'filename')
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   209
    'aborted'
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   210
    >>> unixonly(b'/repo', b'/', b'filename')
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   211
    'aborted'
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   212
    >>> unixonly(b'/repo', b'/', b'repo/filename', b'filename')
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   213
    'filename'
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   214
    >>> unixonly(b'/repo', b'/repo', b'filename', b'filename')
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   215
    'filename'
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   216
    >>> unixonly(b'/repo', b'/repo/subdir', b'filename', b'subdir/filename')
705d0f2bb677 pathutil: add doctests for canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34965
diff changeset
   217
    'subdir/filename'
45942
89a2afe31e82 formating: upgrade to black 20.8b1
Augie Fackler <raf@durin42.com>
parents: 44865
diff changeset
   218
    """
20033
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   219
    if util.endswithsep(root):
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   220
        rootsep = root
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   221
    else:
30614
cfe66dcf45c0 py3: replace os.sep with pycompat.ossep (part 2 of 4)
Pulkit Goyal <7895pulkit@gmail.com>
parents: 30332
diff changeset
   222
        rootsep = root + pycompat.ossep
20033
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   223
    name = myname
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   224
    if not os.path.isabs(name):
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   225
        name = os.path.join(root, cwd, name)
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   226
    name = os.path.normpath(name)
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   227
    if auditor is None:
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   228
        auditor = pathauditor(root)
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   229
    if name != rootsep and name.startswith(rootsep):
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 38592
diff changeset
   230
        name = name[len(rootsep) :]
20033
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   231
        auditor(name)
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   232
        return util.pconvert(name)
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   233
    elif name == root:
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   234
        return b''
20033
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   235
    else:
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   236
        # Determine whether `name' is in the hierarchy at or beneath `root',
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   237
        # by iterating name=dirname(name) until that causes no change (can't
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   238
        # check name == '/', because that doesn't work on windows). The list
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   239
        # `rel' holds the reversed list of components making up the relative
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   240
        # file name we want.
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   241
        rel = []
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   242
        while True:
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   243
            try:
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   244
                s = util.samefile(name, root)
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   245
            except OSError:
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   246
                s = False
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   247
            if s:
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   248
                if not rel:
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   249
                    # name was actually the same as root (maybe a symlink)
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   250
                    return b''
20033
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   251
                rel.reverse()
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   252
                name = os.path.join(*rel)
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   253
                auditor(name)
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   254
                return util.pconvert(name)
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   255
            dirname, basename = util.split(name)
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   256
            rel.append(basename)
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   257
            if dirname == name:
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   258
                break
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   259
            name = dirname
f962870712da pathutil: tease out a new library to break an import cycle from canonpath use
Augie Fackler <raf@durin42.com>
parents:
diff changeset
   260
25011
7d6a507a4c53 pathutil: hint if a path is root relative instead of cwd relative (issue4663)
Matt Harbison <matt_harbison@yahoo.com>
parents: 23602
diff changeset
   261
        # A common mistake is to use -R, but specify a file relative to the repo
7d6a507a4c53 pathutil: hint if a path is root relative instead of cwd relative (issue4663)
Matt Harbison <matt_harbison@yahoo.com>
parents: 23602
diff changeset
   262
        # instead of cwd.  Detect that case, and provide a hint to the user.
7d6a507a4c53 pathutil: hint if a path is root relative instead of cwd relative (issue4663)
Matt Harbison <matt_harbison@yahoo.com>
parents: 23602
diff changeset
   263
        hint = None
7d6a507a4c53 pathutil: hint if a path is root relative instead of cwd relative (issue4663)
Matt Harbison <matt_harbison@yahoo.com>
parents: 23602
diff changeset
   264
        try:
25022
10bbdcd89164 canonpath: fix infinite recursion
Matt Mackall <mpm@selenic.com>
parents: 25011
diff changeset
   265
            if cwd != root:
10bbdcd89164 canonpath: fix infinite recursion
Matt Mackall <mpm@selenic.com>
parents: 25011
diff changeset
   266
                canonpath(root, root, myname, auditor)
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   267
                relpath = util.pathto(root, cwd, b'')
38592
21be76e07148 py3: use bytes.endswith() instead of bytes[n]
Yuya Nishihara <yuya@tcha.org>
parents: 36649
diff changeset
   268
                if relpath.endswith(pycompat.ossep):
34965
f445b10dc7fb pathutil: use util.pathto() to calculate relative cwd in canonpath()
Matt Harbison <matt_harbison@yahoo.com>
parents: 34254
diff changeset
   269
                    relpath = relpath[:-1]
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   270
                hint = _(b"consider using '--cwd %s'") % relpath
26587
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 25964
diff changeset
   271
        except error.Abort:
25011
7d6a507a4c53 pathutil: hint if a path is root relative instead of cwd relative (issue4663)
Matt Harbison <matt_harbison@yahoo.com>
parents: 23602
diff changeset
   272
            pass
7d6a507a4c53 pathutil: hint if a path is root relative instead of cwd relative (issue4663)
Matt Harbison <matt_harbison@yahoo.com>
parents: 23602
diff changeset
   273
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 38592
diff changeset
   274
        raise error.Abort(
43077
687b865b95ad formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents: 43076
diff changeset
   275
            _(b"%s not under root '%s'") % (myname, root), hint=hint
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 38592
diff changeset
   276
        )
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 38592
diff changeset
   277
21568
8dd17b19e722 subrepo: normalize path in the specific way for problematic encodings
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 20033
diff changeset
   278
8dd17b19e722 subrepo: normalize path in the specific way for problematic encodings
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 20033
diff changeset
   279
def normasprefix(path):
46646
5d483e3bb60e typing: add some type annotations to mercurial/pathutil.py
Matt Harbison <matt_harbison@yahoo.com>
parents: 45942
diff changeset
   280
    # type: (bytes) -> bytes
45942
89a2afe31e82 formating: upgrade to black 20.8b1
Augie Fackler <raf@durin42.com>
parents: 44865
diff changeset
   281
    """normalize the specified path as path prefix
21568
8dd17b19e722 subrepo: normalize path in the specific way for problematic encodings
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 20033
diff changeset
   282
23139
e53f6b72a0e4 spelling: fixes from proofreading of spell checker issues
Mads Kiilerich <madski@unity3d.com>
parents: 21568
diff changeset
   283
    Returned value can be used safely for "p.startswith(prefix)",
21568
8dd17b19e722 subrepo: normalize path in the specific way for problematic encodings
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 20033
diff changeset
   284
    "p[len(prefix):]", and so on.
8dd17b19e722 subrepo: normalize path in the specific way for problematic encodings
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 20033
diff changeset
   285
8dd17b19e722 subrepo: normalize path in the specific way for problematic encodings
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 20033
diff changeset
   286
    For efficiency, this expects "path" argument to be already
8dd17b19e722 subrepo: normalize path in the specific way for problematic encodings
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 20033
diff changeset
   287
    normalized by "os.path.normpath", "os.path.realpath", and so on.
8dd17b19e722 subrepo: normalize path in the specific way for problematic encodings
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 20033
diff changeset
   288
8dd17b19e722 subrepo: normalize path in the specific way for problematic encodings
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 20033
diff changeset
   289
    See also issue3033 for detail about need of this function.
8dd17b19e722 subrepo: normalize path in the specific way for problematic encodings
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 20033
diff changeset
   290
34254
cd022a11ec83 py3: use bytes os.sep in doctest of pathutil.py
Yuya Nishihara <yuya@tcha.org>
parents: 34131
diff changeset
   291
    >>> normasprefix(b'/foo/bar').replace(pycompat.ossep, b'/')
21568
8dd17b19e722 subrepo: normalize path in the specific way for problematic encodings
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 20033
diff changeset
   292
    '/foo/bar/'
34254
cd022a11ec83 py3: use bytes os.sep in doctest of pathutil.py
Yuya Nishihara <yuya@tcha.org>
parents: 34131
diff changeset
   293
    >>> normasprefix(b'/').replace(pycompat.ossep, b'/')
21568
8dd17b19e722 subrepo: normalize path in the specific way for problematic encodings
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 20033
diff changeset
   294
    '/'
45942
89a2afe31e82 formating: upgrade to black 20.8b1
Augie Fackler <raf@durin42.com>
parents: 44865
diff changeset
   295
    """
21568
8dd17b19e722 subrepo: normalize path in the specific way for problematic encodings
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 20033
diff changeset
   296
    d, p = os.path.splitdrive(path)
30614
cfe66dcf45c0 py3: replace os.sep with pycompat.ossep (part 2 of 4)
Pulkit Goyal <7895pulkit@gmail.com>
parents: 30332
diff changeset
   297
    if len(p) != len(pycompat.ossep):
cfe66dcf45c0 py3: replace os.sep with pycompat.ossep (part 2 of 4)
Pulkit Goyal <7895pulkit@gmail.com>
parents: 30332
diff changeset
   298
        return path + pycompat.ossep
21568
8dd17b19e722 subrepo: normalize path in the specific way for problematic encodings
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 20033
diff changeset
   299
    else:
8dd17b19e722 subrepo: normalize path in the specific way for problematic encodings
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 20033
diff changeset
   300
        return path
25281
660b178f49c7 pathutil: add dirname and join functions
Durham Goode <durham@fb.com>
parents: 25022
diff changeset
   301
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 38592
diff changeset
   302
43633
0b7733719d21 utils: move finddirs() to pathutil
Martin von Zweigbergk <martinvonz@google.com>
parents: 43523
diff changeset
   303
def finddirs(path):
46646
5d483e3bb60e typing: add some type annotations to mercurial/pathutil.py
Matt Harbison <matt_harbison@yahoo.com>
parents: 45942
diff changeset
   304
    # type: (bytes) -> Iterator[bytes]
43633
0b7733719d21 utils: move finddirs() to pathutil
Martin von Zweigbergk <martinvonz@google.com>
parents: 43523
diff changeset
   305
    pos = path.rfind(b'/')
0b7733719d21 utils: move finddirs() to pathutil
Martin von Zweigbergk <martinvonz@google.com>
parents: 43523
diff changeset
   306
    while pos != -1:
0b7733719d21 utils: move finddirs() to pathutil
Martin von Zweigbergk <martinvonz@google.com>
parents: 43523
diff changeset
   307
        yield path[:pos]
0b7733719d21 utils: move finddirs() to pathutil
Martin von Zweigbergk <martinvonz@google.com>
parents: 43523
diff changeset
   308
        pos = path.rfind(b'/', 0, pos)
0b7733719d21 utils: move finddirs() to pathutil
Martin von Zweigbergk <martinvonz@google.com>
parents: 43523
diff changeset
   309
    yield b''
0b7733719d21 utils: move finddirs() to pathutil
Martin von Zweigbergk <martinvonz@google.com>
parents: 43523
diff changeset
   310
0b7733719d21 utils: move finddirs() to pathutil
Martin von Zweigbergk <martinvonz@google.com>
parents: 43523
diff changeset
   311
48946
642e31cb55f0 py3: use class X: instead of class X(object):
Gregory Szorc <gregory.szorc@gmail.com>
parents: 48913
diff changeset
   312
class dirs:
43523
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   313
    '''a multiset of directory names from a set of file paths'''
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   314
47944
e02f9af7aed1 pathutil: replace the `skip` argument of `dirs` with a boolean
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 47541
diff changeset
   315
    def __init__(self, map, only_tracked=False):
45942
89a2afe31e82 formating: upgrade to black 20.8b1
Augie Fackler <raf@durin42.com>
parents: 44865
diff changeset
   316
        """
44626
11f284c8c5e4 pathutil: document that dirs map type implies manifest/dirstate processing
Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
parents: 44358
diff changeset
   317
        a dict map indicates a dirstate while a list indicates a manifest
45942
89a2afe31e82 formating: upgrade to black 20.8b1
Augie Fackler <raf@durin42.com>
parents: 44865
diff changeset
   318
        """
43523
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   319
        self._dirs = {}
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   320
        addpath = self.addpath
47944
e02f9af7aed1 pathutil: replace the `skip` argument of `dirs` with a boolean
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 47541
diff changeset
   321
        if isinstance(map, dict) and only_tracked:
48913
f254fc73d956 global: bulk replace simple pycompat.iteritems(x) with x.items()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 48875
diff changeset
   322
            for f, s in map.items():
47944
e02f9af7aed1 pathutil: replace the `skip` argument of `dirs` with a boolean
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 47541
diff changeset
   323
                if s.state != b'r':
43523
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   324
                    addpath(f)
47944
e02f9af7aed1 pathutil: replace the `skip` argument of `dirs` with a boolean
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 47541
diff changeset
   325
        elif only_tracked:
e02f9af7aed1 pathutil: replace the `skip` argument of `dirs` with a boolean
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 47541
diff changeset
   326
            msg = b"`only_tracked` is only supported with a dict source"
e02f9af7aed1 pathutil: replace the `skip` argument of `dirs` with a boolean
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 47541
diff changeset
   327
            raise error.ProgrammingError(msg)
43523
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   328
        else:
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   329
            for f in map:
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   330
                addpath(f)
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   331
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   332
    def addpath(self, path):
46646
5d483e3bb60e typing: add some type annotations to mercurial/pathutil.py
Matt Harbison <matt_harbison@yahoo.com>
parents: 45942
diff changeset
   333
        # type: (bytes) -> None
43523
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   334
        dirs = self._dirs
43633
0b7733719d21 utils: move finddirs() to pathutil
Martin von Zweigbergk <martinvonz@google.com>
parents: 43523
diff changeset
   335
        for base in finddirs(path):
43523
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   336
            if base.endswith(b'/'):
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   337
                raise ValueError(
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   338
                    "found invalid consecutive slashes in path: %r" % base
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   339
                )
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   340
            if base in dirs:
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   341
                dirs[base] += 1
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   342
                return
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   343
            dirs[base] = 1
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   344
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   345
    def delpath(self, path):
46646
5d483e3bb60e typing: add some type annotations to mercurial/pathutil.py
Matt Harbison <matt_harbison@yahoo.com>
parents: 45942
diff changeset
   346
        # type: (bytes) -> None
43523
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   347
        dirs = self._dirs
43633
0b7733719d21 utils: move finddirs() to pathutil
Martin von Zweigbergk <martinvonz@google.com>
parents: 43523
diff changeset
   348
        for base in finddirs(path):
43523
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   349
            if dirs[base] > 1:
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   350
                dirs[base] -= 1
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   351
                return
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   352
            del dirs[base]
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   353
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   354
    def __iter__(self):
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   355
        return iter(self._dirs)
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   356
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   357
    def __contains__(self, d):
46646
5d483e3bb60e typing: add some type annotations to mercurial/pathutil.py
Matt Harbison <matt_harbison@yahoo.com>
parents: 45942
diff changeset
   358
        # type: (bytes) -> bool
43523
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   359
        return d in self._dirs
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   360
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   361
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   362
if util.safehasattr(parsers, 'dirs'):
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   363
    dirs = parsers.dirs
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   364
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   365
if rustdirs is not None:
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   366
    dirs = rustdirs
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   367
c21aca51b392 utils: move the `dirs` definition in pathutil (API)
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43077
diff changeset
   368
25286
127a11f705d9 pathutil: demote two local functions to just be forwards
Augie Fackler <augie@google.com>
parents: 25285
diff changeset
   369
# forward two methods from posixpath that do what we need, but we'd
127a11f705d9 pathutil: demote two local functions to just be forwards
Augie Fackler <augie@google.com>
parents: 25285
diff changeset
   370
# rather not let our internals know that we're thinking in posix terms
127a11f705d9 pathutil: demote two local functions to just be forwards
Augie Fackler <augie@google.com>
parents: 25285
diff changeset
   371
# - instead we'll let them be oblivious.
127a11f705d9 pathutil: demote two local functions to just be forwards
Augie Fackler <augie@google.com>
parents: 25285
diff changeset
   372
join = posixpath.join
46646
5d483e3bb60e typing: add some type annotations to mercurial/pathutil.py
Matt Harbison <matt_harbison@yahoo.com>
parents: 45942
diff changeset
   373
dirname = posixpath.dirname  # type: Callable[[bytes], bytes]