match_face/.venv/Lib/site-packages/pipenv/routines/install.py

668 lines
24 KiB
Python
Raw Normal View History

import os
import queue
import sys
import warnings
from collections import defaultdict
from tempfile import NamedTemporaryFile
from pipenv import environments, exceptions
from pipenv.patched.pip._internal.exceptions import PipError
from pipenv.routines.lock import do_lock
from pipenv.utils import console, err, fileutils
from pipenv.utils.dependencies import (
expansive_install_req_from_line,
get_lockfile_section_using_pipfile_category,
install_req_from_pipfile,
)
from pipenv.utils.indexes import get_source_list
from pipenv.utils.internet import download_file, is_valid_url
from pipenv.utils.pip import (
get_trusted_hosts,
pip_install_deps,
)
from pipenv.utils.pipfile import ensure_pipfile
from pipenv.utils.project import ensure_project
from pipenv.utils.requirements import add_index_to_pipfile, import_requirements
from pipenv.utils.shell import temp_environ
from pipenv.utils.virtualenv import cleanup_virtualenv, do_create_virtualenv
def do_install(
project,
packages=False,
editable_packages=False,
index=False,
dev=False,
python=False,
pypi_mirror=None,
system=False,
ignore_pipfile=False,
requirementstxt=False,
pre=False,
deploy=False,
site_packages=None,
extra_pip_args=None,
categories=None,
skip_lock=False,
):
requirements_directory = fileutils.create_tracked_tempdir(
suffix="-requirements", prefix="pipenv-"
)
warnings.filterwarnings("default", category=ResourceWarning)
packages = packages if packages else []
editable_packages = editable_packages if editable_packages else []
package_args = [p for p in packages if p] + [p for p in editable_packages if p]
skip_requirements = False
# Don't search for requirements.txt files if the user provides one
if requirementstxt or package_args or project.pipfile_exists:
skip_requirements = True
# Ensure that virtualenv is available and pipfile are available
ensure_project(
project,
python=python,
system=system,
warn=True,
deploy=deploy,
skip_requirements=skip_requirements,
pypi_mirror=pypi_mirror,
site_packages=site_packages,
categories=categories,
)
do_install_validations(
project,
package_args,
requirements_directory,
dev=dev,
system=system,
ignore_pipfile=ignore_pipfile,
requirementstxt=requirementstxt,
pre=pre,
deploy=deploy,
categories=categories,
skip_lock=skip_lock,
)
# Install all dependencies, if none was provided.
# This basically ensures that we have a pipfile and lockfile, then it locks and
# installs from the lockfile
new_packages = []
if not packages and not editable_packages:
if pre:
project.update_settings({"allow_prereleases": pre})
do_init(
project,
allow_global=system,
ignore_pipfile=ignore_pipfile,
system=system,
deploy=deploy,
pre=pre,
requirements_dir=requirements_directory,
pypi_mirror=pypi_mirror,
extra_pip_args=extra_pip_args,
categories=categories,
skip_lock=skip_lock,
)
# This is for if the user passed in dependencies; handle with the update routine
else:
pkg_list = packages + [f"-e {pkg}" for pkg in editable_packages]
if not system and not project.virtualenv_exists:
do_init(
project,
system=system,
allow_global=system,
requirements_dir=requirements_directory,
deploy=deploy,
pypi_mirror=pypi_mirror,
extra_pip_args=extra_pip_args,
categories=categories,
skip_lock=skip_lock,
packages=packages,
editable_packages=editable_packages,
)
for pkg_line in pkg_list:
console.print(
f"Installing {pkg_line}...",
style="bold green",
)
# pip install:
with temp_environ(), console.status(
"Installing...", spinner=project.s.PIPENV_SPINNER
) as st:
if not system:
os.environ["PIP_USER"] = "0"
if "PYTHONHOME" in os.environ:
del os.environ["PYTHONHOME"]
st.console.print(f"Resolving {pkg_line}...", markup=False)
try:
pkg_requirement, _ = expansive_install_req_from_line(
pkg_line, expand_env=True
)
except ValueError as e:
err.print(f"[red]WARNING[/red]: {e}")
err.print(
environments.PIPENV_SPINNER_FAIL_TEXT.format(
"Installation Failed"
)
)
sys.exit(1)
st.update(f"Installing {pkg_requirement.name}...")
if categories:
pipfile_sections = ""
for c in categories:
pipfile_sections += f"[{c}]"
elif dev:
pipfile_sections = "[dev-packages]"
else:
pipfile_sections = "[packages]"
# Add the package to the Pipfile.
if index:
source = project.get_index_by_name(index)
default_index = project.get_default_index()["name"]
if not source:
index_name = add_index_to_pipfile(project, index)
if index_name != default_index:
pkg_requirement.index = index_name
elif source["name"] != default_index:
pkg_requirement.index = source["name"]
try:
if categories:
for category in categories:
added, cat, normalized_name = project.add_package_to_pipfile(
pkg_requirement, pkg_line, dev, category
)
if added:
new_packages.append((normalized_name, cat))
st.console.print(
f"[bold]Added [green]{normalized_name}[/green][/bold] to Pipfile's "
f"[yellow]\\{pipfile_sections}[/yellow] ..."
)
else:
added, cat, normalized_name = project.add_package_to_pipfile(
pkg_requirement, pkg_line, dev
)
if added:
new_packages.append((normalized_name, cat))
st.console.print(
f"[bold]Added [green]{normalized_name}[/green][/bold] to Pipfile's "
f"[yellow]\\{pipfile_sections}[/yellow] ..."
)
except ValueError:
import traceback
err.print(f"[bold][red]Error:[/red][/bold] {traceback.format_exc()}")
err.print(
environments.PIPENV_SPINNER_FAIL_TEXT.format(
"Failed adding package to Pipfile"
)
)
# ok has a nice v in front, should do something similar with rich
st.console.print(
environments.PIPENV_SPINNER_OK_TEXT.format("Installation Succeeded")
)
# Update project settings with pre-release preference.
if pre:
project.update_settings({"allow_prereleases": pre})
try:
do_init(
project,
system=system,
allow_global=system,
requirements_dir=requirements_directory,
deploy=deploy,
pypi_mirror=pypi_mirror,
extra_pip_args=extra_pip_args,
categories=categories,
skip_lock=skip_lock,
)
do_install_dependencies(
project,
dev=dev,
allow_global=system,
requirements_dir=requirements_directory,
pypi_mirror=pypi_mirror,
extra_pip_args=extra_pip_args,
categories=categories,
skip_lock=skip_lock,
)
except Exception as e:
# If we fail to install, remove the package from the Pipfile.
for pkg_name, category in new_packages:
project.remove_package_from_pipfile(pkg_name, category)
raise e
sys.exit(0)
def do_install_validations(
project,
package_args,
requirements_directory,
dev=False,
system=False,
ignore_pipfile=False,
requirementstxt=False,
pre=False,
deploy=False,
categories=None,
skip_lock=False,
):
# Don't attempt to install develop and default packages if Pipfile is missing
if not project.pipfile_exists and not (package_args or dev):
if not (ignore_pipfile or deploy):
raise exceptions.PipfileNotFound(project.path_to("Pipfile"))
elif ((skip_lock and deploy) or ignore_pipfile) and not project.lockfile_exists:
raise exceptions.LockfileNotFound(project.path_to("Pipfile.lock"))
# Load the --pre settings from the Pipfile.
if not pre:
pre = project.settings.get("allow_prereleases")
remote = requirementstxt and is_valid_url(requirementstxt)
if "default" in categories:
raise exceptions.PipenvUsageError(
message="Cannot install to category `default`-- did you mean `packages`?"
)
if "develop" in categories:
raise exceptions.PipenvUsageError(
message="Cannot install to category `develop`-- did you mean `dev-packages`?"
)
# Warn and exit if --system is used without a pipfile.
if (system and package_args) and not project.s.PIPENV_VIRTUALENV:
raise exceptions.SystemUsageError
# Automatically use an activated virtualenv.
if project.s.PIPENV_USE_SYSTEM:
system = True
if system:
project.s.PIPENV_USE_SYSTEM = True
os.environ["PIPENV_USE_SYSTEM"] = "1"
# Check if the file is remote or not
if remote:
err.print(
"Remote requirements file provided! Downloading...",
style="bold",
)
fd = NamedTemporaryFile(
prefix="pipenv-", suffix="-requirement.txt", dir=requirements_directory
)
temp_reqs = fd.name
requirements_url = requirementstxt
# Download requirements file
try:
download_file(requirements_url, temp_reqs, project.s.PIPENV_MAX_RETRIES)
except OSError:
fd.close()
os.unlink(temp_reqs)
err.print(
f"Unable to find requirements file at {requirements_url}.",
style="red",
)
sys.exit(1)
finally:
fd.close()
# Replace the url with the temporary requirements file
requirementstxt = temp_reqs
if requirementstxt:
error, traceback = None, None
err.print(
"Requirements file provided! Importing into Pipfile...",
style="bold",
)
try:
import_requirements(
project,
r=project.path_to(requirementstxt),
dev=dev,
categories=categories,
)
except (UnicodeDecodeError, PipError) as e:
# Don't print the temp file path if remote since it will be deleted.
req_path = project.path_to(requirementstxt)
error = f"Unexpected syntax in {req_path}. Are you sure this is a requirements.txt style file?"
traceback = e
except AssertionError as e:
error = (
"Requirements file doesn't appear to exist. Please ensure the file exists in your "
"project directory or you provided the correct path."
)
traceback = e
finally:
if error and traceback:
console.print(error, style="red")
err.print(str(traceback), style="yellow")
sys.exit(1)
def do_install_dependencies(
project,
dev=False,
bare=False,
allow_global=False,
ignore_hashes=False,
requirements_dir=None,
pypi_mirror=None,
extra_pip_args=None,
categories=None,
skip_lock=False,
):
"""
Executes the installation functionality.
"""
procs = queue.Queue(maxsize=1)
if not categories:
if dev:
categories = ["packages", "dev-packages"]
else:
categories = ["packages"]
for category in categories:
lockfile = None
pipfile = None
if skip_lock:
ignore_hashes = True
if not bare:
console.print("Installing dependencies from Pipfile...", style="bold")
pipfile = project.get_pipfile_section(category)
else:
lockfile = project.get_or_create_lockfile(categories=categories)
if not bare:
console.print(
f"Installing dependencies from Pipfile.lock "
f"({lockfile['_meta'].get('hash', {}).get('sha256')[-6:]})...",
style="bold",
)
if skip_lock:
deps_list = []
for req_name, pipfile_entry in pipfile.items():
install_req, markers, req_line = install_req_from_pipfile(
req_name, pipfile_entry
)
req_line = f"{req_line}; {markers}" if markers else f"{req_line}"
deps_list.append(
(
install_req,
req_line,
)
)
else:
deps_list = list(
lockfile.get_requirements(dev=dev, only=False, categories=[category])
)
editable_or_vcs_deps = [
(dep, pip_line) for dep, pip_line in deps_list if (dep.link and dep.editable)
]
normal_deps = [
(dep, pip_line)
for dep, pip_line in deps_list
if not (dep.link and dep.editable)
]
install_kwargs = {
"no_deps": not skip_lock,
"ignore_hashes": ignore_hashes,
"allow_global": allow_global,
"pypi_mirror": pypi_mirror,
"sequential_deps": editable_or_vcs_deps,
"extra_pip_args": extra_pip_args,
}
if skip_lock:
lockfile_section = pipfile
else:
lockfile_category = get_lockfile_section_using_pipfile_category(category)
lockfile_section = lockfile[lockfile_category]
batch_install(
project,
normal_deps,
lockfile_section,
procs,
requirements_dir,
**install_kwargs,
)
if not procs.empty():
_cleanup_procs(project, procs)
def batch_install_iteration(
project,
deps_to_install,
sources,
procs,
requirements_dir,
no_deps=True,
ignore_hashes=False,
allow_global=False,
extra_pip_args=None,
):
with temp_environ():
if not allow_global:
os.environ["PIP_USER"] = "0"
if "PYTHONHOME" in os.environ:
del os.environ["PYTHONHOME"]
if "GIT_CONFIG" in os.environ:
del os.environ["GIT_CONFIG"]
cmds = pip_install_deps(
project,
deps=deps_to_install,
sources=sources,
allow_global=allow_global,
ignore_hashes=ignore_hashes,
no_deps=no_deps,
requirements_dir=requirements_dir,
use_pep517=True,
extra_pip_args=extra_pip_args,
)
for c in cmds:
procs.put(c)
_cleanup_procs(project, procs)
def batch_install(
project,
deps_list,
lockfile_section,
procs,
requirements_dir,
no_deps=True,
ignore_hashes=False,
allow_global=False,
pypi_mirror=None,
sequential_deps=None,
extra_pip_args=None,
):
if sequential_deps is None:
sequential_deps = []
deps_to_install = deps_list[:]
deps_to_install.extend(sequential_deps)
deps_to_install = [
(dep, pip_line)
for dep, pip_line in deps_to_install
if not project.environment.is_satisfied(dep)
]
search_all_sources = project.settings.get("install_search_all_sources", False)
sources = get_source_list(
project,
index=None,
extra_indexes=None,
trusted_hosts=get_trusted_hosts(),
pypi_mirror=pypi_mirror,
)
if search_all_sources:
dependencies = [pip_line for _, pip_line in deps_to_install]
batch_install_iteration(
project,
dependencies,
sources,
procs,
requirements_dir,
no_deps=no_deps,
ignore_hashes=ignore_hashes,
allow_global=allow_global,
extra_pip_args=extra_pip_args,
)
else:
# Sort the dependencies out by index -- include editable/vcs in the default group
deps_by_index = defaultdict(list)
for dependency, pip_line in deps_to_install:
index = project.sources_default["name"]
if dependency.name and dependency.name in lockfile_section:
entry = lockfile_section[dependency.name]
if isinstance(entry, dict) and "index" in entry:
index = entry["index"]
deps_by_index[index].append(pip_line)
# Treat each index as its own pip install phase
for index_name, dependencies in deps_by_index.items():
try:
install_source = next(filter(lambda s: s["name"] == index_name, sources))
batch_install_iteration(
project,
dependencies,
[install_source],
procs,
requirements_dir,
no_deps=no_deps,
ignore_hashes=ignore_hashes,
allow_global=allow_global,
extra_pip_args=extra_pip_args,
)
except StopIteration: # noqa: PERF203
console.print(
f"Unable to find {index_name} in sources, please check dependencies: {dependencies}",
style="bold red",
)
sys.exit(1)
def _cleanup_procs(project, procs):
while not procs.empty():
c = procs.get()
try:
out, err = c.communicate()
except AttributeError:
out, err = c.stdout, c.stderr
failed = c.returncode != 0
if project.s.is_verbose():
console.print(out.strip() or err.strip(), style="yellow")
# The Installation failed...
if failed:
# The Installation failed...
# We echo both c.stdout and c.stderr because pip returns error details on out.
err = err.strip().splitlines() if err else []
out = out.strip().splitlines() if out else []
err_lines = [line for message in [out, err] for line in message]
deps = getattr(c, "deps", {}).copy()
# Return the subprocess' return code.
raise exceptions.InstallError(deps, extra=err_lines)
def do_init(
project,
allow_global=False,
ignore_pipfile=False,
system=False,
deploy=False,
pre=False,
requirements_dir=None,
pypi_mirror=None,
extra_pip_args=None,
categories=None,
skip_lock=False,
packages=None,
editable_packages=None,
):
from pipenv.routines.update import do_update
python = None
if project.s.PIPENV_PYTHON is not None:
python = project.s.PIPENV_PYTHON
elif project.s.PIPENV_DEFAULT_PYTHON_VERSION is not None:
python = project.s.PIPENV_DEFAULT_PYTHON_VERSION
if categories is None:
categories = []
if not system and not project.s.PIPENV_USE_SYSTEM and not project.virtualenv_exists:
try:
do_create_virtualenv(project, python=python, pypi_mirror=pypi_mirror)
except KeyboardInterrupt:
cleanup_virtualenv(project, bare=False)
sys.exit(1)
# Ensure the Pipfile exists.
if not deploy:
ensure_pipfile(project, system=system)
if not requirements_dir:
requirements_dir = fileutils.create_tracked_tempdir(
suffix="-requirements", prefix="pipenv-"
)
# Write out the lockfile if it doesn't exist, but not if the Pipfile is being ignored
if (project.lockfile_exists and not ignore_pipfile) and not skip_lock:
old_hash = project.get_lockfile_hash()
new_hash = project.calculate_pipfile_hash()
if new_hash != old_hash:
if deploy:
console.print(
f"Your Pipfile.lock ({old_hash[-6:]}) is out of date. Expected: ({new_hash[-6:]}).",
style="red",
)
raise exceptions.DeployException
if (system or allow_global) and not (project.s.PIPENV_VIRTUALENV):
err.print(
f"Pipfile.lock ({old_hash[-6:]}) out of date, but installation uses --system so"
f"re-building lockfile must happen in isolation."
f" Please rebuild lockfile in a virtualenv. Continuing anyway...",
style="yellow",
)
else:
if old_hash:
msg = "Pipfile.lock ({0}) out of date: run `pipfile lock` to update to ({1})..."
else:
msg = "Pipfile.lock is corrupt, replaced with ({1})..."
err.print(
msg.format(old_hash[-6:], new_hash[-6:]),
style="bold yellow",
)
do_update(
project,
pre=pre,
system=system,
pypi_mirror=pypi_mirror,
categories=categories,
packages=packages,
editable_packages=editable_packages,
)
# Write out the lockfile if it doesn't exist.
if not project.lockfile_exists:
# Unless we're in a virtualenv not managed by pipenv, abort if we're
# using the system's python.
if (
(system or allow_global)
and not (project.s.PIPENV_VIRTUALENV)
and skip_lock is False
):
raise exceptions.PipenvOptionsError(
"--system",
"--system is intended to be used for Pipfile installation, "
"not installation of specific packages. Aborting.\n"
"See also: --deploy flag.",
)
else:
err.print(
"Pipfile.lock not found, creating...",
style="bold",
)
do_lock(
project,
system=system,
pre=pre,
write=True,
pypi_mirror=pypi_mirror,
categories=categories,
)
# Hint the user what to do to activate the virtualenv.
if not allow_global and not deploy and "PIPENV_ACTIVE" not in os.environ:
console.print(
"To activate this project's virtualenv, run [yellow]pipenv shell[/yellow].\n"
"Alternatively, run a command inside the virtualenv with [yellow]pipenv run[/yellow]."
)