From 9716b022f4f6857306a44030cae53d52ed3e1ece Mon Sep 17 00:00:00 2001 From: Nithish <75948660+NithishKumar04@users.noreply.github.com> Date: Wed, 12 Aug 2026 10:44:28 +0530 Subject: [PATCH] gh-88580: Run profile scripts in __main__ namespace The profile CLI executed script globals separately from the module stored in sys.modules["__main__"]. Deferred dataclass annotations therefore resolved against the profiler module and failed to recognize InitVar fields. Create a real __main__ module and execute the profiled script in its namespace, matching direct script execution and cProfile. --- Lib/profile.py | 15 ++++++++++--- Lib/test/test_profile.py | 22 +++++++++++++++++++ ...6-08-12-10-37-22.gh-issue-88580.HC3q9r.rst | 3 +++ 3 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-08-12-10-37-22.gh-issue-88580.HC3q9r.rst diff --git a/Lib/profile.py b/Lib/profile.py index 304284da4211632..ea6a5f05047cbc4 100644 --- a/Lib/profile.py +++ b/Lib/profile.py @@ -24,6 +24,7 @@ import importlib.machinery +import importlib.util import io import sys import time @@ -602,12 +603,20 @@ def main(): code = compile(fp.read(), progname, 'exec') spec = importlib.machinery.ModuleSpec(name='__main__', loader=None, origin=progname) - globs = { - '__spec__': spec, + module = importlib.util.module_from_spec(spec) + # Set __main__ so that importing __main__ in the profiled code will + # return the same namespace that the code is executing under. + sys.modules['__main__'] = module + # Ensure that we're using the same __dict__ instance as the module + # for the global variables so that updates to globals are reflected + # in the module's namespace. + globs = module.__dict__ + globs.update({ + '__spec__': None, '__file__': spec.origin, '__name__': spec.name, '__package__': None, - } + }) try: runctx(code, globs, None, options.outfile, options.sort) except BrokenPipeError as exc: diff --git a/Lib/test/test_profile.py b/Lib/test/test_profile.py index c4b2d7ca05c0a6c..af22c5b210177ec 100644 --- a/Lib/test/test_profile.py +++ b/Lib/test/test_profile.py @@ -120,6 +120,28 @@ def test_run_profile_as_module(self): assert_python_ok('-m', self.profilermodule.__name__, '-m', 'timeit', '-n', '1') + def test_script_with_deferred_dataclass_annotations(self): + # gh-88580: the profiled script must be available as __main__ so + # dataclasses can resolve string annotations in its global namespace. + with temp_dir() as tmpdir: + script = os.path.join(tmpdir, 'profile_dataclass.py') + with open(script, 'w', encoding='utf-8') as f: + f.write("""\ +from __future__ import annotations +from dataclasses import dataclass, InitVar + +@dataclass +class C: + value: InitVar[int] + + def __post_init__(self, value): + assert value == 42 + +C(42) +""") + + assert_python_ok('-m', self.profilermodule.__name__, script) + def test_output_file_when_changing_directory(self): with temp_dir() as tmpdir, change_cwd(tmpdir): os.mkdir('dest') diff --git a/Misc/NEWS.d/next/Library/2026-08-12-10-37-22.gh-issue-88580.HC3q9r.rst b/Misc/NEWS.d/next/Library/2026-08-12-10-37-22.gh-issue-88580.HC3q9r.rst new file mode 100644 index 000000000000000..107f6308a525a2d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-08-12-10-37-22.gh-issue-88580.HC3q9r.rst @@ -0,0 +1,3 @@ +Fix the :mod:`profile` command-line interface to execute scripts in the actual +``__main__`` module namespace. This fixes profiling dataclasses that use +deferred :class:`~dataclasses.InitVar` annotations.