# This code can be run as python2 or python3 in most systems. # # This is a small program that runs two processes, connecting the stdin of each # one to the stdout of the other. # It doesn't perform a lot of checking, so many errors may # be caught internally by Python (e.g., if your command line has incorrect # syntax) or not caught at all (e.g., if the judge or solution hangs). # # Run this as: # python interactive_runner.py -- # # For example, if you have a testing_tool.py in python3 (that takes a single # integer as a command line parameter) to use as judge -- like one # downloaded from a problem statement -- and you would run your solution # in a standalone using one of the following: # 1. python3 my_solution.py # 2. ./my_solution # 3. java Solution # 4. my_solution.exe # Then you could run the judge and solution together, using this, as: # 1. python interactive_runner.py python3 testing_tool.py 0 -- python3 my_solution.py # 2. python interactive_runner.py python3 testing_tool.py 0 -- ./my_solution # 3. python interactive_runner.py python3 testing_tool.py 0 -- java solution # 4. python interactive_runner.py python3 testing_tool.py 0 -- my_solution.exe # Notice that the solution in cases 2, 3 and 4 would usually have a # compilation step before running, which you should run in your usual way # before using this tool. # # This is only intended as a convenient tool to help contestants test solutions # locally. In particular, it is not identical to the implementation on our # server, which is more complex. # # The standard streams are handled the following way: # - judge's stdin is connected to the solution's stdout; # - judge's stdout is connected to the solution's stdin; # - stderrs of both judge and solution are piped to standard error stream, with # lines prepended by "judge: " or "sol: " respectively (note, no # synchronization is done so it's possible for the messages from both programs # to overlap with each other). from __future__ import print_function import sys, subprocess, threading class SubprocessThread(threading.Thread): def __init__(self, args, stdin_pipe=subprocess.PIPE, stdout_pipe=subprocess.PIPE, stderr_prefix=None): threading.Thread.__init__(self) self.stderr_prefix = stderr_prefix self.p = subprocess.Popen( args, stdin=stdin_pipe, stdout=stdout_pipe, stderr=subprocess.PIPE) def run(self): try: self.pipeToStdErr(self.p.stderr) self.return_code = self.p.wait() self.error_message = None except (SystemError, OSError): self.return_code = -1 self.error_message = "The process crashed or produced too much output." # Reads bytes from the stream and writes them to sys.stderr prepending lines # with self.stderr_prefix. # We are not reading by lines to guard against the case when EOL is never # found in the stream. def pipeToStdErr(self, stream): new_line = True while True: chunk = stream.readline(1) if not chunk: return chunk = chunk.decode("UTF-8") if new_line and self.stderr_prefix: chunk = self.stderr_prefix + chunk new_line = False sys.stderr.write(chunk) if chunk.endswith("\n"): new_line = True sys.stderr.flush() assert sys.argv.count("--") == 1, ( "There should be exactly one instance of '--' in the command line.") sep_index = sys.argv.index("--") judge_args = sys.argv[1:sep_index] sol_args = sys.argv[sep_index + 1:] t_sol = SubprocessThread(sol_args, stderr_prefix=" sol: ") t_judge = SubprocessThread( judge_args, stdin_pipe=t_sol.p.stdout, stdout_pipe=t_sol.p.stdin, stderr_prefix="judge: ") t_sol.start() t_judge.start() t_sol.join() t_judge.join() # Print an empty line to handle the case when stderr doesn't print EOL. print() print("Judge return code:", t_judge.return_code) if t_judge.error_message: print("Judge error message:", t_judge.error_message) print("Solution return code:", t_sol.return_code) if t_sol.error_message: print("Solution error message:", t_sol.error_message) if t_sol.return_code: print("A solution finishing with exit code other than 0 (without exceeding " "time or memory limits) would be interpreted as a Runtime Error " "in the system.") elif t_judge.return_code: print("A solution finishing with exit code 0 (without exceeding time or " "memory limits) and a judge finishing with exit code other than 0 " "would be interpreted as a Wrong Answer in the system.") else: print("A solution and judge both finishing with exit code 0 (without " "exceeding time or memory limits) would be interpreted as Correct " "in the system.")