Mercurial > ~astiob > upreckon > hgweb
view compat.py @ 64:94946bc0a3a8
Grand grand -> grand
author | Oleg Oshmyan <chortos@inbox.lv> |
---|---|
date | Wed, 22 Dec 2010 19:12:45 +0200 |
parents | 4ea7133ac25c |
children | e0f8b28e15b5 |
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. try: import builtins except ImportError: import __builtin__ as builtins __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 = getattr(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) try: from os.path import relpath except ImportError: # Python 2.5 import os.path as _path # Adapted from Python 2.7.1 if hasattr(_path, 'splitunc'): def _abspath_split(path): abs = _path.abspath(_path.normpath(path)) prefix, rest = _path.splitunc(abs) is_unc = bool(prefix) if not is_unc: prefix, rest = _path.splitdrive(abs) return is_unc, prefix, [x for x in rest.split(_path.sep) if x] else: def _abspath_split(path): prefix, rest = _path.splitdrive(_path.abspath(_path.normpath(path))) return False, prefix, [x for x in rest.split(_path.sep) if x] def relpath(path, start=_path.curdir): """Return a relative version of a path""" if not path: raise ValueError("no path specified") start_is_unc, start_prefix, start_list = _abspath_split(start) path_is_unc, path_prefix, path_list = _abspath_split(path) if path_is_unc ^ start_is_unc: raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)" % (path, start)) if path_prefix.lower() != start_prefix.lower(): if path_is_unc: raise ValueError("path is on UNC root %s, start on UNC root %s" % (path_prefix, start_prefix)) else: raise ValueError("path is on drive %s, start on drive %s" % (path_prefix, start_prefix)) # Work out how much of the filepath is shared by start and path. i = 0 for e1, e2 in zip(start_list, path_list): if e1.lower() != e2.lower(): break i += 1 rel_list = [_path.pardir] * (len(start_list)-i) + path_list[i:] if not rel_list: return _path.curdir return _path.join(*rel_list) _path.relpath = relpath 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: next = next except NameError: next = lambda obj: obj.next() 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: try: # 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(builtins, name): self.originals[name] = getattr(builtins, name) setattr(builtins, name, g[name]) def __exit__(self, exc_type, exc_val, exc_tb): for name in self.originals: setattr(builtins, name, self.originals[name]) # Support simple testconf.py's written for test.py 1.x builtins.xrange = range