diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ba7ca3d63..058e686d5 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -45,8 +45,6 @@ jobs: image: [linux, windows, macOs] py35: image: [linux, windows, macOs] - py34: - image: [linux, windows] py27: image: [linux, windows, macOs] fix_lint: @@ -67,7 +65,7 @@ jobs: displayName: install fish and csh via brew coverage: with_toxenv: 'coverage' # generate .tox/.coverage, .tox/coverage.xml after tests run - for_envs: [py37, py36, py35, py34, py27] + for_envs: [py38, py37, py36, py35, py27] - ${{ if startsWith(variables['Build.SourceBranch'], 'refs/tags/') }}: - template: publish-pypi.yml@tox diff --git a/setup.cfg b/setup.cfg index 803f9bbb5..731beea2e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -53,7 +53,8 @@ where = src testing = pytest >= 4.0.0, <6 coverage >= 4.5.0, < 5 - pytest-mock + pytest-mock >= 1.12.1, <2 + xonsh >= 0.9.13, <1; python_version > '3.4' docs = sphinx >= 2.0.0, < 3 towncrier >= 18.5.0 @@ -61,6 +62,12 @@ docs = [options.package_data] virtualenv.seed.embed.wheels = *.whl +virtualenv.activation.bash = *.sh +virtualenv.activation.cshell = *.csh +virtualenv.activation.batch = *.bat +virtualenv.activation.fish = *.fish +virtualenv.activation.powershell = *.ps1 +virtualenv.activation.xonosh = *.xsh [options.entry_points] console_scripts = @@ -78,6 +85,13 @@ virtualenv.seed = pip = virtualenv.seed.embed.pip_invoke:PipInvoke link-app-data = virtualenv.seed.embed.link_app_data:LinkFromAppData virtualenv.activate = + bash = virtualenv.activation.bash:BashActivator + cshell = virtualenv.activation.cshell:CShellActivator + batch = virtualenv.activation.batch:BatchActivator + fish = virtualenv.activation.fish:FishActivator + power-shell = virtualenv.activation.powershell:PowerShellActivator + python = virtualenv.activation.python:PythonActivator + xonosh = virtualenv.activation.xonosh:XonoshActivator [sdist] formats = gztar diff --git a/src/virtualenv/activation/__init__.py b/src/virtualenv/activation/__init__.py index 01e6d4f49..dbe54fc1b 100644 --- a/src/virtualenv/activation/__init__.py +++ b/src/virtualenv/activation/__init__.py @@ -1 +1,19 @@ from __future__ import absolute_import, unicode_literals + +from .bash import BashActivator +from .batch import BatchActivator +from .cshell import CShellActivator +from .fish import FishActivator +from .powershell import PowerShellActivator +from .python import PythonActivator +from .xonosh import XonoshActivator + +__all__ = [ + BashActivator, + PowerShellActivator, + XonoshActivator, + CShellActivator, + PythonActivator, + BatchActivator, + FishActivator, +] diff --git a/src/virtualenv/activation/activator.py b/src/virtualenv/activation/activator.py index be8deed7d..d5cd10caa 100644 --- a/src/virtualenv/activation/activator.py +++ b/src/virtualenv/activation/activator.py @@ -12,13 +12,12 @@ def __init__(self, options): @classmethod def add_parser_arguments(cls, parser): - pass + """add activator options""" - def run(self, creator): - self.generate() - if self.flag_prompt is not None: - creator.pyenv_cfg["prompt"] = self.flag_prompt + @classmethod + def supports(cls, interpreter): + return True @abstractmethod - def generate(self): + def generate(self, creator): raise NotImplementedError diff --git a/src/virtualenv/activation/bash/__init__.py b/src/virtualenv/activation/bash/__init__.py new file mode 100644 index 000000000..fd5374149 --- /dev/null +++ b/src/virtualenv/activation/bash/__init__.py @@ -0,0 +1,14 @@ +from __future__ import absolute_import, unicode_literals + +from pathlib2 import Path + +from ..via_template import ViaTemplateActivator + + +class BashActivator(ViaTemplateActivator): + @classmethod + def supports(cls, interpreter): + return interpreter.os != "nt" + + def templates(self): + yield Path("activate.sh") diff --git a/src/virtualenv/activation/bash/activate.sh b/src/virtualenv/activation/bash/activate.sh new file mode 100644 index 000000000..d9b878154 --- /dev/null +++ b/src/virtualenv/activation/bash/activate.sh @@ -0,0 +1,84 @@ +# This file must be used with "source bin/activate" *from bash* +# you cannot run it directly + + +if [ "${BASH_SOURCE-}" = "$0" ]; then + echo "You must source this script: \$ source $0" >&2 + exit 33 +fi + +deactivate () { + unset -f pydoc >/dev/null 2>&1 + + # reset old environment variables + # ! [ -z ${VAR+_} ] returns true if VAR is declared at all + if ! [ -z "${_OLD_VIRTUAL_PATH:+_}" ] ; then + PATH="$_OLD_VIRTUAL_PATH" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if ! [ -z "${_OLD_VIRTUAL_PYTHONHOME+_}" ] ; then + PYTHONHOME="$_OLD_VIRTUAL_PYTHONHOME" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # This should detect bash and zsh, which have a hash command that must + # be called to get it to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + if [ -n "${BASH-}" ] || [ -n "${ZSH_VERSION-}" ] ; then + hash -r 2>/dev/null + fi + + if ! [ -z "${_OLD_VIRTUAL_PS1+_}" ] ; then + PS1="$_OLD_VIRTUAL_PS1" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + if [ ! "${1-}" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +VIRTUAL_ENV="__VIRTUAL_ENV__" +export VIRTUAL_ENV + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/__BIN_NAME__:$PATH" +export PATH + +# unset PYTHONHOME if set +if ! [ -z "${PYTHONHOME+_}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="$PYTHONHOME" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1-}" + if [ "x__VIRTUAL_PROMPT__" != x ] ; then + PS1="__VIRTUAL_PROMPT__${PS1-}" + else + PS1="(`basename \"$VIRTUAL_ENV\"`) ${PS1-}" + fi + export PS1 +fi + +# Make sure to unalias pydoc if it's already there +alias pydoc 2>/dev/null >/dev/null && unalias pydoc || true + +pydoc () { + python -m pydoc "$@" +} + +# This should detect bash and zsh, which have a hash command that must +# be called to get it to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +if [ -n "${BASH-}" ] || [ -n "${ZSH_VERSION-}" ] ; then + hash -r 2>/dev/null +fi diff --git a/src/virtualenv/activation/batch/__init__.py b/src/virtualenv/activation/batch/__init__.py new file mode 100644 index 000000000..4e4b83965 --- /dev/null +++ b/src/virtualenv/activation/batch/__init__.py @@ -0,0 +1,16 @@ +from __future__ import absolute_import, unicode_literals + +from pathlib2 import Path + +from ..via_template import ViaTemplateActivator + + +class BatchActivator(ViaTemplateActivator): + @classmethod + def supports(cls, interpreter): + return interpreter.os == "nt" + + def templates(self): + yield Path("activate.bat") + yield Path("deactivate.bat") + yield Path("pydoc.bat") diff --git a/src/virtualenv/activation/batch/activate.bat b/src/virtualenv/activation/batch/activate.bat new file mode 100644 index 000000000..96e835b52 --- /dev/null +++ b/src/virtualenv/activation/batch/activate.bat @@ -0,0 +1,35 @@ +@echo off + +set "VIRTUAL_ENV=__VIRTUAL_ENV__" + +if defined _OLD_VIRTUAL_PROMPT ( + set "PROMPT=%_OLD_VIRTUAL_PROMPT%" +) else ( + if not defined PROMPT ( + set "PROMPT=$P$G" + ) + if not defined VIRTUAL_ENV_DISABLE_PROMPT ( + set "_OLD_VIRTUAL_PROMPT=%PROMPT%" + ) +) +if not defined VIRTUAL_ENV_DISABLE_PROMPT ( + set "PROMPT=__VIRTUAL_PROMPT__%PROMPT%" +) + +REM Don't use () to avoid problems with them in %PATH% +if defined _OLD_VIRTUAL_PYTHONHOME goto ENDIFVHOME + set "_OLD_VIRTUAL_PYTHONHOME=%PYTHONHOME%" +:ENDIFVHOME + +set PYTHONHOME= + +REM if defined _OLD_VIRTUAL_PATH ( +if not defined _OLD_VIRTUAL_PATH goto ENDIFVPATH1 + set "PATH=%_OLD_VIRTUAL_PATH%" +:ENDIFVPATH1 +REM ) else ( +if defined _OLD_VIRTUAL_PATH goto ENDIFVPATH2 + set "_OLD_VIRTUAL_PATH=%PATH%" +:ENDIFVPATH2 + +set "PATH=%VIRTUAL_ENV%\__BIN_NAME__;%PATH%" diff --git a/src/virtualenv/activation/batch/deactivate.bat b/src/virtualenv/activation/batch/deactivate.bat new file mode 100644 index 000000000..7bbc56882 --- /dev/null +++ b/src/virtualenv/activation/batch/deactivate.bat @@ -0,0 +1,19 @@ +@echo off + +set VIRTUAL_ENV= + +REM Don't use () to avoid problems with them in %PATH% +if not defined _OLD_VIRTUAL_PROMPT goto ENDIFVPROMPT + set "PROMPT=%_OLD_VIRTUAL_PROMPT%" + set _OLD_VIRTUAL_PROMPT= +:ENDIFVPROMPT + +if not defined _OLD_VIRTUAL_PYTHONHOME goto ENDIFVHOME + set "PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME%" + set _OLD_VIRTUAL_PYTHONHOME= +:ENDIFVHOME + +if not defined _OLD_VIRTUAL_PATH goto ENDIFVPATH + set "PATH=%_OLD_VIRTUAL_PATH%" + set _OLD_VIRTUAL_PATH= +:ENDIFVPATH diff --git a/src/virtualenv/activation/batch/pydoc.bat b/src/virtualenv/activation/batch/pydoc.bat new file mode 100644 index 000000000..3d46a231a --- /dev/null +++ b/src/virtualenv/activation/batch/pydoc.bat @@ -0,0 +1 @@ +python.exe -m pydoc %* diff --git a/src/virtualenv/activation/cshell/__init__.py b/src/virtualenv/activation/cshell/__init__.py new file mode 100644 index 000000000..e818edeeb --- /dev/null +++ b/src/virtualenv/activation/cshell/__init__.py @@ -0,0 +1,14 @@ +from __future__ import absolute_import, unicode_literals + +from pathlib2 import Path + +from ..via_template import ViaTemplateActivator + + +class CShellActivator(ViaTemplateActivator): + @classmethod + def supports(cls, interpreter): + return interpreter.os != "nt" + + def templates(self): + yield Path("activate.csh") diff --git a/src/virtualenv/activation/cshell/activate.csh b/src/virtualenv/activation/cshell/activate.csh new file mode 100644 index 000000000..c4a6d584c --- /dev/null +++ b/src/virtualenv/activation/cshell/activate.csh @@ -0,0 +1,55 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. +# Created by Davide Di Blasi . + +set newline='\ +' + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH:q" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT:q" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate && unalias pydoc' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV "__VIRTUAL_ENV__" + +set _OLD_VIRTUAL_PATH="$PATH:q" +setenv PATH "$VIRTUAL_ENV:q/__BIN_NAME__:$PATH:q" + + + +if ("__VIRTUAL_PROMPT__" != "") then + set env_name = "__VIRTUAL_PROMPT__" +else + set env_name = '('"$VIRTUAL_ENV:t:q"') ' +endif + +if ( $?VIRTUAL_ENV_DISABLE_PROMPT ) then + if ( $VIRTUAL_ENV_DISABLE_PROMPT == "" ) then + set do_prompt = "1" + else + set do_prompt = "0" + endif +else + set do_prompt = "1" +endif + +if ( $do_prompt == "1" ) then + # Could be in a non-interactive environment, + # in which case, $prompt is undefined and we wouldn't + # care about the prompt anyway. + if ( $?prompt ) then + set _OLD_VIRTUAL_PROMPT="$prompt:q" + if ( "$prompt:q" =~ *"$newline:q"* ) then + : + else + set prompt = "$env_name:q$prompt:q" + endif + endif +endif + +unset env_name +unset do_prompt + +alias pydoc python -m pydoc + +rehash diff --git a/src/virtualenv/activation/fish/__init__.py b/src/virtualenv/activation/fish/__init__.py new file mode 100644 index 000000000..0e544068b --- /dev/null +++ b/src/virtualenv/activation/fish/__init__.py @@ -0,0 +1,14 @@ +from __future__ import absolute_import, unicode_literals + +from pathlib2 import Path + +from ..via_template import ViaTemplateActivator + + +class FishActivator(ViaTemplateActivator): + def templates(self): + yield Path("activate.fish") + + @classmethod + def supports(cls, interpreter): + return interpreter.os != "nt" diff --git a/src/virtualenv/activation/fish/activate.fish b/src/virtualenv/activation/fish/activate.fish new file mode 100644 index 000000000..4e2976864 --- /dev/null +++ b/src/virtualenv/activation/fish/activate.fish @@ -0,0 +1,102 @@ +# This file must be used using `source bin/activate.fish` *within a running fish ( http://fishshell.com ) session*. +# Do not run it directly. + +function _bashify_path -d "Converts a fish path to something bash can recognize" + set fishy_path $argv + set bashy_path $fishy_path[1] + for path_part in $fishy_path[2..-1] + set bashy_path "$bashy_path:$path_part" + end + echo $bashy_path +end + +function _fishify_path -d "Converts a bash path to something fish can recognize" + echo $argv | tr ':' '\n' +end + +function deactivate -d 'Exit virtualenv mode and return to the normal environment.' + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + # https://github.com/fish-shell/fish-shell/issues/436 altered PATH handling + if test (echo $FISH_VERSION | tr "." "\n")[1] -lt 3 + set -gx PATH (_fishify_path $_OLD_VIRTUAL_PATH) + else + set -gx PATH $_OLD_VIRTUAL_PATH + end + set -e _OLD_VIRTUAL_PATH + end + + if test -n "$_OLD_VIRTUAL_PYTHONHOME" + set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME + set -e _OLD_VIRTUAL_PYTHONHOME + end + + if test -n "$_OLD_FISH_PROMPT_OVERRIDE" + and functions -q _old_fish_prompt + # Set an empty local `$fish_function_path` to allow the removal of `fish_prompt` using `functions -e`. + set -l fish_function_path + + # Erase virtualenv's `fish_prompt` and restore the original. + functions -e fish_prompt + functions -c _old_fish_prompt fish_prompt + functions -e _old_fish_prompt + set -e _OLD_FISH_PROMPT_OVERRIDE + end + + set -e VIRTUAL_ENV + + if test "$argv[1]" != 'nondestructive' + # Self-destruct! + functions -e pydoc + functions -e deactivate + functions -e _bashify_path + functions -e _fishify_path + end +end + +# Unset irrelevant variables. +deactivate nondestructive + +set -gx VIRTUAL_ENV "__VIRTUAL_ENV__" + +# https://github.com/fish-shell/fish-shell/issues/436 altered PATH handling +if test (echo $FISH_VERSION | tr "." "\n")[1] -lt 3 + set -gx _OLD_VIRTUAL_PATH (_bashify_path $PATH) +else + set -gx _OLD_VIRTUAL_PATH $PATH +end +set -gx PATH "$VIRTUAL_ENV/__BIN_NAME__" $PATH + +# Unset `$PYTHONHOME` if set. +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +function pydoc + python -m pydoc $argv +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # Copy the current `fish_prompt` function as `_old_fish_prompt`. + functions -c fish_prompt _old_fish_prompt + + function fish_prompt + # Save the current $status, for fish_prompts that display it. + set -l old_status $status + + # Prompt override provided? + # If not, just prepend the environment name. + if test -n "__VIRTUAL_PROMPT__" + printf '%s%s' "__VIRTUAL_PROMPT__" (set_color normal) + else + printf '%s(%s) ' (set_color normal) (basename "$VIRTUAL_ENV") + end + + # Restore the original $status + echo "exit $old_status" | source + _old_fish_prompt + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" +end diff --git a/src/virtualenv/activation/powershell/__init__.py b/src/virtualenv/activation/powershell/__init__.py new file mode 100644 index 000000000..b5b0a7527 --- /dev/null +++ b/src/virtualenv/activation/powershell/__init__.py @@ -0,0 +1,10 @@ +from __future__ import absolute_import, unicode_literals + +from pathlib2 import Path + +from ..via_template import ViaTemplateActivator + + +class PowerShellActivator(ViaTemplateActivator): + def templates(self): + yield Path("activate.ps1") diff --git a/src/virtualenv/activation/powershell/activate.ps1 b/src/virtualenv/activation/powershell/activate.ps1 new file mode 100644 index 000000000..85b210308 --- /dev/null +++ b/src/virtualenv/activation/powershell/activate.ps1 @@ -0,0 +1,60 @@ +$script:THIS_PATH = $myinvocation.mycommand.path +$script:BASE_DIR = Split-Path (Resolve-Path "$THIS_PATH/..") -Parent + +function global:deactivate([switch] $NonDestructive) { + if (Test-Path variable:_OLD_VIRTUAL_PATH) { + $env:PATH = $variable:_OLD_VIRTUAL_PATH + Remove-Variable "_OLD_VIRTUAL_PATH" -Scope global + } + + if (Test-Path function:_old_virtual_prompt) { + $function:prompt = $function:_old_virtual_prompt + Remove-Item function:\_old_virtual_prompt + } + + if ($env:VIRTUAL_ENV) { + Remove-Item env:VIRTUAL_ENV -ErrorAction SilentlyContinue + } + + if (!$NonDestructive) { + # Self destruct! + Remove-Item function:deactivate + Remove-Item function:pydoc + } +} + +function global:pydoc { + python -m pydoc $args +} + +# unset irrelevant variables +deactivate -nondestructive + +$VIRTUAL_ENV = $BASE_DIR +$env:VIRTUAL_ENV = $VIRTUAL_ENV + +New-Variable -Scope global -Name _OLD_VIRTUAL_PATH -Value $env:PATH + +$env:PATH = "$env:VIRTUAL_ENV/__BIN_NAME____PATH_SEP__" + $env:PATH +if (!$env:VIRTUAL_ENV_DISABLE_PROMPT) { + function global:_old_virtual_prompt { + "" + } + $function:_old_virtual_prompt = $function:prompt + + if ("__VIRTUAL_PROMPT__" -ne "") { + function global:prompt { + # Add the custom prefix to the existing prompt + $previous_prompt_value = & $function:_old_virtual_prompt + ("__VIRTUAL_PROMPT__" + $previous_prompt_value) + } + } + else { + function global:prompt { + # Add a prefix to the current prompt, but don't discard it. + $previous_prompt_value = & $function:_old_virtual_prompt + $new_prompt_value = "($( Split-Path $env:VIRTUAL_ENV -Leaf )) " + ($new_prompt_value + $previous_prompt_value) + } + } +} diff --git a/src/virtualenv/activation/python/__init__.py b/src/virtualenv/activation/python/__init__.py new file mode 100644 index 000000000..1d73e9969 --- /dev/null +++ b/src/virtualenv/activation/python/__init__.py @@ -0,0 +1,19 @@ +from __future__ import absolute_import, unicode_literals + +import json +import os + +from pathlib2 import Path + +from ..via_template import ViaTemplateActivator + + +class PythonActivator(ViaTemplateActivator): + def templates(self): + yield Path("activate_this.py") + + def replacements(self, creator, dest_folder): + replacements = super(PythonActivator, self).replacements(creator, dest_folder) + site_dump = json.dumps([os.path.relpath(str(i), str(dest_folder)) for i in creator.site_packages], indent=2) + replacements.update({"__SITE_PACKAGES__": site_dump}) + return replacements diff --git a/src/virtualenv/activation/python/activate_this.py b/src/virtualenv/activation/python/activate_this.py new file mode 100644 index 000000000..fc8d449e7 --- /dev/null +++ b/src/virtualenv/activation/python/activate_this.py @@ -0,0 +1,45 @@ +"""Activate virtualenv for current interpreter: + +Use exec(open(this_file).read(), {'__file__': this_file}). + +This can be used when you must use an existing Python interpreter, not the virtualenv bin/python. +""" +import json +import os +import site +import sys + +try: + __file__ +except NameError: + raise AssertionError("You must use exec(open(this_file).read(), {'__file__': this_file}))") + +# prepend bin to PATH (this file is inside the bin directory) +bin_dir = os.path.dirname(os.path.abspath(__file__)) +os.environ["PATH"] = os.pathsep.join([bin_dir] + os.environ.get("PATH", "").split(os.pathsep)) + +base = os.path.dirname(bin_dir) + +# virtual env is right above bin directory +os.environ["VIRTUAL_ENV"] = base + +# add the virtual environments site-packages to the host python import mechanism +prev = set(sys.path) + +# fmt: off +# turn formatter off as json dumps will contain " characters - so we really need here ' black +site_packages = r''' +__SITE_PACKAGES__ +''' + +for site_package in json.loads(site_packages): + path = os.path.realpath(os.path.join(os.path.dirname(__file__), site_package)) + site.addsitedir(path) +# fmt: on + +sys.real_prefix = sys.prefix +sys.prefix = base + +# Move the added items to the front of the path, in place +new = list(sys.path) +sys.path[:] = [i for i in new if i not in prev] + [i for i in new if i in prev] diff --git a/src/virtualenv/activation/via_template.py b/src/virtualenv/activation/via_template.py new file mode 100644 index 000000000..864454f95 --- /dev/null +++ b/src/virtualenv/activation/via_template.py @@ -0,0 +1,38 @@ +from __future__ import absolute_import, unicode_literals + +import os +import pkgutil +from abc import ABCMeta, abstractmethod + +import six + +from .activator import Activator + + +@six.add_metaclass(ABCMeta) +class ViaTemplateActivator(Activator): + @abstractmethod + def templates(self): + raise NotImplementedError + + def generate(self, creator): + dest_folder = creator.bin_dir + self._generate(self.replacements(creator, dest_folder), self.templates(), dest_folder) + if self.flag_prompt is not None: + creator.pyenv_cfg["prompt"] = self.flag_prompt + + def replacements(self, creator, dest_folder): + return { + "__VIRTUAL_PROMPT__": "" if self.flag_prompt is None else self.flag_prompt, + "__VIRTUAL_ENV__": str(creator.dest_dir), + "__VIRTUAL_NAME__": str(creator.env_name), + "__BIN_NAME__": str(creator.bin_name), + "__PATH_SEP__": os.pathsep, + } + + def _generate(self, replacements, templates, to_folder): + for template in templates: + text = pkgutil.get_data(self.__module__, str(template)).decode("utf-8") + for start, end in replacements.items(): + text = text.replace(start, end) + (to_folder / template).write_text(text) diff --git a/src/virtualenv/activation/xonosh/__init__.py b/src/virtualenv/activation/xonosh/__init__.py new file mode 100644 index 000000000..ceb534057 --- /dev/null +++ b/src/virtualenv/activation/xonosh/__init__.py @@ -0,0 +1,14 @@ +from __future__ import absolute_import, unicode_literals + +from pathlib2 import Path + +from ..via_template import ViaTemplateActivator + + +class XonoshActivator(ViaTemplateActivator): + def templates(self): + yield Path("activate.xsh") + + @classmethod + def supports(cls, interpreter): + return True if interpreter.version_info >= (3, 5) else False diff --git a/src/virtualenv/activation/xonosh/activate.xsh b/src/virtualenv/activation/xonosh/activate.xsh new file mode 100644 index 000000000..c77ea6278 --- /dev/null +++ b/src/virtualenv/activation/xonosh/activate.xsh @@ -0,0 +1,46 @@ +"""Xonsh activate script for virtualenv""" +from xonsh.tools import get_sep as _get_sep + +def _deactivate(args): + if "pydoc" in aliases: + del aliases["pydoc"] + + if ${...}.get("_OLD_VIRTUAL_PATH", ""): + $PATH = $_OLD_VIRTUAL_PATH + del $_OLD_VIRTUAL_PATH + + if ${...}.get("_OLD_VIRTUAL_PYTHONHOME", ""): + $PYTHONHOME = $_OLD_VIRTUAL_PYTHONHOME + del $_OLD_VIRTUAL_PYTHONHOME + + if "VIRTUAL_ENV" in ${...}: + del $VIRTUAL_ENV + + if "VIRTUAL_ENV_PROMPT" in ${...}: + del $VIRTUAL_ENV_PROMPT + + if "nondestructive" not in args: + # Self destruct! + del aliases["deactivate"] + + +# unset irrelevant variables +_deactivate(["nondestructive"]) +aliases["deactivate"] = _deactivate + +$VIRTUAL_ENV = r"__VIRTUAL_ENV__" + +$_OLD_VIRTUAL_PATH = $PATH +$PATH = $PATH[:] +$PATH.add($VIRTUAL_ENV + _get_sep() + "__BIN_NAME__", front=True, replace=True) + +if ${...}.get("PYTHONHOME", ""): + # unset PYTHONHOME if set + $_OLD_VIRTUAL_PYTHONHOME = $PYTHONHOME + del $PYTHONHOME + +$VIRTUAL_ENV_PROMPT = "__VIRTUAL_PROMPT__" +if not $VIRTUAL_ENV_PROMPT: + del $VIRTUAL_ENV_PROMPT + +aliases["pydoc"] = ["python", "-m", "pydoc"] diff --git a/src/virtualenv/config/cli/parser.py b/src/virtualenv/config/cli/parser.py index 083bda823..e817a423e 100644 --- a/src/virtualenv/config/cli/parser.py +++ b/src/virtualenv/config/cli/parser.py @@ -52,7 +52,7 @@ def parse_args(self, args=None, namespace=None): class HelpFormatter(ArgumentDefaultsHelpFormatter): def __init__(self, prog): - super(HelpFormatter, self).__init__(prog, max_help_position=35, width=240) + super(HelpFormatter, self).__init__(prog, max_help_position=37, width=240) def _get_help_string(self, action): # noinspection PyProtectedMember diff --git a/src/virtualenv/interpreters/create/cpython/common.py b/src/virtualenv/interpreters/create/cpython/common.py index b322be192..d05d47bae 100644 --- a/src/virtualenv/interpreters/create/cpython/common.py +++ b/src/virtualenv/interpreters/create/cpython/common.py @@ -34,7 +34,7 @@ def create(self): self.system_site_package = true_system_site def ensure_directories(self): - dirs = [self.env_dir, self.bin_dir] + dirs = [self.dest_dir, self.bin_dir] dirs.extend(self.site_packages) return dirs @@ -53,7 +53,7 @@ def lib_base(self): @property def lib_dir(self): - return self.env_dir / self.lib_base + return self.dest_dir / self.lib_base @property def system_stdlib(self): diff --git a/src/virtualenv/interpreters/create/creator.py b/src/virtualenv/interpreters/create/creator.py index 079aad933..872d8d10b 100644 --- a/src/virtualenv/interpreters/create/creator.py +++ b/src/virtualenv/interpreters/create/creator.py @@ -102,13 +102,9 @@ def set_pyenv_cfg(self): "virtualenv": __version__, } - @property - def env_dir(self): - return Path(self.dest_dir) - @property def env_name(self): - return self.env_dir.parts[-1] + return self.dest_dir.parts[-1] @property def bin_name(self): @@ -116,7 +112,7 @@ def bin_name(self): @property def bin_dir(self): - return self.env_dir / self.bin_name + return self.dest_dir / self.bin_name @property def lib_dir(self): @@ -127,13 +123,13 @@ def site_packages(self): return [self.lib_dir / "site-packages"] @property - def env_exe(self): + def exe(self): return self.bin_dir / "python{}".format(".exe" if IS_WIN else "") @property def debug(self): if self._debug is None: - self._debug = get_env_debug_info(self.env_exe, self.debug_script()) + self._debug = get_env_debug_info(self.exe, self.debug_script()) return self._debug # noinspection PyMethodMayBeStatic diff --git a/src/virtualenv/run.py b/src/virtualenv/run.py index e6f0a227a..96ad7d708 100644 --- a/src/virtualenv/run.py +++ b/src/virtualenv/run.py @@ -1,6 +1,7 @@ from __future__ import absolute_import, unicode_literals import logging +from argparse import ArgumentTypeError from entrypoints import get_group_named @@ -134,17 +135,28 @@ def _collect_seeders(): def _get_activation(interpreter, parser, options): activator_parser = parser.add_argument_group("activation script generator") - activators = _collect_activators(interpreter) + compatible = collect_activators(interpreter) + default = ",".join(compatible.keys()) + + def _extract_activators(entered_str): + elements = [e.strip() for e in entered_str.split(",") if e.strip()] + missing = [e for e in elements if e not in compatible] + if missing: + raise ArgumentTypeError("the following activators are not available {}".format(",".join(missing))) + return elements + activator_parser.add_argument( "--activators", - choices=list(activators.keys()), - default=list(activators.keys()), + default=default, + metavar="comma_separated_list", required=False, - nargs="*", - help="activators to generate", + help="activators to generate together with virtual environment - default is all available and compatible", + type=_extract_activators, ) yield - active_activators = {k: v for k, v in activators.items() if k in options.activators} + + selected_activators = _extract_activators(default) if options.activators is default else options.activators + active_activators = {k: v for k, v in compatible.items() if k in selected_activators} activator_parser.add_argument( "--prompt", dest="prompt", @@ -160,7 +172,7 @@ def _get_activation(interpreter, parser, options): yield activator_instances -def _collect_activators(interpreter): +def collect_activators(interpreter): all_activators = {e.name: e.load() for e in get_group_named("virtualenv.activate").values()} activators = {k: v for k, v in all_activators.items() if v.supports(interpreter)} return activators diff --git a/src/virtualenv/seed/embed/link_app_data.py b/src/virtualenv/seed/embed/link_app_data.py index f961a4c3c..c400da718 100644 --- a/src/virtualenv/seed/embed/link_app_data.py +++ b/src/virtualenv/seed/embed/link_app_data.py @@ -35,7 +35,7 @@ def run(self, creator): def pip_install(wheels, creator, cache): - site_package, bin_dir, env_exe = creator.site_packages[0], creator.bin_dir, creator.env_exe + site_package, bin_dir, env_exe = creator.site_packages[0], creator.bin_dir, creator.exe folder_link_method, folder_linked = link_folder() for name, wheel in wheels.items(): logging.debug("install %s from wheel %s", name, wheel) diff --git a/src/virtualenv/session.py b/src/virtualenv/session.py index 74a50f2af..d69fc8fd6 100644 --- a/src/virtualenv/session.py +++ b/src/virtualenv/session.py @@ -29,7 +29,7 @@ def _seed(self): def _activate(self): for activator in self.activators: - activator.run(self.creator) + activator.generate(self.creator) _DEBUG_MARKER = "=" * 30 + " target debug " + "=" * 30 diff --git a/tests/unit/activation/conftest.py b/tests/unit/activation/conftest.py new file mode 100644 index 000000000..42312741f --- /dev/null +++ b/tests/unit/activation/conftest.py @@ -0,0 +1,189 @@ +from __future__ import absolute_import, unicode_literals + +import os +import pipes +import re +import subprocess +import sys +from os.path import dirname, normcase, realpath + +import pytest +import six + +from virtualenv.run import run_via_cli + + +class ActivationTester(object): + def __init__(self, of_class, session, cmd, activate_script, extension): + self.of_class = of_class + self._creator = session.creator + self._version_cmd = [cmd, "--version"] + self._invoke_script = [cmd] + self.activate_script = activate_script + self.extension = extension + self.activate_cmd = "source" + self.deactivate = "deactivate" + self.pydoc_call = "pydoc -w pydoc_test" + + def get_version(self, raise_on_fail): + # locally we disable, so that contributors don't need to have everything setup + try: + return subprocess.check_output(self._version_cmd, universal_newlines=True) + except Exception as exception: + if raise_on_fail: + raise + return RuntimeError("{} is not available due {}".format(self, exception)) + + def __call__(self, monkeypatch, tmp_path): + activate_script = self._creator.bin_dir / self.activate_script + test_script = self._generate_test_script(activate_script, tmp_path) + monkeypatch.chdir(tmp_path) + + monkeypatch.delenv(str("VIRTUAL_ENV"), raising=False) + invoke, env = self._invoke_script + [str(test_script)], self.env(tmp_path) + + try: + raw = subprocess.check_output(invoke, universal_newlines=True, stderr=subprocess.STDOUT, env=env) + except subprocess.CalledProcessError as exception: + assert not exception.returncode, exception.output + return + out = re.sub(r"pydev debugger: process \d+ is connecting\n\n", "", raw, re.M).strip().split("\n") + self.assert_output(out, raw, tmp_path) + return env, activate_script + + def non_source_activate(self, activate_script): + return self._invoke_script + [str(activate_script)] + + # noinspection PyMethodMayBeStatic + def env(self, tmp_path): + env = os.environ.copy() + # add the current python executable folder to the path so we already have another python on the path + # also keep the path so the shells (fish, bash, etc can be discovered) + env[str("PATH")] = os.pathsep.join([dirname(sys.executable)] + env.get(str("PATH"), str("")).split(os.pathsep)) + # clear up some environment variables so they don't affect the tests + for key in [k for k in env.keys() if k.startswith("_OLD") or k.startswith("VIRTUALENV_")]: + del env[key] + return env + + def _generate_test_script(self, activate_script, tmp_path): + commands = self._get_test_lines(activate_script) + script = os.linesep.join(commands) + test_script = tmp_path / "script.{}".format(self.extension) + test_script.write_text(script) + return test_script + + def _get_test_lines(self, activate_script): + commands = [ + self.print_python_exe(), + self.print_os_env_var("VIRTUAL_ENV"), + self.activate_call(activate_script), + self.print_python_exe(), + self.print_os_env_var("VIRTUAL_ENV"), + # \\ loads documentation from the virtualenv site packages + self.pydoc_call, + self.deactivate, + self.print_python_exe(), + self.print_os_env_var("VIRTUAL_ENV"), + "", # just finish with an empty new line + ] + return commands + + def assert_output(self, out, raw, tmp_path): + # pre-activation + assert out[0], raw + assert out[1] == "None", raw + # post-activation + assert self.norm_path(out[2]) == self.norm_path(self._creator.exe), raw + assert self.norm_path(out[3]) == self.norm_path(self._creator.dest_dir).replace("\\\\", "\\"), raw + assert out[4] == "wrote pydoc_test.html" + content = tmp_path / "pydoc_test.html" + assert content.exists(), raw + # post deactivation, same as before + assert out[-2] == out[0], raw + assert out[-1] == "None", raw + + def quote(self, s): + return pipes.quote(s) + + def python_cmd(self, cmd): + return "{} -c {}".format(os.path.basename(sys.executable), self.quote(cmd)) + + def print_python_exe(self): + return self.python_cmd("import sys; print(sys.executable)") + + def print_os_env_var(self, var): + val = '"{}"'.format(var) + return self.python_cmd("import os; print(os.environ.get({}, None))".format(val)) + + def activate_call(self, script): + return "{} {}".format(self.quote(str(self.activate_cmd)), self.quote(str(script))).strip() + + @staticmethod + def norm_path(path): + # python may return Windows short paths, normalize + path = realpath(str(path)) + if sys.platform == "win32": + from ctypes import create_unicode_buffer, windll + + buffer_cont = create_unicode_buffer(256) + get_long_path_name = windll.kernel32.GetLongPathNameW + get_long_path_name(six.text_type(path), buffer_cont, 256) # noqa: F821 + result = buffer_cont.value + else: + result = path + return normcase(result) + + +class RaiseOnNonSourceCall(ActivationTester): + def __init__(self, of_class, session, cmd, activate_script, extension, non_source_fail_message): + super(RaiseOnNonSourceCall, self).__init__(of_class, session, cmd, activate_script, extension) + self.non_source_fail_message = non_source_fail_message + + def __call__(self, monkeypatch, tmp_path): + env, activate_script = super(RaiseOnNonSourceCall, self).__call__(monkeypatch, tmp_path) + process = subprocess.Popen( + self.non_source_activate(activate_script), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=env, + universal_newlines=True, + ) + out, err = process.communicate() + assert process.returncode + assert self.non_source_fail_message in err + + +@pytest.fixture(scope="session") +def activation_tester_class(): + return ActivationTester + + +@pytest.fixture(scope="session") +def raise_on_non_source_class(): + return RaiseOnNonSourceCall + + +@pytest.fixture(scope="session") +def activation_python(tmp_path_factory): + dest = tmp_path_factory.mktemp("a") + session = run_via_cli(["--seed", "none", str(dest)]) + pydoc_test = session.creator.site_packages[0] / "pydoc_test.py" + pydoc_test.write_text('"""This is pydoc_test.py"""') + return session + + +IS_INSIDE_CI = "CI_RUN" in os.environ + + +@pytest.fixture() +def activation_tester(activation_python, monkeypatch, tmp_path): + def _tester(tester_class): + tester = tester_class(activation_python) + if not tester.of_class.supports(activation_python.creator.interpreter): + pytest.skip("{} not supported on current environment".format(tester.of_class.__name__)) + version = tester.get_version(raise_on_fail=IS_INSIDE_CI) + if not isinstance(version, six.string_types): + pytest.skip(msg=six.text_type(version)) + return tester(monkeypatch, tmp_path) + + return _tester diff --git a/tests/unit/activation/test_activation_support.py b/tests/unit/activation/test_activation_support.py new file mode 100644 index 000000000..cff940e7c --- /dev/null +++ b/tests/unit/activation/test_activation_support.py @@ -0,0 +1,49 @@ +from argparse import Namespace + +import pytest + +from virtualenv.activation import ( + BashActivator, + BatchActivator, + CShellActivator, + FishActivator, + PowerShellActivator, + PythonActivator, +) +from virtualenv.interpreters.discovery.py_info import PythonInfo + + +@pytest.mark.parametrize("activator_class", [BatchActivator, PowerShellActivator, PythonActivator]) +def test_activator_support_windows(mocker, activator_class): + activator = activator_class(Namespace(prompt=None)) + + interpreter = mocker.Mock(spec=PythonInfo) + interpreter.os = "nt" + assert activator.supports(interpreter) + + +@pytest.mark.parametrize("activator_class", [BashActivator, CShellActivator, FishActivator]) +def test_activator_no_support_windows(mocker, activator_class): + activator = activator_class(Namespace(prompt=None)) + + interpreter = mocker.Mock(spec=PythonInfo) + interpreter.os = "nt" + assert not activator.supports(interpreter) + + +@pytest.mark.parametrize( + "activator_class", [BashActivator, CShellActivator, FishActivator, PowerShellActivator, PythonActivator] +) +def test_activator_support_posix(mocker, activator_class): + activator = activator_class(Namespace(prompt=None)) + interpreter = mocker.Mock(spec=PythonInfo) + interpreter.os = "posix" + assert activator.supports(interpreter) + + +@pytest.mark.parametrize("activator_class", [BatchActivator]) +def test_activator_no_support_posix(mocker, activator_class): + activator = activator_class(Namespace(prompt=None)) + interpreter = mocker.Mock(spec=PythonInfo) + interpreter.os = "posix" + assert not activator.supports(interpreter) diff --git a/tests/unit/activation/test_bash.py b/tests/unit/activation/test_bash.py new file mode 100644 index 000000000..d5d8ad9b0 --- /dev/null +++ b/tests/unit/activation/test_bash.py @@ -0,0 +1,13 @@ +from __future__ import absolute_import, unicode_literals + +from virtualenv.activation import BashActivator + + +def test_bash(raise_on_non_source_class, activation_tester): + class Bash(raise_on_non_source_class): + def __init__(self, session): + super(Bash, self).__init__( + BashActivator, session, "bash", "activate.sh", "sh", "You must source this script: $ source " + ) + + activation_tester(Bash) diff --git a/tests/unit/activation/test_batch.py b/tests/unit/activation/test_batch.py new file mode 100644 index 000000000..143943b1e --- /dev/null +++ b/tests/unit/activation/test_batch.py @@ -0,0 +1,28 @@ +from __future__ import absolute_import, unicode_literals + +import pipes + +from virtualenv.activation import BatchActivator + + +def test_batch(activation_tester_class, activation_tester, tmp_path, activation_python): + version_script = tmp_path / "version.bat" + version_script.write_text("ver") + + class Batch(activation_tester_class): + def __init__(self, session): + super(Batch, self).__init__(BatchActivator, session, None, "activate.bat", "bat") + self._version_cmd = [str(version_script)] + self._invoke_script = [] + self.deactivate = "call deactivate" + self.activate_cmd = "call" + self.pydoc_call = "call {}".format(self.pydoc_call) + + def _get_test_lines(self, activate_script): + return ["@echo off", ""] + super(Batch, self)._get_test_lines(activate_script) + + def quote(self, s): + """double quotes needs to be single, and single need to be double""" + return "".join(("'" if c == '"' else ('"' if c == "'" else c)) for c in pipes.quote(s)) + + activation_tester(Batch) diff --git a/tests/unit/activation/test_csh.py b/tests/unit/activation/test_csh.py new file mode 100644 index 000000000..69b23b68f --- /dev/null +++ b/tests/unit/activation/test_csh.py @@ -0,0 +1,11 @@ +from __future__ import absolute_import, unicode_literals + +from virtualenv.activation import CShellActivator + + +def test_csh(activation_tester_class, activation_tester): + class Csh(activation_tester_class): + def __init__(self, session): + super(Csh, self).__init__(CShellActivator, session, "csh", "activate.csh", "csh") + + activation_tester(Csh) diff --git a/tests/unit/activation/test_fish.py b/tests/unit/activation/test_fish.py new file mode 100644 index 000000000..bf154649c --- /dev/null +++ b/tests/unit/activation/test_fish.py @@ -0,0 +1,11 @@ +from __future__ import absolute_import, unicode_literals + +from virtualenv.activation import FishActivator + + +def test_csh(activation_tester_class, activation_tester): + class Fish(activation_tester_class): + def __init__(self, session): + super(Fish, self).__init__(FishActivator, session, "fish", "activate.fish", "fish") + + activation_tester(Fish) diff --git a/tests/unit/activation/test_powershell.py b/tests/unit/activation/test_powershell.py new file mode 100644 index 000000000..e2edac4d3 --- /dev/null +++ b/tests/unit/activation/test_powershell.py @@ -0,0 +1,25 @@ +from __future__ import absolute_import, unicode_literals + +import pipes +import sys + +from virtualenv.activation import PowerShellActivator + + +def test_powershell(activation_tester_class, activation_tester): + class PowerShell(activation_tester_class): + def __init__(self, session): + cmd = "powershell.exe" if sys.platform == "win32" else "pwsh" + super(PowerShell, self).__init__(PowerShellActivator, session, cmd, "activate.ps1", "ps1") + self._version_cmd = [cmd, "-c", "$PSVersionTable"] + self._invoke_script = [cmd, "-ExecutionPolicy", "ByPass", "-File"] + self.activate_cmd = "." + + def quote(self, s): + """powershell double double quote needed for quotes within single quotes""" + return pipes.quote(s).replace('"', '""') + + def invoke_script(self): + return [self.cmd, "-File"] + + activation_tester(PowerShell) diff --git a/tests/unit/activation/test_python_activator.py b/tests/unit/activation/test_python_activator.py new file mode 100644 index 000000000..6320e82cb --- /dev/null +++ b/tests/unit/activation/test_python_activator.py @@ -0,0 +1,74 @@ +from __future__ import absolute_import, unicode_literals + +import inspect +import os +import sys + +from virtualenv.activation import PythonActivator + + +def test_python(raise_on_non_source_class, activation_tester): + class Python(raise_on_non_source_class): + def __init__(self, session): + super(Python, self).__init__( + PythonActivator, + session, + sys.executable, + activate_script="activate_this.py", + extension="py", + non_source_fail_message="You must use exec(open(this_file).read(), {'__file__': this_file}))", + ) + + def env(self, tmp_path): + env = os.environ.copy() + for key in {"VIRTUAL_ENV", "PYTHONPATH"}: + env.pop(str(key), None) + env[str("PATH")] = os.pathsep.join([str(tmp_path), str(tmp_path / "other")]) + return env + + def _get_test_lines(self, activate_script): + raw = inspect.getsource(self.activate_this_test) + raw = raw.replace("__FILENAME__", str(activate_script)) + return [i.lstrip() for i in raw.splitlines()[2:]] + + # noinspection PyUnresolvedReferences + @staticmethod + def activate_this_test(): + import os + import sys + + print(os.environ.get("VIRTUAL_ENV")) + print(os.environ.get("PATH")) + print(os.pathsep.join(sys.path)) + file_at = r"__FILENAME__" + exec(open(file_at).read(), {"__file__": file_at}) + print(os.environ.get("VIRTUAL_ENV")) + print(os.environ.get("PATH")) + print(os.pathsep.join(sys.path)) + import inspect + import pydoc_test + + print(inspect.getsourcefile(pydoc_test)) + + def assert_output(self, out, raw, tmp_path): + assert out[0] == "None" # start with VIRTUAL_ENV None + + prev_path = out[1].split(os.path.pathsep) + prev_sys_path = out[2].split(os.path.pathsep) + + assert out[3] == str(self._creator.dest_dir) # VIRTUAL_ENV now points to the virtual env folder + + new_path = out[4].split(os.pathsep) # PATH now starts with bin path of current + assert ([str(self._creator.bin_dir)] + prev_path) == new_path + + # sys path contains the site package at its start + new_sys_path = out[5].split(os.path.pathsep) + assert ([str(i) for i in self._creator.site_packages] + prev_sys_path) == new_sys_path + + # manage to import from activate site package + assert self.norm_path(out[6]) == self.norm_path(self._creator.site_packages[0] / "pydoc_test.py") + + def non_source_activate(self, activate_script): + return self._invoke_script + ["-c", 'exec(open(r"{}").read())'.format(activate_script)] + + activation_tester(Python) diff --git a/tests/unit/activation/test_xonosh.py b/tests/unit/activation/test_xonosh.py new file mode 100644 index 000000000..0091dd3a9 --- /dev/null +++ b/tests/unit/activation/test_xonosh.py @@ -0,0 +1,25 @@ +from __future__ import absolute_import, unicode_literals + +import sys + +from virtualenv.activation import XonoshActivator + + +def test_xonosh(activation_tester_class, activation_tester): + class Xonosh(activation_tester_class): + def __init__(self, session): + super(Xonosh, self).__init__( + XonoshActivator, session, "xonsh.exe" if sys.platform == "win32" else "xonsh", "activate.xsh", "xsh" + ) + self._invoke_script = [sys.executable, "-m", "xonsh"] + self._version_cmd = [sys.executable, "-m", "xonsh", "--version"] + + def env(self, tmp_path): + env = super(Xonosh, self).env(tmp_path) + env.update({"XONSH_DEBUG": "1", "XONSH_SHOW_TRACEBACK": "True"}) + return env + + def activate_call(self, script): + return "{} {}".format(self.activate_cmd, repr(str(script))).strip() + + activation_tester(Xonosh) diff --git a/tests/unit/interpreters/boostrap/test_boostrap_link_via_app_data.py b/tests/unit/interpreters/boostrap/test_boostrap_link_via_app_data.py index f92b1feaf..6c5bb20b8 100644 --- a/tests/unit/interpreters/boostrap/test_boostrap_link_via_app_data.py +++ b/tests/unit/interpreters/boostrap/test_boostrap_link_via_app_data.py @@ -32,7 +32,7 @@ def test_base_bootstrap_link_via_app_data(tmp_path, coverage_env): assert pip in files_post_first_create assert setuptools in files_post_first_create - env_exe = result.creator.env_exe + env_exe = result.creator.exe for pip_exe in [ env_exe.with_name("pip{}{}".format(suffix, env_exe.suffix)) for suffix in ( diff --git a/tests/unit/interpreters/create/test_creator.py b/tests/unit/interpreters/create/test_creator.py index 4735ab1fe..a85d158f5 100644 --- a/tests/unit/interpreters/create/test_creator.py +++ b/tests/unit/interpreters/create/test_creator.py @@ -80,7 +80,7 @@ def cleanup_sys_path(path): "use_venv", [False, True] if six.PY3 else [False], ids=["no_venv", "venv"] if six.PY3 else ["no_venv"] ) def test_create_no_seed(python, use_venv, global_access, tmp_path, coverage_env): - cmd = ["-v", "-v", "-p", str(python), str(tmp_path), "--without-pip"] + cmd = ["-v", "-v", "-p", str(python), str(tmp_path), "--without-pip", "--activators", ""] if global_access: cmd.append("--system-site-packages") if use_venv: