Mercurial > ~astiob > upreckon > hgweb
view 2.00/compat.py @ 25:b500e117080e
Bug fixes and overhead reduction
Added the --problem/-p option. (WARNING: not the same as the -p option of test.py 1.x.) The problem names supplied are not validated.
Added zip_longest to compat.py.
Experimental: problem names are now _always_ printed for multi-problem sets.
Overhead: Escape presses are now checked only once every .15 seconds (at least kbhit() on Windows is very slow).
Overhead: sleep(0) is now called in the time-control-and-Escape-watching loop (at least on Windows, it immediately transfers control to some waiting thread).
Bug fix: compat.py now overwrites built-ins only while including testconfs (--help was broken in Python 2).
Bug fix: ReadDeleting in config.py now closes the file it opens (especially important on Windows, where open files cannot be deleted).
Bug fix: added callable to compat.py (it is absent from Python 3).
Bug fix: the default (built-in) output validator now properly handles unwanted trailing data.
Bug fix: testconfs in custom archives no more raise NameError.
Bug fix: if a validator program cannot be launched, CannotStartValidator is now raised instead of the fatal OSError.
author | Oleg Oshmyan <chortos@inbox.lv> |
---|---|
date | Thu, 23 Sep 2010 23:05:58 +0000 |
parents | f07b7a431ea6 |
children | dc4be35d17e0 |
line wrap: on
line source
#! /usr/bin/env python # Copyright (c) 2010 Chortos-2 <chortos@inbox.lv> # A compatibility layer for Python 2.5+. This is what lets test.py # run on all versions of Python starting with 2.5, including Python 3. # A few notes regarding some compatibility-driven peculiarities # in the use of the language that can be seen in all modules: # # * Except statements never specify target; instead, when needed, # the exception is taken from sys.exc_info(). Blame the incompatible # syntaxes of the except clause in Python 2.5 and Python 3 and the lack # of preprocessor macros in Python of any version ;P. # # * Keyword-only parameters are never used, even for parameters # that should never be given in as arguments. The reason is # the laziness of some Python developers who have failed to finish # implementing them in Python 2 even though they had several years # of time and multiple version releases to sneak them in. # # * Abstract classes are only implemented for Python 2.6 and 2.7. # ABC's require the abc module and the specification of metaclasses, # but in Python 2.5, the abc module does not exist, while in Python 3, # metaclasses are specified using a syntax totally incompatible # with Python 2 and not usable conditionally via exec() and such # because it is a detail of the syntax of the class statement itself. import __builtin__ __all__ = ('say', 'basestring', 'range', 'map', 'zip', 'filter', 'items', 'keys', 'values', 'zip_longest', 'callable', 'ABCMeta', 'abstractmethod', 'CompatBuiltins') try: # Python 3 exec('say = print') except SyntaxError: try: # Python 2.6/2.7 # An alternative is exec('from __future__ import print_function; say = print'); # if problems arise with the current line, one should try replacing it # with this one with the future import before abandoning the idea altogether say = __builtins__['print'] except Exception: # Python 2.5 import sys # This should fully emulate the print function of Python 2.6 in Python 2.3+ # The error messages are taken from Python 2.6 # The name bindings at the bottom of this file are in effect def saytypeerror(value, name): return TypeError(' '.join((name, 'must be None, str or unicode, not', type(value).__name__))) def say(*values, **kwargs): sep = kwargs.pop('sep' , None) end = kwargs.pop('end' , None) file = kwargs.pop('file', None) if kwargs: raise TypeError("'%s' is an invalid keyword argument for this function" % kwargs.popitem()[0]) if sep is None: sep = ' ' if end is None: end = '\n' if file is None: file = sys.stdout if not isinstance(sep, basestring): raise saytypeerror(sep, 'sep') if not isinstance(end, basestring): raise saytypeerror(end, 'end') file.write(sep.join(map(str, values)) + end) def import_urllib(): try: # Python 3 import urllib.request return urllib.request, lambda url: urllib.request.urlopen(url).read().decode() except ImportError: # Python 2 import urllib return urllib, lambda url: urllib.urlopen(url).read() try: from abc import ABCMeta, abstractmethod except ImportError: ABCMeta, abstractmethod = None, lambda x: x # In all of the following, the try clause is for Python 2 and the except # clause is for Python 3. More checks are performed than needed # for standard builds of Python to ensure as much as possible works # on custom builds. try: basestring = basestring except NameError: basestring = str try: range = xrange except NameError: range = range try: callable = callable except NameError: callable = lambda obj: hasattr(obj, '__call__') try: from itertools import imap as map except ImportError: map = map try: from itertools import izip as zip except ImportError: zip = zip try: from itertools import ifilter as filter except ImportError: filter = filter items = dict.iteritems if hasattr(dict, 'iteritems') else dict.items keys = dict.iterkeys if hasattr(dict, 'iterkeys') else dict.keys values = dict.itervalues if hasattr(dict, 'itervalues') else dict.values try: # Python 3 from itertools import zip_longest except ImportError: # Python 2.6/2.7 from itertools import izip_longest as zip_longest except ImportError: # Python 2.5 from itertools import chain, repeat # Adapted from the documentation of itertools.izip_longest def zip_longest(*args, **kwargs): fillvalue = kwargs.get('fillvalue') def sentinel(counter=([fillvalue]*(len(args)-1)).pop): yield counter() fillers = repeat(fillvalue) iters = [chain(it, sentinel(), fillers) for it in args] try: for tup in zip(*iters): yield tup except IndexError: pass # Automatically import * from this module into testconf.py's class CompatBuiltins(object): __slots__ = 'originals' def __init__(self): self.originals = {} def __enter__(self): g = globals() for name in __all__: if hasattr(__builtin__, name): self.originals[name] = getattr(__builtin__, name) setattr(__builtin__, name, g[name]) def __exit__(self, exc_type, exc_val, exc_tb): for name in self.originals: setattr(__builtin__, name, self.originals[name]) # Support simple testconf.py's written for test.py 1.x __builtin__.xrange = range