diff options
| author | Jason R. Coombs <jaraco@jaraco.com> | 2022-05-09 10:42:04 -0400 |
|---|---|---|
| committer | Jason R. Coombs <jaraco@jaraco.com> | 2022-05-09 10:42:56 -0400 |
| commit | 9116c7eb52504bec77d26881d2c28e427dc52143 (patch) | |
| tree | 6becb88401eb15bdff6fc924211894e6d9c277d1 /_distutils_hack/__init__.py | |
| parent | 8d12d6196c369c7cf0164a1202e968dd68a2cb6c (diff) | |
| parent | e009a87b5578cb16099b697ba8395c8f6bdd70f3 (diff) | |
| download | python-setuptools-git-debt/remove-easy-install.tar.gz | |
Merge branch 'main' into debt/remove-easy-installdebt/remove-easy-install
Diffstat (limited to '_distutils_hack/__init__.py')
| -rw-r--r-- | _distutils_hack/__init__.py | 91 |
1 files changed, 73 insertions, 18 deletions
diff --git a/_distutils_hack/__init__.py b/_distutils_hack/__init__.py index f7074162..605a6edc 100644 --- a/_distutils_hack/__init__.py +++ b/_distutils_hack/__init__.py @@ -1,18 +1,11 @@ +# don't import any costly modules import sys import os -import re -import importlib -import warnings is_pypy = '__pypy__' in sys.builtin_module_names -warnings.filterwarnings('ignore', - r'.+ distutils\b.+ deprecated', - DeprecationWarning) - - def warn_distutils_present(): if 'distutils' not in sys.modules: return @@ -20,6 +13,7 @@ def warn_distutils_present(): # PyPy for 3.6 unconditionally imports distutils, so bypass the warning # https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250 return + import warnings warnings.warn( "Distutils was imported before Setuptools, but importing Setuptools " "also replaces the `distutils` module in `sys.modules`. This may lead " @@ -32,8 +26,12 @@ def warn_distutils_present(): def clear_distutils(): if 'distutils' not in sys.modules: return + import warnings warnings.warn("Setuptools is replacing distutils.") - mods = [name for name in sys.modules if re.match(r'distutils\b', name)] + mods = [ + name for name in sys.modules + if name == "distutils" or name.startswith("distutils.") + ] for name in mods: del sys.modules[name] @@ -42,23 +40,24 @@ 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' def ensure_local_distutils(): + import importlib clear_distutils() # With the DistutilsMetaFinder in place, # perform an import to cause distutils to be # loaded from setuptools._distutils. Ref #2906. - add_shim() - importlib.import_module('distutils') - remove_shim() + with shim(): + importlib.import_module('distutils') # check that submodules load as expected core = importlib.import_module('distutils.core') assert '_distutils' in core.__file__, core.__file__ + assert 'setuptools._distutils.log' not in sys.modules def do_override(): @@ -73,6 +72,14 @@ def do_override(): ensure_local_distutils() +class _TrivialRe: + def __init__(self, *patterns): + self._patterns = patterns + + def match(self, string): + return all(pat in string for pat in self._patterns) + + class DistutilsMetaFinder: def find_spec(self, fullname, path, target=None): if path is not None: @@ -83,18 +90,46 @@ class DistutilsMetaFinder: return method() def spec_for_distutils(self): + if self.is_cpython(): + return + + import importlib import importlib.abc import importlib.util + try: + mod = importlib.import_module('setuptools._distutils') + except Exception: + # There are a couple of cases where setuptools._distutils + # may not be present: + # - An older Setuptools without a local distutils is + # taking precedence. Ref #2957. + # - Path manipulation during sitecustomize removes + # setuptools from the path but only after the hook + # has been loaded. Ref #2980. + # In either case, fall back to stdlib behavior. + return + class DistutilsLoader(importlib.abc.Loader): def create_module(self, spec): - return importlib.import_module('setuptools._distutils') + mod.__name__ = 'distutils' + return mod def exec_module(self, module): pass - return importlib.util.spec_from_loader('distutils', DistutilsLoader()) + return importlib.util.spec_from_loader( + 'distutils', DistutilsLoader(), origin=mod.__file__ + ) + + @staticmethod + def is_cpython(): + """ + Suppress supplying distutils for CPython (build and tests). + Ref #2965 and #3007. + """ + return os.path.isfile('pybuilddir.txt') def spec_for_pip(self): """ @@ -106,22 +141,42 @@ class DistutilsMetaFinder: clear_distutils() self.spec_for_distutils = lambda: None - @staticmethod - def pip_imported_during_build(): + @classmethod + def pip_imported_during_build(cls): """ Detect if pip is being imported in a build script. Ref #2355. """ import traceback return any( - frame.f_globals['__file__'].endswith('setup.py') + cls.frame_file_is_setup(frame) for frame, line in traceback.walk_stack(None) ) + @staticmethod + def frame_file_is_setup(frame): + """ + Return True if the indicated frame suggests a setup.py file. + """ + # some frames may not have __file__ (#2940) + return frame.f_globals.get('__file__', '').endswith('setup.py') + DISTUTILS_FINDER = DistutilsMetaFinder() def add_shim(): + DISTUTILS_FINDER in sys.meta_path or insert_shim() + + +class shim: + def __enter__(self): + insert_shim() + + def __exit__(self, exc, value, tb): + remove_shim() + + +def insert_shim(): sys.meta_path.insert(0, DISTUTILS_FINDER) |
