Package logilab :: Package common :: Module compat
[frames] | no frames]

Source Code for Module logilab.common.compat

  1  # pylint: disable-msg=E0601,W0622,W0611 
  2  # copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. 
  3  # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr 
  4  # 
  5  # This file is part of logilab-common. 
  6  # 
  7  # logilab-common is free software: you can redistribute it and/or modify it under 
  8  # the terms of the GNU Lesser General Public License as published by the Free 
  9  # Software Foundation, either version 2.1 of the License, or (at your option) any 
 10  # later version. 
 11  # 
 12  # logilab-common is distributed in the hope that it will be useful, but WITHOUT 
 13  # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
 14  # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more 
 15  # details. 
 16  # 
 17  # You should have received a copy of the GNU Lesser General Public License along 
 18  # with logilab-common.  If not, see <http://www.gnu.org/licenses/>. 
 19  """Wrappers around some builtins introduced in python 2.3, 2.4 and 
 20  2.5, making them available in for earlier versions of python. 
 21   
 22   
 23   
 24   
 25  """ 
 26  from __future__ import generators 
 27  __docformat__ = "restructuredtext en" 
 28   
 29  import os 
 30  from warnings import warn 
 31   
 32  import __builtin__ 
 33   
 34  try: 
 35      set = set 
 36      frozenset = frozenset 
 37  except NameError: 
 38      try: 
 39          from sets import Set as set, ImmutableSet as frozenset 
 40      except ImportError: 
41 - class _baseset(object):
42 - def __init__(self, values=()):
43 self._data = {} 44 warn("This implementation of Set is not complete !", 45 stacklevel=2) 46 for v in values: 47 self._data[v] = 1
48
49 - def __or__(self, other):
50 result = self.__class__(self._data.keys()) 51 for val in other: 52 result.add(val) 53 return result
54 __add__ = __or__ 55
56 - def __and__(self, other):
57 result = self.__class__() 58 for val in other: 59 if val in self._data: 60 result.add(val) 61 return result
62
63 - def __sub__(self, other):
64 result = self.__class__(self._data.keys()) 65 for val in other: 66 if val in self._data: 67 result.remove(val) 68 return result
69
70 - def __cmp__(self, other):
71 keys = self._data.keys() 72 okeys = other._data.keys() 73 keys.sort() 74 okeys.sort() 75 return cmp(keys, okeys)
76
77 - def __len__(self):
78 return len(self._data)
79
80 - def __repr__(self):
81 elements = self._data.keys() 82 return 'lcc.%s(%r)' % (self.__class__.__name__, elements)
83 __str__ = __repr__ 84
85 - def __iter__(self):
86 return iter(self._data)
87
88 - class frozenset(_baseset):
89 """immutable set (can be set in dictionaries)"""
90 - def __init__(self, values=()):
91 super(frozenset, self).__init__(values) 92 self._hashcode = None
93
94 - def _compute_hash(self):
95 """taken from python stdlib (sets.py)""" 96 # Calculate hash code for a set by xor'ing the hash codes of 97 # the elements. This ensures that the hash code does not depend 98 # on the order in which elements are added to the set. This is 99 # not called __hash__ because a BaseSet should not be hashable; 100 # only an ImmutableSet is hashable. 101 result = 0 102 for elt in self: 103 result ^= hash(elt) 104 return result
105
106 - def __hash__(self):
107 """taken from python stdlib (sets.py)""" 108 if self._hashcode is None: 109 self._hashcode = self._compute_hash() 110 return self._hashcode
111 112
113 - class set(_baseset):
114 """mutable set"""
115 - def add(self, value):
116 self._data[value] = 1
117
118 - def remove(self, element):
119 """removes <element> from set""" 120 del self._data[element]
121
122 - def pop(self):
123 """pops an arbitrary element from set""" 124 return self._data.popitem()[0]
125
126 - def __hash__(self):
127 """mutable set cannot be hashed.""" 128 raise TypeError("set objects are not hashable")
129 130 del _baseset # don't explicitly provide this class 131 132 try: 133 from itertools import izip, chain, imap 134 except ImportError: 135 # from itertools documentation ###
136 - def izip(*iterables):
137 iterables = map(iter, iterables) 138 while iterables: 139 result = [i.next() for i in iterables] 140 yield tuple(result)
141
142 - def chain(*iterables):
143 for it in iterables: 144 for element in it: 145 yield element
146
147 - def imap(function, *iterables):
148 iterables = map(iter, iterables) 149 while True: 150 args = [i.next() for i in iterables] 151 if function is None: 152 yield tuple(args) 153 else: 154 yield function(*args)
155 try: 156 sum = sum 157 enumerate = enumerate 158 except NameError: 159 # define the sum and enumerate functions (builtins introduced in py 2.3) 160 import operator
161 - def sum(seq, start=0):
162 """Returns the sum of all elements in the sequence""" 163 return reduce(operator.add, seq, start)
164
165 - def enumerate(iterable):
166 """emulates the python2.3 enumerate() function""" 167 i = 0 168 for val in iterable: 169 yield i, val 170 i += 1
171 #return zip(range(len(iterable)), iterable) 172 try: 173 sorted = sorted 174 reversed = reversed 175 except NameError: 176
177 - def sorted(iterable, cmp=None, key=None, reverse=False):
178 original = list(iterable) 179 if key: 180 l2 = [(key(elt), index) for index, elt in enumerate(original)] 181 else: 182 l2 = original 183 l2.sort(cmp) 184 if reverse: 185 l2.reverse() 186 if key: 187 return [original[index] for elt, index in l2] 188 return l2
189
190 - def reversed(l):
191 l2 = list(l) 192 l2.reverse() 193 return l2
194 195 try: # 196 max = max 197 max(("ab","cde"),key=len) 198 except TypeError:
199 - def max( *args, **kargs):
200 if len(args) == 0: 201 raise TypeError("max expected at least 1 arguments, got 0") 202 key= kargs.pop("key", None) 203 #default implementation 204 if key is None: 205 return __builtin__.max(*args,**kargs) 206 207 for karg in kargs: 208 raise TypeError("unexpected keyword argument %s for function max") % karg 209 210 if len(args) == 1: 211 items = iter(args[0]) 212 else: 213 items = iter(args) 214 215 try: 216 best_item = items.next() 217 best_value = key(best_item) 218 except StopIteration: 219 raise ValueError("max() arg is an empty sequence") 220 221 for item in items: 222 value = key(item) 223 if value > best_value: 224 best_item = item 225 best_value = value 226 227 return best_item
228 229 230 # Python2.5 builtins 231 try: 232 any = any 233 all = all 234 except NameError:
235 - def any(iterable):
236 """any(iterable) -> bool 237 238 Return True if bool(x) is True for any x in the iterable. 239 """ 240 for elt in iterable: 241 if elt: 242 return True 243 return False
244
245 - def all(iterable):
246 """all(iterable) -> bool 247 248 Return True if bool(x) is True for all values x in the iterable. 249 """ 250 for elt in iterable: 251 if not elt: 252 return False 253 return True
254 255 256 # Python2.5 subprocess added functions and exceptions 257 try: 258 from subprocess import Popen 259 except ImportError: 260 # gae or python < 2.3 261
262 - class CalledProcessError(Exception):
263 """This exception is raised when a process run by check_call() returns 264 a non-zero exit status. The exit status will be stored in the 265 returncode attribute."""
266 - def __init__(self, returncode, cmd):
267 self.returncode = returncode 268 self.cmd = cmd
269 - def __str__(self):
270 return "Command '%s' returned non-zero exit status %d" % (self.cmd, 271 self.returncode)
272
273 - def call(*popenargs, **kwargs):
274 """Run command with arguments. Wait for command to complete, then 275 return the returncode attribute. 276 277 The arguments are the same as for the Popen constructor. Example: 278 279 retcode = call(["ls", "-l"]) 280 """ 281 # workaround: subprocess.Popen(cmd, stdout=sys.stdout) fails 282 # see http://bugs.python.org/issue1531862 283 if "stdout" in kwargs: 284 fileno = kwargs.get("stdout").fileno() 285 del kwargs['stdout'] 286 return Popen(stdout=os.dup(fileno), *popenargs, **kwargs).wait() 287 return Popen(*popenargs, **kwargs).wait()
288
289 - def check_call(*popenargs, **kwargs):
290 """Run command with arguments. Wait for command to complete. If 291 the exit code was zero then return, otherwise raise 292 CalledProcessError. The CalledProcessError object will have the 293 return code in the returncode attribute. 294 295 The arguments are the same as for the Popen constructor. Example: 296 297 check_call(["ls", "-l"]) 298 """ 299 retcode = call(*popenargs, **kwargs) 300 cmd = kwargs.get("args") 301 if cmd is None: 302 cmd = popenargs[0] 303 if retcode: 304 raise CalledProcessError(retcode, cmd) 305 return retcode
306 307 try: 308 from os.path import relpath 309 except ImportError: # python < 2.6 310 from os.path import curdir, abspath, sep, commonprefix, pardir, join
311 - def relpath(path, start=curdir):
312 """Return a relative version of a path""" 313 314 if not path: 315 raise ValueError("no path specified") 316 317 start_list = abspath(start).split(sep) 318 path_list = abspath(path).split(sep) 319 320 # Work out how much of the filepath is shared by start and path. 321 i = len(commonprefix([start_list, path_list])) 322 323 rel_list = [pardir] * (len(start_list)-i) + path_list[i:] 324 if not rel_list: 325 return curdir 326 return join(*rel_list)
327