Mercurial > ~astiob > upreckon > hgweb
diff compat.py @ 50:4ea7133ac25c
Converted 2.00 into the default branch
author | Oleg Oshmyan <chortos@inbox.lv> |
---|---|
date | Sun, 19 Dec 2010 23:25:13 +0200 |
parents | 2.00/compat.py@23aa8da5be5f |
children | e0f8b28e15b5 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/compat.py Sun Dec 19 23:25:13 2010 +0200 @@ -0,0 +1,218 @@ +#! /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 \ No newline at end of file