comparison upreckon/compat.py @ 146:d5b6708c1955

Distutils support, reorganization and cleaning up * Removed command-line options -t and -u. * Reorganized code: o all modules are now in package upreckon; o TestCaseNotPassed and its descendants now live in a separate module exceptions; o load_problem now lives in module problem. * Commented out mentions of command-line option -c in --help. * Added a distutils-based setup.py.
author Oleg Oshmyan <chortos@inbox.lv>
date Sat, 28 May 2011 14:24:25 +0100
parents compat.py@92f76baebcc6
children e0b2fbd7ebe0
comparison
equal deleted inserted replaced
145:d2c266c8d820 146:d5b6708c1955
1 # Copyright (c) 2010-2011 Chortos-2 <chortos@inbox.lv>
2
3 # A compatibility layer for Python 2.5+. This is what lets test.py
4 # run on all versions of Python starting with 2.5, including Python 3.
5
6 # A few notes regarding some compatibility-driven peculiarities
7 # in the use of the language that can be seen in all modules:
8 #
9 # * Except statements never specify target; instead, when needed,
10 # the exception is taken from sys.exc_info(). Blame the incompatible
11 # syntaxes of the except clause in Python 2.5 and Python 3
12 # and the lack of preprocessor macros in Python of any version ;P.
13 #
14 # * Keyword-only parameters are never used, even for parameters
15 # that should never be given in as arguments. The reason is
16 # the laziness of some Python developers who have failed to finish
17 # implementing them in Python 2 even though they had several years
18 # of time and multiple version releases to sneak them in.
19 #
20 # * Abstract classes are only implemented for Python 2.6 and 2.7.
21 # ABC's require the abc module and the specification of metaclasses,
22 # but in Python 2.5, the abc module does not exist, while in Python 3,
23 # metaclasses are specified using a syntax totally incompatible
24 # with Python 2 and not usable conditionally via exec() and such
25 # because it is a detail of the syntax of the class statement itself.
26
27 # Some code was adapted from Python 2.7.1 and its documentation.
28 # This code is clearly marked as such in preceding comments and is
29 # covered by copyright as follows:
30 #
31 # Copyright (c) 2001-2010 Python Software Foundation; all rights reserved.
32 #
33 # The code is used according to the PSF License Agreement
34 # for Python 2.7.1, whose full text is available from your local
35 # installation of Python (enter 'license()' in the interactive
36 # interpreter) or from the Web at the following URL:
37 #
38 # http://docs.python.org/2.7.1/license.html#terms-and-conditions-for-accessing-or-otherwise-using-python
39
40 try:
41 import builtins
42 except ImportError:
43 import __builtin__ as builtins
44
45 pseudobuiltins = ('say', 'basestring', 'range', 'map', 'zip', 'filter', 'next',
46 'items', 'keys', 'values', 'zip_longest', 'callable', 'ceil')
47 __all__ = pseudobuiltins + ('ABCMeta', 'abstractmethod', 'CompatBuiltins')
48
49 try:
50 # Python 3
51 exec('say = print')
52 except SyntaxError:
53 try:
54 # Python 2.6/2.7
55 # An alternative is exec('from __future__ import print_function; say = print');
56 # if problems arise with the current line, one should try replacing it
57 # with this one with the future import before abandoning the idea altogether
58 say = getattr(builtins, 'print')
59 except Exception:
60 # Python 2.5
61 import sys
62 # This should fully emulate the print function of Python 2.6 in Python 2.3+
63 # The error messages are taken from Python 2.6
64 # The name bindings at the bottom of this file are in effect
65 def saytypeerror(value, name):
66 return TypeError(' '.join((name, 'must be None, str or unicode, not', type(value).__name__)))
67 def say(*values, **kwargs):
68 sep = kwargs.pop('sep' , None)
69 end = kwargs.pop('end' , None)
70 file = kwargs.pop('file', None)
71 if kwargs: raise TypeError("'%s' is an invalid keyword argument for this function" % kwargs.popitem()[0])
72 if sep is None: sep = ' '
73 if end is None: end = '\n'
74 if file is None: file = sys.stdout
75 if not isinstance(sep, basestring): raise saytypeerror(sep, 'sep')
76 if not isinstance(end, basestring): raise saytypeerror(end, 'end')
77 file.write(sep.join(map(str, values)) + end)
78
79 try:
80 from os.path import relpath
81 except ImportError:
82 # Python 2.5
83 import os.path as _path
84
85 # Adapted from Python 2.7.1
86
87 if hasattr(_path, 'splitunc'):
88 def _abspath_split(path):
89 abs = _path.abspath(_path.normpath(path))
90 prefix, rest = _path.splitunc(abs)
91 is_unc = bool(prefix)
92 if not is_unc:
93 prefix, rest = _path.splitdrive(abs)
94 return is_unc, prefix, [x for x in rest.split(_path.sep) if x]
95 else:
96 def _abspath_split(path):
97 prefix, rest = _path.splitdrive(_path.abspath(_path.normpath(path)))
98 return False, prefix, [x for x in rest.split(_path.sep) if x]
99
100 def relpath(path, start=_path.curdir):
101 """Return a relative version of a path"""
102
103 if not path:
104 raise ValueError("no path specified")
105
106 start_is_unc, start_prefix, start_list = _abspath_split(start)
107 path_is_unc, path_prefix, path_list = _abspath_split(path)
108
109 if path_is_unc ^ start_is_unc:
110 raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)"
111 % (path, start))
112 if path_prefix.lower() != start_prefix.lower():
113 if path_is_unc:
114 raise ValueError("path is on UNC root %s, start on UNC root %s"
115 % (path_prefix, start_prefix))
116 else:
117 raise ValueError("path is on drive %s, start on drive %s"
118 % (path_prefix, start_prefix))
119 # Work out how much of the filepath is shared by start and path.
120 i = 0
121 for e1, e2 in zip(start_list, path_list):
122 if e1.lower() != e2.lower():
123 break
124 i += 1
125
126 rel_list = [_path.pardir] * (len(start_list)-i) + path_list[i:]
127 if not rel_list:
128 return _path.curdir
129 return _path.join(*rel_list)
130
131 _path.relpath = relpath
132
133 try:
134 from abc import ABCMeta, abstractmethod
135 except ImportError:
136 ABCMeta, abstractmethod = None, lambda x: x
137
138 try:
139 basestring = basestring
140 except NameError:
141 basestring = str
142
143 # xrange is set to support simple testconf.py's written for test.py 1.x
144 try:
145 xrange = range = xrange
146 except NameError:
147 xrange = range = range
148
149 try:
150 callable = callable
151 except NameError:
152 from collections import Callable
153 callable = lambda obj: isinstance(obj, Callable)
154
155 try:
156 next = next
157 except NameError:
158 next = lambda obj: obj.next()
159
160 try:
161 from itertools import imap as map
162 except ImportError:
163 map = map
164
165 try:
166 from itertools import izip as zip
167 except ImportError:
168 zip = zip
169
170 try:
171 from itertools import ifilter as filter
172 except ImportError:
173 filter = filter
174
175 try:
176 items = dict.iteritems
177 except AttributeError:
178 items = dict.items
179
180 try:
181 keys = dict.iterkeys
182 except AttributeError:
183 keys = dict.keys
184
185 try:
186 values = dict.itervalues
187 except AttributeError:
188 values = dict.values
189
190 from math import ceil
191 if not isinstance(ceil(0), int):
192 def ceil(x):
193 y = int(x)
194 if y < x: y += 1
195 return y
196
197 try:
198 # Python 3
199 from itertools import zip_longest
200 except ImportError:
201 try:
202 # Python 2.6/2.7
203 from itertools import izip_longest as zip_longest
204 except ImportError:
205 # Python 2.5
206 from itertools import chain, repeat
207 # Adapted from the documentation of itertools.izip_longest
208 def zip_longest(*args, **kwargs):
209 fillvalue = kwargs.get('fillvalue')
210 def sentinel(counter=([fillvalue]*(len(args)-1)).pop):
211 yield counter()
212 fillers = repeat(fillvalue)
213 iters = [chain(it, sentinel(), fillers) for it in args]
214 try:
215 for tup in zip(*iters):
216 yield tup
217 except IndexError:
218 pass
219
220 # Automatically import * from this module into testconf.py's
221 class CompatBuiltins(object):
222 __slots__ = 'originals'
223 globals = globals()
224 def __enter__(self):
225 self.originals = {}
226 for name in pseudobuiltins:
227 try:
228 self.originals[name] = getattr(builtins, name)
229 except AttributeError:
230 pass
231 setattr(builtins, name, self.globals[name])
232 return self
233 def __exit__(self, exc_type, exc_val, exc_tb):
234 for name in self.originals:
235 setattr(builtins, name, self.originals[name])