diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/upreckon/compat.py	Sat May 28 14:24:25 2011 +0100
@@ -0,0 +1,235 @@
+# Copyright (c) 2010-2011 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.
+
+# Some code was adapted from Python 2.7.1 and its documentation.
+# This code is clearly marked as such in preceding comments and is
+# covered by copyright as follows:
+#
+# Copyright (c) 2001-2010 Python Software Foundation; all rights reserved.
+#
+# The code is used according to the PSF License Agreement
+# for Python 2.7.1, whose full text is available from your local
+# installation of Python (enter 'license()' in the interactive
+# interpreter) or from the Web at the following URL:
+#
+# http://docs.python.org/2.7.1/license.html#terms-and-conditions-for-accessing-or-otherwise-using-python
+
+try:
+	import builtins
+except ImportError:
+	import __builtin__ as builtins
+
+pseudobuiltins = ('say', 'basestring', 'range', 'map', 'zip', 'filter', 'next',
+                  'items', 'keys', 'values', 'zip_longest', 'callable', 'ceil')
+__all__ = pseudobuiltins + ('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
+
+try:
+	from abc import ABCMeta, abstractmethod
+except ImportError:
+	ABCMeta, abstractmethod = None, lambda x: x
+
+try:
+	basestring = basestring
+except NameError:
+	basestring = str
+
+# xrange is set to support simple testconf.py's written for test.py 1.x
+try:
+	xrange = range = xrange
+except NameError:
+	xrange = range = range
+
+try:
+	callable = callable
+except NameError:
+	from collections import Callable
+	callable = lambda obj: isinstance(obj, Callable)
+
+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
+
+try:
+	items = dict.iteritems
+except AttributeError:
+	items = dict.items
+
+try:
+	keys = dict.iterkeys
+except AttributeError:
+	keys = dict.keys
+
+try:
+	values = dict.itervalues
+except AttributeError:
+	values = dict.values
+
+from math import ceil
+if not isinstance(ceil(0), int):
+	def ceil(x):
+		y = int(x)
+		if y < x: y += 1
+		return y
+
+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'
+	globals = globals()
+	def __enter__(self):
+		self.originals = {}
+		for name in pseudobuiltins:
+			try:
+				self.originals[name] = getattr(builtins, name)
+			except AttributeError:
+				pass
+			setattr(builtins, name, self.globals[name])
+		return self
+	def __exit__(self, exc_type, exc_val, exc_tb):
+		for name in self.originals:
+			setattr(builtins, name, self.originals[name])
\ No newline at end of file