]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Sync log.py from oslo-incubator
authorjohn-griffith <john.griffith@solidfire.com>
Wed, 12 Mar 2014 16:59:45 +0000 (10:59 -0600)
committerjohn-griffith <john.griffith@solidfire.com>
Wed, 12 Mar 2014 17:34:55 +0000 (11:34 -0600)
This pulls in the latest version of log.py from oslo-incubator
which elminates the constant user_identity key error.

Note that this sync does a full sync, in other words it uses
the oslo update script and pulls in the log.py file and all
of the associated dependendencies for that module.

Current HEAD in OSLO:
--------------------
Merge: d9ea4f0 fd33d1e
Date:   Wed Mar 12 17:00:13 2014 +0000
Merge "Fix gettextutil.Message handling of deep copy failures"
--------------------

Change-Id: I7c9f8acd22787f9649a5a1e796238c7788a0484a
Fixes-Bug: 1290503

cinder/openstack/common/__init__.py
cinder/openstack/common/gettextutils.py
cinder/openstack/common/importutils.py
cinder/openstack/common/jsonutils.py
cinder/openstack/common/log.py
cinder/openstack/common/timeutils.py

index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..d1223eaf7656b29ca61b0b2a2fd33d3073feef11 100644 (file)
@@ -0,0 +1,17 @@
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import six
+
+
+six.add_move(six.MovedModule('mox', 'mox', 'mox3.mox'))
index 4a4b1b6975471e64642ca5dd9e115b41769548a0..4957e37a94d17c5a994d6ca67b0f0157efcc8444 100644 (file)
@@ -23,11 +23,11 @@ Usual usage in an openstack.common module:
 """
 
 import copy
+import functools
 import gettext
 import locale
 from logging import handlers
 import os
-import re
 
 from babel import localedata
 import six
@@ -35,6 +35,17 @@ import six
 _localedir = os.environ.get('cinder'.upper() + '_LOCALEDIR')
 _t = gettext.translation('cinder', localedir=_localedir, fallback=True)
 
+# We use separate translation catalogs for each log level, so set up a
+# mapping between the log level name and the translator. The domain
+# for the log level is project_name + "-log-" + log_level so messages
+# for each level end up in their own catalog.
+_t_log_levels = dict(
+    (level, gettext.translation('cinder' + '-log-' + level,
+                                localedir=_localedir,
+                                fallback=True))
+    for level in ['info', 'warning', 'error', 'critical']
+)
+
 _AVAILABLE_LANGUAGES = {}
 USE_LAZY = False
 
@@ -60,6 +71,28 @@ def _(msg):
         return _t.ugettext(msg)
 
 
+def _log_translation(msg, level):
+    """Build a single translation of a log message
+    """
+    if USE_LAZY:
+        return Message(msg, domain='cinder' + '-log-' + level)
+    else:
+        translator = _t_log_levels[level]
+        if six.PY3:
+            return translator.gettext(msg)
+        return translator.ugettext(msg)
+
+# Translators for log levels.
+#
+# The abbreviated names are meant to reflect the usual use of a short
+# name like '_'. The "L" is for "log" and the other letter comes from
+# the level.
+_LI = functools.partial(_log_translation, level='info')
+_LW = functools.partial(_log_translation, level='warning')
+_LE = functools.partial(_log_translation, level='error')
+_LC = functools.partial(_log_translation, level='critical')
+
+
 def install(domain, lazy=False):
     """Install a _() function using the given translation domain.
 
@@ -118,7 +151,8 @@ class Message(six.text_type):
     and can be treated as such.
     """
 
-    def __new__(cls, msgid, msgtext=None, params=None, domain='cinder', *args):
+    def __new__(cls, msgid, msgtext=None, params=None,
+                domain='cinder', *args):
         """Create a new Message object.
 
         In order for translation to work gettext requires a message ID, this
@@ -193,10 +227,11 @@ class Message(six.text_type):
         # When we mod a Message we want the actual operation to be performed
         # by the parent class (i.e. unicode()), the only thing  we do here is
         # save the original msgid and the parameters in case of a translation
-        unicode_mod = super(Message, self).__mod__(other)
+        params = self._sanitize_mod_params(other)
+        unicode_mod = super(Message, self).__mod__(params)
         modded = Message(self.msgid,
                          msgtext=unicode_mod,
-                         params=self._sanitize_mod_params(other),
+                         params=params,
                          domain=self.domain)
         return modded
 
@@ -212,38 +247,22 @@ class Message(six.text_type):
         if other is None:
             params = (other,)
         elif isinstance(other, dict):
-            params = self._trim_dictionary_parameters(other)
+            # Merge the dictionaries
+            # Copy each item in case one does not support deep copy.
+            params = {}
+            if isinstance(self.params, dict):
+                for key, val in self.params.items():
+                    params[key] = self._copy_param(val)
+            for key, val in other.items():
+                params[key] = self._copy_param(val)
         else:
             params = self._copy_param(other)
         return params
 
-    def _trim_dictionary_parameters(self, dict_param):
-        """Return a dict that only has matching entries in the msgid."""
-        # NOTE(luisg): Here we trim down the dictionary passed as parameters
-        # to avoid carrying a lot of unnecessary weight around in the message
-        # object, for example if someone passes in Message() % locals() but
-        # only some params are used, and additionally we prevent errors for
-        # non-deepcopyable objects by unicoding() them.
-
-        # Look for %(param) keys in msgid;
-        # Skip %% and deal with the case where % is first character on the line
-        keys = re.findall('(?:[^%]|^)?%\((\w*)\)[a-z]', self.msgid)
-
-        # If we don't find any %(param) keys but have a %s
-        if not keys and re.findall('(?:[^%]|^)%[a-z]', self.msgid):
-            # Apparently the full dictionary is the parameter
-            params = self._copy_param(dict_param)
-        else:
-            params = {}
-            for key in keys:
-                params[key] = self._copy_param(dict_param[key])
-
-        return params
-
     def _copy_param(self, param):
         try:
             return copy.deepcopy(param)
-        except TypeError:
+        except Exception:
             # Fallback to casting to unicode this will handle the
             # python code-like objects that can't be deep-copied
             return six.text_type(param)
@@ -287,9 +306,27 @@ def get_available_languages(domain):
     list_identifiers = (getattr(localedata, 'list', None) or
                         getattr(localedata, 'locale_identifiers'))
     locale_identifiers = list_identifiers()
+
     for i in locale_identifiers:
         if find(i) is not None:
             language_list.append(i)
+
+    # NOTE(luisg): Babel>=1.0,<1.3 has a bug where some OpenStack supported
+    # locales (e.g. 'zh_CN', and 'zh_TW') aren't supported even though they
+    # are perfectly legitimate locales:
+    #     https://github.com/mitsuhiko/babel/issues/37
+    # In Babel 1.3 they fixed the bug and they support these locales, but
+    # they are still not explicitly "listed" by locale_identifiers().
+    # That is  why we add the locales here explicitly if necessary so that
+    # they are listed as supported.
+    aliases = {'zh': 'zh_CN',
+               'zh_Hant_HK': 'zh_HK',
+               'zh_Hant': 'zh_TW',
+               'fil': 'tl_PH'}
+    for (locale, alias) in six.iteritems(aliases):
+        if locale in language_list and alias not in language_list:
+            language_list.append(alias)
+
     _AVAILABLE_LANGUAGES[domain] = language_list
     return copy.copy(language_list)
 
index 4fd9ae2bc26b280706fd0a499ed4b99cf1afca24..edc38050a5dc2efeb1d5fc73c701d672584a984a 100644 (file)
@@ -58,6 +58,13 @@ def import_module(import_str):
     return sys.modules[import_str]
 
 
+def import_versioned_module(version, submodule=None):
+    module = 'cinder.v%s' % version
+    if submodule:
+        module = '.'.join((module, submodule))
+    return import_module(module)
+
+
 def try_import(import_str, default=None):
     """Try to import a module and if it fails return default."""
     try:
index 205f710f098c95f12f6d3eeb2ed5243630438e53..dbfa46821f279afd6723a80975ca5373910524cc 100644 (file)
@@ -36,17 +36,9 @@ import functools
 import inspect
 import itertools
 import json
-try:
-    import xmlrpclib
-except ImportError:
-    # NOTE(jaypipes): xmlrpclib was renamed to xmlrpc.client in Python3
-    #                 however the function and object call signatures
-    #                 remained the same. This whole try/except block should
-    #                 be removed and replaced with a call to six.moves once
-    #                 six 1.4.2 is released. See http://bit.ly/1bqrVzu
-    import xmlrpc.client as xmlrpclib
 
 import six
+import six.moves.xmlrpc_client as xmlrpclib
 
 from cinder.openstack.common import gettextutils
 from cinder.openstack.common import importutils
index fed683cf65626424add7273674662d57161cdc01..59d08a2f1890643bf2b57f0767af43950d84a315 100644 (file)
@@ -537,7 +537,7 @@ def _setup_logging_from_conf(project, version):
 
     if CONF.publish_errors:
         handler = importutils.import_object(
-            "openstack.common.log_handler.PublishErrorsHandler",
+            "cinder.openstack.common.log_handler.PublishErrorsHandler",
             logging.ERROR)
         log_root.addHandler(handler)
 
@@ -650,7 +650,7 @@ class ContextFormatter(logging.Formatter):
         # NOTE(sdague): default the fancier formatting params
         # to an empty string so we don't throw an exception if
         # they get used
-        for key in ('instance', 'color'):
+        for key in ('instance', 'color', 'user_identity'):
             if key not in record.__dict__:
                 record.__dict__[key] = ''
 
index d5ed81d3e3e255a3f252f5b24f29e226ce3121c6..52688a02687afa866ff0a2fe31dfede00e122d54 100644 (file)
@@ -114,7 +114,7 @@ def utcnow():
 
 
 def iso8601_from_timestamp(timestamp):
-    """Returns a iso8601 formated date from timestamp."""
+    """Returns a iso8601 formatted date from timestamp."""
     return isotime(datetime.datetime.utcfromtimestamp(timestamp))