Skip to content

Commit c52fe3e

Browse files
Support without distutils (#2146)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 0705f20 commit c52fe3e

File tree

11 files changed

+36
-13
lines changed

11 files changed

+36
-13
lines changed

.github/workflows/check.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ jobs:
2424
- windows-latest
2525
- macos-latest
2626
py:
27+
- 3.10.0-beta.4
2728
- 3.9
2829
- 3.8
2930
- 3.7

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ repos:
4242
rev: v1.17.0
4343
hooks:
4444
- id: setup-cfg-fmt
45-
args: [--min-py3-version, "3.4"]
45+
args: [--min-py3-version, "3.5", "--max-py-version", "3.10"]
4646
- repo: https://github.com/PyCQA/flake8
4747
rev: "3.9.2"
4848
hooks:

docs/changelog/1910.feature.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Support Python interpreters without ``distutils`` (fallback to ``syconfig`` in these cases) - by :user:`gaborbernat`.
2+

setup.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ classifiers =
2626
Programming Language :: Python :: 3.7
2727
Programming Language :: Python :: 3.8
2828
Programming Language :: Python :: 3.9
29+
Programming Language :: Python :: 3.10
2930
Programming Language :: Python :: Implementation :: CPython
3031
Programming Language :: Python :: Implementation :: PyPy
3132
Topic :: Software Development :: Libraries

src/virtualenv/create/describe.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,15 @@ def bin_dir(self):
3030

3131
@property
3232
def script_dir(self):
33-
return self.dest / Path(self.interpreter.distutils_install["scripts"])
33+
return self.dest / self.interpreter.install_path("scripts")
3434

3535
@property
3636
def purelib(self):
37-
return self.dest / self.interpreter.distutils_install["purelib"]
37+
return self.dest / self.interpreter.install_path("purelib")
3838

3939
@property
4040
def platlib(self):
41-
return self.dest / self.interpreter.distutils_install["platlib"]
41+
return self.dest / self.interpreter.install_path("platlib")
4242

4343
@property
4444
def libs(self):

src/virtualenv/create/via_global_ref/builtin/cpython/cpython2.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def host_include_marker(cls, interpreter):
3636
@property
3737
def include(self):
3838
# the pattern include the distribution name too at the end, remove that via the parent call
39-
return (self.dest / self.interpreter.distutils_install["headers"]).parent
39+
return (self.dest / self.interpreter.install_path("headers")).parent
4040

4141
@classmethod
4242
def modules(cls):

src/virtualenv/create/via_global_ref/builtin/pypy/pypy2.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def host_include_marker(cls, interpreter):
4141

4242
@property
4343
def include(self):
44-
return self.dest / self.interpreter.distutils_install["headers"]
44+
return self.dest / self.interpreter.install_path("headers")
4545

4646
@classmethod
4747
def modules(cls):

src/virtualenv/discovery/py_info.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@
1212
import re
1313
import sys
1414
import sysconfig
15+
import warnings
1516
from collections import OrderedDict, namedtuple
16-
from distutils import dist
17-
from distutils.command.install import SCHEME_KEYS
1817
from string import digits
1918

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

120+
def install_path(self, key):
121+
result = self.distutils_install.get(key)
122+
if result is None: # use sysconfig if distutils is unavailable
123+
# set prefixes to empty => result is relative from cwd
124+
prefixes = self.prefix, self.exec_prefix, self.base_prefix, self.base_exec_prefix
125+
config_var = {k: "" if v in prefixes else v for k, v in self.sysconfig_vars}
126+
result = self.sysconfig_path(key, config_var=config_var).lstrip(os.sep)
127+
return result
128+
121129
@staticmethod
122130
def _distutils_install():
123-
# follow https://github.com/pypa/pip/blob/main/src/pip/_internal/locations.py#L95
131+
# use distutils primarily because that's what pip does
132+
# https://github.com/pypa/pip/blob/main/src/pip/_internal/locations.py#L95
124133
# note here we don't import Distribution directly to allow setuptools to patch it
134+
with warnings.catch_warnings(): # disable warning for PEP-632
135+
warnings.simplefilter("ignore")
136+
try:
137+
from distutils import dist
138+
from distutils.command.install import SCHEME_KEYS
139+
except ImportError: # if removed or not installed ignore
140+
return {}
141+
125142
d = dist.Distribution({"script_args": "--no-user-cfg"}) # conf files not parsed so they do not hijack paths
126143
if hasattr(sys, "_framework"):
127144
sys._framework = None # disable macOS static paths for framework
@@ -177,7 +194,7 @@ def system_include(self):
177194
)
178195
if not os.path.exists(path): # some broken packaging don't respect the sysconfig, fallback to distutils path
179196
# the pattern include the distribution name too at the end, remove that via the parent call
180-
fallback = os.path.join(self.prefix, os.path.dirname(self.distutils_install["headers"]))
197+
fallback = os.path.join(self.prefix, os.path.dirname(self.install_path("headers")))
181198
if os.path.exists(fallback):
182199
path = fallback
183200
return path

tests/unit/activation/test_powershell.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99

1010

1111
@pytest.mark.slow
12-
def test_powershell(activation_tester_class, activation_tester):
12+
def test_powershell(activation_tester_class, activation_tester, monkeypatch):
13+
monkeypatch.setenv("TERM", "xterm")
14+
1315
class PowerShell(activation_tester_class):
1416
def __init__(self, session):
1517
cmd = "powershell.exe" if sys.platform == "win32" else "pwsh"

tests/unit/discovery/py_info/test_py_info_exe_based_of.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def test_discover_empty_folder(tmp_path, monkeypatch, session_app_data):
1717
CURRENT.discover_exe(session_app_data, prefix=str(tmp_path))
1818

1919

20-
BASE = (CURRENT.distutils_install["scripts"], ".")
20+
BASE = (CURRENT.install_path("scripts"), ".")
2121

2222

2323
@pytest.mark.skipif(not fs_supports_symlink(), reason="symlink is not supported")

tests/unit/discovery/test_discovery.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def test_discovery_via_path(monkeypatch, case, tmp_path, caplog, session_app_dat
2727
elif case == "upper":
2828
name = name.upper()
2929
exe_name = "{}{}{}".format(name, current.version_info.major, ".exe" if sys.platform == "win32" else "")
30-
target = tmp_path / current.distutils_install["scripts"]
30+
target = tmp_path / current.install_path("scripts")
3131
target.mkdir(parents=True)
3232
executable = target / exe_name
3333
os.symlink(sys.executable, ensure_text(str(executable)))

0 commit comments

Comments
 (0)