Skip to content

Commit a368983

Browse files
author
Steve Martinelli
committed
sync oslo bits
update gettextutils.py, strutils.py, install_venv_common.py remove iniparsers.py oslo-incubator commit 1223cf Change-Id: I23923d580f57ab6c12622f10d9f278c44c863feb
1 parent 23f64a4 commit a368983

6 files changed

Lines changed: 156 additions & 251 deletions

File tree

openstack-common.conf

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
[DEFAULT]
22

33
# The list of modules to copy from openstack-common
4-
module=iniparser
54
module=install_venv_common
65
module=strutils
76

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#
2+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
3+
# not use this file except in compliance with the License. You may obtain
4+
# a copy of the License at
5+
#
6+
# http://www.apache.org/licenses/LICENSE-2.0
7+
#
8+
# Unless required by applicable law or agreed to in writing, software
9+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11+
# License for the specific language governing permissions and limitations
12+
# under the License.
13+
14+
import six
15+
16+
17+
six.add_move(six.MovedModule('mox', 'mox', 'mox3.mox'))

openstackclient/openstack/common/gettextutils.py

Lines changed: 128 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -28,70 +28,135 @@
2828
import locale
2929
from logging import handlers
3030
import os
31-
import re
3231

3332
from babel import localedata
3433
import six
3534

36-
_localedir = os.environ.get('openstackclient'.upper() + '_LOCALEDIR')
37-
_t = gettext.translation('openstackclient', localedir=_localedir, fallback=True)
38-
39-
# We use separate translation catalogs for each log level, so set up a
40-
# mapping between the log level name and the translator. The domain
41-
# for the log level is project_name + "-log-" + log_level so messages
42-
# for each level end up in their own catalog.
43-
_t_log_levels = dict(
44-
(level, gettext.translation('openstackclient' + '-log-' + level,
45-
localedir=_localedir,
46-
fallback=True))
47-
for level in ['info', 'warning', 'error', 'critical']
48-
)
49-
5035
_AVAILABLE_LANGUAGES = {}
36+
37+
# FIXME(dhellmann): Remove this when moving to oslo.i18n.
5138
USE_LAZY = False
5239

5340

54-
def enable_lazy():
55-
"""Convenience function for configuring _() to use lazy gettext
56-
57-
Call this at the start of execution to enable the gettextutils._
58-
function to use lazy gettext functionality. This is useful if
59-
your project is importing _ directly instead of using the
60-
gettextutils.install() way of importing the _ function.
41+
class TranslatorFactory(object):
42+
"""Create translator functions
6143
"""
62-
global USE_LAZY
63-
USE_LAZY = True
6444

45+
def __init__(self, domain, lazy=False, localedir=None):
46+
"""Establish a set of translation functions for the domain.
47+
48+
:param domain: Name of translation domain,
49+
specifying a message catalog.
50+
:type domain: str
51+
:param lazy: Delays translation until a message is emitted.
52+
Defaults to False.
53+
:type lazy: Boolean
54+
:param localedir: Directory with translation catalogs.
55+
:type localedir: str
56+
"""
57+
self.domain = domain
58+
self.lazy = lazy
59+
if localedir is None:
60+
localedir = os.environ.get(domain.upper() + '_LOCALEDIR')
61+
self.localedir = localedir
6562

66-
def _(msg):
67-
if USE_LAZY:
68-
return Message(msg, domain='openstackclient')
69-
else:
70-
if six.PY3:
71-
return _t.gettext(msg)
72-
return _t.ugettext(msg)
63+
def _make_translation_func(self, domain=None):
64+
"""Return a new translation function ready for use.
7365
66+
Takes into account whether or not lazy translation is being
67+
done.
7468
75-
def _log_translation(msg, level):
76-
"""Build a single translation of a log message
77-
"""
78-
if USE_LAZY:
79-
return Message(msg, domain='openstackclient' + '-log-' + level)
80-
else:
81-
translator = _t_log_levels[level]
69+
The domain can be specified to override the default from the
70+
factory, but the localedir from the factory is always used
71+
because we assume the log-level translation catalogs are
72+
installed in the same directory as the main application
73+
catalog.
74+
75+
"""
76+
if domain is None:
77+
domain = self.domain
78+
if self.lazy:
79+
return functools.partial(Message, domain=domain)
80+
t = gettext.translation(
81+
domain,
82+
localedir=self.localedir,
83+
fallback=True,
84+
)
8285
if six.PY3:
83-
return translator.gettext(msg)
84-
return translator.ugettext(msg)
86+
return t.gettext
87+
return t.ugettext
88+
89+
@property
90+
def primary(self):
91+
"The default translation function."
92+
return self._make_translation_func()
93+
94+
def _make_log_translation_func(self, level):
95+
return self._make_translation_func(self.domain + '-log-' + level)
96+
97+
@property
98+
def log_info(self):
99+
"Translate info-level log messages."
100+
return self._make_log_translation_func('info')
101+
102+
@property
103+
def log_warning(self):
104+
"Translate warning-level log messages."
105+
return self._make_log_translation_func('warning')
106+
107+
@property
108+
def log_error(self):
109+
"Translate error-level log messages."
110+
return self._make_log_translation_func('error')
111+
112+
@property
113+
def log_critical(self):
114+
"Translate critical-level log messages."
115+
return self._make_log_translation_func('critical')
116+
117+
118+
# NOTE(dhellmann): When this module moves out of the incubator into
119+
# oslo.i18n, these global variables can be moved to an integration
120+
# module within each application.
121+
122+
# Create the global translation functions.
123+
_translators = TranslatorFactory('openstackclient')
124+
125+
# The primary translation function using the well-known name "_"
126+
_ = _translators.primary
85127

86128
# Translators for log levels.
87129
#
88130
# The abbreviated names are meant to reflect the usual use of a short
89131
# name like '_'. The "L" is for "log" and the other letter comes from
90132
# the level.
91-
_LI = functools.partial(_log_translation, level='info')
92-
_LW = functools.partial(_log_translation, level='warning')
93-
_LE = functools.partial(_log_translation, level='error')
94-
_LC = functools.partial(_log_translation, level='critical')
133+
_LI = _translators.log_info
134+
_LW = _translators.log_warning
135+
_LE = _translators.log_error
136+
_LC = _translators.log_critical
137+
138+
# NOTE(dhellmann): End of globals that will move to the application's
139+
# integration module.
140+
141+
142+
def enable_lazy():
143+
"""Convenience function for configuring _() to use lazy gettext
144+
145+
Call this at the start of execution to enable the gettextutils._
146+
function to use lazy gettext functionality. This is useful if
147+
your project is importing _ directly instead of using the
148+
gettextutils.install() way of importing the _ function.
149+
"""
150+
# FIXME(dhellmann): This function will be removed in oslo.i18n,
151+
# because the TranslatorFactory makes it superfluous.
152+
global _, _LI, _LW, _LE, _LC, USE_LAZY
153+
tf = TranslatorFactory('openstackclient', lazy=True)
154+
_ = tf.primary
155+
_LI = tf.log_info
156+
_LW = tf.log_warning
157+
_LE = tf.log_error
158+
_LC = tf.log_critical
159+
USE_LAZY = True
95160

96161

97162
def install(domain, lazy=False):
@@ -113,26 +178,9 @@ def install(domain, lazy=False):
113178
any available locale.
114179
"""
115180
if lazy:
116-
# NOTE(mrodden): Lazy gettext functionality.
117-
#
118-
# The following introduces a deferred way to do translations on
119-
# messages in OpenStack. We override the standard _() function
120-
# and % (format string) operation to build Message objects that can
121-
# later be translated when we have more information.
122-
def _lazy_gettext(msg):
123-
"""Create and return a Message object.
124-
125-
Lazy gettext function for a given domain, it is a factory method
126-
for a project/module to get a lazy gettext function for its own
127-
translation domain (i.e. nova, glance, cinder, etc.)
128-
129-
Message encapsulates a string so that we can translate
130-
it later when needed.
131-
"""
132-
return Message(msg, domain=domain)
133-
134181
from six import moves
135-
moves.builtins.__dict__['_'] = _lazy_gettext
182+
tf = TranslatorFactory(domain, lazy=True)
183+
moves.builtins.__dict__['_'] = tf.primary
136184
else:
137185
localedir = '%s_LOCALEDIR' % domain.upper()
138186
if six.PY3:
@@ -248,47 +296,22 @@ def _sanitize_mod_params(self, other):
248296
if other is None:
249297
params = (other,)
250298
elif isinstance(other, dict):
251-
params = self._trim_dictionary_parameters(other)
252-
else:
253-
params = self._copy_param(other)
254-
return params
255-
256-
def _trim_dictionary_parameters(self, dict_param):
257-
"""Return a dict that only has matching entries in the msgid."""
258-
# NOTE(luisg): Here we trim down the dictionary passed as parameters
259-
# to avoid carrying a lot of unnecessary weight around in the message
260-
# object, for example if someone passes in Message() % locals() but
261-
# only some params are used, and additionally we prevent errors for
262-
# non-deepcopyable objects by unicoding() them.
263-
264-
# Look for %(param) keys in msgid;
265-
# Skip %% and deal with the case where % is first character on the line
266-
keys = re.findall('(?:[^%]|^)?%\((\w*)\)[a-z]', self.msgid)
267-
268-
# If we don't find any %(param) keys but have a %s
269-
if not keys and re.findall('(?:[^%]|^)%[a-z]', self.msgid):
270-
# Apparently the full dictionary is the parameter
271-
params = self._copy_param(dict_param)
272-
else:
299+
# Merge the dictionaries
300+
# Copy each item in case one does not support deep copy.
273301
params = {}
274-
# Save our existing parameters as defaults to protect
275-
# ourselves from losing values if we are called through an
276-
# (erroneous) chain that builds a valid Message with
277-
# arguments, and then does something like "msg % kwds"
278-
# where kwds is an empty dictionary.
279-
src = {}
280302
if isinstance(self.params, dict):
281-
src.update(self.params)
282-
src.update(dict_param)
283-
for key in keys:
284-
params[key] = self._copy_param(src[key])
285-
303+
for key, val in self.params.items():
304+
params[key] = self._copy_param(val)
305+
for key, val in other.items():
306+
params[key] = self._copy_param(val)
307+
else:
308+
params = self._copy_param(other)
286309
return params
287310

288311
def _copy_param(self, param):
289312
try:
290313
return copy.deepcopy(param)
291-
except TypeError:
314+
except Exception:
292315
# Fallback to casting to unicode this will handle the
293316
# python code-like objects that can't be deep-copied
294317
return six.text_type(param)
@@ -300,13 +323,14 @@ def __add__(self, other):
300323
def __radd__(self, other):
301324
return self.__add__(other)
302325

303-
def __str__(self):
304-
# NOTE(luisg): Logging in python 2.6 tries to str() log records,
305-
# and it expects specifically a UnicodeError in order to proceed.
306-
msg = _('Message objects do not support str() because they may '
307-
'contain non-ascii characters. '
308-
'Please use unicode() or translate() instead.')
309-
raise UnicodeError(msg)
326+
if six.PY2:
327+
def __str__(self):
328+
# NOTE(luisg): Logging in python 2.6 tries to str() log records,
329+
# and it expects specifically a UnicodeError in order to proceed.
330+
msg = _('Message objects do not support str() because they may '
331+
'contain non-ascii characters. '
332+
'Please use unicode() or translate() instead.')
333+
raise UnicodeError(msg)
310334

311335

312336
def get_available_languages(domain):

0 commit comments

Comments
 (0)