From 5497808b5d3fc94fab155f3fe18a886db75d8b92 Mon Sep 17 00:00:00 2001 From: Bernat Gabor Date: Fri, 5 Jun 2020 18:14:26 +0100 Subject: [PATCH] PyPy 7.3.1 now generates Scripts instead of bin on Windows Signed-off-by: Bernat Gabor --- docs/changelog/1597.bugfix.rst | 1 + src/tox/config/__init__.py | 15 +++++++++++---- src/tox/helper/get_version.py | 4 +++- src/tox/interpreters/__init__.py | 15 +++++++++++---- src/tox/interpreters/via_path.py | 2 +- tests/unit/interpreters/test_interpreters.py | 9 +++++---- 6 files changed, 32 insertions(+), 14 deletions(-) create mode 100644 docs/changelog/1597.bugfix.rst diff --git a/docs/changelog/1597.bugfix.rst b/docs/changelog/1597.bugfix.rst new file mode 100644 index 0000000000..7482e0882b --- /dev/null +++ b/docs/changelog/1597.bugfix.rst @@ -0,0 +1 @@ +PyPy 7.3.1 on Windows uses the ``Script`` folder instead of ``bin``. - by :user:`gaborbernat` diff --git a/src/tox/config/__init__.py b/src/tox/config/__init__.py index f42a68d573..2ac4cf3aab 100644 --- a/src/tox/config/__init__.py +++ b/src/tox/config/__init__.py @@ -955,10 +955,17 @@ def __init__(self, envname, config, factors, reader): def get_envbindir(self): """Path to directory where scripts/binaries reside.""" - if tox.INFO.IS_WIN and "jython" not in self.basepython and "pypy" not in self.basepython: - return self.envdir.join("Scripts") - else: - return self.envdir.join("bin") + is_bin = ( + isinstance(self.python_info, NoInterpreterInfo) + or tox.INFO.IS_WIN is False + or self.python_info.implementation == "Jython" + or ( + tox.INFO.IS_WIN + and self.python_info.implementation == "PyPy" + and self.python_info.extra_version_info < (7, 3, 1) + ) + ) + return self.envdir.join("bin" if is_bin else "Scripts") @property def envbindir(self): diff --git a/src/tox/helper/get_version.py b/src/tox/helper/get_version.py index 3fcc37e437..b1a8455a4a 100644 --- a/src/tox/helper/get_version.py +++ b/src/tox/helper/get_version.py @@ -1,15 +1,17 @@ from __future__ import unicode_literals import json +import platform import sys info = { "executable": sys.executable, - "name": "pypy" if hasattr(sys, "pypy_version_info") else "python", + "implementation": platform.python_implementation(), "version_info": list(sys.version_info), "version": sys.version, "is_64": sys.maxsize > 2 ** 32, "sysplatform": sys.platform, + "extra_version_info": getattr(sys, "pypy_version_info", None), } info_as_dump = json.dumps(info) print(info_as_dump) diff --git a/src/tox/interpreters/__init__.py b/src/tox/interpreters/__init__.py index 797e5ae578..1734c4bd5c 100644 --- a/src/tox/interpreters/__init__.py +++ b/src/tox/interpreters/__init__.py @@ -59,13 +59,16 @@ def run_and_get_interpreter_info(name, executable): try: result = get_python_info(str(executable)) result["version_info"] = tuple(result["version_info"]) # fix json dump transformation - del result["name"] + if result["extra_version_info"] is not None: + result["extra_version_info"] = tuple( + result["extra_version_info"] + ) # fix json dump transformation del result["version"] result["executable"] = str(executable) except ExecFailed as e: return NoInterpreterInfo(name, executable=e.executable, out=e.out, err=e.err) else: - return InterpreterInfo(name, **result) + return InterpreterInfo(**result) def exec_on_interpreter(*args): @@ -93,12 +96,16 @@ def __init__(self, executable, source, out, err): class InterpreterInfo: - def __init__(self, name, executable, version_info, sysplatform, is_64): - self.name = name + def __init__( + self, implementation, executable, version_info, sysplatform, is_64, extra_version_info, + ): + self.implementation = implementation self.executable = executable + self.version_info = version_info self.sysplatform = sysplatform self.is_64 = is_64 + self.extra_version_info = extra_version_info def __str__(self): return "".format(self.executable, self.version_info) diff --git a/src/tox/interpreters/via_path.py b/src/tox/interpreters/via_path.py index 6708332084..8634d697d6 100644 --- a/src/tox/interpreters/via_path.py +++ b/src/tox/interpreters/via_path.py @@ -38,7 +38,7 @@ def exe_spec(python_exe, base): info = get_python_info(python_exe) if info is not None: found = PythonSpec( - info["name"], + "pypy" if info["implementation"] == "PyPy" else "python", info["version_info"][0], info["version_info"][1], 64 if info["is_64"] else 32, diff --git a/tests/unit/interpreters/test_interpreters.py b/tests/unit/interpreters/test_interpreters.py index 5d532e0bdd..fd364bafb1 100644 --- a/tests/unit/interpreters/test_interpreters.py +++ b/tests/unit/interpreters/test_interpreters.py @@ -1,6 +1,7 @@ from __future__ import unicode_literals import os +import platform import stat import subprocess import sys @@ -101,7 +102,7 @@ def test_run_and_get_interpreter_info(): name = os.path.basename(sys.executable) info = run_and_get_interpreter_info(name, sys.executable) assert info.version_info == tuple(sys.version_info) - assert info.name == name + assert info.implementation == platform.python_implementation() assert info.executable == sys.executable @@ -186,16 +187,16 @@ def test_exec_failed(): class TestInterpreterInfo: @staticmethod def info( - name="my-name", + implementation="CPython", executable="my-executable", version_info="my-version-info", sysplatform="my-sys-platform", ): - return InterpreterInfo(name, executable, version_info, sysplatform, True) + return InterpreterInfo(implementation, executable, version_info, sysplatform, True, None) def test_data(self): x = self.info("larry", "moe", "shemp", "curly") - assert x.name == "larry" + assert x.implementation == "larry" assert x.executable == "moe" assert x.version_info == "shemp" assert x.sysplatform == "curly"