From 938db85a8733eb15be76d8b599c0d6dfc68b7cac Mon Sep 17 00:00:00 2001 From: Andy Kipp Date: Fri, 9 Aug 2024 14:46:19 +0200 Subject: [PATCH] Partial fix: Callable alias can provoke `Bad file descriptor exception` in the internal or external code (#5645) * fix * fix * test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * news * test * test * test * test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * news * bump test * bump test * test * test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * news * test --------- Co-authored-by: a <1@1.1> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- news/fix_bad_file_descriptor.rst | 23 +++++++++++++++++++ tests/test_integrations.py | 38 ++++++++++++++++++++++++++++++++ xonsh/procs/proxies.py | 5 ++++- 3 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 news/fix_bad_file_descriptor.rst diff --git a/news/fix_bad_file_descriptor.rst b/news/fix_bad_file_descriptor.rst new file mode 100644 index 000000000..fa0de084a --- /dev/null +++ b/news/fix_bad_file_descriptor.rst @@ -0,0 +1,23 @@ +**Added:** + +* + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* Partial fix for "Bad file descriptor" in case of callable alias with execx invocation inside e.g. ExecAlias (#5645). + +**Security:** + +* diff --git a/tests/test_integrations.py b/tests/test_integrations.py index f6b422e6c..915a82d59 100644 --- a/tests/test_integrations.py +++ b/tests/test_integrations.py @@ -1508,3 +1508,41 @@ def test_shebang_cr(tmpdir): command = f"cd {testdir}; ./{testfile}\n" out, err, rtn = run_xonsh(command) assert out == f"{expected_out}\n" + + +test_code = [ + """ +$XONSH_SHOW_TRACEBACK = True +@aliases.register +def _e(a,i,o,e): + echo -n O + echo -n E 1>2 + execx("echo -n O") + execx("echo -n E 1>2") + print("o") + print("O", file=o) + print("E", file=e) + +import tempfile +for i in range(0, 12): + echo -n e + print($(e), !(e), $[e], ![e]) + print($(e > @(tempfile.NamedTemporaryFile(delete=False).name))) + print(!(e > @(tempfile.NamedTemporaryFile(delete=False).name))) + print($[e > @(tempfile.NamedTemporaryFile(delete=False).name)]) + print(![e > @(tempfile.NamedTemporaryFile(delete=False).name)]) +""" +] + + +@skip_if_on_windows +@pytest.mark.parametrize("test_code", test_code) +def test_callable_alias_no_bad_file_descriptor(test_code): + """Test no exceptions during any kind of capturing of callable alias. See also #5631.""" + + out, err, ret = run_xonsh( + test_code, interactive=True, single_command=True, timeout=60 + ) + assert ret == 0 + assert "Error" not in out + assert "Exception" not in out diff --git a/xonsh/procs/proxies.py b/xonsh/procs/proxies.py index 6add535ab..6796b77fb 100644 --- a/xonsh/procs/proxies.py +++ b/xonsh/procs/proxies.py @@ -511,7 +511,10 @@ class ProcProxyThread(threading.Thread): # clean up # scopz: not sure why this is needed, but stdin cannot go here # and stdout & stderr must. - handles = [self.stdout, self.stderr] + if xp.ON_WINDOWS: + handles = [self.stdout, self.stderr] + else: + handles = [sp_stdout, sp_stderr] for handle in handles: safe_fdclose(handle, cache=self._closed_handle_cache)