match_face/.venv/Lib/site-packages/pipenv/utils/virtualenv.py

445 lines
17 KiB
Python
Raw Normal View History

import contextlib
import os
import re
import shutil
import sys
from pathlib import Path
from pipenv import environments, exceptions
from pipenv.patched.pip._vendor import rich
from pipenv.utils.dependencies import python_version
from pipenv.utils.environment import ensure_environment
from pipenv.utils.processes import subprocess_run
from pipenv.utils.shell import find_python, shorten_path
from pipenv.vendor import click
console = rich.console.Console()
err = rich.console.Console(stderr=True)
def warn_in_virtualenv(project):
# Only warn if pipenv isn't already active.
if environments.is_in_virtualenv() and not project.s.is_quiet():
click.echo(
"{}: Pipenv found itself running within a virtual environment, "
"so it will automatically use that environment, instead of "
"creating its own for any project. You can set "
"{} to force pipenv to ignore that environment and create "
"its own instead. You can set {} to suppress this "
"warning.".format(
click.style("Courtesy Notice", fg="green"),
click.style("PIPENV_IGNORE_VIRTUALENVS=1", bold=True),
click.style("PIPENV_VERBOSITY=-1", bold=True),
),
err=True,
)
def do_create_virtualenv(project, python=None, site_packages=None, pypi_mirror=None):
"""Creates a virtualenv."""
click.secho("Creating a virtualenv for this project...", bold=True, err=True)
click.echo(
"Pipfile: " + click.style(project.pipfile_location, fg="yellow", bold=True),
err=True,
)
# Default to using sys.executable, if Python wasn't provided.
using_string = "Using"
if not python:
python = sys.executable
using_string = "Using default python from"
click.echo(
"{} {} {} {}".format(
click.style(using_string, bold=True),
click.style(python, fg="yellow", bold=True),
click.style(f"({python_version(python)})", fg="green"),
click.style("to create virtualenv...", bold=True),
),
err=True,
)
if site_packages:
click.secho("Making site-packages available...", bold=True, err=True)
if pypi_mirror:
pip_config = {"PIP_INDEX_URL": pypi_mirror}
else:
pip_config = {}
error = None
with console.status(
"Creating virtual environment...", spinner=project.s.PIPENV_SPINNER
):
cmd = _create_virtualenv_cmd(project, python, site_packages=site_packages)
c = subprocess_run(cmd, env=pip_config)
click.secho(f"{c.stdout}", fg="cyan", err=True)
if c.returncode != 0:
error = (
c.stderr if project.s.is_verbose() else exceptions.prettify_exc(c.stderr)
)
err.print(
environments.PIPENV_SPINNER_FAIL_TEXT.format(
"Failed creating virtual environment"
)
)
else:
err.print(
environments.PIPENV_SPINNER_OK_TEXT.format(
"Successfully created virtual environment!"
)
)
if error is not None:
raise exceptions.VirtualenvCreationException(
extra=click.style(f"{error}", fg="red")
)
# Associate project directory with the environment.
project_file_name = os.path.join(project.virtualenv_location, ".project")
with open(project_file_name, "w") as f:
f.write(project.project_directory)
from pipenv.environment import Environment
sources = project.pipfile_sources()
# project.get_location_for_virtualenv is only for if we are creating a new virtualenv
# whereas virtualenv_location is for the current path to the runtime
project._environment = Environment(
prefix=project.virtualenv_location,
is_venv=True,
sources=sources,
pipfile=project.parsed_pipfile,
project=project,
)
# Say where the virtualenv is.
do_where(project, virtualenv=True, bare=False)
def _create_virtualenv_cmd(project, python, site_packages=False):
cmd = [
Path(sys.executable).absolute().as_posix(),
"-m",
"virtualenv",
]
if project.s.PIPENV_VIRTUALENV_CREATOR:
cmd.append(f"--creator={project.s.PIPENV_VIRTUALENV_CREATOR}")
cmd.append(f"--prompt={project.name}")
cmd.append(f"--python={python}")
cmd.append(project.get_location_for_virtualenv())
if project.s.PIPENV_VIRTUALENV_COPIES:
cmd.append("--copies")
# Pass site-packages flag to virtualenv, if desired...
if site_packages:
cmd.append("--system-site-packages")
return cmd
def ensure_virtualenv(project, python=None, site_packages=None, pypi_mirror=None):
"""Creates a virtualenv, if one doesn't exist."""
def abort():
sys.exit(1)
if not project.virtualenv_exists:
try:
# Ensure environment variables are set properly.
ensure_environment()
# Ensure Python is available.
python = ensure_python(project, python=python)
if python is not None and not isinstance(python, str):
python = python.path.as_posix()
# Create the virtualenv.
# Abort if --system (or running in a virtualenv).
if project.s.PIPENV_USE_SYSTEM:
click.secho(
"You are attempting to recreate a virtualenv that "
"Pipenv did not create. Aborting.",
fg="red",
)
sys.exit(1)
do_create_virtualenv(
project,
python=python,
site_packages=site_packages,
pypi_mirror=pypi_mirror,
)
except KeyboardInterrupt:
# If interrupted, cleanup the virtualenv.
cleanup_virtualenv(project, bare=False)
sys.exit(1)
# If --python or was passed...
elif (python) or (site_packages is not None):
project.s.USING_DEFAULT_PYTHON = False
# Ensure python is installed before deleting existing virtual env
python = ensure_python(project, python=python)
if python is not None and not isinstance(python, str):
python = python.path.as_posix()
click.secho("Virtualenv already exists!", fg="red", err=True)
# If VIRTUAL_ENV is set, there is a possibility that we are
# going to remove the active virtualenv that the user cares
# about, so confirm first.
if "VIRTUAL_ENV" in os.environ and not (
project.s.PIPENV_YES
or click.confirm("Use existing virtualenv?", default=True)
):
abort()
click.echo(click.style("Using existing virtualenv...", bold=True), err=True)
# Remove the virtualenv.
cleanup_virtualenv(project, bare=True)
# Call this function again.
ensure_virtualenv(
project,
python=python,
site_packages=site_packages,
pypi_mirror=pypi_mirror,
)
def cleanup_virtualenv(project, bare=True):
"""Removes the virtualenv directory from the system."""
if not bare:
click.secho("Environment creation aborted.", fg="red")
try:
# Delete the virtualenv.
shutil.rmtree(project.virtualenv_location)
except OSError as e:
click.echo(
"{} An error occurred while removing {}!".format(
click.style("Error: ", fg="red", bold=True),
click.style(project.virtualenv_location, fg="green"),
),
err=True,
)
click.secho(e, fg="cyan", err=True)
def ensure_python(project, python=None):
# Runtime import is necessary due to the possibility that the environments module may have been reloaded.
if project.s.PIPENV_PYTHON and not python:
python = project.s.PIPENV_PYTHON
def abort(msg=""):
click.echo(
"{}\nYou can specify specific versions of Python with:\n{}".format(
click.style(msg, fg="red"),
click.style(
"$ pipenv --python {}".format(os.sep.join(("path", "to", "python"))),
fg="yellow",
),
),
err=True,
)
sys.exit(1)
project.s.USING_DEFAULT_PYTHON = not python
# Find out which python is desired.
if not python:
python = project.required_python_version
if python:
range_pattern = r"^[<>]=?|!="
if re.search(range_pattern, python):
err.print(
f"[bold red]Error[/bold red]: Python version range specifier '[cyan]{python}[/cyan]' is not supported. "
"[yellow]Please use an absolute version number or specify the path to the Python executable on Pipfile.[/yellow]"
)
sys.exit(1)
if not python:
python = project.s.PIPENV_DEFAULT_PYTHON_VERSION
path_to_python = find_a_system_python(python)
if project.s.is_verbose():
click.echo(f"Using python: {python}", err=True)
click.echo(f"Path to python: {path_to_python}", err=True)
if not path_to_python and python is not None:
# We need to install Python.
click.echo(
"{}: Python {} {}".format(
click.style("Warning", fg="red", bold=True),
click.style(python, fg="cyan"),
"was not found on your system...",
),
err=True,
)
# check for python installers
from pipenv.installers import Asdf, InstallerError, InstallerNotFound, Pyenv
# prefer pyenv if both pyenv and asdf are installed as it's
# dedicated to python installs so probably the preferred
# method of the user for new python installs.
installer = None
if not project.s.PIPENV_DONT_USE_PYENV:
with contextlib.suppress(InstallerNotFound):
installer = Pyenv(project)
if installer is None and not project.s.PIPENV_DONT_USE_ASDF:
with contextlib.suppress(InstallerNotFound):
installer = Asdf(project)
if not installer:
abort("Neither 'pyenv' nor 'asdf' could be found to install Python.")
else:
if environments.SESSION_IS_INTERACTIVE or project.s.PIPENV_YES:
try:
version = installer.find_version_to_install(python)
except ValueError:
abort()
except InstallerError as e:
abort(f"Something went wrong while installing Python:\n{e.err}")
s = "{} {} {}".format(
"Would you like us to install",
click.style(f"CPython {version}", fg="green"),
f"with {installer}?",
)
# Prompt the user to continue...
if not (project.s.PIPENV_YES or click.confirm(s, default=True)):
abort()
else:
# Tell the user we're installing Python.
click.echo(
"{} {} {} {}{}".format(
click.style("Installing", bold=True),
click.style(f"CPython {version}", bold=True, fg="green"),
click.style(f"with {installer.cmd}", bold=True),
click.style("(this may take a few minutes)"),
click.style("...", bold=True),
)
)
with console.status(
"Installing python...", spinner=project.s.PIPENV_SPINNER
):
try:
c = installer.install(version)
except InstallerError as e:
err.print(
environments.PIPENV_SPINNER_FAIL_TEXT.format("Failed...")
)
click.echo("Something went wrong...", err=True)
click.secho(e.err, fg="cyan", err=True)
else:
console.print(
environments.PIPENV_SPINNER_OK_TEXT.format("Success!")
)
# Print the results, in a beautiful blue...
click.secho(c.stdout, fg="cyan", err=True)
# Find the newly installed Python, hopefully.
version = str(version)
path_to_python = find_a_system_python(version)
try:
assert python_version(path_to_python) == version
except AssertionError:
click.echo(
"{}: The Python you just installed is not available on your {}, apparently."
"".format(
click.style("Warning", fg="red", bold=True),
click.style("PATH", bold=True),
),
err=True,
)
sys.exit(1)
return path_to_python
def find_a_system_python(line):
"""Find a Python installation from a given line.
This tries to parse the line in various of ways:
* Looks like an absolute path? Use it directly.
* Looks like a py.exe call? Use py.exe to get the executable.
* Starts with "py" something? Looks like a python command. Try to find it
in PATH, and use it directly.
* Search for "python" and "pythonX.Y" executables in PATH to find a match.
* Nothing fits, return None.
"""
from pipenv.vendor.pythonfinder import Finder
finder = Finder(system=True, global_search=True)
if not line:
return next(iter(finder.find_all_python_versions()), None)
# Use the windows finder executable
if (line.startswith(("py ", "py.exe "))) and os.name == "nt":
line = line.split(" ", 1)[1].lstrip("-")
python_entry = find_python(finder, line)
return python_entry
def do_where(project, virtualenv=False, bare=True):
"""Executes the where functionality."""
if not virtualenv:
if not project.pipfile_exists:
click.echo(
"No Pipfile present at project home. Consider running "
"{} first to automatically generate a Pipfile for you."
"".format(click.style("`pipenv install`", fg="green")),
err=True,
)
return
location = project.pipfile_location
# Shorten the virtual display of the path to the virtualenv.
if not bare:
location = shorten_path(location)
click.echo(
"Pipfile found at {}.\n Considering this to be the project home."
"".format(click.style(location, fg="green")),
err=True,
)
else:
click.echo(project.project_directory)
else:
location = project.virtualenv_location
if not bare:
click.secho(f"Virtualenv location: {location}", fg="green", err=True)
else:
click.echo(location)
def inline_activate_virtual_environment(project):
root = project.virtualenv_location
if os.path.exists(os.path.join(root, "pyvenv.cfg")):
_inline_activate_venv(project)
else:
_inline_activate_virtualenv(project)
if "VIRTUAL_ENV" not in os.environ:
os.environ["VIRTUAL_ENV"] = root
def _inline_activate_venv(project):
"""Built-in venv doesn't have activate_this.py, but doesn't need it anyway.
As long as we find the correct executable, built-in venv sets up the
environment automatically.
See: https://bugs.python.org/issue21496#msg218455
"""
components = []
for name in ("bin", "Scripts"):
bindir = os.path.join(project.virtualenv_location, name)
if os.path.exists(bindir):
components.append(bindir)
if "PATH" in os.environ:
components.append(os.environ["PATH"])
os.environ["PATH"] = os.pathsep.join(components)
def _inline_activate_virtualenv(project):
try:
activate_this = project._which("activate_this.py")
if not activate_this or not os.path.exists(activate_this):
raise exceptions.VirtualenvActivationException()
with open(activate_this) as f:
code = compile(f.read(), activate_this, "exec")
exec(code, {"__file__": activate_this})
# Catch all errors, just in case.
except Exception:
click.echo(
"{}: There was an unexpected error while activating your "
"virtualenv. Continuing anyway...".format(
click.style("Warning", fg="red", bold=True)
),
err=True,
)