From c18173c09c73c5595893c06c79e3b2482efed31f Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 4 Sep 2020 17:48:23 +0200 Subject: [PATCH] bpo-41718: Reduce test.libregrtest imports test.libregrtest.save_env no longer imports asyncio, multiprocessing nor urllib to reduce the number of imports, to better isolate unit tests. --- Lib/test/libregrtest/save_env.py | 42 +------------------ Lib/test/test_regrtest.py | 18 +++++++- Lib/test/test_support.py | 16 ++++++- .../2020-09-04-17-50-28.bpo-41718.MBxAOC.rst | 2 + 4 files changed, 35 insertions(+), 43 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2020-09-04-17-50-28.bpo-41718.MBxAOC.rst diff --git a/Lib/test/libregrtest/save_env.py b/Lib/test/libregrtest/save_env.py index 4c9c692400b920..d7fcb5b03673e7 100644 --- a/Lib/test/libregrtest/save_env.py +++ b/Lib/test/libregrtest/save_env.py @@ -1,4 +1,3 @@ -import asyncio import builtins import locale import logging @@ -7,15 +6,10 @@ import sys import sysconfig import threading -import urllib.request import warnings from test import support from test.support import os_helper from test.libregrtest.utils import print_warning -try: - import _multiprocessing, multiprocessing.process -except ImportError: - multiprocessing = None # Unit tests are supposed to leave the execution environment unchanged @@ -63,32 +57,12 @@ def __init__(self, testname, verbose=0, quiet=False, *, pgo=False): 'warnings.filters', 'asyncore.socket_map', 'logging._handlers', 'logging._handlerList', 'sys.gettrace', 'sys.warnoptions', - # multiprocessing.process._cleanup() may release ref - # to a thread, so check processes first. - 'multiprocessing.process._dangling', 'threading._dangling', + 'threading._dangling', 'sysconfig._CONFIG_VARS', 'sysconfig._INSTALL_SCHEMES', 'files', 'locale', 'warnings.showwarning', 'shutil_archive_formats', 'shutil_unpack_formats', - 'asyncio.events._event_loop_policy', - 'urllib.requests._url_tempfiles', 'urllib.requests._opener', ) - def get_urllib_requests__url_tempfiles(self): - return list(urllib.request._url_tempfiles) - def restore_urllib_requests__url_tempfiles(self, tempfiles): - for filename in tempfiles: - os_helper.unlink(filename) - - def get_urllib_requests__opener(self): - return urllib.request._opener - def restore_urllib_requests__opener(self, opener): - urllib.request._opener = opener - - def get_asyncio_events__event_loop_policy(self): - return support.maybe_get_event_loop_policy() - def restore_asyncio_events__event_loop_policy(self, policy): - asyncio.set_event_loop_policy(policy) - def get_sys_argv(self): return id(sys.argv), sys.argv, sys.argv[:] def restore_sys_argv(self, saved_argv): @@ -206,20 +180,6 @@ def restore_threading__dangling(self, saved): threading._dangling.clear() threading._dangling.update(saved) - # Same for Process objects - def get_multiprocessing_process__dangling(self): - if not multiprocessing: - return None - # Unjoined process objects can survive after process exits - multiprocessing.process._cleanup() - # This copies the weakrefs without making any strong reference - return multiprocessing.process._dangling.copy() - def restore_multiprocessing_process__dangling(self, saved): - if not multiprocessing: - return - multiprocessing.process._dangling.clear() - multiprocessing.process._dangling.update(saved) - def get_sysconfig__CONFIG_VARS(self): # make sure the dict is initialized sysconfig.get_config_var('prefix') diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index 38321e04b54a9e..92234bbcc67fd2 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -520,7 +520,7 @@ def run_command(self, args, input=None, exitcode=0, **kw): if 'stderr' not in kw: kw['stderr'] = subprocess.STDOUT proc = subprocess.run(args, - universal_newlines=True, + text=True, input=input, stdout=subprocess.PIPE, **kw) @@ -1307,5 +1307,21 @@ def test_format_duration(self): '3 hour 1 sec') +class MiscTestCase(BaseTestCase): + def test_num_imports(self): + # bpo-41718: "import test.libregrtest" must not import too many + # modules. + code = textwrap.dedent(""" + import sys + import test.libregrtest + print(len(sys.modules)) + """) + proc = self.run_command([sys.executable, '-I', '-c', code]) + out = proc.stdout.strip() + nmodules = int(out) + # Python 3.10 on Linux imports 149 modules + self.assertLess(nmodules, 155) + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index 71a66c27aa21df..9e43d814944ffb 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -468,7 +468,7 @@ def check_options(self, args, func, expected=None): proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, - universal_newlines=True, + text=True, env=env) if expected is None: expected = args @@ -656,6 +656,20 @@ def test_print_warning(self): self.check_print_warning("a\nb", 'Warning -- a\nWarning -- b\n') + def test_num_imports(self): + # bpo-41718: "import test.support" must not import too many modules. + code = textwrap.dedent(""" + import sys + import test.support + print(len(sys.modules)) + """) + cmd = [sys.executable, '-I', '-c', code] + proc = subprocess.run(cmd, stdout=subprocess.PIPE, text=True, check=True) + out = proc.stdout.strip() + nmodules = int(out) + # Python 3.10 on Linux imports 96 modules + self.assertLess(nmodules, 100) + # XXX -follows a list of untested API # make_legacy_pyc # is_resource_enabled diff --git a/Misc/NEWS.d/next/Tests/2020-09-04-17-50-28.bpo-41718.MBxAOC.rst b/Misc/NEWS.d/next/Tests/2020-09-04-17-50-28.bpo-41718.MBxAOC.rst new file mode 100644 index 00000000000000..c5f34b19215653 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2020-09-04-17-50-28.bpo-41718.MBxAOC.rst @@ -0,0 +1,2 @@ +test.libregrtest.save_env no longer imports asyncio, multiprocessing nor +urllib to reduce the number of imports, to better isolate unit tests.