Mercurial > ~astiob > upreckon > hgweb
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]) |