From 9e58d95017656ee33d2cf642cf1be6c77298f16d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 12 Jul 2020 10:56:14 -0400 Subject: Re-enable distutils patch by default. --- changelog.d/2232.breaking.rst | 1 + setuptools/distutils_patch.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelog.d/2232.breaking.rst diff --git a/changelog.d/2232.breaking.rst b/changelog.d/2232.breaking.rst new file mode 100644 index 00000000..b2fd926f --- /dev/null +++ b/changelog.d/2232.breaking.rst @@ -0,0 +1 @@ +Once again, Setuptools overrides the stdlib distutils on import. For environments or invocations where this behavior is undesirable, users are provided with a temporary escape hatch. If the environment variable ``SETUPTOOLS_USE_DISTUTILS`` is set to ``stdlib``, Setuptools will fall back to the legacy behavior. Use of this escape hatch is discouraged, but it is provided to ease the transition while proper fixes for edge cases can be addressed. diff --git a/setuptools/distutils_patch.py b/setuptools/distutils_patch.py index c5f273dd..e6b2aad5 100644 --- a/setuptools/distutils_patch.py +++ b/setuptools/distutils_patch.py @@ -25,7 +25,7 @@ def enabled(): """ Allow selection of distutils by environment variable. """ - which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'stdlib') + which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'local') return which == 'local' -- cgit v1.2.1 From 4837f218ea05a304880f8448e27fe0affad3a1a5 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 13 Aug 2020 21:49:25 -0400 Subject: Update tests to reflect new expectation. --- setuptools/tests/test_distutils_adoption.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/setuptools/tests/test_distutils_adoption.py b/setuptools/tests/test_distutils_adoption.py index daccc473..8edd3f9b 100644 --- a/setuptools/tests/test_distutils_adoption.py +++ b/setuptools/tests/test_distutils_adoption.py @@ -48,15 +48,15 @@ def test_distutils_stdlib(venv): """ Ensure stdlib distutils is used when appropriate. """ - assert venv.name not in find_distutils(venv, env=dict()).split(os.sep) + env = dict(SETUPTOOLS_USE_DISTUTILS='stdlib') + assert venv.name not in find_distutils(venv, env=env).split(os.sep) def test_distutils_local_with_setuptools(venv): """ Ensure local distutils is used when appropriate. """ - env = dict(SETUPTOOLS_USE_DISTUTILS='local') - loc = find_distutils(venv, imports='setuptools, distutils', env=env) + loc = find_distutils(venv, imports='setuptools, distutils', env=dict()) assert venv.name in loc.split(os.sep) @@ -66,5 +66,4 @@ def test_distutils_local(venv): Even without importing, the setuptools-local copy of distutils is preferred. """ - env = dict(SETUPTOOLS_USE_DISTUTILS='local') - assert venv.name in find_distutils(venv, env=env).split(os.sep) + assert venv.name in find_distutils(venv, env=dict()).split(os.sep) -- cgit v1.2.1 From be7a0a31a116f6df9bef616ef1adef72b9d0d48d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 14 Aug 2020 19:32:18 -0400 Subject: Bypass .pth loader when distutils is loaded from pip. Workaround for pypa/pip#8761. --- _distutils_hack/__init__.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/_distutils_hack/__init__.py b/_distutils_hack/__init__.py index 814ee97e..2d358c37 100644 --- a/_distutils_hack/__init__.py +++ b/_distutils_hack/__init__.py @@ -3,6 +3,7 @@ import os import re import importlib import warnings +import inspect is_pypy = '__pypy__' in sys.builtin_module_names @@ -66,7 +67,7 @@ def do_override(): class DistutilsMetaFinder: def find_spec(self, fullname, path, target=None): - if path is not None or fullname != "distutils": + if path is not None or fullname != "distutils" or self._bypass(): return None return self.get_distutils_spec() @@ -84,6 +85,16 @@ class DistutilsMetaFinder: return importlib.util.spec_from_loader('distutils', DistutilsLoader()) + def _bypass(self): + """ + Suppress the import of distutils from setuptools when running under pip. + See pypa/pip#8761 for rationale. + """ + return any( + level.frame.f_globals['__name__'].startswith('pip.') + for level in inspect.stack(context=False) + ) + DISTUTILS_FINDER = DistutilsMetaFinder() -- cgit v1.2.1 From 77eeb77f48484a6e227275a9a8cc7d4c322596f7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 15 Aug 2020 14:39:25 -0400 Subject: When pip is imported, unload any existing distutils and disable the DistutilsMetaFinder. --- _distutils_hack/__init__.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/_distutils_hack/__init__.py b/_distutils_hack/__init__.py index 2d358c37..bdb365d5 100644 --- a/_distutils_hack/__init__.py +++ b/_distutils_hack/__init__.py @@ -3,7 +3,6 @@ import os import re import importlib import warnings -import inspect is_pypy = '__pypy__' in sys.builtin_module_names @@ -67,7 +66,9 @@ def do_override(): class DistutilsMetaFinder: def find_spec(self, fullname, path, target=None): - if path is not None or fullname != "distutils" or self._bypass(): + self._disable_for_pip(fullname, path) + + if path is not None or fullname != "distutils": return None return self.get_distutils_spec() @@ -85,15 +86,17 @@ class DistutilsMetaFinder: return importlib.util.spec_from_loader('distutils', DistutilsLoader()) - def _bypass(self): + def _disable_for_pip(self, fullname, path): """ - Suppress the import of distutils from setuptools when running under pip. + Ensure stdlib distutils when running under pip. See pypa/pip#8761 for rationale. """ - return any( - level.frame.f_globals['__name__'].startswith('pip.') - for level in inspect.stack(context=False) - ) + if path is not None or fullname != "pip": + return + + # pip is being imported the first time. + clear_distutils() + self.get_distutils_spec = lambda: None DISTUTILS_FINDER = DistutilsMetaFinder() -- cgit v1.2.1 From 3d404fd3268b0fb7d84916779ca2e282f4586396 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 15 Aug 2020 14:59:37 -0400 Subject: Refactor to use lookups and consolidate behaviors. --- _distutils_hack/__init__.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/_distutils_hack/__init__.py b/_distutils_hack/__init__.py index bdb365d5..074bd5e9 100644 --- a/_distutils_hack/__init__.py +++ b/_distutils_hack/__init__.py @@ -66,14 +66,14 @@ def do_override(): class DistutilsMetaFinder: def find_spec(self, fullname, path, target=None): - self._disable_for_pip(fullname, path) - - if path is not None or fullname != "distutils": - return None + if path is not None: + return - return self.get_distutils_spec() + method_name = 'spec_for_{fullname}'.format(**locals()) + method = getattr(self, method_name, lambda: None) + return method() - def get_distutils_spec(self): + def spec_for_distutils(self): import importlib.util class DistutilsLoader(importlib.util.abc.Loader): @@ -86,17 +86,13 @@ class DistutilsMetaFinder: return importlib.util.spec_from_loader('distutils', DistutilsLoader()) - def _disable_for_pip(self, fullname, path): + def spec_for_pip(self): """ Ensure stdlib distutils when running under pip. See pypa/pip#8761 for rationale. """ - if path is not None or fullname != "pip": - return - - # pip is being imported the first time. clear_distutils() - self.get_distutils_spec = lambda: None + self.spec_for_distutils = lambda: None DISTUTILS_FINDER = DistutilsMetaFinder() -- cgit v1.2.1