Skip to content

Commit 6d50b0c

Browse files
committed
Merge pull request #1 from AA-Turner/3.14-importtime=2
`-X importtime=2` feedback (Thanks so much, @AA-Turner)
2 parents 58d07be + f9c07ce commit 6d50b0c

File tree

14 files changed

+97
-107
lines changed

14 files changed

+97
-107
lines changed

Doc/c-api/init_config.rst

Lines changed: 8 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,14 +1477,19 @@ PyConfig
14771477
14781478
.. c:member:: int import_time
14791479
1480-
If non-zero, profile import time. If ``2``, include additional output that
1481-
indicates when an imported module has already been loaded.
1480+
If ``1``, profile import time.
1481+
If ``2``, include additional output that indicates
1482+
when an imported module has already been loaded.
14821483
14831484
Set by the :option:`-X importtime <-X>` option and the
14841485
:envvar:`PYTHONPROFILEIMPORTTIME` environment variable.
14851486
14861487
Default: ``0``.
14871488
1489+
.. versionchanged:: next
1490+
1491+
Added support for ``import_time = 2``
1492+
14881493
.. c:member:: int inspect
14891494
14901495
Enter interactive mode after executing a script or a command.

Doc/using/cmdline.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -539,12 +539,13 @@ 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'``.
544543

545544
``-X importtime=2`` enables additional output that indicates when an
546545
imported module has already been loaded. In such cases, the string
547-
``cached`` will be printed in the self time and cumulative time columns.
546+
``cached`` will be printed in both time columns.
547+
548+
See also :envvar:`PYTHONPROFILEIMPORTTIME`.
548549

549550
.. versionadded:: 3.7
550551

Doc/whatsnew/3.14.rst

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -626,12 +626,12 @@ Other language changes
626626
of HMAC is not available.
627627
(Contributed by Bénédikt Tran in :gh:`99108`.)
628628

629-
* :option:`-X importtime <-X>` now accepts value ``2``, which indicates that
630-
an ``importtime`` entry should also be printed if an imported module has
631-
already been loaded. The ``self`` and ``cumulative`` times for such entries
629+
* The import time flag can now track modules that are already loaded ('cached'),
630+
via the new :option:`-X importtime=2 <-X>`.
631+
When such a module is imported, the ``self`` and ``cumulative`` times
632632
are replaced by the string ``cached``.
633-
Values above ``2`` are now reserved for future use.
634-
(Contributed by Noah Kim in :gh:`118655`.)
633+
Values above ``2`` for ``-X importtime`` are now reserved for future use.
634+
(Contributed by Noah Kim and Adam Turner in :gh:`118655`.)
635635

636636
* When subclassing from a pure C type, the C slots for the new type are no
637637
longer replaced with a wrapped version on class creation if they are not

Lib/_pyrepl/unix_console.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242

4343
# declare posix optional to allow None assignment on other platforms
4444
posix: types.ModuleType | None
45-
4645
try:
4746
import posix
4847
except ImportError:
@@ -574,8 +573,8 @@ def clear(self):
574573

575574
@property
576575
def input_hook(self):
577-
# avoid inline imports here so the repl doesn't get flooded with import
578-
# logging from -Ximporttime=2
576+
# avoid inline imports here so the repl doesn't get flooded
577+
# with import logging from -X importtime=2
579578
if posix is not None and posix._is_inputhook_installed():
580579
return posix._inputhook
581580

Lib/_pyrepl/windows_console.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ def __init__(self, err: int | None, descr: str | None = None) -> None:
5959
self.err = err
6060
self.descr = descr
6161

62+
# declare nt optional to allow None assignment on other platforms
6263
nt: types.ModuleType | None
63-
6464
try:
6565
import nt
6666
except ImportError:
@@ -128,7 +128,7 @@ class _error(Exception):
128128

129129
def _supports_vt():
130130
try:
131-
nt._supports_virtual_terminal()
131+
return nt._supports_virtual_terminal()
132132
except AttributeError:
133133
return False
134134

@@ -241,8 +241,8 @@ def refresh(self, screen: list[str], c_xy: tuple[int, int]) -> None:
241241

242242
@property
243243
def input_hook(self):
244-
# avoid inline imports here so the repl doesn't get flooded with import
245-
# logging from -Ximporttime=2
244+
# avoid inline imports here so the repl doesn't get flooded
245+
# with import logging from -X importtime=2
246246
if nt is not None and nt._is_inputhook_installed():
247247
return nt._inputhook
248248

Lib/test/test_cmd_line.py

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1159,23 +1159,19 @@ def test_cpu_count_default(self):
11591159
self.assertEqual(self.res2int(res), (os.cpu_count(), os.process_cpu_count()))
11601160

11611161
def test_import_time(self):
1162-
code = "import os"
1163-
res = assert_python_ok('-X', 'importtime', '-c', code)
1164-
res_err = res.err.decode("utf-8")
1165-
self.assertRegex(res_err, r"import time: \s*\d+ \| \s*\d+ \| \s*os")
1166-
self.assertNotRegex(res_err, r"import time: cached\s* \| cached\s* \| os")
1167-
1168-
code = "import os"
1169-
res = assert_python_ok('-X', 'importtime=true', '-c', code)
1170-
res_err = res.err.decode("utf-8")
1171-
self.assertRegex(res_err, r"import time: \s*\d+ \| \s*\d+ \| \s*os")
1172-
self.assertNotRegex(res_err, r"import time: cached\s* \| cached\s* \| os")
1173-
1174-
code = "import os; import os"
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+
11751171
res = assert_python_ok('-X', 'importtime=2', '-c', code)
1176-
res_err = res.err.decode("utf-8")
1177-
self.assertRegex(res_err, r"import time: \s*\d+ \| \s*\d+ \| \s*os")
1178-
self.assertRegex(res_err, r"import time: cached\s* \| cached\s* \| os")
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')
11791175

11801176
assert_python_failure('-X', 'importtime=-1', '-c', code)
11811177
assert_python_failure('-X', 'importtime=3', '-c', code)

Lib/test/test_embed.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -998,7 +998,7 @@ def test_init_from_config(self):
998998
'hash_seed': 123,
999999
'tracemalloc': 2,
10001000
'perf_profiling': 0,
1001-
'import_time': 0,
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': 0,
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': 0,
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
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
:option:`-X importtime <-X>` now accepts value ``2``, which indicates that
22
an ``importtime`` entry should also be printed if an imported module has
33
already been loaded.
4+
Patch by Noah Kim and Adam Turner.

Misc/python.man

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,10 @@ Set implementation-specific option. The following options are available:
344344
application. Typical usage is
345345
\fBpython3 \-X importtime \-c 'import asyncio'\fR
346346

347+
\fB\-X importtime=2\fR enables additional output that indicates when an
348+
imported module has already been loaded. In such cases, the string
349+
\fBcached\fR will be printed in both time columns.
350+
347351
\fB\-X faulthandler\fR: enable faulthandler
348352

349353
\fB\-X frozen_modules=\fR[\fBon\fR|\fBoff\fR]: whether or not frozen modules
@@ -648,9 +652,10 @@ See also the \fB\-X perf\fR option.
648652
.IP PYTHONPLATLIBDIR
649653
Override sys.platlibdir.
650654
.IP PYTHONPROFILEIMPORTTIME
651-
If this environment variable is set to a non-empty string, Python will
652-
show how long each import takes. This is exactly equivalent to setting
653-
\fB\-X importtime\fP on the command line.
655+
If this environment variable is set to \fB1\fR, Python will show
656+
how long each import takes. If set to \fB2\fR, Python will include output for
657+
imported modules that have already been loaded.
658+
This is exactly equivalent to setting \fB\-X importtime\fP on the command line.
654659
.IP PYTHONPYCACHEPREFIX
655660
If this is set, Python will write \fB.pyc\fR files in a mirror directory tree
656661
at this path, instead of in \fB__pycache__\fR directories within the source

Programs/_testembed.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -651,8 +651,8 @@ static int test_init_from_config(void)
651651
putenv("PYTHONTRACEMALLOC=0");
652652
config.tracemalloc = 2;
653653

654-
putenv("PYTHONPROFILEIMPORTTIME=0");
655-
config.import_time = 0;
654+
putenv("PYTHONPROFILEIMPORTTIME=1");
655+
config.import_time = 2;
656656

657657
putenv("PYTHONNODEBUGRANGES=0");
658658
config.code_debug_ranges = 0;

Python/import.c

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,15 @@ static struct _inittab *inittab_copy = NULL;
103103
#define FIND_AND_LOAD(interp) \
104104
(interp)->imports.find_and_load
105105

106+
#define _IMPORT_TIME_HEADER(interp) \
107+
do { \
108+
if (FIND_AND_LOAD((interp)).header) { \
109+
fputs("import time: self [us] | cumulative | imported package\n", \
110+
stderr); \
111+
FIND_AND_LOAD((interp)).header = 0; \
112+
} \
113+
} while (0)
114+
106115

107116
/*******************/
108117
/* the import lock */
@@ -262,13 +271,14 @@ import_ensure_initialized(PyInterpreterState *interp, PyObject *mod, PyObject *n
262271
Py_DECREF(value);
263272

264273
done:
265-
/* When -Ximporttime=2, print an import time entry even if an
266-
* imported module has already been loaded.
274+
/* When -X importtime=2, print an import time entry even if an
275+
imported module has already been loaded.
267276
*/
268-
if (_PyInterpreterState_GetConfig(interp)->import_time >= 2) {
277+
if (_PyInterpreterState_GetConfig(interp)->import_time == 2) {
278+
_IMPORT_TIME_HEADER(interp);
269279
#define import_level FIND_AND_LOAD(interp).import_level
270280
fprintf(stderr, "import time: cached | cached | %*s\n",
271-
import_level*2, PyUnicode_AsUTF8(name));
281+
import_level*2, PyUnicode_AsUTF8(name));
272282
#undef import_level
273283
}
274284

@@ -3702,13 +3712,7 @@ import_find_and_load(PyThreadState *tstate, PyObject *abs_name)
37023712
* _PyDict_GetItemIdWithError().
37033713
*/
37043714
if (import_time) {
3705-
#define header FIND_AND_LOAD(interp).header
3706-
if (header) {
3707-
fputs("import time: self [us] | cumulative | imported package\n",
3708-
stderr);
3709-
header = 0;
3710-
}
3711-
#undef header
3715+
_IMPORT_TIME_HEADER(interp);
37123716

37133717
import_level++;
37143718
// ignore error: don't block import if reading the clock fails

0 commit comments

Comments
 (0)