[pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci
This commit is contained in:
pre-commit-ci[bot] 2024-04-13 00:00:18 +00:00
parent 72ad6dc953
commit f4cd1ba0d6
813 changed files with 66015 additions and 58839 deletions

View file

@ -2,14 +2,14 @@
# the vcs package don't need to import deeper than `pip._internal.vcs`.
# (The test directory may still need to import from a vcs sub-package.)
# Import all vcs modules to register each VCS in the VcsSupport object.
from __future__ import annotations
import pip._internal.vcs.bazaar
import pip._internal.vcs.git
import pip._internal.vcs.mercurial
import pip._internal.vcs.subversion # noqa: F401
from pip._internal.vcs.versioncontrol import ( # noqa: F401
RemoteNotFoundError,
RemoteNotValidError,
is_url,
make_vcs_requirement_url,
vcs,
)
from pip._internal.vcs.versioncontrol import is_url
from pip._internal.vcs.versioncontrol import make_vcs_requirement_url
from pip._internal.vcs.versioncontrol import RemoteNotFoundError
from pip._internal.vcs.versioncontrol import RemoteNotValidError
from pip._internal.vcs.versioncontrol import vcs

View file

@ -1,80 +1,83 @@
import logging
from typing import List, Optional, Tuple
from __future__ import annotations
from pip._internal.utils.misc import HiddenText, display_path
import logging
from typing import List
from typing import Optional
from typing import Tuple
from pip._internal.utils.misc import display_path
from pip._internal.utils.misc import HiddenText
from pip._internal.utils.subprocess import make_command
from pip._internal.utils.urls import path_to_url
from pip._internal.vcs.versioncontrol import (
AuthInfo,
RemoteNotFoundError,
RevOptions,
VersionControl,
vcs,
)
from pip._internal.vcs.versioncontrol import AuthInfo
from pip._internal.vcs.versioncontrol import RemoteNotFoundError
from pip._internal.vcs.versioncontrol import RevOptions
from pip._internal.vcs.versioncontrol import vcs
from pip._internal.vcs.versioncontrol import VersionControl
logger = logging.getLogger(__name__)
class Bazaar(VersionControl):
name = "bzr"
dirname = ".bzr"
repo_name = "branch"
name = 'bzr'
dirname = '.bzr'
repo_name = 'branch'
schemes = (
"bzr+http",
"bzr+https",
"bzr+ssh",
"bzr+sftp",
"bzr+ftp",
"bzr+lp",
"bzr+file",
'bzr+http',
'bzr+https',
'bzr+ssh',
'bzr+sftp',
'bzr+ftp',
'bzr+lp',
'bzr+file',
)
@staticmethod
def get_base_rev_args(rev: str) -> List[str]:
return ["-r", rev]
def get_base_rev_args(rev: str) -> list[str]:
return ['-r', rev]
def fetch_new(
self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int
self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int,
) -> None:
rev_display = rev_options.to_display()
logger.info(
"Checking out %s%s to %s",
'Checking out %s%s to %s',
url,
rev_display,
display_path(dest),
)
if verbosity <= 0:
flag = "--quiet"
flag = '--quiet'
elif verbosity == 1:
flag = ""
flag = ''
else:
flag = f"-{'v'*verbosity}"
cmd_args = make_command("branch", flag, rev_options.to_args(), url, dest)
cmd_args = make_command('branch', flag, rev_options.to_args(), url, dest)
self.run_command(cmd_args)
def switch(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None:
self.run_command(make_command("switch", url), cwd=dest)
self.run_command(make_command('switch', url), cwd=dest)
def update(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None:
cmd_args = make_command("pull", "-q", rev_options.to_args())
cmd_args = make_command('pull', '-q', rev_options.to_args())
self.run_command(cmd_args, cwd=dest)
@classmethod
def get_url_rev_and_auth(cls, url: str) -> Tuple[str, Optional[str], AuthInfo]:
def get_url_rev_and_auth(cls, url: str) -> tuple[str, str | None, AuthInfo]:
# hotfix the URL scheme after removing bzr+ from bzr+ssh:// readd it
url, rev, user_pass = super().get_url_rev_and_auth(url)
if url.startswith("ssh://"):
url = "bzr+" + url
if url.startswith('ssh://'):
url = 'bzr+' + url
return url, rev, user_pass
@classmethod
def get_remote_url(cls, location: str) -> str:
urls = cls.run_command(
["info"], show_stdout=False, stdout_only=True, cwd=location
['info'], show_stdout=False, stdout_only=True, cwd=location,
)
for line in urls.splitlines():
line = line.strip()
for x in ("checkout of branch: ", "parent branch: "):
for x in ('checkout of branch: ', 'parent branch: '):
if line.startswith(x):
repo = line.split(x)[1]
if cls._is_local_repository(repo):
@ -85,7 +88,7 @@ class Bazaar(VersionControl):
@classmethod
def get_revision(cls, location: str) -> str:
revision = cls.run_command(
["revno"],
['revno'],
show_stdout=False,
stdout_only=True,
cwd=location,
@ -93,7 +96,7 @@ class Bazaar(VersionControl):
return revision.splitlines()[-1]
@classmethod
def is_commit_id_equal(cls, dest: str, name: Optional[str]) -> bool:
def is_commit_id_equal(cls, dest: str, name: str | None) -> bool:
"""Always assume the versions don't match"""
return False

View file

@ -1,23 +1,28 @@
from __future__ import annotations
import logging
import os.path
import pathlib
import re
import urllib.parse
import urllib.request
from typing import List, Optional, Tuple
from typing import List
from typing import Optional
from typing import Tuple
from pip._internal.exceptions import BadCommand, InstallationError
from pip._internal.utils.misc import HiddenText, display_path, hide_url
from pip._internal.exceptions import BadCommand
from pip._internal.exceptions import InstallationError
from pip._internal.utils.misc import display_path
from pip._internal.utils.misc import HiddenText
from pip._internal.utils.misc import hide_url
from pip._internal.utils.subprocess import make_command
from pip._internal.vcs.versioncontrol import (
AuthInfo,
RemoteNotFoundError,
RemoteNotValidError,
RevOptions,
VersionControl,
find_path_to_project_root_from_repo_root,
vcs,
)
from pip._internal.vcs.versioncontrol import AuthInfo
from pip._internal.vcs.versioncontrol import find_path_to_project_root_from_repo_root
from pip._internal.vcs.versioncontrol import RemoteNotFoundError
from pip._internal.vcs.versioncontrol import RemoteNotValidError
from pip._internal.vcs.versioncontrol import RevOptions
from pip._internal.vcs.versioncontrol import vcs
from pip._internal.vcs.versioncontrol import VersionControl
urlsplit = urllib.parse.urlsplit
urlunsplit = urllib.parse.urlunsplit
@ -27,14 +32,14 @@ logger = logging.getLogger(__name__)
GIT_VERSION_REGEX = re.compile(
r"^git version " # Prefix.
r"(\d+)" # Major.
r"\.(\d+)" # Dot, minor.
r"(?:\.(\d+))?" # Optional dot, patch.
r".*$" # Suffix, including any pre- and post-release segments we don't care about.
r'^git version ' # Prefix.
r'(\d+)' # Major.
r'\.(\d+)' # Dot, minor.
r'(?:\.(\d+))?' # Optional dot, patch.
r'.*$', # Suffix, including any pre- and post-release segments we don't care about.
)
HASH_REGEX = re.compile("^[a-fA-F0-9]{40}$")
HASH_REGEX = re.compile('^[a-fA-F0-9]{40}$')
# SCP (Secure copy protocol) shorthand. e.g. 'git@example.com:foo/bar.git'
SCP_REGEX = re.compile(
@ -57,23 +62,23 @@ def looks_like_hash(sha: str) -> bool:
class Git(VersionControl):
name = "git"
dirname = ".git"
repo_name = "clone"
name = 'git'
dirname = '.git'
repo_name = 'clone'
schemes = (
"git+http",
"git+https",
"git+ssh",
"git+git",
"git+file",
'git+http',
'git+https',
'git+ssh',
'git+git',
'git+file',
)
# Prevent the user's environment variables from interfering with pip:
# https://github.com/pypa/pip/issues/1130
unset_environ = ("GIT_DIR", "GIT_WORK_TREE")
default_arg_rev = "HEAD"
unset_environ = ('GIT_DIR', 'GIT_WORK_TREE')
default_arg_rev = 'HEAD'
@staticmethod
def get_base_rev_args(rev: str) -> List[str]:
def get_base_rev_args(rev: str) -> list[str]:
return [rev]
def is_immutable_rev_checkout(self, url: str, dest: str) -> bool:
@ -90,10 +95,10 @@ class Git(VersionControl):
is_tag_or_branch = bool(self.get_revision_sha(dest, rev_options.rev)[0])
return not is_tag_or_branch
def get_git_version(self) -> Tuple[int, ...]:
def get_git_version(self) -> tuple[int, ...]:
version = self.run_command(
["version"],
command_desc="git version",
['version'],
command_desc='git version',
show_stdout=False,
stdout_only=True,
)
@ -104,7 +109,7 @@ class Git(VersionControl):
return tuple(int(c) for c in match.groups())
@classmethod
def get_current_branch(cls, location: str) -> Optional[str]:
def get_current_branch(cls, location: str) -> str | None:
"""
Return the current branch, or None if HEAD isn't at a branch
(e.g. detached HEAD).
@ -113,7 +118,7 @@ class Git(VersionControl):
# HEAD rather than a symbolic ref. In addition, the -q causes the
# command to exit with status code 1 instead of 128 in this case
# and to suppress the message to stderr.
args = ["symbolic-ref", "-q", "HEAD"]
args = ['symbolic-ref', '-q', 'HEAD']
output = cls.run_command(
args,
extra_ok_returncodes=(1,),
@ -123,13 +128,13 @@ class Git(VersionControl):
)
ref = output.strip()
if ref.startswith("refs/heads/"):
return ref[len("refs/heads/") :]
if ref.startswith('refs/heads/'):
return ref[len('refs/heads/'):]
return None
@classmethod
def get_revision_sha(cls, dest: str, rev: str) -> Tuple[Optional[str], bool]:
def get_revision_sha(cls, dest: str, rev: str) -> tuple[str | None, bool]:
"""
Return (sha_or_none, is_branch), where sha_or_none is a commit hash
if the revision names a remote branch or tag, otherwise None.
@ -140,31 +145,31 @@ class Git(VersionControl):
"""
# Pass rev to pre-filter the list.
output = cls.run_command(
["show-ref", rev],
['show-ref', rev],
cwd=dest,
show_stdout=False,
stdout_only=True,
on_returncode="ignore",
on_returncode='ignore',
)
refs = {}
# NOTE: We do not use splitlines here since that would split on other
# unicode separators, which can be maliciously used to install a
# different revision.
for line in output.strip().split("\n"):
line = line.rstrip("\r")
for line in output.strip().split('\n'):
line = line.rstrip('\r')
if not line:
continue
try:
ref_sha, ref_name = line.split(" ", maxsplit=2)
ref_sha, ref_name = line.split(' ', maxsplit=2)
except ValueError:
# Include the offending line to simplify troubleshooting if
# this error ever occurs.
raise ValueError(f"unexpected show-ref line: {line!r}")
raise ValueError(f'unexpected show-ref line: {line!r}')
refs[ref_name] = ref_sha
branch_ref = f"refs/remotes/origin/{rev}"
tag_ref = f"refs/tags/{rev}"
branch_ref = f'refs/remotes/origin/{rev}'
tag_ref = f'refs/tags/{rev}'
sha = refs.get(branch_ref)
if sha is not None:
@ -183,7 +188,7 @@ class Git(VersionControl):
assumed to be always available locally (which is a normal outcome of
``git clone`` and ``git fetch --tags``).
"""
if rev.startswith("refs/"):
if rev.startswith('refs/'):
# Always fetch remote refs.
return True
@ -199,7 +204,7 @@ class Git(VersionControl):
@classmethod
def resolve_revision(
cls, dest: str, url: HiddenText, rev_options: RevOptions
cls, dest: str, url: HiddenText, rev_options: RevOptions,
) -> RevOptions:
"""
Resolve a revision to a new RevOptions object with the SHA1 of the
@ -234,17 +239,17 @@ class Git(VersionControl):
# fetch the requested revision
cls.run_command(
make_command("fetch", "-q", url, rev_options.to_args()),
make_command('fetch', '-q', url, rev_options.to_args()),
cwd=dest,
)
# Change the revision to the SHA of the ref we fetched
sha = cls.get_revision(dest, rev="FETCH_HEAD")
sha = cls.get_revision(dest, rev='FETCH_HEAD')
rev_options = rev_options.make_new(sha)
return rev_options
@classmethod
def is_commit_id_equal(cls, dest: str, name: Optional[str]) -> bool:
def is_commit_id_equal(cls, dest: str, name: str | None) -> bool:
"""
Return whether the current commit hash equals the given name.
@ -259,56 +264,56 @@ class Git(VersionControl):
return cls.get_revision(dest) == name
def fetch_new(
self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int
self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int,
) -> None:
rev_display = rev_options.to_display()
logger.info("Cloning %s%s to %s", url, rev_display, display_path(dest))
logger.info('Cloning %s%s to %s', url, rev_display, display_path(dest))
if verbosity <= 0:
flags: Tuple[str, ...] = ("--quiet",)
flags: tuple[str, ...] = ('--quiet',)
elif verbosity == 1:
flags = ()
else:
flags = ("--verbose", "--progress")
flags = ('--verbose', '--progress')
if self.get_git_version() >= (2, 17):
# Git added support for partial clone in 2.17
# https://git-scm.com/docs/partial-clone
# Speeds up cloning by functioning without a complete copy of repository
self.run_command(
make_command(
"clone",
"--filter=blob:none",
'clone',
'--filter=blob:none',
*flags,
url,
dest,
)
),
)
else:
self.run_command(make_command("clone", *flags, url, dest))
self.run_command(make_command('clone', *flags, url, dest))
if rev_options.rev:
# Then a specific revision was requested.
rev_options = self.resolve_revision(dest, url, rev_options)
branch_name = getattr(rev_options, "branch_name", None)
logger.debug("Rev options %s, branch_name %s", rev_options, branch_name)
branch_name = getattr(rev_options, 'branch_name', None)
logger.debug('Rev options %s, branch_name %s', rev_options, branch_name)
if branch_name is None:
# Only do a checkout if the current commit id doesn't match
# the requested revision.
if not self.is_commit_id_equal(dest, rev_options.rev):
cmd_args = make_command(
"checkout",
"-q",
'checkout',
'-q',
rev_options.to_args(),
)
self.run_command(cmd_args, cwd=dest)
elif self.get_current_branch(dest) != branch_name:
# Then a specific branch was requested, and that branch
# is not yet checked out.
track_branch = f"origin/{branch_name}"
track_branch = f'origin/{branch_name}'
cmd_args = [
"checkout",
"-b",
'checkout',
'-b',
branch_name,
"--track",
'--track',
track_branch,
]
self.run_command(cmd_args, cwd=dest)
@ -316,17 +321,17 @@ class Git(VersionControl):
sha = self.get_revision(dest)
rev_options = rev_options.make_new(sha)
logger.info("Resolved %s to commit %s", url, rev_options.rev)
logger.info('Resolved %s to commit %s', url, rev_options.rev)
#: repo may contain submodules
self.update_submodules(dest)
def switch(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None:
self.run_command(
make_command("config", "remote.origin.url", url),
make_command('config', 'remote.origin.url', url),
cwd=dest,
)
cmd_args = make_command("checkout", "-q", rev_options.to_args())
cmd_args = make_command('checkout', '-q', rev_options.to_args())
self.run_command(cmd_args, cwd=dest)
self.update_submodules(dest)
@ -335,12 +340,12 @@ class Git(VersionControl):
# First fetch changes from the default remote
if self.get_git_version() >= (1, 9):
# fetch tags in addition to everything else
self.run_command(["fetch", "-q", "--tags"], cwd=dest)
self.run_command(['fetch', '-q', '--tags'], cwd=dest)
else:
self.run_command(["fetch", "-q"], cwd=dest)
self.run_command(['fetch', '-q'], cwd=dest)
# Then reset to wanted revision (maybe even origin/master)
rev_options = self.resolve_revision(dest, url, rev_options)
cmd_args = make_command("reset", "--hard", "-q", rev_options.to_args())
cmd_args = make_command('reset', '--hard', '-q', rev_options.to_args())
self.run_command(cmd_args, cwd=dest)
#: update submodules
self.update_submodules(dest)
@ -356,7 +361,7 @@ class Git(VersionControl):
# We need to pass 1 for extra_ok_returncodes since the command
# exits with return code 1 if there are no matching lines.
stdout = cls.run_command(
["config", "--get-regexp", r"remote\..*\.url"],
['config', '--get-regexp', r'remote\..*\.url'],
extra_ok_returncodes=(1,),
show_stdout=False,
stdout_only=True,
@ -369,10 +374,10 @@ class Git(VersionControl):
raise RemoteNotFoundError
for remote in remotes:
if remote.startswith("remote.origin.url "):
if remote.startswith('remote.origin.url '):
found_remote = remote
break
url = found_remote.split(" ")[1]
url = found_remote.split(' ')[1]
return cls._git_remote_to_pip_url(url.strip())
@staticmethod
@ -392,7 +397,7 @@ class Git(VersionControl):
See the corresponding test test_git_remote_url_to_pip() for examples of
sample inputs/outputs.
"""
if re.match(r"\w+://", url):
if re.match(r'\w+://', url):
# This is already valid. Pass it though as-is.
return url
if os.path.exists(url):
@ -402,7 +407,7 @@ class Git(VersionControl):
scp_match = SCP_REGEX.match(url)
if scp_match:
# Add an ssh:// prefix and replace the ':' with a '/'.
return scp_match.expand(r"ssh://\1\2/\3")
return scp_match.expand(r'ssh://\1\2/\3')
# Otherwise, bail out.
raise RemoteNotValidError(url)
@ -413,7 +418,7 @@ class Git(VersionControl):
"""
try:
cls.run_command(
["rev-parse", "-q", "--verify", "sha^" + rev],
['rev-parse', '-q', '--verify', 'sha^' + rev],
cwd=location,
log_failed_cmd=False,
)
@ -423,11 +428,11 @@ class Git(VersionControl):
return True
@classmethod
def get_revision(cls, location: str, rev: Optional[str] = None) -> str:
def get_revision(cls, location: str, rev: str | None = None) -> str:
if rev is None:
rev = "HEAD"
rev = 'HEAD'
current_rev = cls.run_command(
["rev-parse", rev],
['rev-parse', rev],
show_stdout=False,
stdout_only=True,
cwd=location,
@ -435,25 +440,25 @@ class Git(VersionControl):
return current_rev.strip()
@classmethod
def get_subdirectory(cls, location: str) -> Optional[str]:
def get_subdirectory(cls, location: str) -> str | None:
"""
Return the path to Python project root, relative to the repo root.
Return None if the project root is in the repo root.
"""
# find the repo root
git_dir = cls.run_command(
["rev-parse", "--git-dir"],
['rev-parse', '--git-dir'],
show_stdout=False,
stdout_only=True,
cwd=location,
).strip()
if not os.path.isabs(git_dir):
git_dir = os.path.join(location, git_dir)
repo_root = os.path.abspath(os.path.join(git_dir, ".."))
repo_root = os.path.abspath(os.path.join(git_dir, '..'))
return find_path_to_project_root_from_repo_root(location, repo_root)
@classmethod
def get_url_rev_and_auth(cls, url: str) -> Tuple[str, Optional[str], AuthInfo]:
def get_url_rev_and_auth(cls, url: str) -> tuple[str, str | None, AuthInfo]:
"""
Prefixes stub URLs like 'user@hostname:user/repo.git' with 'ssh://'.
That's required because although they use SSH they sometimes don't
@ -463,21 +468,21 @@ class Git(VersionControl):
# Works around an apparent Git bug
# (see https://article.gmane.org/gmane.comp.version-control.git/146500)
scheme, netloc, path, query, fragment = urlsplit(url)
if scheme.endswith("file"):
initial_slashes = path[: -len(path.lstrip("/"))]
if scheme.endswith('file'):
initial_slashes = path[: -len(path.lstrip('/'))]
newpath = initial_slashes + urllib.request.url2pathname(path).replace(
"\\", "/"
).lstrip("/")
after_plus = scheme.find("+") + 1
'\\', '/',
).lstrip('/')
after_plus = scheme.find('+') + 1
url = scheme[:after_plus] + urlunsplit(
(scheme[after_plus:], netloc, newpath, query, fragment),
)
if "://" not in url:
assert "file:" not in url
url = url.replace("git+", "git+ssh://")
if '://' not in url:
assert 'file:' not in url
url = url.replace('git+', 'git+ssh://')
url, rev, user_pass = super().get_url_rev_and_auth(url)
url = url.replace("ssh://", "")
url = url.replace('ssh://', '')
else:
url, rev, user_pass = super().get_url_rev_and_auth(url)
@ -485,37 +490,37 @@ class Git(VersionControl):
@classmethod
def update_submodules(cls, location: str) -> None:
if not os.path.exists(os.path.join(location, ".gitmodules")):
if not os.path.exists(os.path.join(location, '.gitmodules')):
return
cls.run_command(
["submodule", "update", "--init", "--recursive", "-q"],
['submodule', 'update', '--init', '--recursive', '-q'],
cwd=location,
)
@classmethod
def get_repository_root(cls, location: str) -> Optional[str]:
def get_repository_root(cls, location: str) -> str | None:
loc = super().get_repository_root(location)
if loc:
return loc
try:
r = cls.run_command(
["rev-parse", "--show-toplevel"],
['rev-parse', '--show-toplevel'],
cwd=location,
show_stdout=False,
stdout_only=True,
on_returncode="raise",
on_returncode='raise',
log_failed_cmd=False,
)
except BadCommand:
logger.debug(
"could not determine if %s is under git control "
"because git is not available",
'could not determine if %s is under git control '
'because git is not available',
location,
)
return None
except InstallationError:
return None
return os.path.normpath(r.rstrip("\r\n"))
return os.path.normpath(r.rstrip('\r\n'))
@staticmethod
def should_add_vcs_url_prefix(repo_url: str) -> bool:

View file

@ -1,85 +1,89 @@
from __future__ import annotations
import configparser
import logging
import os
from typing import List, Optional, Tuple
from typing import List
from typing import Optional
from typing import Tuple
from pip._internal.exceptions import BadCommand, InstallationError
from pip._internal.utils.misc import HiddenText, display_path
from pip._internal.exceptions import BadCommand
from pip._internal.exceptions import InstallationError
from pip._internal.utils.misc import display_path
from pip._internal.utils.misc import HiddenText
from pip._internal.utils.subprocess import make_command
from pip._internal.utils.urls import path_to_url
from pip._internal.vcs.versioncontrol import (
RevOptions,
VersionControl,
find_path_to_project_root_from_repo_root,
vcs,
)
from pip._internal.vcs.versioncontrol import find_path_to_project_root_from_repo_root
from pip._internal.vcs.versioncontrol import RevOptions
from pip._internal.vcs.versioncontrol import vcs
from pip._internal.vcs.versioncontrol import VersionControl
logger = logging.getLogger(__name__)
class Mercurial(VersionControl):
name = "hg"
dirname = ".hg"
repo_name = "clone"
name = 'hg'
dirname = '.hg'
repo_name = 'clone'
schemes = (
"hg+file",
"hg+http",
"hg+https",
"hg+ssh",
"hg+static-http",
'hg+file',
'hg+http',
'hg+https',
'hg+ssh',
'hg+static-http',
)
@staticmethod
def get_base_rev_args(rev: str) -> List[str]:
def get_base_rev_args(rev: str) -> list[str]:
return [rev]
def fetch_new(
self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int
self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int,
) -> None:
rev_display = rev_options.to_display()
logger.info(
"Cloning hg %s%s to %s",
'Cloning hg %s%s to %s',
url,
rev_display,
display_path(dest),
)
if verbosity <= 0:
flags: Tuple[str, ...] = ("--quiet",)
flags: tuple[str, ...] = ('--quiet',)
elif verbosity == 1:
flags = ()
elif verbosity == 2:
flags = ("--verbose",)
flags = ('--verbose',)
else:
flags = ("--verbose", "--debug")
self.run_command(make_command("clone", "--noupdate", *flags, url, dest))
flags = ('--verbose', '--debug')
self.run_command(make_command('clone', '--noupdate', *flags, url, dest))
self.run_command(
make_command("update", *flags, rev_options.to_args()),
make_command('update', *flags, rev_options.to_args()),
cwd=dest,
)
def switch(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None:
repo_config = os.path.join(dest, self.dirname, "hgrc")
repo_config = os.path.join(dest, self.dirname, 'hgrc')
config = configparser.RawConfigParser()
try:
config.read(repo_config)
config.set("paths", "default", url.secret)
with open(repo_config, "w") as config_file:
config.set('paths', 'default', url.secret)
with open(repo_config, 'w') as config_file:
config.write(config_file)
except (OSError, configparser.NoSectionError) as exc:
logger.warning("Could not switch Mercurial repository to %s: %s", url, exc)
logger.warning('Could not switch Mercurial repository to %s: %s', url, exc)
else:
cmd_args = make_command("update", "-q", rev_options.to_args())
cmd_args = make_command('update', '-q', rev_options.to_args())
self.run_command(cmd_args, cwd=dest)
def update(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None:
self.run_command(["pull", "-q"], cwd=dest)
cmd_args = make_command("update", "-q", rev_options.to_args())
self.run_command(['pull', '-q'], cwd=dest)
cmd_args = make_command('update', '-q', rev_options.to_args())
self.run_command(cmd_args, cwd=dest)
@classmethod
def get_remote_url(cls, location: str) -> str:
url = cls.run_command(
["showconfig", "paths.default"],
['showconfig', 'paths.default'],
show_stdout=False,
stdout_only=True,
cwd=location,
@ -94,7 +98,7 @@ class Mercurial(VersionControl):
Return the repository-local changeset revision number, as an integer.
"""
current_revision = cls.run_command(
["parents", "--template={rev}"],
['parents', '--template={rev}'],
show_stdout=False,
stdout_only=True,
cwd=location,
@ -108,7 +112,7 @@ class Mercurial(VersionControl):
hexadecimal string
"""
current_rev_hash = cls.run_command(
["parents", "--template={node}"],
['parents', '--template={node}'],
show_stdout=False,
stdout_only=True,
cwd=location,
@ -116,48 +120,48 @@ class Mercurial(VersionControl):
return current_rev_hash
@classmethod
def is_commit_id_equal(cls, dest: str, name: Optional[str]) -> bool:
def is_commit_id_equal(cls, dest: str, name: str | None) -> bool:
"""Always assume the versions don't match"""
return False
@classmethod
def get_subdirectory(cls, location: str) -> Optional[str]:
def get_subdirectory(cls, location: str) -> str | None:
"""
Return the path to Python project root, relative to the repo root.
Return None if the project root is in the repo root.
"""
# find the repo root
repo_root = cls.run_command(
["root"], show_stdout=False, stdout_only=True, cwd=location
['root'], show_stdout=False, stdout_only=True, cwd=location,
).strip()
if not os.path.isabs(repo_root):
repo_root = os.path.abspath(os.path.join(location, repo_root))
return find_path_to_project_root_from_repo_root(location, repo_root)
@classmethod
def get_repository_root(cls, location: str) -> Optional[str]:
def get_repository_root(cls, location: str) -> str | None:
loc = super().get_repository_root(location)
if loc:
return loc
try:
r = cls.run_command(
["root"],
['root'],
cwd=location,
show_stdout=False,
stdout_only=True,
on_returncode="raise",
on_returncode='raise',
log_failed_cmd=False,
)
except BadCommand:
logger.debug(
"could not determine if %s is under hg control "
"because hg is not available",
'could not determine if %s is under hg control '
'because hg is not available',
location,
)
return None
except InstallationError:
return None
return os.path.normpath(r.rstrip("\r\n"))
return os.path.normpath(r.rstrip('\r\n'))
vcs.register(Mercurial)

View file

@ -1,45 +1,46 @@
from __future__ import annotations
import logging
import os
import re
from typing import List, Optional, Tuple
from typing import List
from typing import Optional
from typing import Tuple
from pip._internal.utils.misc import (
HiddenText,
display_path,
is_console_interactive,
is_installable_dir,
split_auth_from_netloc,
)
from pip._internal.utils.subprocess import CommandArgs, make_command
from pip._internal.vcs.versioncontrol import (
AuthInfo,
RemoteNotFoundError,
RevOptions,
VersionControl,
vcs,
)
from pip._internal.utils.misc import display_path
from pip._internal.utils.misc import HiddenText
from pip._internal.utils.misc import is_console_interactive
from pip._internal.utils.misc import is_installable_dir
from pip._internal.utils.misc import split_auth_from_netloc
from pip._internal.utils.subprocess import CommandArgs
from pip._internal.utils.subprocess import make_command
from pip._internal.vcs.versioncontrol import AuthInfo
from pip._internal.vcs.versioncontrol import RemoteNotFoundError
from pip._internal.vcs.versioncontrol import RevOptions
from pip._internal.vcs.versioncontrol import vcs
from pip._internal.vcs.versioncontrol import VersionControl
logger = logging.getLogger(__name__)
_svn_xml_url_re = re.compile('url="([^"]+)"')
_svn_rev_re = re.compile(r'committed-rev="(\d+)"')
_svn_info_xml_rev_re = re.compile(r'\s*revision="(\d+)"')
_svn_info_xml_url_re = re.compile(r"<url>(.*)</url>")
_svn_info_xml_url_re = re.compile(r'<url>(.*)</url>')
class Subversion(VersionControl):
name = "svn"
dirname = ".svn"
repo_name = "checkout"
schemes = ("svn+ssh", "svn+http", "svn+https", "svn+svn", "svn+file")
name = 'svn'
dirname = '.svn'
repo_name = 'checkout'
schemes = ('svn+ssh', 'svn+http', 'svn+https', 'svn+svn', 'svn+file')
@classmethod
def should_add_vcs_url_prefix(cls, remote_url: str) -> bool:
return True
@staticmethod
def get_base_rev_args(rev: str) -> List[str]:
return ["-r", rev]
def get_base_rev_args(rev: str) -> list[str]:
return ['-r', rev]
@classmethod
def get_revision(cls, location: str) -> str:
@ -54,7 +55,7 @@ class Subversion(VersionControl):
dirs[:] = []
continue # no sense walking uncontrolled subdirs
dirs.remove(cls.dirname)
entries_fn = os.path.join(base, cls.dirname, "entries")
entries_fn = os.path.join(base, cls.dirname, 'entries')
if not os.path.exists(entries_fn):
# FIXME: should we warn?
continue
@ -63,7 +64,7 @@ class Subversion(VersionControl):
if base == location:
assert dirurl is not None
base = dirurl + "/" # save the root url
base = dirurl + '/' # save the root url
elif not dirurl or not dirurl.startswith(base):
dirs[:] = []
continue # not part of the same svn tree, skip it
@ -72,13 +73,13 @@ class Subversion(VersionControl):
@classmethod
def get_netloc_and_auth(
cls, netloc: str, scheme: str
) -> Tuple[str, Tuple[Optional[str], Optional[str]]]:
cls, netloc: str, scheme: str,
) -> tuple[str, tuple[str | None, str | None]]:
"""
This override allows the auth information to be passed to svn via the
--username and --password options instead of via the URL.
"""
if scheme == "ssh":
if scheme == 'ssh':
# The --username and --password options can't be used for
# svn+ssh URLs, so keep the auth information in the URL.
return super().get_netloc_and_auth(netloc, scheme)
@ -86,22 +87,22 @@ class Subversion(VersionControl):
return split_auth_from_netloc(netloc)
@classmethod
def get_url_rev_and_auth(cls, url: str) -> Tuple[str, Optional[str], AuthInfo]:
def get_url_rev_and_auth(cls, url: str) -> tuple[str, str | None, AuthInfo]:
# hotfix the URL scheme after removing svn+ from svn+ssh:// readd it
url, rev, user_pass = super().get_url_rev_and_auth(url)
if url.startswith("ssh://"):
url = "svn+" + url
if url.startswith('ssh://'):
url = 'svn+' + url
return url, rev, user_pass
@staticmethod
def make_rev_args(
username: Optional[str], password: Optional[HiddenText]
username: str | None, password: HiddenText | None,
) -> CommandArgs:
extra_args: CommandArgs = []
if username:
extra_args += ["--username", username]
extra_args += ['--username', username]
if password:
extra_args += ["--password", password]
extra_args += ['--password', password]
return extra_args
@ -117,8 +118,8 @@ class Subversion(VersionControl):
# We've traversed up to the root of the filesystem without
# finding a Python project.
logger.warning(
"Could not find Python project for directory %s (tried all "
"parent directories)",
'Could not find Python project for directory %s (tried all '
'parent directories)',
orig_location,
)
raise RemoteNotFoundError
@ -130,26 +131,26 @@ class Subversion(VersionControl):
return url
@classmethod
def _get_svn_url_rev(cls, location: str) -> Tuple[Optional[str], int]:
def _get_svn_url_rev(cls, location: str) -> tuple[str | None, int]:
from pip._internal.exceptions import InstallationError
entries_path = os.path.join(location, cls.dirname, "entries")
entries_path = os.path.join(location, cls.dirname, 'entries')
if os.path.exists(entries_path):
with open(entries_path) as f:
data = f.read()
else: # subversion >= 1.7 does not have the 'entries' file
data = ""
data = ''
url = None
if data.startswith("8") or data.startswith("9") or data.startswith("10"):
entries = list(map(str.splitlines, data.split("\n\x0c\n")))
if data.startswith('8') or data.startswith('9') or data.startswith('10'):
entries = list(map(str.splitlines, data.split('\n\x0c\n')))
del entries[0][0] # get rid of the '8'
url = entries[0][3]
revs = [int(d[9]) for d in entries if len(d) > 9 and d[9]] + [0]
elif data.startswith("<?xml"):
elif data.startswith('<?xml'):
match = _svn_xml_url_re.search(data)
if not match:
raise ValueError(f"Badly formatted data: {data!r}")
raise ValueError(f'Badly formatted data: {data!r}')
url = match.group(1) # get repository URL
revs = [int(m.group(1)) for m in _svn_rev_re.finditer(data)] + [0]
else:
@ -161,7 +162,7 @@ class Subversion(VersionControl):
# is being used to prompt for passwords, because passwords
# are only potentially needed for remote server requests.
xml = cls.run_command(
["info", "--xml", location],
['info', '--xml', location],
show_stdout=False,
stdout_only=True,
)
@ -180,7 +181,7 @@ class Subversion(VersionControl):
return url, rev
@classmethod
def is_commit_id_equal(cls, dest: str, name: Optional[str]) -> bool:
def is_commit_id_equal(cls, dest: str, name: str | None) -> bool:
"""Always assume the versions don't match"""
return False
@ -194,11 +195,11 @@ class Subversion(VersionControl):
# Special value definitions:
# None: Not evaluated yet.
# Empty tuple: Could not parse version.
self._vcs_version: Optional[Tuple[int, ...]] = None
self._vcs_version: tuple[int, ...] | None = None
super().__init__()
def call_vcs_version(self) -> Tuple[int, ...]:
def call_vcs_version(self) -> tuple[int, ...]:
"""Query the version of the currently installed Subversion client.
:return: A tuple containing the parts of the version information or
@ -212,13 +213,13 @@ class Subversion(VersionControl):
# compiled Mar 28 2018, 08:49:13 on x86_64-pc-linux-gnu
# svn, version 1.12.0-SlikSvn (SlikSvn/1.12.0)
# compiled May 28 2019, 13:44:56 on x86_64-microsoft-windows6.2
version_prefix = "svn, version "
version = self.run_command(["--version"], show_stdout=False, stdout_only=True)
version_prefix = 'svn, version '
version = self.run_command(['--version'], show_stdout=False, stdout_only=True)
if not version.startswith(version_prefix):
return ()
version = version[len(version_prefix) :].split()[0]
version_list = version.partition("-")[0].split(".")
version = version[len(version_prefix):].split()[0]
version_list = version.partition('-')[0].split('.')
try:
parsed_version = tuple(map(int, version_list))
except ValueError:
@ -226,7 +227,7 @@ class Subversion(VersionControl):
return parsed_version
def get_vcs_version(self) -> Tuple[int, ...]:
def get_vcs_version(self) -> tuple[int, ...]:
"""Return the version of the currently installed Subversion client.
If the version of the Subversion client has already been queried,
@ -261,7 +262,7 @@ class Subversion(VersionControl):
if not self.use_interactive:
# --non-interactive switch is available since Subversion 0.14.4.
# Subversion < 1.8 runs in interactive mode by default.
return ["--non-interactive"]
return ['--non-interactive']
svn_version = self.get_vcs_version()
# By default, Subversion >= 1.8 runs in non-interactive mode if
@ -273,26 +274,26 @@ class Subversion(VersionControl):
# SVN 1.7, pip should continue to support SVN 1.7. Therefore, pip
# can't safely add the option if the SVN version is < 1.8 (or unknown).
if svn_version >= (1, 8):
return ["--force-interactive"]
return ['--force-interactive']
return []
def fetch_new(
self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int
self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int,
) -> None:
rev_display = rev_options.to_display()
logger.info(
"Checking out %s%s to %s",
'Checking out %s%s to %s',
url,
rev_display,
display_path(dest),
)
if verbosity <= 0:
flag = "--quiet"
flag = '--quiet'
else:
flag = ""
flag = ''
cmd_args = make_command(
"checkout",
'checkout',
flag,
self.get_remote_call_options(),
rev_options.to_args(),
@ -303,7 +304,7 @@ class Subversion(VersionControl):
def switch(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None:
cmd_args = make_command(
"switch",
'switch',
self.get_remote_call_options(),
rev_options.to_args(),
url,
@ -313,7 +314,7 @@ class Subversion(VersionControl):
def update(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None:
cmd_args = make_command(
"update",
'update',
self.get_remote_call_options(),
rev_options.to_args(),
dest,

View file

@ -1,42 +1,38 @@
"""Handles all VCS (version control) support"""
from __future__ import annotations
import logging
import os
import shutil
import sys
import urllib.parse
from typing import (
TYPE_CHECKING,
Any,
Dict,
Iterable,
Iterator,
List,
Mapping,
Optional,
Tuple,
Type,
Union,
)
from typing import Any
from typing import Dict
from typing import Iterable
from typing import Iterator
from typing import List
from typing import Mapping
from typing import Optional
from typing import Tuple
from typing import Type
from typing import TYPE_CHECKING
from typing import Union
from pip._internal.cli.spinners import SpinnerInterface
from pip._internal.exceptions import BadCommand, InstallationError
from pip._internal.utils.misc import (
HiddenText,
ask_path_exists,
backup_dir,
display_path,
hide_url,
hide_value,
is_installable_dir,
rmtree,
)
from pip._internal.utils.subprocess import (
CommandArgs,
call_subprocess,
format_command_args,
make_command,
)
from pip._internal.exceptions import BadCommand
from pip._internal.exceptions import InstallationError
from pip._internal.utils.misc import ask_path_exists
from pip._internal.utils.misc import backup_dir
from pip._internal.utils.misc import display_path
from pip._internal.utils.misc import HiddenText
from pip._internal.utils.misc import hide_url
from pip._internal.utils.misc import hide_value
from pip._internal.utils.misc import is_installable_dir
from pip._internal.utils.misc import rmtree
from pip._internal.utils.subprocess import call_subprocess
from pip._internal.utils.subprocess import CommandArgs
from pip._internal.utils.subprocess import format_command_args
from pip._internal.utils.subprocess import make_command
from pip._internal.utils.urls import get_url_scheme
if TYPE_CHECKING:
@ -46,7 +42,7 @@ if TYPE_CHECKING:
from typing import Literal
__all__ = ["vcs"]
__all__ = ['vcs']
logger = logging.getLogger(__name__)
@ -61,11 +57,11 @@ def is_url(name: str) -> bool:
scheme = get_url_scheme(name)
if scheme is None:
return False
return scheme in ["http", "https", "file", "ftp"] + vcs.all_schemes
return scheme in ['http', 'https', 'file', 'ftp'] + vcs.all_schemes
def make_vcs_requirement_url(
repo_url: str, rev: str, project_name: str, subdir: Optional[str] = None
repo_url: str, rev: str, project_name: str, subdir: str | None = None,
) -> str:
"""
Return the URL for a VCS requirement.
@ -74,17 +70,17 @@ def make_vcs_requirement_url(
repo_url: the remote VCS url, with any needed VCS prefix (e.g. "git+").
project_name: the (unescaped) project name.
"""
egg_project_name = project_name.replace("-", "_")
req = f"{repo_url}@{rev}#egg={egg_project_name}"
egg_project_name = project_name.replace('-', '_')
req = f'{repo_url}@{rev}#egg={egg_project_name}'
if subdir:
req += f"&subdirectory={subdir}"
req += f'&subdirectory={subdir}'
return req
def find_path_to_project_root_from_repo_root(
location: str, repo_root: str
) -> Optional[str]:
location: str, repo_root: str,
) -> str | None:
"""
Find the the Python project's root by searching up the filesystem from
`location`. Return the path to project root relative to `repo_root`.
@ -99,8 +95,8 @@ def find_path_to_project_root_from_repo_root(
# We've traversed up to the root of the filesystem without
# finding a Python project.
logger.warning(
"Could not find a Python project for directory %s (tried all "
"parent directories)",
'Could not find a Python project for directory %s (tried all '
'parent directories)',
orig_location,
)
return None
@ -132,9 +128,9 @@ class RevOptions:
def __init__(
self,
vc_class: Type["VersionControl"],
rev: Optional[str] = None,
extra_args: Optional[CommandArgs] = None,
vc_class: type[VersionControl],
rev: str | None = None,
extra_args: CommandArgs | None = None,
) -> None:
"""
Args:
@ -148,13 +144,13 @@ class RevOptions:
self.extra_args = extra_args
self.rev = rev
self.vc_class = vc_class
self.branch_name: Optional[str] = None
self.branch_name: str | None = None
def __repr__(self) -> str:
return f"<RevOptions {self.vc_class.name}: rev={self.rev!r}>"
return f'<RevOptions {self.vc_class.name}: rev={self.rev!r}>'
@property
def arg_rev(self) -> Optional[str]:
def arg_rev(self) -> str | None:
if self.rev is None:
return self.vc_class.default_arg_rev
@ -174,11 +170,11 @@ class RevOptions:
def to_display(self) -> str:
if not self.rev:
return ""
return ''
return f" (to revision {self.rev})"
return f' (to revision {self.rev})'
def make_new(self, rev: str) -> "RevOptions":
def make_new(self, rev: str) -> RevOptions:
"""
Make a copy of the current instance, but with a new rev.
@ -189,8 +185,8 @@ class RevOptions:
class VcsSupport:
_registry: Dict[str, "VersionControl"] = {}
schemes = ["ssh", "git", "hg", "bzr", "sftp", "svn"]
_registry: dict[str, VersionControl] = {}
schemes = ['ssh', 'git', 'hg', 'bzr', 'sftp', 'svn']
def __init__(self) -> None:
# Register more schemes with urlparse for various version control
@ -202,33 +198,33 @@ class VcsSupport:
return self._registry.__iter__()
@property
def backends(self) -> List["VersionControl"]:
def backends(self) -> list[VersionControl]:
return list(self._registry.values())
@property
def dirnames(self) -> List[str]:
def dirnames(self) -> list[str]:
return [backend.dirname for backend in self.backends]
@property
def all_schemes(self) -> List[str]:
schemes: List[str] = []
def all_schemes(self) -> list[str]:
schemes: list[str] = []
for backend in self.backends:
schemes.extend(backend.schemes)
return schemes
def register(self, cls: Type["VersionControl"]) -> None:
if not hasattr(cls, "name"):
logger.warning("Cannot register VCS %s", cls.__name__)
def register(self, cls: type[VersionControl]) -> None:
if not hasattr(cls, 'name'):
logger.warning('Cannot register VCS %s', cls.__name__)
return
if cls.name not in self._registry:
self._registry[cls.name] = cls()
logger.debug("Registered VCS backend: %s", cls.name)
logger.debug('Registered VCS backend: %s', cls.name)
def unregister(self, name: str) -> None:
if name in self._registry:
del self._registry[name]
def get_backend_for_dir(self, location: str) -> Optional["VersionControl"]:
def get_backend_for_dir(self, location: str) -> VersionControl | None:
"""
Return a VersionControl object if a repository of that type is found
at the given directory.
@ -238,7 +234,7 @@ class VcsSupport:
repo_path = vcs_backend.get_repository_root(location)
if not repo_path:
continue
logger.debug("Determine that %s uses VCS: %s", location, vcs_backend.name)
logger.debug('Determine that %s uses VCS: %s', location, vcs_backend.name)
vcs_backends[repo_path] = vcs_backend
if not vcs_backends:
@ -251,7 +247,7 @@ class VcsSupport:
inner_most_repo_path = max(vcs_backends, key=len)
return vcs_backends[inner_most_repo_path]
def get_backend_for_scheme(self, scheme: str) -> Optional["VersionControl"]:
def get_backend_for_scheme(self, scheme: str) -> VersionControl | None:
"""
Return a VersionControl object or None.
"""
@ -260,7 +256,7 @@ class VcsSupport:
return vcs_backend
return None
def get_backend(self, name: str) -> Optional["VersionControl"]:
def get_backend(self, name: str) -> VersionControl | None:
"""
Return a VersionControl object or None.
"""
@ -272,14 +268,14 @@ vcs = VcsSupport()
class VersionControl:
name = ""
dirname = ""
repo_name = ""
name = ''
dirname = ''
repo_name = ''
# List of supported schemes for this Version Control
schemes: Tuple[str, ...] = ()
schemes: tuple[str, ...] = ()
# Iterable of environment variable names to pass to call_subprocess().
unset_environ: Tuple[str, ...] = ()
default_arg_rev: Optional[str] = None
unset_environ: tuple[str, ...] = ()
default_arg_rev: str | None = None
@classmethod
def should_add_vcs_url_prefix(cls, remote_url: str) -> bool:
@ -287,10 +283,10 @@ class VersionControl:
Return whether the vcs prefix (e.g. "git+") should be added to a
repository's remote url when used in a requirement.
"""
return not remote_url.lower().startswith(f"{cls.name}:")
return not remote_url.lower().startswith(f'{cls.name}:')
@classmethod
def get_subdirectory(cls, location: str) -> Optional[str]:
def get_subdirectory(cls, location: str) -> str | None:
"""
Return the path to Python project root, relative to the repo root.
Return None if the project root is in the repo root.
@ -320,7 +316,7 @@ class VersionControl:
repo_url = cls.get_remote_url(repo_dir)
if cls.should_add_vcs_url_prefix(repo_url):
repo_url = f"{cls.name}+{repo_url}"
repo_url = f'{cls.name}+{repo_url}'
revision = cls.get_requirement_revision(repo_dir)
subdir = cls.get_subdirectory(repo_dir)
@ -329,7 +325,7 @@ class VersionControl:
return req
@staticmethod
def get_base_rev_args(rev: str) -> List[str]:
def get_base_rev_args(rev: str) -> list[str]:
"""
Return the base revision arguments for a vcs command.
@ -353,7 +349,7 @@ class VersionControl:
@classmethod
def make_rev_options(
cls, rev: Optional[str] = None, extra_args: Optional[CommandArgs] = None
cls, rev: str | None = None, extra_args: CommandArgs | None = None,
) -> RevOptions:
"""
Return a RevOptions object.
@ -375,8 +371,8 @@ class VersionControl:
@classmethod
def get_netloc_and_auth(
cls, netloc: str, scheme: str
) -> Tuple[str, Tuple[Optional[str], Optional[str]]]:
cls, netloc: str, scheme: str,
) -> tuple[str, tuple[str | None, str | None]]:
"""
Parse the repository URL's netloc, and return the new netloc to use
along with auth information.
@ -395,7 +391,7 @@ class VersionControl:
return netloc, (None, None)
@classmethod
def get_url_rev_and_auth(cls, url: str) -> Tuple[str, Optional[str], AuthInfo]:
def get_url_rev_and_auth(cls, url: str) -> tuple[str, str | None, AuthInfo]:
"""
Parse the repository URL to use, and return the URL, revision,
and auth info to use.
@ -403,44 +399,44 @@ class VersionControl:
Returns: (url, rev, (username, password)).
"""
scheme, netloc, path, query, frag = urllib.parse.urlsplit(url)
if "+" not in scheme:
if '+' not in scheme:
raise ValueError(
"Sorry, {!r} is a malformed VCS url. "
"The format is <vcs>+<protocol>://<url>, "
"e.g. svn+http://myrepo/svn/MyApp#egg=MyApp".format(url)
'Sorry, {!r} is a malformed VCS url. '
'The format is <vcs>+<protocol>://<url>, '
'e.g. svn+http://myrepo/svn/MyApp#egg=MyApp'.format(url),
)
# Remove the vcs prefix.
scheme = scheme.split("+", 1)[1]
scheme = scheme.split('+', 1)[1]
netloc, user_pass = cls.get_netloc_and_auth(netloc, scheme)
rev = None
if "@" in path:
path, rev = path.rsplit("@", 1)
if '@' in path:
path, rev = path.rsplit('@', 1)
if not rev:
raise InstallationError(
"The URL {!r} has an empty revision (after @) "
"which is not supported. Include a revision after @ "
"or remove @ from the URL.".format(url)
'The URL {!r} has an empty revision (after @) '
'which is not supported. Include a revision after @ '
'or remove @ from the URL.'.format(url),
)
url = urllib.parse.urlunsplit((scheme, netloc, path, query, ""))
url = urllib.parse.urlunsplit((scheme, netloc, path, query, ''))
return url, rev, user_pass
@staticmethod
def make_rev_args(
username: Optional[str], password: Optional[HiddenText]
username: str | None, password: HiddenText | None,
) -> CommandArgs:
"""
Return the RevOptions "extra arguments" to use in obtain().
"""
return []
def get_url_rev_options(self, url: HiddenText) -> Tuple[HiddenText, RevOptions]:
def get_url_rev_options(self, url: HiddenText) -> tuple[HiddenText, RevOptions]:
"""
Return the URL and RevOptions object to use in obtain(),
as a tuple (url, rev_options).
"""
secret_url, rev, user_pass = self.get_url_rev_and_auth(url.secret)
username, secret_password = user_pass
password: Optional[HiddenText] = None
password: HiddenText | None = None
if secret_password is not None:
password = hide_value(secret_password)
extra_args = self.make_rev_args(username, password)
@ -454,7 +450,7 @@ class VersionControl:
Normalize a URL for comparison by unquoting it and removing any
trailing slash.
"""
return urllib.parse.unquote(url).rstrip("/")
return urllib.parse.unquote(url).rstrip('/')
@classmethod
def compare_urls(cls, url1: str, url2: str) -> bool:
@ -464,7 +460,7 @@ class VersionControl:
return cls.normalize_url(url1) == cls.normalize_url(url2)
def fetch_new(
self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int
self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int,
) -> None:
"""
Fetch a revision from a repository, in the case that this is the
@ -496,7 +492,7 @@ class VersionControl:
raise NotImplementedError
@classmethod
def is_commit_id_equal(cls, dest: str, name: Optional[str]) -> bool:
def is_commit_id_equal(cls, dest: str, name: str | None) -> bool:
"""
Return whether the id of the current commit equals the given name.
@ -526,68 +522,68 @@ class VersionControl:
existing_url = self.get_remote_url(dest)
if self.compare_urls(existing_url, url.secret):
logger.debug(
"%s in %s exists, and has correct URL (%s)",
'%s in %s exists, and has correct URL (%s)',
self.repo_name.title(),
display_path(dest),
url,
)
if not self.is_commit_id_equal(dest, rev_options.rev):
logger.info(
"Updating %s %s%s",
'Updating %s %s%s',
display_path(dest),
self.repo_name,
rev_display,
)
self.update(dest, url, rev_options)
else:
logger.info("Skipping because already up-to-date.")
logger.info('Skipping because already up-to-date.')
return
logger.warning(
"%s %s in %s exists with URL %s",
'%s %s in %s exists with URL %s',
self.name,
self.repo_name,
display_path(dest),
existing_url,
)
prompt = ("(s)witch, (i)gnore, (w)ipe, (b)ackup ", ("s", "i", "w", "b"))
prompt = ('(s)witch, (i)gnore, (w)ipe, (b)ackup ', ('s', 'i', 'w', 'b'))
else:
logger.warning(
"Directory %s already exists, and is not a %s %s.",
'Directory %s already exists, and is not a %s %s.',
dest,
self.name,
self.repo_name,
)
# https://github.com/python/mypy/issues/1174
prompt = ("(i)gnore, (w)ipe, (b)ackup ", ("i", "w", "b")) # type: ignore
prompt = ('(i)gnore, (w)ipe, (b)ackup ', ('i', 'w', 'b')) # type: ignore
logger.warning(
"The plan is to install the %s repository %s",
'The plan is to install the %s repository %s',
self.name,
url,
)
response = ask_path_exists("What to do? {}".format(prompt[0]), prompt[1])
response = ask_path_exists(f'What to do? {prompt[0]}', prompt[1])
if response == "a":
if response == 'a':
sys.exit(-1)
if response == "w":
logger.warning("Deleting %s", display_path(dest))
if response == 'w':
logger.warning('Deleting %s', display_path(dest))
rmtree(dest)
self.fetch_new(dest, url, rev_options, verbosity=verbosity)
return
if response == "b":
if response == 'b':
dest_dir = backup_dir(dest)
logger.warning("Backing up %s to %s", display_path(dest), dest_dir)
logger.warning('Backing up %s to %s', display_path(dest), dest_dir)
shutil.move(dest, dest_dir)
self.fetch_new(dest, url, rev_options, verbosity=verbosity)
return
# Do nothing if the response is "i".
if response == "s":
if response == 's':
logger.info(
"Switching %s %s to %s%s",
'Switching %s %s to %s%s',
self.repo_name,
display_path(dest),
url,
@ -627,14 +623,14 @@ class VersionControl:
@classmethod
def run_command(
cls,
cmd: Union[List[str], CommandArgs],
cmd: list[str] | CommandArgs,
show_stdout: bool = True,
cwd: Optional[str] = None,
on_returncode: 'Literal["raise", "warn", "ignore"]' = "raise",
extra_ok_returncodes: Optional[Iterable[int]] = None,
command_desc: Optional[str] = None,
extra_environ: Optional[Mapping[str, Any]] = None,
spinner: Optional[SpinnerInterface] = None,
cwd: str | None = None,
on_returncode: Literal["raise", "warn", "ignore"] = 'raise',
extra_ok_returncodes: Iterable[int] | None = None,
command_desc: str | None = None,
extra_environ: Mapping[str, Any] | None = None,
spinner: SpinnerInterface | None = None,
log_failed_cmd: bool = True,
stdout_only: bool = False,
) -> str:
@ -664,8 +660,8 @@ class VersionControl:
# errno.ENOENT = no such file or directory
# In other words, the VCS executable isn't available
raise BadCommand(
f"Cannot find command {cls.name!r} - do you have "
f"{cls.name!r} installed and in your PATH?"
f'Cannot find command {cls.name!r} - do you have '
f'{cls.name!r} installed and in your PATH?',
)
except PermissionError:
# errno.EACCES = Permission denied
@ -673,11 +669,11 @@ class VersionControl:
# only for another user. So, the current user don't have
# permission to call the other user command.
raise BadCommand(
f"No permission to execute {cls.name!r} - install it "
f"locally, globally (ask admin), or check your PATH. "
f"See possible solutions at "
f"https://pip.pypa.io/en/latest/reference/pip_freeze/"
f"#fixing-permission-denied."
f'No permission to execute {cls.name!r} - install it '
f'locally, globally (ask admin), or check your PATH. '
f'See possible solutions at '
f'https://pip.pypa.io/en/latest/reference/pip_freeze/'
f'#fixing-permission-denied.',
)
@classmethod
@ -685,11 +681,11 @@ class VersionControl:
"""
Return whether a directory path is a repository directory.
"""
logger.debug("Checking in %s for %s (%s)...", path, cls.dirname, cls.name)
logger.debug('Checking in %s for %s (%s)...', path, cls.dirname, cls.name)
return os.path.exists(os.path.join(path, cls.dirname))
@classmethod
def get_repository_root(cls, location: str) -> Optional[str]:
def get_repository_root(cls, location: str) -> str | None:
"""
Return the "root" (top-level) directory controlled by the vcs,
or `None` if the directory is not in any.