Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Speedup startup of Python by importing less #3009

Merged
merged 7 commits into from Jan 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
62 changes: 35 additions & 27 deletions _distutils_hack/__init__.py
@@ -1,26 +1,19 @@
# don't import any costly modules
import sys
import os
import re
import importlib
import warnings
import contextlib


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
if is_pypy and sys.version_info < (3, 7):
# 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 "
Expand All @@ -33,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]

Expand All @@ -48,6 +45,7 @@ def enabled():


def ensure_local_distutils():
import importlib
clear_distutils()

# With the DistutilsMetaFinder in place,
Expand All @@ -73,15 +71,12 @@ def do_override():
ensure_local_distutils()


class suppress(contextlib.suppress, contextlib.ContextDecorator):
"""
A version of contextlib.suppress with decorator support.
class _TrivialRe:
def __init__(self, *patterns):
self._patterns = patterns

>>> @suppress(KeyError)
... def key_error():
... {}['']
>>> key_error()
"""
def match(self, string):
return all(pat in string for pat in self._patterns)


class DistutilsMetaFinder:
Expand All @@ -94,8 +89,20 @@ def find_spec(self, fullname, path, target=None):
return method()

def spec_for_distutils(self):
import importlib
import importlib.abc
import importlib.util
import warnings

# warnings.filterwarnings() imports the re module
warnings._add_filter(
'ignore',
_TrivialRe("distutils", "deprecated"),
DeprecationWarning,
None,
0,
append=True
)

try:
mod = importlib.import_module('setuptools._distutils')
Expand Down Expand Up @@ -144,13 +151,15 @@ def pip_imported_during_build(cls):
)

@classmethod
@suppress(AttributeError)
def is_get_pip(cls):
"""
Detect if get-pip is being invoked. Ref #2993.
"""
import __main__
return os.path.basename(__main__.__file__) == 'get-pip.py'
try:
import __main__
return os.path.basename(__main__.__file__) == 'get-pip.py'
except AttributeError:
pass

@staticmethod
def frame_file_is_setup(frame):
Expand All @@ -168,12 +177,11 @@ def add_shim():
DISTUTILS_FINDER in sys.meta_path or insert_shim()


@contextlib.contextmanager
def shim():
insert_shim()
try:
yield
finally:
class shim:
def __enter__(self):
insert_shim()

def __exit__(self, exc, value, tb):
remove_shim()


Expand Down
2 changes: 2 additions & 0 deletions changelog.d/3006.change.rst
@@ -0,0 +1,2 @@
Fixed startup performance issue of Python interpreter due to imports of
costly modules in ``_distutils_hack`` -- by :user:`tiran`
1 change: 1 addition & 0 deletions pytest.ini
Expand Up @@ -38,6 +38,7 @@ filterwarnings=
# SETUPTOOLS_USE_DISTUTILS=stdlib but for
# https://github.com/pytest-dev/pytest/discussions/9296
ignore:The distutils.sysconfig module is deprecated, use sysconfig instead
ignore:The distutils package is deprecated.*

# Workaround for pypa/setuptools#2868
# ideally would apply to PyPy only but for
Expand Down