match_face/.venv/Lib/site-packages/pipenv/shells.py

249 lines
7.4 KiB
Python
Raw Normal View History

import collections
import contextlib
import os
import re
import signal
import subprocess
import sys
from pathlib import Path
from shutil import get_terminal_size
from pipenv.utils.shell import temp_environ
from pipenv.vendor import shellingham
ShellDetectionFailure = shellingham.ShellDetectionFailure
def _build_info(value):
return (os.path.splitext(os.path.basename(value))[0], value)
def detect_info(project):
if project.s.PIPENV_SHELL_EXPLICIT:
return _build_info(project.s.PIPENV_SHELL_EXPLICIT)
try:
return shellingham.detect_shell()
except (shellingham.ShellDetectionFailure, TypeError):
if project.s.PIPENV_SHELL:
return _build_info(project.s.PIPENV_SHELL)
raise ShellDetectionFailure
def _get_activate_script(cmd, venv):
"""Returns the string to activate a virtualenv.
This is POSIX-only at the moment since the compat (pexpect-based) shell
does not work elsewhere anyway.
"""
# Suffix and source command for other shells.
# Support for fish shell.
if "fish" in cmd:
suffix = ".fish"
command = "source"
# Support for csh shell.
elif "csh" in cmd:
suffix = ".csh"
command = "source"
elif "xonsh" in cmd:
suffix = ".xsh"
command = "source"
elif "nu" in cmd:
suffix = ".nu"
command = "overlay use"
else:
suffix = ""
command = "."
# Escape any special characters located within the virtualenv path to allow
# for proper activation.
venv_location = re.sub(r"([ &$()\[\]])", r"\\\1", str(venv))
# The leading space can make history cleaner in some shells.
return f" {command} {venv_location}/bin/activate{suffix}"
def _handover(cmd, args):
args = [cmd] + args
if os.name != "nt":
os.execvp(cmd, args)
else:
sys.exit(subprocess.call(args, shell=True, universal_newlines=True))
class Shell:
def __init__(self, cmd):
self.cmd = cmd
self.args = []
def __repr__(self):
return "{type(self).__name__}(cmd={self.cmd!r})"
@contextlib.contextmanager
def inject_path(self, venv):
with temp_environ():
os.environ["PATH"] = "{}{}{}".format(
os.pathsep.join(str(p.parent) for p in _iter_python(venv)),
os.pathsep,
os.environ["PATH"],
)
yield
def fork(self, venv, cwd, args):
# FIXME: This isn't necessarily the correct prompt. We should read the
# actual prompt by peeking into the activation script.
name = os.path.basename(venv)
os.environ["VIRTUAL_ENV"] = str(venv)
if "PROMPT" in os.environ:
os.environ["PROMPT"] = "({}) {}".format(name, os.environ["PROMPT"])
if "PS1" in os.environ:
os.environ["PS1"] = "({}) {}".format(name, os.environ["PS1"])
with self.inject_path(venv):
os.chdir(cwd)
_handover(self.cmd, self.args + list(args))
def fork_compat(self, venv, cwd, args):
from .vendor import pexpect
# Grab current terminal dimensions to replace the hardcoded default
# dimensions of pexpect.
dims = get_terminal_size()
with temp_environ():
c = pexpect.spawn(self.cmd, ["-i"], dimensions=(dims.lines, dims.columns))
c.sendline(_get_activate_script(self.cmd, venv))
if args:
c.sendline(" ".join(args))
# Handler for terminal resizing events
# Must be defined here to have the shell process in its context, since
# we can't pass it as an argument
def sigwinch_passthrough(sig, data):
dims = get_terminal_size()
c.setwinsize(dims.lines, dims.columns)
signal.signal(signal.SIGWINCH, sigwinch_passthrough)
# Interact with the new shell.
c.interact(escape_character=None)
c.close()
sys.exit(c.exitstatus)
POSSIBLE_ENV_PYTHON = [Path("bin", "python"), Path("Scripts", "python.exe")]
def _iter_python(venv):
for path in POSSIBLE_ENV_PYTHON:
full_path = Path(venv, path)
if full_path.is_file():
yield full_path
class Bash(Shell):
def _format_path(self, python):
return python.parent.as_posix()
# The usual PATH injection technique does not work with Bash.
# https://github.com/berdario/pew/issues/58#issuecomment-102182346
@contextlib.contextmanager
def inject_path(self, venv):
from tempfile import NamedTemporaryFile
bashrc_path = Path.home().joinpath(".bashrc")
with NamedTemporaryFile("w+") as rcfile:
if bashrc_path.is_file():
base_rc_src = f'source "{bashrc_path.as_posix()}"\n'
rcfile.write(base_rc_src)
export_path = 'export PATH="{}:$PATH"\n'.format(
":".join(self._format_path(python) for python in _iter_python(venv))
)
rcfile.write(export_path)
rcfile.flush()
self.args.extend(["--rcfile", rcfile.name])
yield
class MsysBash(Bash):
def _format_path(self, python):
s = super()._format_path(python)
if not python.drive:
return s
# Convert "C:/something" to "/c/something".
return f"/{s[0].lower()}{s[2:]}"
class CmderEmulatedShell(Shell):
def fork(self, venv, cwd, args):
if cwd:
os.environ["CMDER_START"] = cwd
super().fork(venv, cwd, args)
class CmderCommandPrompt(CmderEmulatedShell):
def fork(self, venv, cwd, args):
rc = os.path.expandvars("%CMDER_ROOT%\\vendor\\init.bat")
if os.path.exists(rc):
self.args.extend(["/k", rc])
super().fork(venv, cwd, args)
class CmderPowershell(Shell):
def fork(self, venv, cwd, args):
rc = os.path.expandvars("%CMDER_ROOT%\\vendor\\profile.ps1")
if os.path.exists(rc):
self.args.extend(
[
"-ExecutionPolicy",
"Bypass",
"-NoLogo",
"-NoProfile",
"-NoExit",
"-Command",
f"Invoke-Expression '. ''{rc}'''",
]
)
super().fork(venv, cwd, args)
# Two dimensional dict. First is the shell type, second is the emulator type.
# Example: SHELL_LOOKUP['powershell']['cmder'] => CmderPowershell.
SHELL_LOOKUP = collections.defaultdict(
lambda: collections.defaultdict(lambda: Shell),
{
"bash": collections.defaultdict(
lambda: Bash,
{"msys": MsysBash},
),
"cmd": collections.defaultdict(
lambda: Shell,
{"cmder": CmderCommandPrompt},
),
"powershell": collections.defaultdict(
lambda: Shell,
{"cmder": CmderPowershell},
),
"pwsh": collections.defaultdict(
lambda: Shell,
{"cmder": CmderPowershell},
),
},
)
def _detect_emulator():
keys = []
if os.environ.get("CMDER_ROOT"):
keys.append("cmder")
if os.environ.get("MSYSTEM"):
keys.append("msys")
return ",".join(keys)
def choose_shell(project):
emulator = project.s.PIPENV_EMULATOR.lower() or _detect_emulator()
type_, command = detect_info(project)
shell_types = SHELL_LOOKUP[type_]
for key in emulator.split(","):
key = key.strip().lower()
if key in shell_types:
return shell_types[key](command)
return shell_types[""](command)