Merge branch 'devel-2.7'
[ganeti-local] / lib / compat.py
index 013206f..5f1409e 100644 (file)
@@ -1,7 +1,7 @@
 #
 #
 
 #
 #
 
-# Copyright (C) 2010 Google Inc.
+# Copyright (C) 2010, 2011 Google Inc.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 """
 
 import itertools
 """
 
 import itertools
+import operator
 
 try:
 
 try:
+  # pylint: disable=F0401
   import functools
 except ImportError:
   functools = None
 
 try:
   import functools
 except ImportError:
   functools = None
 
 try:
+  # pylint: disable=F0401
   import roman
 except ImportError:
   roman = None
 
 
   import roman
 except ImportError:
   roman = None
 
 
-def all(seq, pred=bool): # pylint: disable-msg=W0622
-  """Returns True if pred(x) is True for every element in the iterable.
+# compat.md5_hash and compat.sha1_hash can be called to generate and md5 and a
+# sha1 hashing modules, under python 2.4, 2.5 and 2.6, even though some changes
+# went on. compat.sha1 is python-version specific and is used for python
+# modules (hmac, for example) which have changed their behavior as well from
+# one version to the other.
+try:
+  # Yes, these don't always exist, that's why we're testing
+  # Yes, we're not using the imports in this module.
+  from hashlib import md5 as md5_hash # pylint: disable=W0611,E0611,F0401
+  from hashlib import sha1 as sha1_hash # pylint: disable=W0611,E0611,F0401
+  # this additional version is needed for compatibility with the hmac module
+  sha1 = sha1_hash
+except ImportError:
+  from md5 import new as md5_hash
+  import sha
+  sha1 = sha
+  sha1_hash = sha.new
 
 
-  Please note that this function provides a C{pred} parameter which isn't
-  available in the version included in Python 2.5 and above.
+
+def _all(seq):
+  """Returns True if all elements in the iterable are True.
 
   """
 
   """
-  for _ in itertools.ifilterfalse(pred, seq):
+  for _ in itertools.ifilterfalse(bool, seq):
     return False
   return True
 
 
     return False
   return True
 
 
-def any(seq, pred=bool): # pylint: disable-msg=W0622
-  """Returns True if pred(x) is True for at least one element in the iterable.
-
-  Please note that this function provides a C{pred} parameter which isn't
-  available in the version included in Python 2.5 and above.
+def _any(seq):
+  """Returns True if any element of the iterable are True.
 
   """
 
   """
-  for _ in itertools.ifilter(pred, seq):
+  for _ in itertools.ifilter(bool, seq):
     return True
   return False
 
 
     return True
   return False
 
 
-def partition(seq, pred=bool): # pylint: disable-msg=W0622
+try:
+  # pylint: disable=E0601
+  # pylint: disable=W0622
+  all = all
+except NameError:
+  all = _all
+
+try:
+  # pylint: disable=E0601
+  # pylint: disable=W0622
+  any = any
+except NameError:
+  any = _any
+
+
+def partition(seq, pred=bool): # pylint: disable=W0622
   """Partition a list in two, based on the given predicate.
 
   """
   """Partition a list in two, based on the given predicate.
 
   """
@@ -70,7 +101,7 @@ def partition(seq, pred=bool): # pylint: disable-msg=W0622
 
 # Even though we're using Python's built-in "partial" function if available,
 # this one is always defined for testing.
 
 # Even though we're using Python's built-in "partial" function if available,
 # this one is always defined for testing.
-def _partial(func, *args, **keywords): # pylint: disable-msg=W0622
+def _partial(func, *args, **keywords): # pylint: disable=W0622
   """Decorator with partial application of arguments and keywords.
 
   This function was copied from Python's documentation.
   """Decorator with partial application of arguments and keywords.
 
   This function was copied from Python's documentation.
@@ -79,7 +110,7 @@ def _partial(func, *args, **keywords): # pylint: disable-msg=W0622
   def newfunc(*fargs, **fkeywords):
     newkeywords = keywords.copy()
     newkeywords.update(fkeywords)
   def newfunc(*fargs, **fkeywords):
     newkeywords = keywords.copy()
     newkeywords.update(fkeywords)
-    return func(*(args + fargs), **newkeywords) # pylint: disable-msg=W0142
+    return func(*(args + fargs), **newkeywords) # pylint: disable=W0142
 
   newfunc.func = func
   newfunc.args = args
 
   newfunc.func = func
   newfunc.args = args
@@ -87,6 +118,12 @@ def _partial(func, *args, **keywords): # pylint: disable-msg=W0622
   return newfunc
 
 
   return newfunc
 
 
+if functools is None:
+  partial = _partial
+else:
+  partial = functools.partial
+
+
 def TryToRoman(val, convert=True):
   """Try to convert a value to roman numerals
 
 def TryToRoman(val, convert=True):
   """Try to convert a value to roman numerals
 
@@ -110,7 +147,27 @@ def TryToRoman(val, convert=True):
     return val
 
 
     return val
 
 
-if functools is None:
-  partial = _partial
-else:
-  partial = functools.partial
+def UniqueFrozenset(seq):
+  """Makes C{frozenset} from sequence after checking for duplicate elements.
+
+  @raise ValueError: When there are duplicate elements
+
+  """
+  if isinstance(seq, (list, tuple)):
+    items = seq
+  else:
+    items = list(seq)
+
+  result = frozenset(items)
+
+  if len(items) != len(result):
+    raise ValueError("Duplicate values found")
+
+  return result
+
+
+#: returns the first element of a list-like value
+fst = operator.itemgetter(0)
+
+#: returns the second element of a list-like value
+snd = operator.itemgetter(1)