Sync oslo-incubator for py33 fixes

Change-Id: I261ec6bb34b29169ba3547305deab051f85a3d4d
This commit is contained in:
Dean Troyer 2013-10-11 11:52:37 -05:00
parent bca4cf9578
commit fa649f4654
2 changed files with 157 additions and 89 deletions

View File

@ -1,8 +1,8 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4 # vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 Red Hat, Inc. # Copyright 2012 Red Hat, Inc.
# All Rights Reserved.
# Copyright 2013 IBM Corp. # Copyright 2013 IBM Corp.
# All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # 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 # not use this file except in compliance with the License. You may obtain
@ -26,22 +26,46 @@ Usual usage in an openstack.common module:
import copy import copy
import gettext import gettext
import logging.handlers import logging
import os import os
import re import re
import UserString try:
import UserString as _userString
except ImportError:
import collections as _userString
from babel import localedata
import six import six
_localedir = os.environ.get('openstackclient'.upper() + '_LOCALEDIR') _localedir = os.environ.get('openstackclient'.upper() + '_LOCALEDIR')
_t = gettext.translation('openstackclient', localedir=_localedir, fallback=True) _t = gettext.translation('openstackclient', localedir=_localedir, fallback=True)
_AVAILABLE_LANGUAGES = {}
USE_LAZY = False
def enable_lazy():
"""Convenience function for configuring _() to use lazy gettext
Call this at the start of execution to enable the gettextutils._
function to use lazy gettext functionality. This is useful if
your project is importing _ directly instead of using the
gettextutils.install() way of importing the _ function.
"""
global USE_LAZY
USE_LAZY = True
def _(msg): def _(msg):
if USE_LAZY:
return Message(msg, 'openstackclient')
else:
if six.PY3:
return _t.gettext(msg)
return _t.ugettext(msg) return _t.ugettext(msg)
def install(domain): def install(domain, lazy=False):
"""Install a _() function using the given translation domain. """Install a _() function using the given translation domain.
Given a translation domain, install a _() function using gettext's Given a translation domain, install a _() function using gettext's
@ -51,52 +75,60 @@ def install(domain):
overriding the default localedir (e.g. /usr/share/locale) using overriding the default localedir (e.g. /usr/share/locale) using
a translation-domain-specific environment variable (e.g. a translation-domain-specific environment variable (e.g.
NOVA_LOCALEDIR). NOVA_LOCALEDIR).
:param domain: the translation domain
:param lazy: indicates whether or not to install the lazy _() function.
The lazy _() introduces a way to do deferred translation
of messages by installing a _ that builds Message objects,
instead of strings, which can then be lazily translated into
any available locale.
""" """
gettext.install(domain, if lazy:
localedir=os.environ.get(domain.upper() + '_LOCALEDIR'), # NOTE(mrodden): Lazy gettext functionality.
unicode=True) #
# The following introduces a deferred way to do translations on
# messages in OpenStack. We override the standard _() function
""" # and % (format string) operation to build Message objects that can
Lazy gettext functionality. # later be translated when we have more information.
#
The following is an attempt to introduce a deferred way # Also included below is an example LocaleHandler that translates
to do translations on messages in OpenStack. We attempt to # Messages to an associated locale, effectively allowing many logs,
override the standard _() function and % (format string) operation # each with their own locale.
to build Message objects that can later be translated when we have
more information. Also included is an example LogHandler that
translates Messages to an associated locale, effectively allowing
many logs, each with their own locale.
"""
def get_lazy_gettext(domain):
"""Assemble and return a lazy gettext function for a given domain.
Factory method for a project/module to get a lazy gettext function
for its own translation domain (i.e. nova, glance, cinder, etc.)
"""
def _lazy_gettext(msg): def _lazy_gettext(msg):
"""Create and return a Message object. """Create and return a Message object.
Message encapsulates a string so that we can translate it later when Lazy gettext function for a given domain, it is a factory method
needed. for a project/module to get a lazy gettext function for its own
translation domain (i.e. nova, glance, cinder, etc.)
Message encapsulates a string so that we can translate
it later when needed.
""" """
return Message(msg, domain) return Message(msg, domain)
return _lazy_gettext from six import moves
moves.builtins.__dict__['_'] = _lazy_gettext
else:
localedir = '%s_LOCALEDIR' % domain.upper()
if six.PY3:
gettext.install(domain,
localedir=os.environ.get(localedir))
else:
gettext.install(domain,
localedir=os.environ.get(localedir),
unicode=True)
class Message(UserString.UserString, object): class Message(_userString.UserString, object):
"""Class used to encapsulate translatable messages.""" """Class used to encapsulate translatable messages."""
def __init__(self, msg, domain): def __init__(self, msg, domain):
# _msg is the gettext msgid and should never change # _msg is the gettext msgid and should never change
self._msg = msg self._msg = msg
self._left_extra_msg = '' self._left_extra_msg = ''
self._right_extra_msg = '' self._right_extra_msg = ''
self._locale = None
self.params = None self.params = None
self.locale = None
self.domain = domain self.domain = domain
@property @property
@ -116,8 +148,13 @@ class Message(UserString.UserString, object):
localedir=localedir, localedir=localedir,
fallback=True) fallback=True)
if six.PY3:
ugettext = lang.gettext
else:
ugettext = lang.ugettext
full_msg = (self._left_extra_msg + full_msg = (self._left_extra_msg +
lang.ugettext(self._msg) + ugettext(self._msg) +
self._right_extra_msg) self._right_extra_msg)
if self.params is not None: if self.params is not None:
@ -125,12 +162,39 @@ class Message(UserString.UserString, object):
return six.text_type(full_msg) return six.text_type(full_msg)
@property
def locale(self):
return self._locale
@locale.setter
def locale(self, value):
self._locale = value
if not self.params:
return
# This Message object may have been constructed with one or more
# Message objects as substitution parameters, given as a single
# Message, or a tuple or Map containing some, so when setting the
# locale for this Message we need to set it for those Messages too.
if isinstance(self.params, Message):
self.params.locale = value
return
if isinstance(self.params, tuple):
for param in self.params:
if isinstance(param, Message):
param.locale = value
return
if isinstance(self.params, dict):
for param in self.params.values():
if isinstance(param, Message):
param.locale = value
def _save_dictionary_parameter(self, dict_param): def _save_dictionary_parameter(self, dict_param):
full_msg = self.data full_msg = self.data
# look for %(blah) fields in string; # look for %(blah) fields in string;
# ignore %% and deal with the # ignore %% and deal with the
# case where % is first character on the line # case where % is first character on the line
keys = re.findall('(?:[^%]|^)%\((\w*)\)[a-z]', full_msg) keys = re.findall('(?:[^%]|^)?%\((\w*)\)[a-z]', full_msg)
# if we don't find any %(blah) blocks but have a %s # if we don't find any %(blah) blocks but have a %s
if not keys and re.findall('(?:[^%]|^)%[a-z]', full_msg): if not keys and re.findall('(?:[^%]|^)%[a-z]', full_msg):
@ -143,7 +207,7 @@ class Message(UserString.UserString, object):
params[key] = copy.deepcopy(dict_param[key]) params[key] = copy.deepcopy(dict_param[key])
except TypeError: except TypeError:
# cast uncopyable thing to unicode string # cast uncopyable thing to unicode string
params[key] = unicode(dict_param[key]) params[key] = six.text_type(dict_param[key])
return params return params
@ -162,7 +226,7 @@ class Message(UserString.UserString, object):
try: try:
self.params = copy.deepcopy(other) self.params = copy.deepcopy(other)
except TypeError: except TypeError:
self.params = unicode(other) self.params = six.text_type(other)
return self return self
@ -171,11 +235,13 @@ class Message(UserString.UserString, object):
return self.data return self.data
def __str__(self): def __str__(self):
if six.PY3:
return self.__unicode__()
return self.data.encode('utf-8') return self.data.encode('utf-8')
def __getstate__(self): def __getstate__(self):
to_copy = ['_msg', '_right_extra_msg', '_left_extra_msg', to_copy = ['_msg', '_right_extra_msg', '_left_extra_msg',
'domain', 'params', 'locale'] 'domain', 'params', '_locale']
new_dict = self.__dict__.fromkeys(to_copy) new_dict = self.__dict__.fromkeys(to_copy)
for attr in to_copy: for attr in to_copy:
new_dict[attr] = copy.deepcopy(self.__dict__[attr]) new_dict[attr] = copy.deepcopy(self.__dict__[attr])
@ -229,7 +295,47 @@ class Message(UserString.UserString, object):
if name in ops: if name in ops:
return getattr(self.data, name) return getattr(self.data, name)
else: else:
return UserString.UserString.__getattribute__(self, name) return _userString.UserString.__getattribute__(self, name)
def get_available_languages(domain):
"""Lists the available languages for the given translation domain.
:param domain: the domain to get languages for
"""
if domain in _AVAILABLE_LANGUAGES:
return copy.copy(_AVAILABLE_LANGUAGES[domain])
localedir = '%s_LOCALEDIR' % domain.upper()
find = lambda x: gettext.find(domain,
localedir=os.environ.get(localedir),
languages=[x])
# NOTE(mrodden): en_US should always be available (and first in case
# order matters) since our in-line message strings are en_US
language_list = ['en_US']
# NOTE(luisg): Babel <1.0 used a function called list(), which was
# renamed to locale_identifiers() in >=1.0, the requirements master list
# requires >=0.9.6, uncapped, so defensively work with both. We can remove
# this check when the master list updates to >=1.0, and all projects udpate
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)
_AVAILABLE_LANGUAGES[domain] = language_list
return copy.copy(language_list)
def get_localized_message(message, user_locale):
"""Gets a localized version of the given message in the given locale."""
if isinstance(message, Message):
if user_locale:
message.locale = user_locale
return six.text_type(message)
else:
return message
class LocaleHandler(logging.Handler): class LocaleHandler(logging.Handler):

View File

@ -114,15 +114,12 @@ class InstallVenv(object):
print('Installing dependencies with pip (this can take a while)...') print('Installing dependencies with pip (this can take a while)...')
# First things first, make sure our venv has the latest pip and # First things first, make sure our venv has the latest pip and
# setuptools. # setuptools and pbr
self.pip_install('pip>=1.3') self.pip_install('pip>=1.4')
self.pip_install('setuptools') self.pip_install('setuptools')
self.pip_install('pbr')
self.pip_install('-r', self.requirements) self.pip_install('-r', self.requirements, '-r', self.test_requirements)
self.pip_install('-r', self.test_requirements)
def post_process(self):
self.get_distro().post_process()
def parse_args(self, argv): def parse_args(self, argv):
"""Parses command-line arguments.""" """Parses command-line arguments."""
@ -156,14 +153,6 @@ class Distro(InstallVenv):
' requires virtualenv, please install it using your' ' requires virtualenv, please install it using your'
' favorite package management tool' % self.project) ' favorite package management tool' % self.project)
def post_process(self):
"""Any distribution-specific post-processing gets done here.
In particular, this is useful for applying patches to code inside
the venv.
"""
pass
class Fedora(Distro): class Fedora(Distro):
"""This covers all Fedora-based distributions. """This covers all Fedora-based distributions.
@ -175,10 +164,6 @@ class Fedora(Distro):
return self.run_command_with_code(['rpm', '-q', pkg], return self.run_command_with_code(['rpm', '-q', pkg],
check_exit_code=False)[1] == 0 check_exit_code=False)[1] == 0
def apply_patch(self, originalfile, patchfile):
self.run_command(['patch', '-N', originalfile, patchfile],
check_exit_code=False)
def install_virtualenv(self): def install_virtualenv(self):
if self.check_cmd('virtualenv'): if self.check_cmd('virtualenv'):
return return
@ -187,26 +172,3 @@ class Fedora(Distro):
self.die("Please install 'python-virtualenv'.") self.die("Please install 'python-virtualenv'.")
super(Fedora, self).install_virtualenv() super(Fedora, self).install_virtualenv()
def post_process(self):
"""Workaround for a bug in eventlet.
This currently affects RHEL6.1, but the fix can safely be
applied to all RHEL and Fedora distributions.
This can be removed when the fix is applied upstream.
Nova: https://bugs.launchpad.net/nova/+bug/884915
Upstream: https://bitbucket.org/eventlet/eventlet/issue/89
RHEL: https://bugzilla.redhat.com/958868
"""
# Install "patch" program if it's not there
if not self.check_pkg('patch'):
self.die("Please install 'patch'.")
# Apply the eventlet patch
self.apply_patch(os.path.join(self.venv, 'lib', self.py_version,
'site-packages',
'eventlet/green/subprocess.py'),
'contrib/redhat-eventlet.patch')