# HG changeset patch # User Oleg Oshmyan # Date 1339011704 -3600 # Node ID 928a6091454c7fefdb40d45d6da27e303c82a750 # Parent 1b775632cbd91f3a09894dcafa18e3ee4ea77b27# Parent 715e3525a904ec09bb2eff859aa9cd4a18e623c5 Fixed crashing on testconfs inside archives on Python 3 diff -r 715e3525a904 -r 928a6091454c .hgtags --- a/.hgtags Wed Jun 06 20:41:44 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -ad4362bf98589c4be765ff91b4d631a18347c4ed 2.03.0 -fbdea5b8cc684e67fc96030eb7f429dcae93bd53 2.03.1 diff -r 715e3525a904 -r 928a6091454c setup-exe.py --- a/setup-exe.py Wed Jun 06 20:41:44 2012 +0100 +++ b/setup-exe.py Wed Jun 06 20:41:44 2012 +0100 @@ -30,7 +30,7 @@ os.rename('upreckon/unix.py', 'upreckon/unix.py~') try: setup(name='Upreckon', - version='2.03.1', + version='2.04.0dev', author='Oleg Oshmyan', author_email='chortos@inbox.lv', url='http://chortos.selfip.net/~astiob/upreckon/', diff -r 715e3525a904 -r 928a6091454c setup.py --- a/setup.py Wed Jun 06 20:41:44 2012 +0100 +++ b/setup.py Wed Jun 06 20:41:44 2012 +0100 @@ -28,7 +28,7 @@ ext_modules = [] setup(name='upreckon', - version='2.03.1', + version='2.04.0dev', author='Oleg Oshmyan', author_email='chortos@inbox.lv', url='http://chortos.selfip.net/~astiob/upreckon/', diff -r 715e3525a904 -r 928a6091454c upreckon/_unixmodule.cpp --- a/upreckon/_unixmodule.cpp Wed Jun 06 20:41:44 2012 +0100 +++ b/upreckon/_unixmodule.cpp Wed Jun 06 20:41:44 2012 +0100 @@ -2,7 +2,6 @@ #include #include -#include #ifdef HAVE_SYS_TYPES_H #include @@ -155,6 +154,9 @@ #ifdef HAVE_TERMIOS_H static bool catch_escape = false; static struct termios orig_termios; +#ifdef O_ASYNC +static int orig_stdout_fl; +#endif #endif typedef struct @@ -405,6 +407,10 @@ #endif #endif +#if defined HAVE_TERMIOS_H && defined O_ASYNC + signal(SIGIO, SIG_DFL); +#endif + if (c2ppipe[1] < 3) { int newfd; @@ -1275,6 +1281,9 @@ #ifdef HAVE_TERMIOS_H if (catch_escape) { +#ifdef O_ASYNC + signal(SIGIO, SIG_DFL); +#endif char c; while (read(0, &c, 1) == -1 && errno == EINTR) { @@ -1309,6 +1318,9 @@ static void restore_termios(void) { tcsetattr(0, TCSAFLUSH, &orig_termios); +#ifdef O_ASYNC + fcntl(0, F_SETFL, orig_stdout_fl); +#endif #ifdef USE_WAKEUP_FD close_intpipe(); #endif @@ -1443,6 +1455,10 @@ if (!Py_AtExit(restore_termios) && !tcsetattr(0, TCSAFLUSH, &new_termios)) { catch_escape = true; +#ifdef O_ASYNC + orig_stdout_fl = fcntl(0, F_GETFL); + fcntl(0, F_SETFL, orig_stdout_fl | O_ASYNC); +#endif } } #ifdef USE_WAKEUP_FD diff -r 715e3525a904 -r 928a6091454c upreckon/exceptions.py --- a/upreckon/exceptions.py Wed Jun 06 20:41:44 2012 +0100 +++ b/upreckon/exceptions.py Wed Jun 06 20:41:44 2012 +0100 @@ -13,7 +13,7 @@ class CPUTimeLimitExceeded(TimeLimitExceeded): __slots__ = () class WallTimeLimitExceeded(TimeLimitExceeded): __slots__ = () class MemoryLimitExceeded(TestCaseNotPassed): __slots__ = () -class CanceledByUser(TestCaseNotPassed): __slots__ = () +class CanceledByUser(BaseException): __slots__ = () class WrongAnswer(TestCaseNotPassed): __slots__ = 'comment' diff -r 715e3525a904 -r 928a6091454c upreckon/files.py --- a/upreckon/files.py Wed Jun 06 20:41:44 2012 +0100 +++ b/upreckon/files.py Wed Jun 06 20:41:44 2012 +0100 @@ -230,7 +230,7 @@ intpath = self._internal_path.split('/') if self._internal_path else () return os.path.join(self._external_path, *(filename.replace('.', os.path.extsep) - for filename in intpath)) + for filename in intpath)) def exists(self): if self.archive: diff -r 715e3525a904 -r 928a6091454c upreckon/testcases.py --- a/upreckon/testcases.py Wed Jun 06 20:41:44 2012 +0100 +++ b/upreckon/testcases.py Wed Jun 06 20:41:44 2012 +0100 @@ -19,6 +19,8 @@ def __enter__(self): pass def __exit__(self, exc_type, exc_value, traceback): pass signal_ignorer = DummySignalIgnorer() +def install_escape_handler(): pass +def remove_escape_handler(): pass try: from .win32 import * @@ -113,7 +115,7 @@ case.realoutname = case.problem.config.dummyoutname @abstractmethod - def test(case): + def test(case, callback): raise NotImplementedError def __call__(case, callback): @@ -121,8 +123,10 @@ case.files_to_delete = [] case.time_limit_string = case.wall_time_limit_string try: + install_escape_handler() return case.test(callback) finally: + remove_escape_handler() now = clock() if getattr(case, 'time_started', None) is None: case.time_started = case.time_stopped = now diff -r 715e3525a904 -r 928a6091454c upreckon/unix.py --- a/upreckon/unix.py Wed Jun 06 20:41:44 2012 +0100 +++ b/upreckon/unix.py Wed Jun 06 20:41:44 2012 +0100 @@ -8,10 +8,13 @@ from subprocess import Popen import os, sys, time -if sys.platform.startswith('java'): - from time import clock -else: - from time import time as clock +try: + from time import steady as clock +except ImportError: + if sys.platform.startswith('java'): + from time import clock + else: + from time import time as clock try: from signal import SIGTERM, SIGKILL @@ -29,6 +32,10 @@ from fcntl import fcntl, F_SETFD, F_GETFD, F_SETFL, F_GETFL from os import O_NONBLOCK try: + from os import O_CLOEXEC, pipe2 + except ImportError: + from os import pipe + try: import cPickle as pickle except ImportError: import pickle @@ -61,11 +68,6 @@ time.sleep(.001) else: try: - from fcntl import FD_CLOEXEC - except ImportError: - FD_CLOEXEC = 1 - - try: from signal import siginterrupt except ImportError: # Sucks. @@ -90,12 +92,27 @@ except ImportError: setrlimit = None + try: + pipe2 + except NameError: + try: + from fcntl import FD_CLOEXEC + except ImportError: + FD_CLOEXEC = 1 + # Pick an arbitrary unique value for O_CLOEXEC + O_CLOEXEC = 1 if O_NONBLOCK != 1 else 2 + def pipe2(flags): + r, w = pipe() + if flags & O_NONBLOCK: + fcntl(r, F_SETFL, fcntl(r, F_GETFL) | O_NONBLOCK) + fcntl(w, F_SETFL, fcntl(w, F_GETFL) | O_NONBLOCK) + if flags & O_CLOEXEC: + fcntl(r, F_SETFD, fcntl(r, F_GETFD) | FD_CLOEXEC) + fcntl(w, F_SETFD, fcntl(w, F_GETFD) | FD_CLOEXEC) + return r, w + # Make SIGCHLD interrupt sleep() and select() - sigchld_pipe_read, sigchld_pipe_write = os.pipe() - fcntl(sigchld_pipe_read, F_SETFL, - fcntl(sigchld_pipe_read, F_GETFL) | O_NONBLOCK) - fcntl(sigchld_pipe_write, F_SETFL, - fcntl(sigchld_pipe_write, F_GETFL) | O_NONBLOCK) + sigchld_pipe_read, sigchld_pipe_write = pipe2(O_NONBLOCK) def bury_child(signum, frame): try: bury_child.case.time_stopped = clock() @@ -115,8 +132,7 @@ def call(*args, **kwargs): global last_rusage bury_child.case = case = kwargs.pop('case') - read, write = os.pipe() - fcntl(write, F_SETFD, fcntl(write, F_GETFD) | FD_CLOEXEC) + read, write = pipe2(O_CLOEXEC) def preexec_fn(): os.close(read) if setrlimit and case.maxmemory: @@ -308,3 +324,21 @@ tty.setcbreak(sys.stdin.fileno()) def pause(): sys.stdin.read(1) +else: + try: + from signal import signal, SIGIO, SIG_DFL + from select import select + except ImportError: + pass + else: + def sigio_handler(signum, frame): + if select((sys.stdin,), (), (), 0)[0]: + if os.read(sys.stdin.fileno(), 1) == '\33'.encode('ascii'): + remove_escape_handler() + raise CanceledByUser + def install_escape_handler(): + signal(SIGIO, sigio_handler) + sigio_handler(SIGIO, None) + def remove_escape_handler(): + signal(SIGIO, SIG_DFL) + __all__ += 'install_escape_handler', 'remove_escape_handler' diff -r 715e3525a904 -r 928a6091454c upreckon/upreckon-vcs --- a/upreckon/upreckon-vcs Wed Jun 06 20:41:44 2012 +0100 +++ b/upreckon/upreckon-vcs Wed Jun 06 20:41:44 2012 +0100 @@ -7,12 +7,14 @@ from upreckon import compat from upreckon.compat import * -parser = optparse.OptionParser(version='Upreckon 2.03.1 ($$REV$$)', epilog='Python 2.6 or newer is required.') +parser = optparse.OptionParser(version='Upreckon 2.04.0 ($$REV$$)', epilog='Python 2.6 or newer is required.') parser.add_option('-1', dest='legacy', action='store_true', default=False, help='handle configuration files in a way more compatible with test.py 1.x') parser.add_option('-p', '--problem', dest='problems', metavar='PROBLEM', action='append', help='test only the PROBLEM (this option can be specified more than once with different problem names, all of which will be tested)') parser.add_option('--list-problems', action='store_true', default=False, help='just list all problem names') +parser.add_option('-c', '--cleanup', dest='cleanup', action='store_true', default=False, help='delete the copies of input/output files and exit') +parser.add_option('-m', '--copy-io', dest='copyonly', action='store_true', default=False, help='create a copy of the input/output files of the last test case for manual testing and exit') parser.add_option('-x', '--auto-exit', dest='pause', action='store_false', default=True, help='do not wait for a key to be pressed after finishing testing') -parser.add_option('-s', '--save-io', dest='erase', action='store_false', default=True, help='do not delete the copies of input/output files after the last test case; create copies of input files and store output in files even if the solution uses standard I/O') +parser.add_option('-s', '--save-io', dest='erase', action='store_false', default=True, help='do not delete the copies of input/output files after the last test case; create copies of input files and store output in files even if the solution uses standard I/O; delete the stored input/output files if the solution uses standard I/O and the -c/--cleanup option is specified') parser.add_option('-k', '--skim', action='store_true', default=False, help='skip test groups as soon as one test case is failed') parser.add_option('--no-time-limits', dest='no_maxtime', action='store_true', default=False, help='disable all time limits') @@ -74,16 +76,24 @@ for taskname in options.problems or globalconf.problems: problem = Problem(taskname) - if ntasks: say() + if ntasks and not (options.cleanup or options.copyonly): say() if shouldprintnames: say(taskname) - real, max = problem.test() + if options.cleanup: + problem.cleanup() + elif options.copyonly: + problem.copytestdata() + else: + real, max = problem.test() ntasks += 1 nfulltasks += real == max realscore += real maxscore += max + if options.cleanup or options.copyonly: + sys.exit() + if ntasks != 1: say() say('Grand total: %g/%g weighted points; %d/%d problems solved fully' % (realscore, maxscore, nfulltasks, ntasks))