Mercurial > ~astiob > upreckon > hgweb
view 2.00/problem.py @ 40:af9c45708987
Cemented a decision previously being unsure about
The mere presense of the tasknames configuration variable now always makes problem names to be printed.
This is not new, but the old behaviour (only printing names if we test more than one problem), previously commented out, has now been removed altogether.
author | Oleg Oshmyan <chortos@inbox.lv> |
---|---|
date | Sun, 05 Dec 2010 14:34:24 +0100 |
parents | 2b459f9743b4 |
children |
line wrap: on
line source
#! /usr/bin/env python # Copyright (c) 2010 Chortos-2 <chortos@inbox.lv> from __future__ import division, with_statement try: from compat import * import config, testcases except ImportError: import __main__ __main__.import_error(sys.exc_info()[1]) else: from __main__ import clock, options import os, re, sys try: import signal except ImportError: signalnames = () else: # Construct a cache of all signal names available on the current # platform. Prefer names from the UNIX standards over other versions. unixnames = frozenset(('HUP', 'INT', 'QUIT', 'ILL', 'ABRT', 'FPE', 'KILL', 'SEGV', 'PIPE', 'ALRM', 'TERM', 'USR1', 'USR2', 'CHLD', 'CONT', 'STOP', 'TSTP', 'TTIN', 'TTOU', 'BUS', 'POLL', 'PROF', 'SYS', 'TRAP', 'URG', 'VTALRM', 'XCPU', 'XFSZ')) signalnames = {} for name in dir(signal): if re.match('SIG[A-Z]+$', name): value = signal.__dict__[name] if isinstance(value, int) and (value not in signalnames or name[3:] in unixnames): signalnames[value] = name del unixnames __all__ = 'Problem', 'TestContext', 'test_context_end', 'TestGroup' def strerror(e): s = getattr(e, 'strerror') if not s: s = str(e) return ' (%s%s)' % (s[0].lower(), s[1:]) if s else '' class Cache(object): def __init__(self, mydict): self.__dict__ = mydict class TestContext(object): pass test_context_end = object() class TestGroup(TestContext): __slots__ = 'case', 'log', 'correct', 'allcorrect', 'real', 'max', 'ntotal', 'nvalued', 'ncorrect', 'ncorrectvalued' def __init__(self): self.real = self.max = self.ntotal = self.nvalued = self.ncorrect = self.ncorrectvalued = 0 self.allcorrect = True self.log = [] def case_start(self, case): self.case = case self.correct = False self.ntotal += 1 self.max += case.points if case.points: self.nvalued += 1 def case_correct(self): self.correct = True self.ncorrect += 1 if self.case.points: self.ncorrectvalued += 1 def case_end(self, granted): self.log.append((self.case, self.correct, granted)) self.real += granted del self.case if not self.correct: self.allcorrect = False def end(self): say('Group total: %d/%d tests; %d/%d points' % (self.ncorrect, self.ntotal, self.real if self.allcorrect else 0, self.max)) # No real need to flush stdout, as it will anyway be flushed in a moment, # when either the problem total or the next test case's ID is printed if self.allcorrect: return self.log else: return ((case, correct, 0) for case, correct, granted in self.log) class Problem(object): __slots__ = 'name', 'config', 'cache', 'testcases' def __init__(prob, name): if not isinstance(name, basestring): # This shouldn't happen, of course raise TypeError('Problem() argument 1 must be string, not ' + type(name).__name__) prob.name = name prob.config = config.load_problem(name) if not getattr(prob.config, 'kind', None): prob.config.kind = 'batch' prob.cache = Cache({'padoutput': 0}) prob.testcases = testcases.load_problem(prob) # TODO def build(prob): raise NotImplementedError def test(prob): case = None try: contexts = [TestGroup()] for case in prob.testcases: if case is test_context_end: for case, correct, granted in contexts.pop().end(): contexts[-1].case_start(case) if correct: contexts[-1].case_correct() contexts[-1].case_end(granted) continue elif isinstance(case, TestContext): contexts.append(case) continue contexts[-1].case_start(case) granted = 0 id = str(case.id) if case.isdummy: id = 'sample ' + id say('%*s: ' % (prob.cache.padoutput, id), end='') sys.stdout.flush() try: granted = case(lambda: (say('%7.3f%s s, ' % (case.time_stopped - case.time_started, case.time_limit_string), end=''), sys.stdout.flush())) except testcases.CanceledByUser: verdict = 'canceled by the user' except testcases.TimeLimitExceeded: verdict = 'time limit exceeded' except testcases.WrongAnswer: e = sys.exc_info()[1] if e.comment: verdict = 'wrong answer (%s)' % e.comment else: verdict = 'wrong answer' except testcases.NonZeroExitCode: e = sys.exc_info()[1] if e.exitcode < 0: if sys.platform == 'win32': verdict = 'terminated with error 0x%X' % (e.exitcode + 0x100000000) elif -e.exitcode in signalnames: verdict = 'terminated by signal %d (%s)' % (-e.exitcode, signalnames[-e.exitcode]) else: verdict = 'terminated by signal %d' % -e.exitcode else: verdict = 'non-zero return code %d' % e.exitcode except testcases.CannotStartTestee: verdict = 'cannot launch the program to test%s' % strerror(sys.exc_info()[1].upstream) except testcases.CannotStartValidator: verdict = 'cannot launch the validator%s' % strerror(sys.exc_info()[1].upstream) except testcases.CannotReadOutputFile: verdict = 'cannot read the output file%s' % strerror(sys.exc_info()[1].upstream) except testcases.CannotReadInputFile: verdict = 'cannot read the input file%s' % strerror(sys.exc_info()[1].upstream) except testcases.CannotReadAnswerFile: verdict = 'cannot read the reference output file%s' % strerror(sys.exc_info()[1].upstream) except testcases.TestCaseNotPassed: verdict = 'unspecified reason [this may be a bug in test.py]%s' % strerror(sys.exc_info()[1]) #except Exception: # verdict = 'unknown error [this may be a bug in test.py]%s' % strerror(sys.exc_info()[1]) else: try: granted, comment = granted except TypeError: comment = '' else: if comment: comment = ' (%s)' % comment if granted >= 1: contexts[-1].case_correct() verdict = 'OK' + comment elif not granted: verdict = 'wrong answer' + comment else: verdict = 'partly correct' + comment granted *= case.points say('%g/%g, %s' % (granted, case.points, verdict)) contexts[-1].case_end(granted) weighted = contexts[0].real * prob.config.taskweight / contexts[0].max if contexts[0].max else 0 if contexts[0].nvalued != contexts[0].ntotal: say('Problem total: %d/%d tests (%d/%d valued); %g/%g points; weighted score: %g/%g' % (contexts[0].ncorrect, contexts[0].ntotal, contexts[0].ncorrectvalued, contexts[0].nvalued, contexts[0].real, contexts[0].max, weighted, prob.config.taskweight)) else: say('Problem total: %d/%d tests; %g/%g points; weighted score: %g/%g' % (contexts[0].ncorrect, contexts[0].ntotal, contexts[0].real, contexts[0].max, weighted, prob.config.taskweight)) sys.stdout.flush() return weighted, prob.config.taskweight finally: if options.erase and (not prob.config.stdio or case and case.validator): for var in 'in', 'out': name = getattr(prob.config, var + 'name') if name: try: os.remove(name) except Exception: pass if case.validator and not callable(case.validator): if prob.config.ansname: try: os.remove(prob.config.ansname) except Exception: pass