From 1fb0687263305d5c7c1334cde36d12b02df4dce9 Mon Sep 17 00:00:00 2001 From: donBarbos Date: Fri, 7 Feb 2025 17:05:09 +0400 Subject: [PATCH 1/2] Always lazy import traceback --- Lib/_pyrepl/_threading_handler.py | 2 +- Lib/code.py | 2 +- Lib/doctest.py | 3 ++- Lib/logging/config.py | 2 +- Lib/pdb.py | 5 ++++- Lib/py_compile.py | 2 +- Lib/pydoc.py | 2 +- Lib/unittest/case.py | 2 +- Lib/unittest/loader.py | 4 +++- Lib/unittest/result.py | 2 +- Lib/xmlrpc/server.py | 7 ++----- .../Library/2025-02-07-17-04-08.gh-issue-118761.hkNOEA.rst | 2 ++ 12 files changed, 20 insertions(+), 15 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-02-07-17-04-08.gh-issue-118761.hkNOEA.rst diff --git a/Lib/_pyrepl/_threading_handler.py b/Lib/_pyrepl/_threading_handler.py index 82f5e8650a2072..37e34ca9e8a25e 100644 --- a/Lib/_pyrepl/_threading_handler.py +++ b/Lib/_pyrepl/_threading_handler.py @@ -1,7 +1,6 @@ from __future__ import annotations from dataclasses import dataclass, field -import traceback TYPE_CHECKING = False @@ -55,6 +54,7 @@ def add(self, s: str) -> None: self.messages.append(s) def exception(self, args: ExceptHookArgs) -> None: + import traceback lines = traceback.format_exception( args.exc_type, args.exc_value, diff --git a/Lib/code.py b/Lib/code.py index 1cc2ed8b1dbf28..500fee0c923ab4 100644 --- a/Lib/code.py +++ b/Lib/code.py @@ -7,7 +7,6 @@ import builtins import sys -import traceback from codeop import CommandCompiler, compile_command __all__ = ["InteractiveInterpreter", "InteractiveConsole", "interact", @@ -161,6 +160,7 @@ def _showtraceback(self, typ, value, tb, source): def _excepthook(self, typ, value, tb): # This method is being overwritten in # _pyrepl.console.InteractiveColoredConsole + import traceback lines = traceback.format_exception(typ, value, tb) self.write(''.join(lines)) diff --git a/Lib/doctest.py b/Lib/doctest.py index e02e73ed722f7e..a97ca902a54c36 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -100,7 +100,6 @@ def _test(): import pdb import re import sys -import traceback import unittest from io import StringIO, IncrementalNewlineDecoder from collections import namedtuple @@ -273,6 +272,7 @@ def _exception_traceback(exc_info): # Get a traceback message. excout = StringIO() exc_type, exc_val, exc_tb = exc_info + import traceback traceback.print_exception(exc_type, exc_val, exc_tb, file=excout) return excout.getvalue() @@ -1414,6 +1414,7 @@ def __run(self, test, compileflags, out): # The example raised an exception: check if it was expected. else: + import traceback formatted_ex = traceback.format_exception_only(*exception[:2]) if issubclass(exception[0], SyntaxError): # SyntaxError / IndentationError is special: diff --git a/Lib/logging/config.py b/Lib/logging/config.py index 6a6a7f726f7e0c..c668c1b651a5f8 100644 --- a/Lib/logging/config.py +++ b/Lib/logging/config.py @@ -34,7 +34,6 @@ import re import struct import threading -import traceback from socketserver import ThreadingTCPServer, StreamRequestHandler @@ -994,6 +993,7 @@ def handle(self): try: fileConfig(file) except Exception: + import traceback traceback.print_exc() if self.server.ready: self.server.ready.set() diff --git a/Lib/pdb.py b/Lib/pdb.py index beef74d792250b..a2160ba6fee3b2 100644 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -83,7 +83,6 @@ import textwrap import tokenize import itertools -import traceback import linecache import _colorize @@ -219,6 +218,7 @@ def __init__(self, target): print(f"ImportError: {e}") sys.exit(1) except Exception: + import traceback traceback.print_exc() sys.exit(1) @@ -257,6 +257,7 @@ def __init__(self, target): print(f"ImportError: {e}") sys.exit(1) except Exception: + import traceback traceback.print_exc() sys.exit(1) @@ -2262,6 +2263,7 @@ def _run(self, target: _ExecutableTarget): self.run(target.code) def _format_exc(self, exc: BaseException): + import traceback return traceback.format_exception_only(exc)[-1].strip() def _compile_error_message(self, expr): @@ -2531,6 +2533,7 @@ def main(): print("The program exited via sys.exit(). Exit status:", end=' ') print(e) except BaseException as e: + import traceback traceback.print_exception(e, colorize=_colorize.can_colorize()) print("Uncaught exception. Entering post mortem debugging") print("Running 'cont' or 'step' will restart the program") diff --git a/Lib/py_compile.py b/Lib/py_compile.py index 388614e51b1847..b26a4637305f96 100644 --- a/Lib/py_compile.py +++ b/Lib/py_compile.py @@ -10,7 +10,6 @@ import os import os.path import sys -import traceback __all__ = ["compile", "main", "PyCompileError", "PycInvalidationMode"] @@ -46,6 +45,7 @@ class PyCompileError(Exception): def __init__(self, exc_type, exc_value, file, msg=''): exc_type_name = exc_type.__name__ if exc_type is SyntaxError: + import traceback tbtext = ''.join(traceback.format_exception_only( exc_type, exc_value)) errmsg = tbtext.replace('File ""', 'File "%s"' % file) diff --git a/Lib/pydoc.py b/Lib/pydoc.py index 1839b88fec28b1..ac5a1b9159e8e5 100644 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -75,7 +75,6 @@ class or function within a module or module in a package. If the from annotationlib import Format from collections import deque from reprlib import Repr -from traceback import format_exception_only from _pyrepl.pager import (get_pager, pipe_pager, plain_pager, tempfile_pager, tty_pager) @@ -2628,6 +2627,7 @@ def html_getobj(url): return title, content def html_error(url, exc): + from traceback import format_exception_only heading = html.heading( 'Error', ) diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py index 10c3b7e122371e..1e423b59b47ad2 100644 --- a/Lib/unittest/case.py +++ b/Lib/unittest/case.py @@ -8,7 +8,6 @@ import warnings import collections import contextlib -import traceback import time import types @@ -262,6 +261,7 @@ def __exit__(self, exc_type, exc_value, tb): else: self._raiseFailure("{} not raised".format(exc_name)) else: + import traceback traceback.clear_frames(tb) if not issubclass(exc_type, self.expected): # let unexpected exceptions pass through diff --git a/Lib/unittest/loader.py b/Lib/unittest/loader.py index a52950dad224ee..c6fe7ccedf6988 100644 --- a/Lib/unittest/loader.py +++ b/Lib/unittest/loader.py @@ -3,7 +3,6 @@ import os import re import sys -import traceback import types import functools @@ -35,11 +34,13 @@ def testFailure(): def _make_failed_import_test(name, suiteClass): + import traceback message = 'Failed to import test module: %s\n%s' % ( name, traceback.format_exc()) return _make_failed_test(name, ImportError(message), suiteClass, message) def _make_failed_load_tests(name, exception, suiteClass): + import traceback message = 'Failed to call load_tests:\n%s' % (traceback.format_exc(),) return _make_failed_test( name, exception, suiteClass, message) @@ -163,6 +164,7 @@ def loadTestsFromName(self, name, module=None): return error_case else: # Otherwise, we signal that an AttributeError has occurred. + import traceback error_case, error_message = _make_failed_test( part, e, self.suiteClass, 'Failed to access attribute:\n%s' % ( diff --git a/Lib/unittest/result.py b/Lib/unittest/result.py index b8ea396db6772e..c9e3594713a9b2 100644 --- a/Lib/unittest/result.py +++ b/Lib/unittest/result.py @@ -2,7 +2,6 @@ import io import sys -import traceback from . import util from functools import wraps @@ -186,6 +185,7 @@ def _exc_info_to_string(self, err, test): """Converts a sys.exc_info()-style tuple of values into a string.""" exctype, value, tb = err tb = self._clean_tracebacks(exctype, value, tb, test) + import traceback tb_e = traceback.TracebackException( exctype, value, tb, capture_locals=self.tb_locals, compact=True) diff --git a/Lib/xmlrpc/server.py b/Lib/xmlrpc/server.py index 90a356fbb8eae4..e6065983265e8f 100644 --- a/Lib/xmlrpc/server.py +++ b/Lib/xmlrpc/server.py @@ -115,11 +115,7 @@ def export_add(self, x, y): import os import re import pydoc -import traceback -try: - import fcntl -except ImportError: - fcntl = None + def resolve_dotted_attribute(obj, attr, allow_dotted_names=True): """resolve_dotted_attribute(a, 'b.c.d') => a.b.c.d @@ -512,6 +508,7 @@ def do_POST(self): if hasattr(self.server, '_send_traceback_header') and \ self.server._send_traceback_header: self.send_header("X-exception", str(e)) + import traceback trace = traceback.format_exc() trace = str(trace.encode('ASCII', 'backslashreplace'), 'ASCII') self.send_header("X-traceback", trace) diff --git a/Misc/NEWS.d/next/Library/2025-02-07-17-04-08.gh-issue-118761.hkNOEA.rst b/Misc/NEWS.d/next/Library/2025-02-07-17-04-08.gh-issue-118761.hkNOEA.rst new file mode 100644 index 00000000000000..79eb4516685803 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-02-07-17-04-08.gh-issue-118761.hkNOEA.rst @@ -0,0 +1,2 @@ +Improve import time of various stdlib modules by lazy import ``traceback``. +Patch by Semyon Moroz. From 792c0c50e54cc46105f5f3d1223d922ffb173c3b Mon Sep 17 00:00:00 2001 From: donBarbos Date: Fri, 7 Feb 2025 15:41:45 +0000 Subject: [PATCH 2/2] Update Misc/NEWS.d/next/Library/2025-02-07-17-04-08.gh-issue-118761.hkNOEA.rst Co-authored-by: Peter Bierma --- .../next/Library/2025-02-07-17-04-08.gh-issue-118761.hkNOEA.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2025-02-07-17-04-08.gh-issue-118761.hkNOEA.rst b/Misc/NEWS.d/next/Library/2025-02-07-17-04-08.gh-issue-118761.hkNOEA.rst index 79eb4516685803..bfd2c18626477f 100644 --- a/Misc/NEWS.d/next/Library/2025-02-07-17-04-08.gh-issue-118761.hkNOEA.rst +++ b/Misc/NEWS.d/next/Library/2025-02-07-17-04-08.gh-issue-118761.hkNOEA.rst @@ -1,2 +1,2 @@ -Improve import time of various stdlib modules by lazy import ``traceback``. +Improve the import time of several modules by lazy importing :mod:`traceback`. Patch by Semyon Moroz.