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

445 lines
17 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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,
)