Skip to content

Commit c4bcc6a

Browse files
noahbkimAA-Turner
andauthored
gh-102567: Add -X importtime=2 for logging an importtime message for already-loaded modules (#118655)
Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com>
1 parent e6f8e0a commit c4bcc6a

File tree

15 files changed

+166
-44
lines changed

15 files changed

+166
-44
lines changed

Doc/c-api/init_config.rst

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ Configuration Options
363363
- Read-only
364364
* - ``"import_time"``
365365
- :c:member:`import_time <PyConfig.import_time>`
366-
- ``bool``
366+
- ``int``
367367
- Read-only
368368
* - ``"inspect"``
369369
- :c:member:`inspect <PyConfig.inspect>`
@@ -1477,13 +1477,19 @@ PyConfig
14771477
14781478
.. c:member:: int import_time
14791479
1480-
If non-zero, profile import time.
1480+
If ``1``, profile import time.
1481+
If ``2``, include additional output that indicates
1482+
when an imported module has already been loaded.
14811483
1482-
Set the ``1`` by the :option:`-X importtime <-X>` option and the
1484+
Set by the :option:`-X importtime <-X>` option and the
14831485
:envvar:`PYTHONPROFILEIMPORTTIME` environment variable.
14841486
14851487
Default: ``0``.
14861488
1489+
.. versionchanged:: next
1490+
1491+
Added support for ``import_time = 2``
1492+
14871493
.. c:member:: int inspect
14881494
14891495
Enter interactive mode after executing a script or a command.

Doc/using/cmdline.rst

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -539,11 +539,21 @@ Miscellaneous options
539539
* ``-X importtime`` to show how long each import takes. It shows module
540540
name, cumulative time (including nested imports) and self time (excluding
541541
nested imports). Note that its output may be broken in multi-threaded
542-
application. Typical usage is ``python3 -X importtime -c 'import
543-
asyncio'``. See also :envvar:`PYTHONPROFILEIMPORTTIME`.
542+
application. Typical usage is ``python -X importtime -c 'import asyncio'``.
543+
544+
``-X importtime=2`` enables additional output that indicates when an
545+
imported module has already been loaded. In such cases, the string
546+
``cached`` will be printed in both time columns.
547+
548+
See also :envvar:`PYTHONPROFILEIMPORTTIME`.
544549

545550
.. versionadded:: 3.7
546551

552+
.. versionchanged:: next
553+
554+
Added ``-X importtime=2`` to also trace imports of loaded modules,
555+
and reserved values other than ``1`` and ``2`` for future use.
556+
547557
* ``-X dev``: enable :ref:`Python Development Mode <devmode>`, introducing
548558
additional runtime checks that are too expensive to be enabled by
549559
default. See also :envvar:`PYTHONDEVMODE`.
@@ -982,12 +992,17 @@ conflict.
982992

983993
.. envvar:: PYTHONPROFILEIMPORTTIME
984994

985-
If this environment variable is set to a non-empty string, Python will
986-
show how long each import takes.
995+
If this environment variable is set to ``1``, Python will show
996+
how long each import takes. If set to ``2``, Python will include output for
997+
imported modules that have already been loaded.
987998
This is equivalent to setting the :option:`-X` ``importtime`` option.
988999

9891000
.. versionadded:: 3.7
9901001

1002+
.. versionchanged:: next
1003+
1004+
Added ``PYTHONPROFILEIMPORTTIME=2`` to also trace imports of loaded modules.
1005+
9911006

9921007
.. envvar:: PYTHONASYNCIODEBUG
9931008

Doc/whatsnew/3.14.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,13 @@ Other language changes
789789
of HMAC is not available.
790790
(Contributed by Bénédikt Tran in :gh:`99108`.)
791791

792+
* The import time flag can now track modules that are already loaded ('cached'),
793+
via the new :option:`-X importtime=2 <-X>`.
794+
When such a module is imported, the ``self`` and ``cumulative`` times
795+
are replaced by the string ``cached``.
796+
Values above ``2`` for ``-X importtime`` are now reserved for future use.
797+
(Contributed by Noah Kim and Adam Turner in :gh:`118655`.)
798+
792799
* When subclassing from a pure C type, the C slots for the new type are no
793800
longer replaced with a wrapped version on class creation if they are not
794801
explicitly overridden in the subclass.

Lib/_pyrepl/unix_console.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import struct
3030
import termios
3131
import time
32+
import types
3233
import platform
3334
from fcntl import ioctl
3435

@@ -39,6 +40,12 @@
3940
from .unix_eventqueue import EventQueue
4041
from .utils import wlen
4142

43+
# declare posix optional to allow None assignment on other platforms
44+
posix: types.ModuleType | None
45+
try:
46+
import posix
47+
except ImportError:
48+
posix = None
4249

4350
TYPE_CHECKING = False
4451

@@ -556,11 +563,9 @@ def clear(self):
556563

557564
@property
558565
def input_hook(self):
559-
try:
560-
import posix
561-
except ImportError:
562-
return None
563-
if posix._is_inputhook_installed():
566+
# avoid inline imports here so the repl doesn't get flooded
567+
# with import logging from -X importtime=2
568+
if posix is not None and posix._is_inputhook_installed():
564569
return posix._inputhook
565570

566571
def __enable_bracketed_paste(self) -> None:

Lib/_pyrepl/windows_console.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import sys
2525

2626
import ctypes
27+
import types
2728
from ctypes.wintypes import (
2829
_COORD,
2930
WORD,
@@ -58,6 +59,12 @@ def __init__(self, err: int | None, descr: str | None = None) -> None:
5859
self.err = err
5960
self.descr = descr
6061

62+
# declare nt optional to allow None assignment on other platforms
63+
nt: types.ModuleType | None
64+
try:
65+
import nt
66+
except ImportError:
67+
nt = None
6168

6269
TYPE_CHECKING = False
6370

@@ -121,9 +128,8 @@ class _error(Exception):
121128

122129
def _supports_vt():
123130
try:
124-
import nt
125131
return nt._supports_virtual_terminal()
126-
except (ImportError, AttributeError):
132+
except AttributeError:
127133
return False
128134

129135
class WindowsConsole(Console):
@@ -235,11 +241,9 @@ def refresh(self, screen: list[str], c_xy: tuple[int, int]) -> None:
235241

236242
@property
237243
def input_hook(self):
238-
try:
239-
import nt
240-
except ImportError:
241-
return None
242-
if nt._is_inputhook_installed():
244+
# avoid inline imports here so the repl doesn't get flooded
245+
# with import logging from -X importtime=2
246+
if nt is not None and nt._is_inputhook_installed():
243247
return nt._inputhook
244248

245249
def __write_changed_line(

Lib/test/test_capi/test_config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def test_config_get(self):
5757
("home", str | None, None),
5858
("thread_inherit_context", int, None),
5959
("context_aware_warnings", int, None),
60-
("import_time", bool, None),
60+
("import_time", int, None),
6161
("inspect", bool, None),
6262
("install_signal_handlers", bool, None),
6363
("int_max_str_digits", int, None),

Lib/test/test_cmd_line.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,6 +1158,24 @@ def test_cpu_count_default(self):
11581158
res = assert_python_ok('-c', code, PYTHON_CPU_COUNT='default')
11591159
self.assertEqual(self.res2int(res), (os.cpu_count(), os.process_cpu_count()))
11601160

1161+
def test_import_time(self):
1162+
# os is not imported at startup
1163+
code = 'import os; import os'
1164+
1165+
for case in 'importtime', 'importtime=1', 'importtime=true':
1166+
res = assert_python_ok('-X', case, '-c', code)
1167+
res_err = res.err.decode('utf-8')
1168+
self.assertRegex(res_err, r'import time: \s*\d+ \| \s*\d+ \| \s*os')
1169+
self.assertNotRegex(res_err, r'import time: cached\s* \| cached\s* \| os')
1170+
1171+
res = assert_python_ok('-X', 'importtime=2', '-c', code)
1172+
res_err = res.err.decode('utf-8')
1173+
self.assertRegex(res_err, r'import time: \s*\d+ \| \s*\d+ \| \s*os')
1174+
self.assertRegex(res_err, r'import time: cached\s* \| cached\s* \| os')
1175+
1176+
assert_python_failure('-X', 'importtime=-1', '-c', code)
1177+
assert_python_failure('-X', 'importtime=3', '-c', code)
1178+
11611179
def res2int(self, res):
11621180
out = res.out.strip().decode("utf-8")
11631181
return tuple(int(i) for i in out.split())

Lib/test/test_embed.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
585585
'faulthandler': False,
586586
'tracemalloc': 0,
587587
'perf_profiling': 0,
588-
'import_time': False,
588+
'import_time': 0,
589589
'thread_inherit_context': DEFAULT_THREAD_INHERIT_CONTEXT,
590590
'context_aware_warnings': DEFAULT_CONTEXT_AWARE_WARNINGS,
591591
'code_debug_ranges': True,
@@ -998,7 +998,7 @@ def test_init_from_config(self):
998998
'hash_seed': 123,
999999
'tracemalloc': 2,
10001000
'perf_profiling': 0,
1001-
'import_time': True,
1001+
'import_time': 2,
10021002
'code_debug_ranges': False,
10031003
'show_ref_count': True,
10041004
'malloc_stats': True,
@@ -1064,7 +1064,7 @@ def test_init_compat_env(self):
10641064
'use_hash_seed': True,
10651065
'hash_seed': 42,
10661066
'tracemalloc': 2,
1067-
'import_time': True,
1067+
'import_time': 1,
10681068
'code_debug_ranges': False,
10691069
'malloc_stats': True,
10701070
'inspect': True,
@@ -1100,7 +1100,7 @@ def test_init_python_env(self):
11001100
'use_hash_seed': True,
11011101
'hash_seed': 42,
11021102
'tracemalloc': 2,
1103-
'import_time': True,
1103+
'import_time': 1,
11041104
'code_debug_ranges': False,
11051105
'malloc_stats': True,
11061106
'inspect': True,

Lib/test/test_support.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,7 @@ def test_args_from_interpreter_flags(self):
561561
['-Wignore', '-X', 'dev'],
562562
['-X', 'faulthandler'],
563563
['-X', 'importtime'],
564+
['-X', 'importtime=2'],
564565
['-X', 'showrefcount'],
565566
['-X', 'tracemalloc'],
566567
['-X', 'tracemalloc=3'],

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -966,6 +966,7 @@ Beomsoo Bombs Kim
966966
Derek D. Kim
967967
Gihwan Kim
968968
Jan Kim
969+
Noah Kim
969970
Taek Joo Kim
970971
Yeojin Kim
971972
Sam Kimbrel

0 commit comments

Comments
 (0)