def getTestcaseStatus(self, id): req = self.getAuthRequest(self.BASE_URL + '/widgets/single_submission?id=' + str(id)) if req.status_code == 401: raise InvalidSessionException(req.status_code) soup = bs.BeautifulSoup(req.text, 'lxml') status = soup.find_all('span', attrs={'class': 'status'})[0].contents[0] time = soup.find_all('div', attrs={'class': 'time'})[-1].contents[0].strip() memory = soup.find_all('div', attrs={'class': 'memory'})[0].contents[0] done = status not in self.gradingStatuses if memory == '---': memory = None if time == '---': time = None problemName = soup.find_all('div', attrs={'class': 'name'})[0].find('a').contents[0] req = self.getAuthRequest(self.BASE_URL + '/widgets/submission_testcases?id=' + str(id)) if req.status_code == 401: raise InvalidSessionException(req.status_code) soup = bs.BeautifulSoup(req.text, 'lxml') raw_result = soup.find('body').contents[0] cases = [] try: caseTable = soup.find_all( 'table', attrs={'class': 'submissions-status-table'})[0] except IndexError: return Result(cases, raw_result, status, problemName, time, memory, done) for row in caseTable.find_all('tr'): testcase = Testcase() try: testcase.id = int(row.get('id')) except ValueError: continue children = row.find_all('td') testcase.descriptor = children[0].find('b').contents[0] testcase.status = children[1].find('span').contents[0] testcase.details = { 'time': children[2].find('span').contents[0].replace(',', ''), 'memory': children[3].contents[0].replace('\xa0', ' ').replace(']', ''), 'points': children[4].contents[0] } cases.append(testcase) return Result(cases, raw_result, status, problemName, time, memory, done)
def grade(self, case): result = Result(case) input = case.input_data() # cache generator data self._current_proc = self.binary.launch(time=self.problem.time_limit, memory=self.problem.memory_limit, pipe_stderr=True, unbuffered=case.config.unbuffered, io_redirects=case.io_redirects()) error = self._interact_with_process(case, result, input) process = self._current_proc result.max_memory = process.max_memory or 0.0 result.execution_time = process.execution_time or 0.0 result.r_execution_time = process.r_execution_time or 0.0 # Translate status codes/process results into Result object for status codes self.set_result_flag(process, result) check = self.check_result(case, result) # checkers must either return a boolean (True: full points, False: 0 points) # or a CheckerResult, so convert to CheckerResult if it returned bool if not isinstance(check, CheckerResult): check = CheckerResult(check, case.points if check else 0.0) result.result_flag |= [Result.WA, Result.AC][check.passed] result.points = check.points self.update_feedback(check, error, process, result) return result
def grade_cases(self, grader, cases, short_circuit=False, is_short_circuiting=False): for case in cases: # Yield notifying objects for batch begin/end, and unwrap all cases inside the batches if isinstance(case, BatchedTestCase): yield BatchBegin() for _ in self.grade_cases(grader, case.batched_cases, short_circuit=True, is_short_circuiting=is_short_circuiting): yield _ yield BatchEnd() continue # Stop grading if we're short circuiting if is_short_circuiting: result = Result(case) result.result_flag = Result.SC yield result continue # Must check here because we might be interrupted mid-execution # If we don't bail out, we get an IR. # In Java's case, all the code after this will crash. if self._terminate_grading: raise TerminateGrading() result = grader.grade(case) # If the WA bit of result_flag is set and we are set to short-circuit (e.g., in a batch), # short circuit the rest of the cases. # Do the same if the case is a pretest (i.e. has 0 points) if (result.result_flag & Result.WA) > 0 and (short_circuit or case.points == 0): is_short_circuiting = True yield result
def grade(self, case): result = Result(case) case.input_data() # cache generator data self._current_proc = self.binary.launch(time=self.problem.time_limit, memory=self.problem.memory_limit, pipe_stderr=True, unbuffered=case.config.unbuffered) error = self._interact_with_process(case, result) process = self._current_proc result.max_memory = process.max_memory or 0.0 result.execution_time = process.execution_time or 0.0 result.r_execution_time = process.r_execution_time or 0.0 # checkers might crash if any data is None, so force at least empty string check = case.checker()(result.proc_output or '', case.output_data() or '', submission_source=self.source, judge_input=case.input_data() or '', point_value=case.points) # checkers must either return a boolean (True: full points, False: 0 points) # or a CheckerResult, so convert to CheckerResult if it returned bool if not isinstance(check, CheckerResult): check = CheckerResult(check, case.points if check else 0.0) result.result_flag |= [Result.WA, Result.AC][check.passed] # Translate status codes/process results into Result object for status codes if process.returncode > 0: # print>> sys.stderr, 'Exited with error: %d' % process.returncode result.result_flag |= Result.IR if process.returncode < 0: # None < 0 == True # if process.returncode is not None: # print>> sys.stderr, 'Killed by signal %d' % -process.returncode result.result_flag |= Result.RTE # Killed by signal if process.tle: result.result_flag |= Result.TLE if process.mle: result.result_flag |= Result.MLE if result.result_flag & ~Result.WA: check.points = 0 result.points = check.points result.feedback = (check.feedback or (process.feedback if hasattr(process, 'feedback') else getattr(self.binary, 'get_feedback', lambda x, y: '')(error, result))) if not result.feedback and hasattr(process, 'signal') and process.signal and result.get_main_code() in [Result.IR, Result.RTE]: result.feedback = strsignal(process.signal) return result
def _ipc_result(self, report, batch_number: Optional[int], case_number: int, result: Result) -> None: codes = result.readable_codes() is_sc = result.result_flag & Result.SC colored_codes = [ '#ansi[%s](%s|bold)' % ('--' if x == 'SC' else x, Result.COLORS_BYID[x]) for x in codes ] colored_aux_codes = '{%s}' % ', '.join( colored_codes[1:]) if len(codes) > 1 else '' colored_feedback = '(#ansi[%s](|underline)) ' % utf8text( result.feedback) if result.feedback else '' if is_sc: case_info = '' else: case_info = '[%.3fs (%.3fs) | %dkb] %s%s' % ( result.execution_time, result.wall_clock_time, result.max_memory, colored_feedback, colored_aux_codes, ) case_padding = ' ' if batch_number is not None else '' report( ansi_style( '%sTest case %2d %-3s %s' % (case_padding, case_number, colored_codes[0], case_info))) self.packet_manager.test_case_status_packet(case_number, result)
def test_case_status_packet(self, position: int, result: Result): log.info('Test case on %d: #%d, %s [%.3fs | %.2f MB], %.1f/%.0f', self.judge.current_submission_id, position, ', '.join(result.readable_codes()), result.execution_time, result.max_memory / 1024.0, result.points, result.total_points) with self._testcase_queue_lock: self._testcase_queue.append((position, result))
def populate_result(self, stderr: bytes, result: Result, process: TracedPopen) -> None: # Translate status codes/process results into Result object for status codes result.max_memory = process.max_memory or 0.0 result.execution_time = process.execution_time or 0.0 result.wall_clock_time = process.wall_clock_time or 0.0 if process.is_ir: result.result_flag |= Result.IR if process.is_rte: result.result_flag |= Result.RTE if process.is_ole: result.result_flag |= Result.OLE if process.is_tle: result.result_flag |= Result.TLE if process.is_mle: result.result_flag |= Result.MLE result.update_feedback(stderr, process, self)
def grade_cases(self, grader, cases, short_circuit=False, is_short_circuiting=False): for case in cases: # Yield notifying objects for batch begin/end, and unwrap all cases inside the batches if isinstance(case, BatchedTestCase): yield BatchBegin() for batched_case in self.grade_cases(grader, case.batched_cases, short_circuit=case.config['short_circuit'], is_short_circuiting=is_short_circuiting): # A batched case just failed. # There are two cases where this means that we should completely short-circuit: # 1. If the batch was worth 0 points, to emulate the property of 0-point cases. # 2. If the short_circuit flag is true, see <https://github.com/DMOJ/judge/issues/341>. if (batched_case.result_flag & Result.WA) and (not case.points or short_circuit): is_short_circuiting = True yield batched_case yield BatchEnd() continue # Stop grading if we're short circuiting if is_short_circuiting: result = Result(case) result.result_flag = Result.SC yield result continue # Must check here because we might be interrupted mid-execution # If we don't bail out, we get an IR. # In Java's case, all the code after this will crash. if self._terminate_grading: raise TerminateGrading() result = grader.grade(case) # If the WA bit of result_flag is set and we are set to short-circuit (e.g., in a batch), # short circuit the rest of the cases. # Do the same if the case is a pretest (i.e. has 0 points) if (result.result_flag & Result.WA) > 0 and (short_circuit or not case.points): is_short_circuiting = True yield result
def parse_helper_file_error(proc, executor, name, stderr, time_limit, memory_limit): if proc.is_tle: error = '%s timed out (> %d seconds)' % (name, time_limit) elif proc.is_mle: error = '%s ran out of memory (> %s Kb)' % (name, memory_limit) elif proc.protection_fault: syscall, callname, args = proc.protection_fault error = '%s invoked disallowed syscall %s (%s)' % (name, syscall, callname) elif proc.returncode: if proc.returncode > 0: error = '%s exited with nonzero code %d' % (name, proc.returncode) else: error = '%s exited with %s' % (name, strsignal(proc.signal)) feedback = Result.get_feedback_str(stderr, proc, executor) if feedback: error += ' with feedback %s' % feedback else: return raise InternalError(error)
def grade(self, case): result = Result(case) input = case.input_data() # cache generator data self._current_proc = self.binary.launch(time=self.problem.time_limit, memory=self.problem.memory_limit, symlinks=case.config.symlinks, pipe_stderr=True, wall_time=case.config.wall_time_factor * self.problem.time_limit) error = self._interact_with_process(case, result, input) process = self._current_proc result.max_memory = process.max_memory or 0.0 result.execution_time = process.execution_time or 0.0 result.r_execution_time = process.r_execution_time or 0.0 # Translate status codes/process results into Result object for status codes self.set_result_flag(process, result) check = self.check_result(case, result) # checkers must either return a boolean (True: full points, False: 0 points) # or a CheckerResult, so convert to CheckerResult if it returned bool if not isinstance(check, CheckerResult): check = CheckerResult(check, case.points if check else 0.0) result.result_flag |= [Result.WA, Result.AC][check.passed] result.points = check.points result.extended_feedback = check.extended_feedback self.update_feedback(check, error, process, result) case.free_data() # Where CPython has reference counting and a GC, PyPy only has a GC. This means that while CPython # will have freed any (usually massive) generated data from the line above by reference counting, it might # - and probably still is - in memory by now. We need to be able to fork() immediately, which has a good chance # of failing if there's not a lot of swap space available. # # We don't really have a way to force the generated data to disappear, so calling a gc here is the best # chance we have. if platform.python_implementation() == 'PyPy': gc.collect() return result
def parse_helper_file_error(proc: 'TracedPopen', executor: 'BaseExecutor', name: str, stderr: bytes, time_limit: int, memory_limit: int) -> None: if proc.is_tle: error = f'{name} timed out (> {time_limit} seconds)' elif proc.is_mle: error = f'{name} ran out of memory (> {memory_limit} KB)' elif proc.protection_fault: syscall, callname, args, update_errno = proc.protection_fault error = f'{name} invoked disallowed syscall {syscall} ({callname})' elif proc.returncode: if proc.returncode > 0: error = f'{name} exited with nonzero code {proc.returncode}' else: assert proc.signal is not None error = f'{name} exited with {strsignal(proc.signal)}' feedback = Result.get_feedback_str(stderr, proc, executor) if feedback: error += f' with feedback {feedback}' else: return raise InternalError(error)
def populate_result(self, stderr: bytes, result: Result, process: TracedPopen) -> None: # Translate status codes/process results into Result object for status codes result.max_memory = process.max_memory or 0.0 result.execution_time = process.execution_time or 0.0 result.wall_clock_time = process.wall_clock_time or 0.0 result.context_switches = process.context_switches or (0, 0) result.runtime_version = ', '.join( f'{runtime} {".".join(map(str, version))}' for runtime, version in self.get_runtime_versions()) if process.is_ir: result.result_flag |= Result.IR if process.is_rte: result.result_flag |= Result.RTE if process.is_ole: result.result_flag |= Result.OLE if process.is_tle: result.result_flag |= Result.TLE if process.is_mle: result.result_flag |= Result.MLE result.update_feedback(stderr, process, self)
def _run_generator(self, gen, args=None): flags = [] args = args or [] # resource limits on how to run the generator time_limit = env.generator_time_limit memory_limit = env.generator_memory_limit compiler_time_limit = env.generator_compiler_time_limit lang = None # Default to C/C++ base = get_problem_root(self.problem.id) if isinstance(gen, str): filenames = gen elif isinstance(gen.unwrap(), list): filenames = list(gen.unwrap()) else: if isinstance(gen.source, str): filenames = gen.source elif isinstance(gen.source.unwrap(), list): filenames = list(gen.source.unwrap()) else: raise InvalidInitException("invalid generator declaration") if gen.flags: flags += gen.flags if not args and gen.args: args += gen.args time_limit = gen.time_limit or time_limit memory_limit = gen.memory_limit or memory_limit compiler_time_limit = gen.compiler_time_limit or compiler_time_limit lang = gen.language if not isinstance(filenames, list): filenames = [filenames] filenames = [os.path.join(base, name) for name in filenames] executor = self.problem.generator_manager.get_generator( filenames, flags, lang=lang, compiler_time_limit=compiler_time_limit) # convert all args to str before launching; allows for smoother int passing args = map(str, args) # setting large buffers is really important, because otherwise stderr is unbuffered # and the generator begins calling into cptbox Python code really frequently proc = executor.launch(*args, time=time_limit, memory=memory_limit, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stderr_buffer_size=65536, stdout_buffer_size=65536) try: input = self.problem.problem_data[ self.config['in']] if self.config['in'] else None except KeyError: input = None stdout, stderr = proc.unsafe_communicate(input) self._generated = list(map(self._normalize, (stdout, stderr))) if proc.tle: raise InternalError('generator timed out (> %s seconds)' % time_limit) if proc.mle: raise InternalError('generator ran out of memory (> %s Kb)' % memory_limit) if proc.protection_fault: syscall, callname, args = proc.protection_fault raise InternalError( 'generator invoked disallowed syscall %s (%s)' % (syscall, callname)) if proc.returncode: error = 'generator exited with nonzero code %s' % proc.returncode # To get the feedback, we need a Result object, but we lack a Case object # So we set it to None because we don't need to access it result = Result(None) result.set_result_flag(proc) feedback = (proc.feedback if hasattr(executor, 'feedback') and proc.feedback else (getattr(executor, 'get_feedback', lambda x, y, z: '')( stderr, result, proc))) if feedback: error += ' with feedback: %s' % feedback raise InternalError(error)