[MERGE] forward port of branch saas-3 up to d36eee9
[odoo/odoo.git] / openerp / tools / misc.py
index 0fc3b41..1f90207 100644 (file)
@@ -3,7 +3,7 @@
 #
 #    OpenERP, Open Source Management Solution
 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
-#    Copyright (C) 2010-2013 OpenERP s.a. (<http://openerp.com>).
+#    Copyright (C) 2010-2014 OpenERP s.a. (<http://openerp.com>).
 #
 #    This program is free software: you can redistribute it and/or modify
 #    it under the terms of the GNU Affero General Public License as
@@ -27,6 +27,7 @@ Miscellaneous tools used by OpenERP.
 
 from functools import wraps
 import cProfile
+from contextlib import contextmanager
 import subprocess
 import logging
 import os
@@ -34,6 +35,7 @@ import socket
 import sys
 import threading
 import time
+import werkzeug.utils
 import zipfile
 from collections import defaultdict, Mapping
 from datetime import datetime
@@ -50,6 +52,7 @@ except ImportError:
 
 from config import config
 from cache import *
+from .parse_version import parse_version 
 
 import openerp
 # get_encodings, ustr and exception_to_unicode were originally from tools.misc.
@@ -83,7 +86,8 @@ def exec_pg_command(name, *args):
         raise Exception('Couldn\'t find %s' % name)
     args2 = (prog,) + args
 
-    return subprocess.call(args2)
+    with open(os.devnull) as dn:
+        return subprocess.call(args2, stdout=dn, stderr=subprocess.STDOUT)
 
 def exec_pg_command_pipe(name, *args):
     prog = find_pg_tool(name)
@@ -274,6 +278,36 @@ def reverse_enumerate(l):
     """
     return izip(xrange(len(l)-1, -1, -1), reversed(l))
 
+def topological_sort(elems):
+    """ Return a list of elements sorted so that their dependencies are listed
+    before them in the result.
+
+    :param elems: specifies the elements to sort with their dependencies; it is
+        a dictionary like `{element: dependencies}` where `dependencies` is a
+        collection of elements that must appear before `element`. The elements
+        of `dependencies` are not required to appear in `elems`; they will
+        simply not appear in the result.
+
+    :returns: a list with the keys of `elems` sorted according to their
+        specification.
+    """
+    # the algorithm is inspired by [Tarjan 1976],
+    # http://en.wikipedia.org/wiki/Topological_sorting#Algorithms
+    result = []
+    visited = set()
+
+    def visit(n):
+        if n not in visited:
+            visited.add(n)
+            if n in elems:
+                # first visit all dependencies of n, then append n to result
+                map(visit, elems[n])
+                result.append(n)
+
+    map(visit, elems)
+
+    return result
+
 
 class UpdateableStr(local):
     """ Class that stores an updateable string (used in wizards)
@@ -458,6 +492,7 @@ ALL_LANGUAGES = {
         'fa_IR': u'Persian / فارس',
         'fi_FI': u'Finnish / Suomi',
         'fr_BE': u'French (BE) / Français (BE)',
+        'fr_CA': u'French (CA) / Français (CA)',
         'fr_CH': u'French (CH) / Français (CH)',
         'fr_FR': u'French / Français',
         'gl_ES': u'Galician / Galego',
@@ -1171,7 +1206,7 @@ class ConstantMapping(Mapping):
         return self._value
 
 
-def dumpstacks(sig, frame):
+def dumpstacks(sig=None, frame=None):
     """ Signal handler: dump a stack trace for each existing thread."""
     code = []
 
@@ -1207,6 +1242,36 @@ def dumpstacks(sig, frame):
 
     _logger.info("\n".join(code))
 
-
+class frozendict(dict):
+    """ An implementation of an immutable dictionary. """
+    def __delitem__(self, key):
+        raise NotImplementedError("'__delitem__' not supported on frozendict")
+    def __setitem__(self, key, val):
+        raise NotImplementedError("'__setitem__' not supported on frozendict")
+    def clear(self):
+        raise NotImplementedError("'clear' not supported on frozendict")
+    def pop(self, key, default=None):
+        raise NotImplementedError("'pop' not supported on frozendict")
+    def popitem(self):
+        raise NotImplementedError("'popitem' not supported on frozendict")
+    def setdefault(self, key, default=None):
+        raise NotImplementedError("'setdefault' not supported on frozendict")
+    def update(self, *args, **kwargs):
+        raise NotImplementedError("'update' not supported on frozendict")
+
+@contextmanager
+def ignore(*exc):
+    try:
+        yield
+    except exc:
+        pass
+
+# Avoid DeprecationWarning while still remaining compatible with werkzeug pre-0.9
+if parse_version(getattr(werkzeug, '__version__', '0.0')) < parse_version('0.9.0'):
+    def html_escape(text):
+        return werkzeug.utils.escape(text, quote=True)
+else:
+    def html_escape(text):
+        return werkzeug.utils.escape(text)
 
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: