You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
244 lines
7.6 KiB
Python
244 lines
7.6 KiB
Python
# pylint: disable=E0601,W0622,W0611
|
|
# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
|
|
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
|
|
#
|
|
# This file is part of logilab-common.
|
|
#
|
|
# logilab-common is free software: you can redistribute it and/or modify it under
|
|
# the terms of the GNU Lesser General Public License as published by the Free
|
|
# Software Foundation, either version 2.1 of the License, or (at your option) any
|
|
# later version.
|
|
#
|
|
# logilab-common is distributed in the hope that it will be useful, but WITHOUT
|
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
|
# details.
|
|
#
|
|
# You should have received a copy of the GNU Lesser General Public License along
|
|
# with logilab-common. If not, see <http://www.gnu.org/licenses/>.
|
|
"""Wrappers around some builtins introduced in python 2.3, 2.4 and
|
|
2.5, making them available in for earlier versions of python.
|
|
|
|
See another compatibility snippets from other projects:
|
|
|
|
:mod:`lib2to3.fixes`
|
|
:mod:`coverage.backward`
|
|
:mod:`unittest2.compatibility`
|
|
"""
|
|
|
|
from __future__ import generators
|
|
|
|
__docformat__ = "restructuredtext en"
|
|
|
|
import os
|
|
import sys
|
|
import types
|
|
from warnings import warn
|
|
|
|
import __builtin__ as builtins # 2to3 will tranform '__builtin__' to 'builtins'
|
|
|
|
if sys.version_info < (3, 0):
|
|
str_to_bytes = str
|
|
def str_encode(string, encoding):
|
|
if isinstance(string, unicode):
|
|
return string.encode(encoding)
|
|
return str(string)
|
|
else:
|
|
def str_to_bytes(string):
|
|
return str.encode(string)
|
|
# we have to ignore the encoding in py3k to be able to write a string into a
|
|
# TextIOWrapper or like object (which expect an unicode string)
|
|
def str_encode(string, encoding):
|
|
return str(string)
|
|
|
|
# XXX callable built-in seems back in all python versions
|
|
try:
|
|
callable = builtins.callable
|
|
except AttributeError:
|
|
from collections import Callable
|
|
def callable(something):
|
|
return isinstance(something, Callable)
|
|
del Callable
|
|
|
|
# See also http://bugs.python.org/issue11776
|
|
if sys.version_info[0] == 3:
|
|
def method_type(callable, instance, klass):
|
|
# api change. klass is no more considered
|
|
return types.MethodType(callable, instance)
|
|
else:
|
|
# alias types otherwise
|
|
method_type = types.MethodType
|
|
|
|
if sys.version_info < (3, 0):
|
|
raw_input = raw_input
|
|
else:
|
|
raw_input = input
|
|
|
|
# Pythons 2 and 3 differ on where to get StringIO
|
|
if sys.version_info < (3, 0):
|
|
from cStringIO import StringIO
|
|
FileIO = file
|
|
BytesIO = StringIO
|
|
reload = reload
|
|
else:
|
|
from io import FileIO, BytesIO, StringIO
|
|
from imp import reload
|
|
|
|
# Where do pickles come from?
|
|
try:
|
|
import cPickle as pickle
|
|
except ImportError:
|
|
import pickle
|
|
|
|
from logilab.common.deprecation import deprecated
|
|
|
|
from itertools import izip, chain, imap
|
|
if sys.version_info < (3, 0):# 2to3 will remove the imports
|
|
izip = deprecated('izip exists in itertools since py2.3')(izip)
|
|
imap = deprecated('imap exists in itertools since py2.3')(imap)
|
|
chain = deprecated('chain exists in itertools since py2.3')(chain)
|
|
|
|
sum = deprecated('sum exists in builtins since py2.3')(sum)
|
|
enumerate = deprecated('enumerate exists in builtins since py2.3')(enumerate)
|
|
frozenset = deprecated('frozenset exists in builtins since py2.4')(frozenset)
|
|
reversed = deprecated('reversed exists in builtins since py2.4')(reversed)
|
|
sorted = deprecated('sorted exists in builtins since py2.4')(sorted)
|
|
max = deprecated('max exists in builtins since py2.4')(max)
|
|
|
|
|
|
# Python2.5 builtins
|
|
try:
|
|
any = any
|
|
all = all
|
|
except NameError:
|
|
def any(iterable):
|
|
"""any(iterable) -> bool
|
|
|
|
Return True if bool(x) is True for any x in the iterable.
|
|
"""
|
|
for elt in iterable:
|
|
if elt:
|
|
return True
|
|
return False
|
|
|
|
def all(iterable):
|
|
"""all(iterable) -> bool
|
|
|
|
Return True if bool(x) is True for all values x in the iterable.
|
|
"""
|
|
for elt in iterable:
|
|
if not elt:
|
|
return False
|
|
return True
|
|
|
|
|
|
# Python2.5 subprocess added functions and exceptions
|
|
try:
|
|
from subprocess import Popen
|
|
except ImportError:
|
|
# gae or python < 2.3
|
|
|
|
class CalledProcessError(Exception):
|
|
"""This exception is raised when a process run by check_call() returns
|
|
a non-zero exit status. The exit status will be stored in the
|
|
returncode attribute."""
|
|
def __init__(self, returncode, cmd):
|
|
self.returncode = returncode
|
|
self.cmd = cmd
|
|
def __str__(self):
|
|
return "Command '%s' returned non-zero exit status %d" % (self.cmd,
|
|
self.returncode)
|
|
|
|
def call(*popenargs, **kwargs):
|
|
"""Run command with arguments. Wait for command to complete, then
|
|
return the returncode attribute.
|
|
|
|
The arguments are the same as for the Popen constructor. Example:
|
|
|
|
retcode = call(["ls", "-l"])
|
|
"""
|
|
# workaround: subprocess.Popen(cmd, stdout=sys.stdout) fails
|
|
# see http://bugs.python.org/issue1531862
|
|
if "stdout" in kwargs:
|
|
fileno = kwargs.get("stdout").fileno()
|
|
del kwargs['stdout']
|
|
return Popen(stdout=os.dup(fileno), *popenargs, **kwargs).wait()
|
|
return Popen(*popenargs, **kwargs).wait()
|
|
|
|
def check_call(*popenargs, **kwargs):
|
|
"""Run command with arguments. Wait for command to complete. If
|
|
the exit code was zero then return, otherwise raise
|
|
CalledProcessError. The CalledProcessError object will have the
|
|
return code in the returncode attribute.
|
|
|
|
The arguments are the same as for the Popen constructor. Example:
|
|
|
|
check_call(["ls", "-l"])
|
|
"""
|
|
retcode = call(*popenargs, **kwargs)
|
|
cmd = kwargs.get("args")
|
|
if cmd is None:
|
|
cmd = popenargs[0]
|
|
if retcode:
|
|
raise CalledProcessError(retcode, cmd)
|
|
return retcode
|
|
|
|
try:
|
|
from os.path import relpath
|
|
except ImportError: # python < 2.6
|
|
from os.path import curdir, abspath, sep, commonprefix, pardir, join
|
|
def relpath(path, start=curdir):
|
|
"""Return a relative version of a path"""
|
|
|
|
if not path:
|
|
raise ValueError("no path specified")
|
|
|
|
start_list = abspath(start).split(sep)
|
|
path_list = abspath(path).split(sep)
|
|
|
|
# Work out how much of the filepath is shared by start and path.
|
|
i = len(commonprefix([start_list, path_list]))
|
|
|
|
rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
|
|
if not rel_list:
|
|
return curdir
|
|
return join(*rel_list)
|
|
|
|
|
|
# XXX don't know why tests don't pass if I don't do that :
|
|
_real_set, set = set, deprecated('set exists in builtins since py2.4')(set)
|
|
if (2, 5) <= sys.version_info[:2]:
|
|
InheritableSet = _real_set
|
|
else:
|
|
class InheritableSet(_real_set):
|
|
"""hacked resolving inheritancy issue from old style class in 2.4"""
|
|
def __new__(cls, *args, **kwargs):
|
|
if args:
|
|
new_args = (args[0], )
|
|
else:
|
|
new_args = ()
|
|
obj = _real_set.__new__(cls, *new_args)
|
|
obj.__init__(*args, **kwargs)
|
|
return obj
|
|
|
|
# XXX shouldn't we remove this and just let 2to3 do his job ?
|
|
# range or xrange?
|
|
try:
|
|
range = xrange
|
|
except NameError:
|
|
range = range
|
|
|
|
# ConfigParser was renamed to the more-standard configparser
|
|
try:
|
|
import configparser
|
|
except ImportError:
|
|
import ConfigParser as configparser
|
|
|
|
try:
|
|
import json
|
|
except ImportError:
|
|
try:
|
|
import simplejson as json
|
|
except ImportError:
|
|
json = None
|