diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 7ac2289f535b6d..f19e99699fe84e 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -912,7 +912,7 @@ def __init__(self, args, bufsize=-1, executable=None, else: # POSIX if pass_fds and not close_fds: - warnings.warn("pass_fds overriding close_fds.", RuntimeWarning) + warnings.warn("pass_fds overriding close_fds.", RuntimeWarning, stacklevel=2) close_fds = True if startupinfo is not None: raise ValueError("startupinfo is only supported on Windows " @@ -1567,7 +1567,8 @@ def _execute_child(self, args, executable, preexec_fn, close_fds, if handle_list: if not close_fds: warnings.warn("startupinfo.lpAttributeList['handle_list'] " - "overriding close_fds", RuntimeWarning) + "overriding close_fds", RuntimeWarning, + stacklevel=3) # When using the handle_list we always request to inherit # handles but the only handles that will be inherited are diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 0c5679611848ea..b7f43548dc9bdd 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -3177,6 +3177,18 @@ def test_pass_fds(self): close_fds=False, pass_fds=(fd, ))) self.assertIn('overriding close_fds', str(context.warning)) + def test_pass_fds_overriding_close_fds_warning_location(self): + # gh-148402: the warning should point to the caller, not subprocess.py + fd = os.dup(1) + self.addCleanup(os.close, fd) + os.set_inheritable(fd, True) + with self.assertWarns(RuntimeWarning) as context: + p = subprocess.Popen(ZERO_RETURN_CMD, + close_fds=False, pass_fds=(fd,)) + p.wait() + self.assertIn('overriding close_fds', str(context.warning)) + self.assertEqual(context.filename, __file__) + def test_pass_fds_inheritable(self): script = support.findfile("fd_status.py", subdir="subprocessdata") @@ -3762,8 +3774,7 @@ def test_close_fds_with_stdio(self): self.assertIn(b"OSError", stderr) # Check for a warning due to using handle_list and close_fds=False - with warnings_helper.check_warnings((".*overriding close_fds", - RuntimeWarning)): + with self.assertWarns(RuntimeWarning) as context: startupinfo = subprocess.STARTUPINFO() startupinfo.lpAttributeList = {"handle_list": handles[:]} p = subprocess.Popen([sys.executable, "-c", @@ -3772,6 +3783,9 @@ def test_close_fds_with_stdio(self): startupinfo=startupinfo, close_fds=False) stdout, stderr = p.communicate() self.assertEqual(p.returncode, 0) + self.assertIn('overriding close_fds', str(context.warning)) + # gh-148402: warning should point to the caller, not subprocess.py + self.assertEqual(context.filename, __file__) def test_empty_attribute_list(self): startupinfo = subprocess.STARTUPINFO() diff --git a/Misc/NEWS.d/next/Library/2026-04-11-14-28-56.gh-issue-148402.zey6m1.rst b/Misc/NEWS.d/next/Library/2026-04-11-14-28-56.gh-issue-148402.zey6m1.rst new file mode 100644 index 00000000000000..fa7bfc8e4feca9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-04-11-14-28-56.gh-issue-148402.zey6m1.rst @@ -0,0 +1,3 @@ +Add missing ``stacklevel`` to two :func:`warnings.warn` calls in +:class:`subprocess.Popen` so that warnings correctly point to the caller's +code instead of ``subprocess.py`` internals.