From a5c232ae0bacde3d4807bb4cc76dc67c58bbc83f Mon Sep 17 00:00:00 2001 From: Alex Domingo Date: Tue, 24 Dec 2024 01:03:20 +0100 Subject: [PATCH 1/2] attach /dev/null to STDIN on run_shell_cmd without input content --- easybuild/tools/run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easybuild/tools/run.py b/easybuild/tools/run.py index 78051c9ff3..5bb5d863f4 100644 --- a/easybuild/tools/run.py +++ b/easybuild/tools/run.py @@ -496,7 +496,7 @@ def to_cmd_str(cmd): executable, shell = None, False stderr_handle = subprocess.PIPE if split_stderr else subprocess.STDOUT - stdin_handle = subprocess.PIPE if stdin or qa_patterns else None + stdin_handle = subprocess.PIPE if stdin or qa_patterns else subprocess.DEVNULL log_msg = f"Running {interactive_msg}shell command '{cmd_str}' in {work_dir}" if thread_id: From 27c10beb39be71f89995bb6f648704f6fb53782b Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Thu, 26 Dec 2024 15:59:52 +0100 Subject: [PATCH 2/2] add test to check running of (potentially interactive) Perl script via run_shell_cmd --- test/framework/run.py | 62 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/test/framework/run.py b/test/framework/run.py index 765efc6b62..7f5622703f 100644 --- a/test/framework/run.py +++ b/test/framework/run.py @@ -266,6 +266,68 @@ def test_run_shell_cmd_basic(self): self.assertTrue(isinstance(res.output, str)) self.assertTrue(res.work_dir and isinstance(res.work_dir, str)) + def test_run_shell_cmd_perl(self): + """ + Test running of Perl script via run_shell_cmd that detects type of shell + """ + perl_script = os.path.join(self.test_prefix, 'test.pl') + perl_script_txt = """#!/usr/bin/perl + + # wait for input, see what happens (should not hang) + print STDOUT "foo:\n"; + STDOUT->autoflush(1); + my $stdin = ; + print "stdin: $stdin\n"; + + # conditional print statements below should *not* be triggered + print "stdin+stdout are terminals\n" if -t STDIN && -t STDOUT; + print "stdin is terminal\n" if -t STDIN; + print "stdout is terminal\n" if -t STDOUT; + my $ISA_TTY = -t STDIN && (-t STDOUT || !(-f STDOUT || -c STDOUT)) ; + print "ISA_TTY" if $ISA_TTY; + + print "PS1 is set\n" if $ENV{PS1}; + + print "tty -s returns 0\n" if system("tty -s") == 0; + + # check if parent process is a shell + my $ppid = getppid(); + my $parent_cmd = `ps -p $ppid -o comm=`; + print "parent process is bash\n" if ($parent_cmd =~ '/bash$'); + """ + write_file(perl_script, perl_script_txt) + adjust_permissions(perl_script, stat.S_IXUSR) + + def handler(signum, _): + raise RuntimeError(f"Test for running Perl script via run_shell_cmd took too long, signal {signum}") + + orig_sigalrm_handler = signal.getsignal(signal.SIGALRM) + + try: + # set the signal handler and a 3-second alarm + signal.signal(signal.SIGALRM, handler) + signal.alarm(3) + + res = run_shell_cmd(perl_script, hidden=True) + self.assertEqual(res.exit_code, 0) + self.assertEqual(res.output, 'foo:\nstdin: \n') + + res = run_shell_cmd(perl_script, hidden=True, stdin="test") + self.assertEqual(res.exit_code, 0) + self.assertEqual(res.output, 'foo:\nstdin: test\n') + + res = run_shell_cmd(perl_script, hidden=True, qa_patterns=[('foo:', 'bar')], qa_timeout=1) + self.assertEqual(res.exit_code, 0) + self.assertEqual(res.output, 'foo:\nstdin: bar\n\n') + + error_pattern = "No matching questions found for current command output" + self.assertErrorRegex(EasyBuildError, error_pattern, run_shell_cmd, perl_script, + hidden=True, qa_patterns=[('bleh', 'blah')], qa_timeout=1) + finally: + # cleanup: disable the alarm + reset signal handler for SIGALRM + signal.signal(signal.SIGALRM, orig_sigalrm_handler) + signal.alarm(0) + def test_run_shell_cmd_env(self): """Test env option in run_shell_cmd."""