-
-
Notifications
You must be signed in to change notification settings - Fork 31.9k
gh-111881: Use lazy import in unittest #111887
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
Conversation
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Making difflib
a lazy import in unittest.case
looks like a very good idea to me. The module is only used in three assert*
methods, and they're all reasonably obscure assert*
methods.
I'm not so sure about the changes to the other two submodules. fnmatch
seems quite core to the functionality of unittest.loader
; argparse
seems quite core to the functionality of unittest.main
. It seems odd to me to make fnmatch
a lazy import in unittest.loader
or argparse
a lazy import in unittest.main
.
Personally, I think I would prefer to just do the difflib
import in unittest.case
, and leave the others, in the name of code readability and elegance.
Python test suite declares tests using This PR doesn't remove any unittest feature, it doesn't affect any API. It's just about reducing the number of modules imported at Python startup. The problem is that the from .result import TestResult
from .case import (addModuleCleanup, TestCase, FunctionTestCase, SkipTest, skip,
skipIf, skipUnless, expectedFailure, doModuleCleanups,
enterModuleContext)
from .suite import BaseTestSuite, TestSuite
from .loader import TestLoader, defaultTestLoader
from .main import TestProgram, main
from .runner import TextTestRunner, TextTestResult
from .signals import installHandler, registerResult, removeResult, removeHandler
Maybe the lazyness can be implemented differently? |
Yes, it's a slightly unfortunate design...
For diff --git a/Lib/unittest/__init__.py b/Lib/unittest/__init__.py
index f1f6c911ef..b09a2f4047 100644
--- a/Lib/unittest/__init__.py
+++ b/Lib/unittest/__init__.py
@@ -58,23 +58,29 @@ def testMultiply(self):
skipIf, skipUnless, expectedFailure, doModuleCleanups,
enterModuleContext)
from .suite import BaseTestSuite, TestSuite
-from .loader import TestLoader, defaultTestLoader
from .main import TestProgram, main
from .runner import TextTestRunner, TextTestResult
from .signals import installHandler, registerResult, removeResult, removeHandler
-# IsolatedAsyncioTestCase will be imported lazily.
+# Some things will be imported lazily.
# Lazy import of IsolatedAsyncioTestCase from .async_case
# It imports asyncio, which is relatively heavy, but most tests
-# do not need it.
+# do not need it. Similarly, unittest.loader imports fnmatch:
+# unneeded by most tests.
+
def __dir__():
- return globals().keys() | {'IsolatedAsyncioTestCase'}
+ extras = {'IsolatedAsyncioTestCase', 'TestLoader', 'defaultTestLoader'}
+ return globals().keys() | extras
def __getattr__(name):
if name == 'IsolatedAsyncioTestCase':
- global IsolatedAsyncioTestCase
- from .async_case import IsolatedAsyncioTestCase
- return IsolatedAsyncioTestCase
- raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ from .async_case import IsolatedAsyncioTestCase as obj
+ elif name in {"TestLoader", "defaultTestLoader"}:
+ import unittest.loader
+ obj = getattr(unittest.loader, name)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = obj
+ return obj It's harder to do something like that for the |
I lost track of this change, so I just close it. |
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.