Skip to content

Commit 18857ad

Browse files
authored
Merge pull request #309 from mkmkl93/main
Add flag to capture stderr in chkwer
2 parents fdcdcd7 + cee1ee4 commit 18857ad

File tree

6 files changed

+33
-24
lines changed

6 files changed

+33
-24
lines changed

src/sinol_make/commands/chkwer/__init__.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ def configure_subparser(self, subparser):
3434
)
3535
parser.add_argument('-t', '--tests', type=str, nargs='+',
3636
help='test to run, for example in/abc{0,1}*')
37+
parser.add_argument('--cerr', action='store_true', help='capture cerr output')
3738
parsers.add_cpus_argument(parser, 'number of cpus to use when verifying tests')
3839
parsers.add_compilation_arguments(parser)
3940
return parser
@@ -59,11 +60,11 @@ def run_test(self, execution: ChkwerExecution) -> RunResult:
5960
with open(execution.in_test_path, 'r') as inf, open(output_file, 'w') as outf:
6061
process = subprocess.Popen([execution.model_exe], stdin=inf, stdout=outf)
6162
process.wait()
62-
ok, points, comment = self.task_type.check_output(execution.in_test_path, output_file, execution.out_test_path)
63+
ok, points, comment, stderr = self.task_type.check_output(execution.in_test_path, output_file, execution.out_test_path)
6364

64-
return RunResult(execution.in_test_path, ok, int(points), comment)
65+
return RunResult(execution.in_test_path, ok, int(points), comment, stderr)
6566

66-
def run_and_print_table(self) -> Dict[str, TestResult]:
67+
def run_and_print_table(self, args) -> Dict[str, TestResult]:
6768
results = {}
6869
sorted_tests = sorted(self.tests, key=lambda test: package_util.get_group(test, self.task_id))
6970
executions: List[ChkwerExecution] = []
@@ -77,14 +78,14 @@ def run_and_print_table(self) -> Dict[str, TestResult]:
7778
if has_terminal:
7879
run_event = threading.Event()
7980
run_event.set()
80-
thr = threading.Thread(target=printer.printer_thread, args=(run_event, chkwer_util.print_view, table_data))
81+
thr = threading.Thread(target=printer.printer_thread, args=(run_event, chkwer_util.print_view, table_data, args))
8182
thr.start()
8283

8384
keyboard_interrupt = False
8485
try:
8586
with mp.Pool(self.cpus) as pool:
8687
for i, result in enumerate(pool.imap(self.run_test, executions)):
87-
table_data.results[result.test_path].set_results(result.points, result.ok, result.comment)
88+
table_data.results[result.test_path].set_results(result.points, result.ok, result.comment, result.stderr)
8889
table_data.i = i
8990
except KeyboardInterrupt:
9091
keyboard_interrupt = True
@@ -93,7 +94,7 @@ def run_and_print_table(self) -> Dict[str, TestResult]:
9394
run_event.clear()
9495
thr.join()
9596

96-
print("\n".join(chkwer_util.print_view(terminal_width, terminal_height, table_data)[0]))
97+
print("\n".join(chkwer_util.print_view(terminal_width, terminal_height, table_data, args)[0]))
9798
if keyboard_interrupt:
9899
util.exit_with_error("Keyboard interrupt.")
99100
return results
@@ -129,7 +130,7 @@ def run(self, args):
129130
"model solution", args.compile_mode)
130131
print()
131132

132-
results = self.run_and_print_table()
133+
results = self.run_and_print_table(args)
133134
for result in results.values():
134135
if not result.ok or result.points != self.contest_type.max_score_per_test():
135136
util.exit_with_error("Model solution didn't score maximum points.")

src/sinol_make/commands/chkwer/chkwer_util.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
from sinol_make.commands.inwer.inwer_util import sort_tests
66
from sinol_make.structs.chkwer_structs import TableData
77

8-
9-
def print_view(term_width, term_height, table_data: TableData):
8+
def print_view(term_width, term_height, table_data: TableData, args):
109
"""
1110
Prints current results of test verification.
1211
"""
@@ -65,6 +64,11 @@ def print_line_separator():
6564
print(util.color_gray("No comment"))
6665

6766
print_line_separator()
67+
68+
if args.cerr:
69+
for test_path in tests:
70+
result = results[test_path]
71+
print(util.bold(f"Stderr on {result.test_name}: ") + result.stderr)
6872
print()
6973
print()
7074

src/sinol_make/structs/chkwer_structs.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ class TestResult:
1414
points: int
1515
ok: bool
1616
comment: str
17+
stderr: str
1718

1819
def __init__(self, test_path, task_id):
1920
self.test_path = test_path
@@ -24,12 +25,14 @@ def __init__(self, test_path, task_id):
2425
self.points = 0
2526
self.ok = False
2627
self.run = False
28+
self.stderr = ""
2729

28-
def set_results(self, points, ok, output):
30+
def set_results(self, points, ok, output, stderr):
2931
self.run = True
3032
self.points = points
3133
self.ok = ok
3234
self.comment = output
35+
self.stderr = stderr
3336

3437

3538
@dataclass
@@ -66,3 +69,4 @@ class RunResult:
6669
ok: bool
6770
points: int
6871
comment: str
72+
stderr: str

src/sinol_make/task_type/__init__.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -135,51 +135,51 @@ def _output_to_fraction(self, output_str):
135135
except TypeError:
136136
raise CheckerException(f'Invalid checker output "{output_str}"')
137137

138-
def _parse_checker_output(self, output: List[str]) -> Tuple[bool, Fraction, str]:
138+
def _parse_checker_output(self, output: List[str], stderr: str) -> Tuple[bool, Fraction, str, str]:
139139
while len(output) < 3:
140140
output.append('')
141141

142142
if output[0].strip() == "OK":
143143
points = self._output_to_fraction(output[2])
144-
return True, points, output[1].strip()
144+
return True, points, output[1].strip(), stderr
145145
else:
146-
return False, Fraction(0, 1), output[1].strip()
146+
return False, Fraction(0, 1), output[1].strip(), stderr
147147

148-
def _run_checker(self, input_file_path, output_file_path, answer_file_path) -> Tuple[bool, Fraction, str]:
148+
def _run_checker(self, input_file_path, output_file_path, answer_file_path) -> Tuple[bool, Fraction, str, str]:
149149
proc = subprocess.Popen([self.checker_path, input_file_path, output_file_path, answer_file_path],
150150
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
151151
proc.wait()
152152
output, stderr = proc.communicate()
153153
if proc.returncode > 2:
154154
return False, Fraction(0, 1), (f"Checker returned with code {proc.returncode}, "
155155
f"stderr: '{stderr.decode('utf-8')}'")
156-
return self._parse_checker_output(output.decode('utf-8').split('\n'))
156+
return self._parse_checker_output(output.decode('utf-8').split('\n'), stderr.decode('utf-8'))
157157

158-
def _run_diff(self, output_file_path, answer_file_path) -> Tuple[bool, Fraction, str]:
158+
def _run_diff(self, output_file_path, answer_file_path) -> Tuple[bool, Fraction, str, str]:
159159
same = oicompare.compare(output_file_path, answer_file_path)
160160
if same:
161-
return True, Fraction(100, 1), ""
161+
return True, Fraction(100, 1), "", ""
162162
else:
163-
return False, Fraction(0, 1), ""
163+
return False, Fraction(0, 1), "", ""
164164

165-
def _run_oicompare(self, output_file_path, answer_file_path) -> Tuple[bool, Fraction, str]:
165+
def _run_oicompare(self, output_file_path, answer_file_path) -> Tuple[bool, Fraction, str, str]:
166166
path = oicompare.get_path()
167167
proc = subprocess.Popen([path, output_file_path, answer_file_path, 'english_abbreviated'],
168168
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
169169
proc.wait()
170170
output, stderr = proc.communicate()
171171
if proc.returncode == 0:
172-
return True, Fraction(100, 1), ""
172+
return True, Fraction(100, 1), "", ""
173173
elif proc.returncode == 1:
174-
return False, Fraction(0, 1), output.decode('utf-8').strip()
174+
return False, Fraction(0, 1), output.decode('utf-8').strip(), stderr.decode('utf-8')
175175
else:
176176
raise CheckerException(f"!!! oicompare failed with code {proc.returncode}. This is a huge bug, please report"
177177
f" it here https://github.com/sio2project/sinol-make/issues/new/choose and provide "
178178
f"these files: {output_file_path}, {answer_file_path}.\n"
179179
f"Output: {output.decode('utf-8').strip()}\n"
180180
f"Stderr: {stderr.decode('utf-8').strip()}")
181181

182-
def check_output(self, input_file_path, output_file_path, answer_file_path) -> Tuple[bool, Fraction, str]:
182+
def check_output(self, input_file_path, output_file_path, answer_file_path) -> Tuple[bool, Fraction, str, str]:
183183
"""
184184
Runs the checker (or runs diff) and returns a tuple of three values:
185185
- bool: whether the solution is correct

src/sinol_make/task_type/interactive.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ def _fill_result(self, result: ExecutionResult, iresult: ExecutionResult, intera
116116
result.Error = None
117117
else:
118118
try:
119-
ok, points, comment = self._parse_checker_output(interactor_output)
119+
ok, points, comment, _ = self._parse_checker_output(interactor_output, '')
120120
except CheckerException as e:
121121
result.Status = Status.RE
122122
result.Error = str(e)

src/sinol_make/task_type/normal.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def run(self, time_limit, hard_time_limit, memory_limit, input_file_path, output
2525
result.Status = Status.ML
2626
elif result.Status == Status.OK:
2727
try:
28-
correct, points, comment = self.check_output(input_file_path, output_file_path, answer_file_path)
28+
correct, points, comment, _ = self.check_output(input_file_path, output_file_path, answer_file_path)
2929
result.Points = float(points)
3030
result.Comment = comment
3131
if not correct:

0 commit comments

Comments
 (0)