Skip to content

Commit

Permalink
implement qa_wait_patterns option in run_shell_cmd
Browse files Browse the repository at this point in the history
  • Loading branch information
boegel committed Apr 3, 2024
1 parent 5783910 commit 717f555
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 5 deletions.
22 changes: 17 additions & 5 deletions easybuild/tools/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,9 +252,8 @@ def to_cmd_str(cmd):
if not isinstance(qa_patterns, list) or any(not isinstance(x, tuple) or len(x) != 2 for x in qa_patterns):
raise EasyBuildError("qa_patterns passed to run_shell_cmd should be a list of 2-tuples!")

# temporarily raise a NotImplementedError until all options are implemented
if qa_wait_patterns:
raise NotImplementedError
if qa_wait_patterns is None:
qa_wait_patterns = []

if work_dir is None:
work_dir = os.getcwd()
Expand Down Expand Up @@ -377,10 +376,10 @@ def to_cmd_str(cmd):
if split_stderr:
stderr += proc.stderr.read1(read_size) or b''

# only consider answering questions if there's new output beyond additional whitespace
if qa_patterns:
match_found = False
for question, answers in qa_patterns:

# allow extra whitespace at the end
question += r'[\s\n]*$'
regex = re.compile(question.encode())
if regex.search(stdout):
Expand All @@ -397,8 +396,21 @@ def to_cmd_str(cmd):
answer += '\n'
os.write(proc.stdin.fileno(), answer.encode())
time_no_match = 0
match_found = True
break
else:
# if no match was found among question patterns,
# take into account patterns for non-questions (qa_wait_patterns)
for pattern in qa_wait_patterns:
# allow extra whitespace at the end
pattern += r'[\s\n]*$'
regex = re.compile(pattern.encode())
if regex.search(stdout):
time_no_match = 0
match_found = True
break

if not match_found:
# this will only run if the for loop above was *not* stopped by the break statement
time_no_match += check_interval_secs
if time_no_match > qa_timeout:
Expand Down
39 changes: 39 additions & 0 deletions test/framework/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,24 @@ def test_run_cmd_qa(self):
self.assertTrue(out.startswith("question\nanswer\nfoo "))
self.assertTrue(out.endswith('bar'))

# test handling of output that is not actually a question
cmd = ';'.join([
"echo not-a-question-but-a-statement",
"sleep 3",
"echo question",
"read x",
"echo $x",
])
qa = {'question': 'answer'}

# fails because non-question is encountered
error_pattern = "Max nohits 1 reached: end of output not-a-question-but-a-statement"
self.assertErrorRegex(EasyBuildError, error_pattern, run_cmd_qa, cmd, qa, maxhits=1, trace=False)

(out, ec) = run_cmd_qa(cmd, qa, no_qa=["not-a-question-but-a-statement"], maxhits=1, trace=False)
self.assertEqual(out, "not-a-question-but-a-statement\nquestion\nanswer\n")
self.assertEqual(ec, 0)

def test_run_shell_cmd_qa(self):
"""Basic test for Q&A support in run_shell_cmd function."""

Expand Down Expand Up @@ -820,6 +838,27 @@ def test_run_shell_cmd_qa(self):
with self.mocked_stdout_stderr():
self.assertErrorRegex(EasyBuildError, error_pattern, run_shell_cmd, cmd, qa_patterns=qa, qa_timeout=1)

# test handling of output that is not actually a question
cmd = ';'.join([
"echo not-a-question-but-a-statement",
"sleep 3",
"echo question",
"read x",
"echo $x",
])
qa = [('question', 'answer')]

# fails because non-question is encountered
error_pattern = "No matching questions found for current command output, giving up after 1 seconds!"
self.assertErrorRegex(EasyBuildError, error_pattern, run_shell_cmd, cmd, qa_patterns=qa, qa_timeout=1,
hidden=True)

qa_wait_patterns = ["not-a-question-but-a-statement"]
with self.mocked_stdout_stderr():
res = run_shell_cmd(cmd, qa_patterns=qa, qa_wait_patterns=qa_wait_patterns, qa_timeout=1)
self.assertEqual(res.exit_code, 0)
self.assertEqual(res.output, "not-a-question-but-a-statement\nquestion\nanswer\n")

def test_run_cmd_qa_buffering(self):
"""Test whether run_cmd_qa uses unbuffered output."""

Expand Down

0 comments on commit 717f555

Please sign in to comment.