From 333545df96e4786761f6b5c14e23990532179413 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 9 Nov 2023 15:08:12 +0100 Subject: [PATCH] gh-111881: Use lazy import in unittest Use lazy imports for argparse, difflib and fnmatch imports in unittest to speedup Python startup time when running tests, and the number of imported modules when running tests. --- Lib/unittest/case.py | 5 ++++- Lib/unittest/loader.py | 11 +++++++---- Lib/unittest/main.py | 6 ++++-- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py index 811557498bb30e..3d8eb41d946f2e 100644 --- a/Lib/unittest/case.py +++ b/Lib/unittest/case.py @@ -2,7 +2,6 @@ import sys import functools -import difflib import pprint import re import warnings @@ -1065,6 +1064,8 @@ def assertSequenceEqual(self, seq1, seq2, msg=None, seq_type=None): except (TypeError, IndexError, NotImplementedError): differing += ('Unable to index element %d ' 'of second %s\n' % (len1, seq_type_name)) + + import difflib standardMsg = differing diffMsg = '\n' + '\n'.join( difflib.ndiff(pprint.pformat(seq1).splitlines(), @@ -1178,6 +1179,7 @@ def assertDictEqual(self, d1, d2, msg=None): self.assertIsInstance(d2, dict, 'Second argument is not a dictionary') if d1 != d2: + import difflib standardMsg = '%s != %s' % _common_shorten_repr(d1, d2) diff = ('\n' + '\n'.join(difflib.ndiff( pprint.pformat(d1).splitlines(), @@ -1247,6 +1249,7 @@ def assertMultiLineEqual(self, first, second, msg=None): secondlines = second_presplit.splitlines(keepends=True) # Generate the message and diff, then raise the exception + import difflib standardMsg = '%s != %s' % _common_shorten_repr(first, second) diff = '\n' + ''.join(difflib.ndiff(firstlines, secondlines)) standardMsg = self._truncateMessage(standardMsg, diff) diff --git a/Lib/unittest/loader.py b/Lib/unittest/loader.py index 9a3e5cc4bf30e5..cf6543e19ac8d4 100644 --- a/Lib/unittest/loader.py +++ b/Lib/unittest/loader.py @@ -7,8 +7,6 @@ import types import functools -from fnmatch import fnmatch, fnmatchcase - from . import case, suite, util __unittest = True @@ -219,8 +217,12 @@ def shouldIncludeMethod(attrname): fullName = f'%s.%s.%s' % ( testCaseClass.__module__, testCaseClass.__qualname__, attrname ) - return self.testNamePatterns is None or \ - any(fnmatchcase(fullName, pattern) for pattern in self.testNamePatterns) + if self.testNamePatterns is None: + return True + else: + from fnmatch import fnmatchcase + return any(fnmatchcase(fullName, pattern) for pattern in self.testNamePatterns) + testFnNames = list(filter(shouldIncludeMethod, dir(testCaseClass))) if self.sortTestMethodsUsing: testFnNames.sort(key=functools.cmp_to_key(self.sortTestMethodsUsing)) @@ -339,6 +341,7 @@ def _get_module_from_name(self, name): def _match_path(self, path, full_path, pattern): # override this method to use alternative matching strategy + from fnmatch import fnmatch return fnmatch(path, pattern) def _find_tests(self, start_dir, pattern): diff --git a/Lib/unittest/main.py b/Lib/unittest/main.py index d29a9f91fcca42..787f46e902022e 100644 --- a/Lib/unittest/main.py +++ b/Lib/unittest/main.py @@ -1,8 +1,7 @@ """Unittest main program""" -import sys -import argparse import os +import sys from . import loader, runner from .signals import installHandler @@ -159,6 +158,7 @@ def _initArgParsers(self): self._discovery_parser = self._getDiscoveryArgParser(parent_parser) def _getParentArgParser(self): + import argparse parser = argparse.ArgumentParser(add_help=False) parser.add_argument('-v', '--verbose', dest='verbosity', @@ -197,6 +197,7 @@ def _getParentArgParser(self): return parser def _getMainArgParser(self, parent): + import argparse parser = argparse.ArgumentParser(parents=[parent]) parser.prog = self.progName parser.print_help = self._print_help @@ -208,6 +209,7 @@ def _getMainArgParser(self, parent): return parser def _getDiscoveryArgParser(self, parent): + import argparse parser = argparse.ArgumentParser(parents=[parent]) parser.prog = '%s discover' % self.progName parser.epilog = ('For test discovery all test modules must be '