Skip to content

Commit

Permalink
Support without distutils (#2146)
Browse files Browse the repository at this point in the history
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
gaborbernat and pre-commit-ci[bot] committed Jul 14, 2021
1 parent 0705f20 commit c52fe3e
Show file tree
Hide file tree
Showing 11 changed files with 36 additions and 13 deletions.
1 change: 1 addition & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ jobs:
- windows-latest
- macos-latest
py:
- 3.10.0-beta.4
- 3.9
- 3.8
- 3.7
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ repos:
rev: v1.17.0
hooks:
- id: setup-cfg-fmt
args: [--min-py3-version, "3.4"]
args: [--min-py3-version, "3.5", "--max-py-version", "3.10"]
- repo: https://github.com/PyCQA/flake8
rev: "3.9.2"
hooks:
Expand Down
2 changes: 2 additions & 0 deletions docs/changelog/1910.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Support Python interpreters without ``distutils`` (fallback to ``syconfig`` in these cases) - by :user:`gaborbernat`.

1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ classifiers =
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Programming Language :: Python :: Implementation :: CPython
Programming Language :: Python :: Implementation :: PyPy
Topic :: Software Development :: Libraries
Expand Down
6 changes: 3 additions & 3 deletions src/virtualenv/create/describe.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ def bin_dir(self):

@property
def script_dir(self):
return self.dest / Path(self.interpreter.distutils_install["scripts"])
return self.dest / self.interpreter.install_path("scripts")

@property
def purelib(self):
return self.dest / self.interpreter.distutils_install["purelib"]
return self.dest / self.interpreter.install_path("purelib")

@property
def platlib(self):
return self.dest / self.interpreter.distutils_install["platlib"]
return self.dest / self.interpreter.install_path("platlib")

@property
def libs(self):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def host_include_marker(cls, interpreter):
@property
def include(self):
# the pattern include the distribution name too at the end, remove that via the parent call
return (self.dest / self.interpreter.distutils_install["headers"]).parent
return (self.dest / self.interpreter.install_path("headers")).parent

@classmethod
def modules(cls):
Expand Down
2 changes: 1 addition & 1 deletion src/virtualenv/create/via_global_ref/builtin/pypy/pypy2.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def host_include_marker(cls, interpreter):

@property
def include(self):
return self.dest / self.interpreter.distutils_install["headers"]
return self.dest / self.interpreter.install_path("headers")

@classmethod
def modules(cls):
Expand Down
25 changes: 21 additions & 4 deletions src/virtualenv/discovery/py_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@
import re
import sys
import sysconfig
import warnings
from collections import OrderedDict, namedtuple
from distutils import dist
from distutils.command.install import SCHEME_KEYS
from string import digits

VersionInfo = namedtuple("VersionInfo", ["major", "minor", "micro", "releaselevel", "serial"])
Expand Down Expand Up @@ -118,10 +117,28 @@ def _fast_get_system_executable(self):
# note we must choose the original and not the pure executable as shim scripts might throw us off
return self.original_executable

def install_path(self, key):
result = self.distutils_install.get(key)
if result is None: # use sysconfig if distutils is unavailable
# set prefixes to empty => result is relative from cwd
prefixes = self.prefix, self.exec_prefix, self.base_prefix, self.base_exec_prefix
config_var = {k: "" if v in prefixes else v for k, v in self.sysconfig_vars}
result = self.sysconfig_path(key, config_var=config_var).lstrip(os.sep)
return result

@staticmethod
def _distutils_install():
# follow https://github.com/pypa/pip/blob/main/src/pip/_internal/locations.py#L95
# use distutils primarily because that's what pip does
# https://github.com/pypa/pip/blob/main/src/pip/_internal/locations.py#L95
# note here we don't import Distribution directly to allow setuptools to patch it
with warnings.catch_warnings(): # disable warning for PEP-632
warnings.simplefilter("ignore")
try:
from distutils import dist
from distutils.command.install import SCHEME_KEYS
except ImportError: # if removed or not installed ignore
return {}

d = dist.Distribution({"script_args": "--no-user-cfg"}) # conf files not parsed so they do not hijack paths
if hasattr(sys, "_framework"):
sys._framework = None # disable macOS static paths for framework
Expand Down Expand Up @@ -177,7 +194,7 @@ def system_include(self):
)
if not os.path.exists(path): # some broken packaging don't respect the sysconfig, fallback to distutils path
# the pattern include the distribution name too at the end, remove that via the parent call
fallback = os.path.join(self.prefix, os.path.dirname(self.distutils_install["headers"]))
fallback = os.path.join(self.prefix, os.path.dirname(self.install_path("headers")))
if os.path.exists(fallback):
path = fallback
return path
Expand Down
4 changes: 3 additions & 1 deletion tests/unit/activation/test_powershell.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@


@pytest.mark.slow
def test_powershell(activation_tester_class, activation_tester):
def test_powershell(activation_tester_class, activation_tester, monkeypatch):
monkeypatch.setenv("TERM", "xterm")

class PowerShell(activation_tester_class):
def __init__(self, session):
cmd = "powershell.exe" if sys.platform == "win32" else "pwsh"
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/discovery/py_info/test_py_info_exe_based_of.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def test_discover_empty_folder(tmp_path, monkeypatch, session_app_data):
CURRENT.discover_exe(session_app_data, prefix=str(tmp_path))


BASE = (CURRENT.distutils_install["scripts"], ".")
BASE = (CURRENT.install_path("scripts"), ".")


@pytest.mark.skipif(not fs_supports_symlink(), reason="symlink is not supported")
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/discovery/test_discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def test_discovery_via_path(monkeypatch, case, tmp_path, caplog, session_app_dat
elif case == "upper":
name = name.upper()
exe_name = "{}{}{}".format(name, current.version_info.major, ".exe" if sys.platform == "win32" else "")
target = tmp_path / current.distutils_install["scripts"]
target = tmp_path / current.install_path("scripts")
target.mkdir(parents=True)
executable = target / exe_name
os.symlink(sys.executable, ensure_text(str(executable)))
Expand Down

0 comments on commit c52fe3e

Please sign in to comment.