Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix python 2 activator from python 3 invalid #1794

Merged
merged 1 commit into from
May 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/changelog/1776.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix generating a Python 2 environment from Python 3 creates invalid python activator - by :user:`gaborbernat`.
3 changes: 2 additions & 1 deletion src/virtualenv/activation/python/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import absolute_import, unicode_literals

import os
import sys
from collections import OrderedDict

from virtualenv.util.path import Path
Expand Down Expand Up @@ -29,5 +30,5 @@ def replacements(self, creator, dest_folder):
def _repr_unicode(creator, value):
py2 = creator.interpreter.version_info.major == 2
if py2: # on Python 2 we need to encode this into explicit utf-8, py3 supports unicode literals
value = ensure_text(repr(value.encode("utf-8"))[1:-1])
value = ensure_text(repr(value.encode("utf-8"))[2 if sys.version_info[0] == 3 else 1 : -1])
return value
4 changes: 3 additions & 1 deletion src/virtualenv/activation/via_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ def templates(self):
def generate(self, creator):
dest_folder = creator.bin_dir
replacements = self.replacements(creator, dest_folder)
self._generate(replacements, self.templates(), dest_folder, creator)
at_path = self._generate(replacements, self.templates(), dest_folder, creator)
if self.flag_prompt is not None:
creator.pyenv_cfg["prompt"] = self.flag_prompt
return at_path

def replacements(self, creator, dest_folder):
return {
Expand All @@ -43,6 +44,7 @@ def _generate(self, replacements, templates, to_folder, creator):
text = self.instantiate_template(replacements, template, creator)
dest = to_folder / self.as_name(template)
dest.write_text(text, encoding="utf-8")
return dest

def as_name(self, template):
return template.name
Expand Down
14 changes: 14 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import pytest
import six

from virtualenv.discovery.builtin import get_interpreter
from virtualenv.discovery.py_info import PythonInfo
from virtualenv.info import IS_PYPY, IS_WIN, fs_supports_symlink
from virtualenv.report import LOGGER
Expand Down Expand Up @@ -318,3 +319,16 @@ def temp_app_data(monkeypatch, tmp_path):
app_data = tmp_path / "app-data"
monkeypatch.setenv(str("VIRTUALENV_OVERRIDE_APP_DATA"), str(app_data))
return app_data


@pytest.fixture(scope="session")
def cross_python(is_inside_ci, session_app_data):
current = PythonInfo.current(session_app_data)
spec = "{}{}".format(current.implementation, 2 if current.version_info.major == 3 else 3)
interpreter = get_interpreter(spec, session_app_data)
if interpreter is None:
msg = "could not find {}".format(spec)
if is_inside_ci:
raise RuntimeError(msg)
pytest.skip(msg=msg)
yield interpreter
23 changes: 23 additions & 0 deletions tests/unit/activation/test_activate_this.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from virtualenv.activation import PythonActivator
from virtualenv.config.cli.parser import VirtualEnvOptions
from virtualenv.run import session_via_cli


def test_from_py3_to_py2(session_app_data, cross_python, special_name_dir):
options = VirtualEnvOptions()
cli_args = [
str(special_name_dir),
"-p",
str(cross_python.executable),
"--app-data",
str(session_app_data.path),
"--without-pip",
"--activators",
"",
]
session = session_via_cli(cli_args, options)
activator = PythonActivator(options)
session.creator.bin_dir.mkdir(parents=True)
result = activator.generate(session.creator)
content = result.read_text()
assert "\"'" not in content
13 changes: 0 additions & 13 deletions tests/unit/create/test_creator.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
from virtualenv.create.via_global_ref.builtin.cpython.cpython2 import CPython2PosixBase
from virtualenv.create.via_global_ref.builtin.cpython.cpython3 import CPython3Posix
from virtualenv.create.via_global_ref.builtin.python2.python2 import Python2
from virtualenv.discovery.builtin import get_interpreter
from virtualenv.discovery.py_info import PythonInfo
from virtualenv.info import IS_PYPY, IS_WIN, PY2, PY3, fs_is_case_sensitive
from virtualenv.pyenv_cfg import PyEnvCfg
Expand Down Expand Up @@ -315,18 +314,6 @@ def test_prompt_set(tmp_path, creator, prompt):
assert cfg["prompt"] == actual_prompt


@pytest.fixture(scope="session")
def cross_python(is_inside_ci, session_app_data):
spec = "{}{}".format(CURRENT.implementation, 2 if CURRENT.version_info.major == 3 else 3)
interpreter = get_interpreter(spec, session_app_data)
if interpreter is None:
msg = "could not find {}".format(spec)
if is_inside_ci:
raise RuntimeError(msg)
pytest.skip(msg=msg)
yield interpreter


@pytest.mark.slow
def test_cross_major(cross_python, coverage_env, tmp_path, session_app_data, current_fastest):
cmd = [
Expand Down