diff options
author | Doug Hellmann <doug.hellmann@dreamhost.com> | 2014-05-07 12:31:43 -0700 |
---|---|---|
committer | Doug Hellmann <doug.hellmann@dreamhost.com> | 2014-06-04 09:50:34 -0700 |
commit | d601e1c55ba44e4eaacdf7277fd4cbff1cbd755e (patch) | |
tree | a73151344b557fda8bce55481db2975c580600fa | |
parent | 73d16d8134a841a394265b037837d706e661d666 (diff) | |
download | oslo-i18n-d601e1c55ba44e4eaacdf7277fd4cbff1cbd755e.tar.gz |
Split up monolithic test file
Split the tests up into multiple files for easier
maintenance.
Update the import order of modules used by the
tests.
bp graduate-oslo-i18n
Change-Id: If225bfa0a4d319ebc530c6497f74a20d0f25835b
-rw-r--r-- | tests/test_factory.py | 60 | ||||
-rw-r--r-- | tests/test_gettextutils.py | 668 | ||||
-rw-r--r-- | tests/test_handler.py | 107 | ||||
-rw-r--r-- | tests/test_logging.py | 42 | ||||
-rw-r--r-- | tests/test_message.py | 522 | ||||
-rw-r--r-- | tests/utils.py | 42 |
6 files changed, 779 insertions, 662 deletions
diff --git a/tests/test_factory.py b/tests/test_factory.py new file mode 100644 index 0000000..2cf031a --- /dev/null +++ b/tests/test_factory.py @@ -0,0 +1,60 @@ +# Copyright 2012 Red Hat, Inc. +# Copyright 2013 IBM Corp. +# All Rights Reserved. +# +# 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 mock +import six + +from oslotest import base as test_base + +from oslo.i18n import gettextutils + + +class TranslatorFactoryTest(test_base.BaseTestCase): + + def test_lazy(self): + with mock.patch.object(gettextutils, 'Message') as msg: + tf = gettextutils.TranslatorFactory('domain', lazy=True) + tf.primary('some text') + msg.assert_called_with('some text', domain='domain') + + def test_py2(self): + with mock.patch.object(six, 'PY3', False): + with mock.patch('gettext.translation') as translation: + trans = mock.Mock() + translation.return_value = trans + trans.gettext.side_effect = AssertionError( + 'should have called ugettext') + tf = gettextutils.TranslatorFactory('domain', lazy=False) + tf.primary('some text') + trans.ugettext.assert_called_with('some text') + + def test_py3(self): + with mock.patch.object(six, 'PY3', True): + with mock.patch('gettext.translation') as translation: + trans = mock.Mock() + translation.return_value = trans + trans.ugettext.side_effect = AssertionError( + 'should have called gettext') + tf = gettextutils.TranslatorFactory('domain', lazy=False) + tf.primary('some text') + trans.gettext.assert_called_with('some text') + + def test_log_level_domain_name(self): + with mock.patch.object(gettextutils.TranslatorFactory, + '_make_translation_func') as mtf: + tf = gettextutils.TranslatorFactory('domain', lazy=False) + tf._make_log_translation_func('mylevel') + mtf.assert_called_with('domain-log-mylevel') diff --git a/tests/test_gettextutils.py b/tests/test_gettextutils.py index db26fc8..210dd94 100644 --- a/tests/test_gettextutils.py +++ b/tests/test_gettextutils.py @@ -17,16 +17,18 @@ import gettext import logging -from oslo.i18n import gettextutils - from babel import localedata import mock import six -import testtools from oslotest import base as test_base from oslotest import moxstubout + from tests import fakes +from tests import utils + +from oslo.i18n import gettextutils + LOG = logging.getLogger(__name__) @@ -141,664 +143,6 @@ class GettextTest(test_base.BaseTestCase): mock_translation.side_effect = translator # translate() works on msgs and on objects whose unicode reps are msgs - obj = SomeObject(message) + obj = utils.SomeObject(message) self.assertEqual(es_translation, gettextutils.translate(message, 'es')) self.assertEqual(es_translation, gettextutils.translate(obj, 'es')) - - -class MessageTestCase(test_base.BaseTestCase): - """Unit tests for locale Message class.""" - - @staticmethod - def message(msg): - return gettextutils.Message(msg) - - def test_message_id_and_message_text(self): - message = gettextutils.Message('1') - self.assertEqual('1', message.msgid) - self.assertEqual('1', message) - message = gettextutils.Message('1', msgtext='A') - self.assertEqual('1', message.msgid) - self.assertEqual('A', message) - - def test_message_is_unicode(self): - message = self.message('some %s') % 'message' - self.assertIsInstance(message, six.text_type) - - @mock.patch('locale.getdefaultlocale') - @mock.patch('gettext.translation') - def test_create_message_non_english_default_locale(self, - mock_translation, - mock_getdefaultlocale): - msgid = 'A message in English' - es_translation = 'A message in Spanish' - - es_translations = {msgid: es_translation} - translations_map = {'es': es_translations} - translator = fakes.FakeTranslations.translator(translations_map) - mock_translation.side_effect = translator - mock_getdefaultlocale.return_value = ('es',) - - message = gettextutils.Message(msgid) - - # The base representation of the message is in Spanish, as well as - # the default translation, since the default locale was Spanish. - self.assertEqual(es_translation, message) - self.assertEqual(es_translation, message.translate()) - - def test_translate_returns_unicode(self): - message = self.message('some %s') % 'message' - self.assertIsInstance(message.translate(), six.text_type) - - def test_mod_with_named_parameters(self): - msgid = ("%(description)s\nCommand: %(cmd)s\n" - "Exit code: %(exit_code)s\nStdout: %(stdout)r\n" - "Stderr: %(stderr)r %%(something)s") - params = {'description': 'test1', - 'cmd': 'test2', - 'exit_code': 'test3', - 'stdout': 'test4', - 'stderr': 'test5', - 'something': 'trimmed'} - - result = self.message(msgid) % params - - expected = msgid % params - self.assertEqual(result, expected) - self.assertEqual(result.translate(), expected) - - def test_multiple_mod_with_named_parameter(self): - msgid = ("%(description)s\nCommand: %(cmd)s\n" - "Exit code: %(exit_code)s\nStdout: %(stdout)r\n" - "Stderr: %(stderr)r") - params = {'description': 'test1', - 'cmd': 'test2', - 'exit_code': 'test3', - 'stdout': 'test4', - 'stderr': 'test5'} - - # Run string interpolation the first time to make a new Message - first = self.message(msgid) % params - - # Run string interpolation on the new Message, to replicate - # one of the error paths with some Exception classes we've - # implemented in OpenStack. We should receive a second Message - # object, but the translation results should be the same. - # - # The production code that triggers this problem does something - # like: - # - # msg = _('there was a problem %(name)s') % {'name': 'some value'} - # LOG.error(msg) - # raise BadExceptionClass(msg) - # - # where BadExceptionClass does something like: - # - # class BadExceptionClass(Exception): - # def __init__(self, msg, **kwds): - # super(BadExceptionClass, self).__init__(msg % kwds) - # - expected = first % {} - - # Base message id should be the same - self.assertEqual(first.msgid, expected.msgid) - - # Preserved arguments should be the same - self.assertEqual(first.params, expected.params) - - # Should have different objects - self.assertIsNot(expected, first) - - # Final translations should be the same - self.assertEqual(expected.translate(), first.translate()) - - def test_mod_with_named_parameters_no_space(self): - msgid = ("Request: %(method)s http://%(server)s:" - "%(port)s%(url)s with headers %(headers)s") - params = {'method': 'POST', - 'server': 'test1', - 'port': 1234, - 'url': 'test2', - 'headers': {'h1': 'val1'}} - - result = self.message(msgid) % params - - expected = msgid % params - self.assertEqual(result, expected) - self.assertEqual(result.translate(), expected) - - def test_mod_with_dict_parameter(self): - msgid = "Test that we can inject a dictionary %s" - params = {'description': 'test1'} - - result = self.message(msgid) % params - - expected = msgid % params - self.assertEqual(result, expected) - self.assertEqual(result.translate(), expected) - - def test_mod_with_integer_parameters(self): - msgid = "Some string with params: %d" - params = [0, 1, 10, 24124] - - messages = [] - results = [] - for param in params: - messages.append(msgid % param) - results.append(self.message(msgid) % param) - - for message, result in zip(messages, results): - self.assertEqual(type(result), gettextutils.Message) - self.assertEqual(result.translate(), message) - - # simulate writing out as string - result_str = '%s' % result.translate() - self.assertEqual(result_str, message) - self.assertEqual(result, message) - - def test_mod_copies_parameters(self): - msgid = "Found object: %(current_value)s" - changing_dict = {'current_value': 1} - # A message created with some params - result = self.message(msgid) % changing_dict - # The parameters may change - changing_dict['current_value'] = 2 - # Even if the param changes when the message is - # translated it should use the original param - self.assertEqual(result.translate(), 'Found object: 1') - - def test_mod_deep_copies_parameters(self): - msgid = "Found list: %(current_list)s" - changing_list = list([1, 2, 3]) - params = {'current_list': changing_list} - # Apply the params - result = self.message(msgid) % params - # Change the list - changing_list.append(4) - # Even though the list changed the message - # translation should use the original list - self.assertEqual(result.translate(), "Found list: [1, 2, 3]") - - def test_mod_deep_copies_param_nodeep_param(self): - msgid = "Value: %s" - params = NoDeepCopyObject(5) - # Apply the params - result = self.message(msgid) % params - self.assertEqual(result.translate(), "Value: 5") - - def test_mod_deep_copies_param_nodeep_dict(self): - msgid = "Values: %(val1)s %(val2)s" - params = {'val1': 1, 'val2': NoDeepCopyObject(2)} - # Apply the params - result = self.message(msgid) % params - self.assertEqual(result.translate(), "Values: 1 2") - - # Apply again to make sure other path works as well - params = {'val1': 3, 'val2': NoDeepCopyObject(4)} - result = self.message(msgid) % params - self.assertEqual(result.translate(), "Values: 3 4") - - def test_mod_returns_a_copy(self): - msgid = "Some msgid string: %(test1)s %(test2)s" - message = self.message(msgid) - m1 = message % {'test1': 'foo', 'test2': 'bar'} - m2 = message % {'test1': 'foo2', 'test2': 'bar2'} - - self.assertIsNot(message, m1) - self.assertIsNot(message, m2) - self.assertEqual(m1.translate(), - msgid % {'test1': 'foo', 'test2': 'bar'}) - self.assertEqual(m2.translate(), - msgid % {'test1': 'foo2', 'test2': 'bar2'}) - - def test_mod_with_none_parameter(self): - msgid = "Some string with params: %s" - message = self.message(msgid) % None - self.assertEqual(msgid % None, message) - self.assertEqual(msgid % None, message.translate()) - - def test_mod_with_missing_parameters(self): - msgid = "Some string with params: %s %s" - test_me = lambda: self.message(msgid) % 'just one' - # Just like with strings missing parameters raise TypeError - self.assertRaises(TypeError, test_me) - - def test_mod_with_extra_parameters(self): - msgid = "Some string with params: %(param1)s %(param2)s" - params = {'param1': 'test', - 'param2': 'test2', - 'param3': 'notinstring'} - - result = self.message(msgid) % params - - expected = msgid % params - self.assertEqual(result, expected) - self.assertEqual(result.translate(), expected) - - # Make sure unused params still there - self.assertEqual(result.params.keys(), params.keys()) - - def test_mod_with_missing_named_parameters(self): - msgid = ("Some string with params: %(param1)s %(param2)s" - " and a missing one %(missing)s") - params = {'param1': 'test', - 'param2': 'test2'} - - test_me = lambda: self.message(msgid) % params - # Just like with strings missing named parameters raise KeyError - self.assertRaises(KeyError, test_me) - - def test_add_disabled(self): - msgid = "A message" - test_me = lambda: self.message(msgid) + ' some string' - self.assertRaises(TypeError, test_me) - - def test_radd_disabled(self): - msgid = "A message" - test_me = lambda: SomeObject('test') + self.message(msgid) - self.assertRaises(TypeError, test_me) - - @testtools.skipIf(six.PY3, 'test specific to Python 2') - def test_str_disabled(self): - msgid = "A message" - test_me = lambda: str(self.message(msgid)) - self.assertRaises(UnicodeError, test_me) - - @mock.patch('gettext.translation') - def test_translate(self, mock_translation): - en_message = 'A message in the default locale' - es_translation = 'A message in Spanish' - message = gettextutils.Message(en_message) - - es_translations = {en_message: es_translation} - translations_map = {'es': es_translations} - translator = fakes.FakeTranslations.translator(translations_map) - mock_translation.side_effect = translator - - self.assertEqual(es_translation, message.translate('es')) - - @mock.patch('gettext.translation') - def test_translate_message_from_unicoded_object(self, mock_translation): - en_message = 'A message in the default locale' - es_translation = 'A message in Spanish' - message = gettextutils.Message(en_message) - es_translations = {en_message: es_translation} - translations_map = {'es': es_translations} - translator = fakes.FakeTranslations.translator(translations_map) - mock_translation.side_effect = translator - - # Here we are not testing the Message object directly but the result - # of unicoding() an object whose unicode representation is a Message - obj = SomeObject(message) - unicoded_obj = six.text_type(obj) - - self.assertEqual(es_translation, unicoded_obj.translate('es')) - - @mock.patch('gettext.translation') - def test_translate_multiple_languages(self, mock_translation): - en_message = 'A message in the default locale' - es_translation = 'A message in Spanish' - zh_translation = 'A message in Chinese' - message = gettextutils.Message(en_message) - - es_translations = {en_message: es_translation} - zh_translations = {en_message: zh_translation} - translations_map = {'es': es_translations, - 'zh': zh_translations} - translator = fakes.FakeTranslations.translator(translations_map) - mock_translation.side_effect = translator - - self.assertEqual(es_translation, message.translate('es')) - self.assertEqual(zh_translation, message.translate('zh')) - self.assertEqual(en_message, message.translate(None)) - self.assertEqual(en_message, message.translate('en')) - self.assertEqual(en_message, message.translate('XX')) - - @mock.patch('gettext.translation') - def test_translate_message_with_param(self, mock_translation): - message_with_params = 'A message: %s' - es_translation = 'A message in Spanish: %s' - param = 'A Message param' - - translations = {message_with_params: es_translation} - translator = fakes.FakeTranslations.translator({'es': translations}) - mock_translation.side_effect = translator - - msg = gettextutils.Message(message_with_params) - msg = msg % param - - default_translation = message_with_params % param - expected_translation = es_translation % param - self.assertEqual(expected_translation, msg.translate('es')) - self.assertEqual(default_translation, msg.translate('XX')) - - @mock.patch('gettext.translation') - def test_translate_message_with_object_param(self, mock_translation): - message_with_params = 'A message: %s' - es_translation = 'A message in Spanish: %s' - param = 'A Message param' - param_translation = 'A Message param in Spanish' - - translations = {message_with_params: es_translation, - param: param_translation} - translator = fakes.FakeTranslations.translator({'es': translations}) - mock_translation.side_effect = translator - - msg = gettextutils.Message(message_with_params) - param_msg = gettextutils.Message(param) - - # Here we are testing translation of a Message with another object - # that can be translated via its unicode() representation, this is - # very common for instance when modding an Exception with a Message - obj = SomeObject(param_msg) - msg = msg % obj - - default_translation = message_with_params % param - expected_translation = es_translation % param_translation - - self.assertEqual(expected_translation, msg.translate('es')) - self.assertEqual(default_translation, msg.translate('XX')) - - @mock.patch('gettext.translation') - def test_translate_message_with_param_from_unicoded_obj(self, - mock_translation): - message_with_params = 'A message: %s' - es_translation = 'A message in Spanish: %s' - param = 'A Message param' - - translations = {message_with_params: es_translation} - translator = fakes.FakeTranslations.translator({'es': translations}) - mock_translation.side_effect = translator - - msg = gettextutils.Message(message_with_params) - msg = msg % param - - default_translation = message_with_params % param - expected_translation = es_translation % param - - obj = SomeObject(msg) - unicoded_obj = six.text_type(obj) - - self.assertEqual(expected_translation, unicoded_obj.translate('es')) - self.assertEqual(default_translation, unicoded_obj.translate('XX')) - - @mock.patch('gettext.translation') - def test_translate_message_with_message_parameter(self, mock_translation): - message_with_params = 'A message with param: %s' - es_translation = 'A message with param in Spanish: %s' - message_param = 'A message param' - es_param_translation = 'A message param in Spanish' - - translations = {message_with_params: es_translation, - message_param: es_param_translation} - translator = fakes.FakeTranslations.translator({'es': translations}) - mock_translation.side_effect = translator - - msg = gettextutils.Message(message_with_params) - msg_param = gettextutils.Message(message_param) - msg = msg % msg_param - - default_translation = message_with_params % message_param - expected_translation = es_translation % es_param_translation - self.assertEqual(expected_translation, msg.translate('es')) - self.assertEqual(default_translation, msg.translate('XX')) - - @mock.patch('gettext.translation') - def test_translate_message_with_message_parameters(self, mock_translation): - message_with_params = 'A message with params: %s %s' - es_translation = 'A message with params in Spanish: %s %s' - message_param = 'A message param' - es_param_translation = 'A message param in Spanish' - another_message_param = 'Another message param' - another_es_param_translation = 'Another message param in Spanish' - - translations = {message_with_params: es_translation, - message_param: es_param_translation, - another_message_param: another_es_param_translation} - translator = fakes.FakeTranslations.translator({'es': translations}) - mock_translation.side_effect = translator - - msg = gettextutils.Message(message_with_params) - param_1 = gettextutils.Message(message_param) - param_2 = gettextutils.Message(another_message_param) - msg = msg % (param_1, param_2) - - default_translation = message_with_params % (message_param, - another_message_param) - expected_translation = es_translation % (es_param_translation, - another_es_param_translation) - self.assertEqual(expected_translation, msg.translate('es')) - self.assertEqual(default_translation, msg.translate('XX')) - - @mock.patch('gettext.translation') - def test_translate_message_with_named_parameters(self, mock_translation): - message_with_params = 'A message with params: %(param)s' - es_translation = 'A message with params in Spanish: %(param)s' - message_param = 'A Message param' - es_param_translation = 'A message param in Spanish' - - translations = {message_with_params: es_translation, - message_param: es_param_translation} - translator = fakes.FakeTranslations.translator({'es': translations}) - mock_translation.side_effect = translator - - msg = gettextutils.Message(message_with_params) - msg_param = gettextutils.Message(message_param) - msg = msg % {'param': msg_param} - - default_translation = message_with_params % {'param': message_param} - expected_translation = es_translation % {'param': es_param_translation} - self.assertEqual(expected_translation, msg.translate('es')) - self.assertEqual(default_translation, msg.translate('XX')) - - @mock.patch('locale.getdefaultlocale') - @mock.patch('gettext.translation') - def test_translate_message_non_default_locale(self, - mock_translation, - mock_getdefaultlocale): - message_with_params = 'A message with params: %(param)s' - es_translation = 'A message with params in Spanish: %(param)s' - zh_translation = 'A message with params in Chinese: %(param)s' - fr_translation = 'A message with params in French: %(param)s' - - message_param = 'A Message param' - es_param_translation = 'A message param in Spanish' - zh_param_translation = 'A message param in Chinese' - fr_param_translation = 'A message param in French' - - es_translations = {message_with_params: es_translation, - message_param: es_param_translation} - zh_translations = {message_with_params: zh_translation, - message_param: zh_param_translation} - fr_translations = {message_with_params: fr_translation, - message_param: fr_param_translation} - - translator = fakes.FakeTranslations.translator({'es': es_translations, - 'zh': zh_translations, - 'fr': fr_translations}) - mock_translation.side_effect = translator - mock_getdefaultlocale.return_value = ('es',) - - msg = gettextutils.Message(message_with_params) - msg_param = gettextutils.Message(message_param) - msg = msg % {'param': msg_param} - - es_translation = es_translation % {'param': es_param_translation} - zh_translation = zh_translation % {'param': zh_param_translation} - fr_translation = fr_translation % {'param': fr_param_translation} - - # Because sys.getdefaultlocale() was Spanish, - # the default translation will be to Spanish - self.assertEqual(es_translation, msg) - self.assertEqual(es_translation, msg.translate()) - self.assertEqual(es_translation, msg.translate('es')) - - # Translation into other locales still works - self.assertEqual(zh_translation, msg.translate('zh')) - self.assertEqual(fr_translation, msg.translate('fr')) - - -class TranslationHandlerTestCase(test_base.BaseTestCase): - - def setUp(self): - super(TranslationHandlerTestCase, self).setUp() - - self.stream = six.StringIO() - self.destination_handler = logging.StreamHandler(self.stream) - self.translation_handler = gettextutils.TranslationHandler('zh_CN') - self.translation_handler.setTarget(self.destination_handler) - - self.logger = logging.getLogger('localehander_logger') - self.logger.setLevel(logging.DEBUG) - self.logger.addHandler(self.translation_handler) - - def test_set_formatter(self): - formatter = 'some formatter' - self.translation_handler.setFormatter(formatter) - self.assertEqual(formatter, self.translation_handler.target.formatter) - - @mock.patch('gettext.translation') - def test_emit_translated_message(self, mock_translation): - log_message = 'A message to be logged' - log_message_translation = 'A message to be logged in Chinese' - translations = {log_message: log_message_translation} - translations_map = {'zh_CN': translations} - translator = fakes.FakeTranslations.translator(translations_map) - mock_translation.side_effect = translator - - msg = gettextutils.Message(log_message) - - self.logger.info(msg) - self.assertIn(log_message_translation, self.stream.getvalue()) - - @mock.patch('gettext.translation') - def test_emit_translated_message_with_args(self, mock_translation): - log_message = 'A message to be logged %s' - log_message_translation = 'A message to be logged in Chinese %s' - log_arg = 'Arg to be logged' - log_arg_translation = 'An arg to be logged in Chinese' - - translations = {log_message: log_message_translation, - log_arg: log_arg_translation} - translations_map = {'zh_CN': translations} - translator = fakes.FakeTranslations.translator(translations_map) - mock_translation.side_effect = translator - - msg = gettextutils.Message(log_message) - arg = gettextutils.Message(log_arg) - - self.logger.info(msg, arg) - self.assertIn(log_message_translation % log_arg_translation, - self.stream.getvalue()) - - @mock.patch('gettext.translation') - def test_emit_translated_message_with_named_args(self, mock_translation): - log_message = 'A message to be logged %(arg1)s $(arg2)s' - log_message_translation = 'Chinese msg to be logged %(arg1)s $(arg2)s' - log_arg_1 = 'Arg1 to be logged' - log_arg_1_translation = 'Arg1 to be logged in Chinese' - log_arg_2 = 'Arg2 to be logged' - log_arg_2_translation = 'Arg2 to be logged in Chinese' - - translations = {log_message: log_message_translation, - log_arg_1: log_arg_1_translation, - log_arg_2: log_arg_2_translation} - translations_map = {'zh_CN': translations} - translator = fakes.FakeTranslations.translator(translations_map) - mock_translation.side_effect = translator - - msg = gettextutils.Message(log_message) - arg_1 = gettextutils.Message(log_arg_1) - arg_2 = gettextutils.Message(log_arg_2) - - self.logger.info(msg, {'arg1': arg_1, 'arg2': arg_2}) - translation = log_message_translation % {'arg1': log_arg_1_translation, - 'arg2': log_arg_2_translation} - self.assertIn(translation, self.stream.getvalue()) - - -class TranslatorFactoryTest(test_base.BaseTestCase): - - def test_lazy(self): - with mock.patch.object(gettextutils, 'Message') as msg: - tf = gettextutils.TranslatorFactory('domain', lazy=True) - tf.primary('some text') - msg.assert_called_with('some text', domain='domain') - - def test_py2(self): - with mock.patch.object(six, 'PY3', False): - with mock.patch('gettext.translation') as translation: - trans = mock.Mock() - translation.return_value = trans - trans.gettext.side_effect = AssertionError( - 'should have called ugettext') - tf = gettextutils.TranslatorFactory('domain', lazy=False) - tf.primary('some text') - trans.ugettext.assert_called_with('some text') - - def test_py3(self): - with mock.patch.object(six, 'PY3', True): - with mock.patch('gettext.translation') as translation: - trans = mock.Mock() - translation.return_value = trans - trans.ugettext.side_effect = AssertionError( - 'should have called gettext') - tf = gettextutils.TranslatorFactory('domain', lazy=False) - tf.primary('some text') - trans.gettext.assert_called_with('some text') - - def test_log_level_domain_name(self): - with mock.patch.object(gettextutils.TranslatorFactory, - '_make_translation_func') as mtf: - tf = gettextutils.TranslatorFactory('domain', lazy=False) - tf._make_log_translation_func('mylevel') - mtf.assert_called_with('domain-log-mylevel') - - -class LogLevelTranslationsTest(test_base.BaseTestCase): - - def test_info(self): - self._test('info') - - def test_warning(self): - self._test('warning') - - def test_error(self): - self._test('error') - - def test_critical(self): - self._test('critical') - - def _test(self, level): - with mock.patch.object(gettextutils.TranslatorFactory, - '_make_translation_func') as mtf: - tf = gettextutils.TranslatorFactory('domain', lazy=False) - getattr(tf, 'log_%s' % level) - mtf.assert_called_with('domain-log-%s' % level) - - -class SomeObject(object): - - def __init__(self, message): - self.message = message - - def __unicode__(self): - return self.message - # alias for Python 3 - __str__ = __unicode__ - - -class NoDeepCopyObject(object): - - def __init__(self, value): - self.value = value - - if six.PY3: - def __str__(self): - return str(self.value) - else: - def __unicode__(self): - return unicode(self.value) - - def __deepcopy__(self, memo): - raise TypeError('Deep Copy not supported') diff --git a/tests/test_handler.py b/tests/test_handler.py new file mode 100644 index 0000000..6b634da --- /dev/null +++ b/tests/test_handler.py @@ -0,0 +1,107 @@ +# Copyright 2012 Red Hat, Inc. +# Copyright 2013 IBM Corp. +# All Rights Reserved. +# +# 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 logging + +import mock +import six + +from oslotest import base as test_base + +from tests import fakes + +from oslo.i18n import gettextutils + +LOG = logging.getLogger(__name__) + + +class TranslationHandlerTestCase(test_base.BaseTestCase): + + def setUp(self): + super(TranslationHandlerTestCase, self).setUp() + + self.stream = six.StringIO() + self.destination_handler = logging.StreamHandler(self.stream) + self.translation_handler = gettextutils.TranslationHandler('zh_CN') + self.translation_handler.setTarget(self.destination_handler) + + self.logger = logging.getLogger('localehander_logger') + self.logger.setLevel(logging.DEBUG) + self.logger.addHandler(self.translation_handler) + + def test_set_formatter(self): + formatter = 'some formatter' + self.translation_handler.setFormatter(formatter) + self.assertEqual(formatter, self.translation_handler.target.formatter) + + @mock.patch('gettext.translation') + def test_emit_translated_message(self, mock_translation): + log_message = 'A message to be logged' + log_message_translation = 'A message to be logged in Chinese' + translations = {log_message: log_message_translation} + translations_map = {'zh_CN': translations} + translator = fakes.FakeTranslations.translator(translations_map) + mock_translation.side_effect = translator + + msg = gettextutils.Message(log_message) + + self.logger.info(msg) + self.assertIn(log_message_translation, self.stream.getvalue()) + + @mock.patch('gettext.translation') + def test_emit_translated_message_with_args(self, mock_translation): + log_message = 'A message to be logged %s' + log_message_translation = 'A message to be logged in Chinese %s' + log_arg = 'Arg to be logged' + log_arg_translation = 'An arg to be logged in Chinese' + + translations = {log_message: log_message_translation, + log_arg: log_arg_translation} + translations_map = {'zh_CN': translations} + translator = fakes.FakeTranslations.translator(translations_map) + mock_translation.side_effect = translator + + msg = gettextutils.Message(log_message) + arg = gettextutils.Message(log_arg) + + self.logger.info(msg, arg) + self.assertIn(log_message_translation % log_arg_translation, + self.stream.getvalue()) + + @mock.patch('gettext.translation') + def test_emit_translated_message_with_named_args(self, mock_translation): + log_message = 'A message to be logged %(arg1)s $(arg2)s' + log_message_translation = 'Chinese msg to be logged %(arg1)s $(arg2)s' + log_arg_1 = 'Arg1 to be logged' + log_arg_1_translation = 'Arg1 to be logged in Chinese' + log_arg_2 = 'Arg2 to be logged' + log_arg_2_translation = 'Arg2 to be logged in Chinese' + + translations = {log_message: log_message_translation, + log_arg_1: log_arg_1_translation, + log_arg_2: log_arg_2_translation} + translations_map = {'zh_CN': translations} + translator = fakes.FakeTranslations.translator(translations_map) + mock_translation.side_effect = translator + + msg = gettextutils.Message(log_message) + arg_1 = gettextutils.Message(log_arg_1) + arg_2 = gettextutils.Message(log_arg_2) + + self.logger.info(msg, {'arg1': arg_1, 'arg2': arg_2}) + translation = log_message_translation % {'arg1': log_arg_1_translation, + 'arg2': log_arg_2_translation} + self.assertIn(translation, self.stream.getvalue()) diff --git a/tests/test_logging.py b/tests/test_logging.py new file mode 100644 index 0000000..b4273d6 --- /dev/null +++ b/tests/test_logging.py @@ -0,0 +1,42 @@ +# Copyright 2012 Red Hat, Inc. +# Copyright 2013 IBM Corp. +# All Rights Reserved. +# +# 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 mock +from oslotest import base as test_base + +from oslo.i18n import gettextutils + + +class LogLevelTranslationsTest(test_base.BaseTestCase): + + def test_info(self): + self._test('info') + + def test_warning(self): + self._test('warning') + + def test_error(self): + self._test('error') + + def test_critical(self): + self._test('critical') + + def _test(self, level): + with mock.patch.object(gettextutils.TranslatorFactory, + '_make_translation_func') as mtf: + tf = gettextutils.TranslatorFactory('domain', lazy=False) + getattr(tf, 'log_%s' % level) + mtf.assert_called_with('domain-log-%s' % level) diff --git a/tests/test_message.py b/tests/test_message.py new file mode 100644 index 0000000..f324472 --- /dev/null +++ b/tests/test_message.py @@ -0,0 +1,522 @@ +# Copyright 2012 Red Hat, Inc. +# Copyright 2013 IBM Corp. +# All Rights Reserved. +# +# 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 logging + +import mock +import six +import testtools + +from oslotest import base as test_base + +from tests import fakes +from tests import utils + +from oslo.i18n import gettextutils + +LOG = logging.getLogger(__name__) + + +class MessageTestCase(test_base.BaseTestCase): + """Unit tests for locale Message class.""" + + @staticmethod + def message(msg): + return gettextutils.Message(msg) + + def test_message_id_and_message_text(self): + message = gettextutils.Message('1') + self.assertEqual('1', message.msgid) + self.assertEqual('1', message) + message = gettextutils.Message('1', msgtext='A') + self.assertEqual('1', message.msgid) + self.assertEqual('A', message) + + def test_message_is_unicode(self): + message = self.message('some %s') % 'message' + self.assertIsInstance(message, six.text_type) + + @mock.patch('locale.getdefaultlocale') + @mock.patch('gettext.translation') + def test_create_message_non_english_default_locale(self, + mock_translation, + mock_getdefaultlocale): + msgid = 'A message in English' + es_translation = 'A message in Spanish' + + es_translations = {msgid: es_translation} + translations_map = {'es': es_translations} + translator = fakes.FakeTranslations.translator(translations_map) + mock_translation.side_effect = translator + mock_getdefaultlocale.return_value = ('es',) + + message = gettextutils.Message(msgid) + + # The base representation of the message is in Spanish, as well as + # the default translation, since the default locale was Spanish. + self.assertEqual(es_translation, message) + self.assertEqual(es_translation, message.translate()) + + def test_translate_returns_unicode(self): + message = self.message('some %s') % 'message' + self.assertIsInstance(message.translate(), six.text_type) + + def test_mod_with_named_parameters(self): + msgid = ("%(description)s\nCommand: %(cmd)s\n" + "Exit code: %(exit_code)s\nStdout: %(stdout)r\n" + "Stderr: %(stderr)r %%(something)s") + params = {'description': 'test1', + 'cmd': 'test2', + 'exit_code': 'test3', + 'stdout': 'test4', + 'stderr': 'test5', + 'something': 'trimmed'} + + result = self.message(msgid) % params + + expected = msgid % params + self.assertEqual(result, expected) + self.assertEqual(result.translate(), expected) + + def test_multiple_mod_with_named_parameter(self): + msgid = ("%(description)s\nCommand: %(cmd)s\n" + "Exit code: %(exit_code)s\nStdout: %(stdout)r\n" + "Stderr: %(stderr)r") + params = {'description': 'test1', + 'cmd': 'test2', + 'exit_code': 'test3', + 'stdout': 'test4', + 'stderr': 'test5'} + + # Run string interpolation the first time to make a new Message + first = self.message(msgid) % params + + # Run string interpolation on the new Message, to replicate + # one of the error paths with some Exception classes we've + # implemented in OpenStack. We should receive a second Message + # object, but the translation results should be the same. + # + # The production code that triggers this problem does something + # like: + # + # msg = _('there was a problem %(name)s') % {'name': 'some value'} + # LOG.error(msg) + # raise BadExceptionClass(msg) + # + # where BadExceptionClass does something like: + # + # class BadExceptionClass(Exception): + # def __init__(self, msg, **kwds): + # super(BadExceptionClass, self).__init__(msg % kwds) + # + expected = first % {} + + # Base message id should be the same + self.assertEqual(first.msgid, expected.msgid) + + # Preserved arguments should be the same + self.assertEqual(first.params, expected.params) + + # Should have different objects + self.assertIsNot(expected, first) + + # Final translations should be the same + self.assertEqual(expected.translate(), first.translate()) + + def test_mod_with_named_parameters_no_space(self): + msgid = ("Request: %(method)s http://%(server)s:" + "%(port)s%(url)s with headers %(headers)s") + params = {'method': 'POST', + 'server': 'test1', + 'port': 1234, + 'url': 'test2', + 'headers': {'h1': 'val1'}} + + result = self.message(msgid) % params + + expected = msgid % params + self.assertEqual(result, expected) + self.assertEqual(result.translate(), expected) + + def test_mod_with_dict_parameter(self): + msgid = "Test that we can inject a dictionary %s" + params = {'description': 'test1'} + + result = self.message(msgid) % params + + expected = msgid % params + self.assertEqual(result, expected) + self.assertEqual(result.translate(), expected) + + def test_mod_with_integer_parameters(self): + msgid = "Some string with params: %d" + params = [0, 1, 10, 24124] + + messages = [] + results = [] + for param in params: + messages.append(msgid % param) + results.append(self.message(msgid) % param) + + for message, result in zip(messages, results): + self.assertEqual(type(result), gettextutils.Message) + self.assertEqual(result.translate(), message) + + # simulate writing out as string + result_str = '%s' % result.translate() + self.assertEqual(result_str, message) + self.assertEqual(result, message) + + def test_mod_copies_parameters(self): + msgid = "Found object: %(current_value)s" + changing_dict = {'current_value': 1} + # A message created with some params + result = self.message(msgid) % changing_dict + # The parameters may change + changing_dict['current_value'] = 2 + # Even if the param changes when the message is + # translated it should use the original param + self.assertEqual(result.translate(), 'Found object: 1') + + def test_mod_deep_copies_parameters(self): + msgid = "Found list: %(current_list)s" + changing_list = list([1, 2, 3]) + params = {'current_list': changing_list} + # Apply the params + result = self.message(msgid) % params + # Change the list + changing_list.append(4) + # Even though the list changed the message + # translation should use the original list + self.assertEqual(result.translate(), "Found list: [1, 2, 3]") + + def test_mod_deep_copies_param_nodeep_param(self): + msgid = "Value: %s" + params = utils.NoDeepCopyObject(5) + # Apply the params + result = self.message(msgid) % params + self.assertEqual(result.translate(), "Value: 5") + + def test_mod_deep_copies_param_nodeep_dict(self): + msgid = "Values: %(val1)s %(val2)s" + params = {'val1': 1, 'val2': utils.NoDeepCopyObject(2)} + # Apply the params + result = self.message(msgid) % params + self.assertEqual(result.translate(), "Values: 1 2") + + # Apply again to make sure other path works as well + params = {'val1': 3, 'val2': utils.NoDeepCopyObject(4)} + result = self.message(msgid) % params + self.assertEqual(result.translate(), "Values: 3 4") + + def test_mod_returns_a_copy(self): + msgid = "Some msgid string: %(test1)s %(test2)s" + message = self.message(msgid) + m1 = message % {'test1': 'foo', 'test2': 'bar'} + m2 = message % {'test1': 'foo2', 'test2': 'bar2'} + + self.assertIsNot(message, m1) + self.assertIsNot(message, m2) + self.assertEqual(m1.translate(), + msgid % {'test1': 'foo', 'test2': 'bar'}) + self.assertEqual(m2.translate(), + msgid % {'test1': 'foo2', 'test2': 'bar2'}) + + def test_mod_with_none_parameter(self): + msgid = "Some string with params: %s" + message = self.message(msgid) % None + self.assertEqual(msgid % None, message) + self.assertEqual(msgid % None, message.translate()) + + def test_mod_with_missing_parameters(self): + msgid = "Some string with params: %s %s" + test_me = lambda: self.message(msgid) % 'just one' + # Just like with strings missing parameters raise TypeError + self.assertRaises(TypeError, test_me) + + def test_mod_with_extra_parameters(self): + msgid = "Some string with params: %(param1)s %(param2)s" + params = {'param1': 'test', + 'param2': 'test2', + 'param3': 'notinstring'} + + result = self.message(msgid) % params + + expected = msgid % params + self.assertEqual(result, expected) + self.assertEqual(result.translate(), expected) + + # Make sure unused params still there + self.assertEqual(result.params.keys(), params.keys()) + + def test_mod_with_missing_named_parameters(self): + msgid = ("Some string with params: %(param1)s %(param2)s" + " and a missing one %(missing)s") + params = {'param1': 'test', + 'param2': 'test2'} + + test_me = lambda: self.message(msgid) % params + # Just like with strings missing named parameters raise KeyError + self.assertRaises(KeyError, test_me) + + def test_add_disabled(self): + msgid = "A message" + test_me = lambda: self.message(msgid) + ' some string' + self.assertRaises(TypeError, test_me) + + def test_radd_disabled(self): + msgid = "A message" + test_me = lambda: utils.SomeObject('test') + self.message(msgid) + self.assertRaises(TypeError, test_me) + + @testtools.skipIf(six.PY3, 'test specific to Python 2') + def test_str_disabled(self): + msgid = "A message" + test_me = lambda: str(self.message(msgid)) + self.assertRaises(UnicodeError, test_me) + + @mock.patch('gettext.translation') + def test_translate(self, mock_translation): + en_message = 'A message in the default locale' + es_translation = 'A message in Spanish' + message = gettextutils.Message(en_message) + + es_translations = {en_message: es_translation} + translations_map = {'es': es_translations} + translator = fakes.FakeTranslations.translator(translations_map) + mock_translation.side_effect = translator + + self.assertEqual(es_translation, message.translate('es')) + + @mock.patch('gettext.translation') + def test_translate_message_from_unicoded_object(self, mock_translation): + en_message = 'A message in the default locale' + es_translation = 'A message in Spanish' + message = gettextutils.Message(en_message) + es_translations = {en_message: es_translation} + translations_map = {'es': es_translations} + translator = fakes.FakeTranslations.translator(translations_map) + mock_translation.side_effect = translator + + # Here we are not testing the Message object directly but the result + # of unicoding() an object whose unicode representation is a Message + obj = utils.SomeObject(message) + unicoded_obj = six.text_type(obj) + + self.assertEqual(es_translation, unicoded_obj.translate('es')) + + @mock.patch('gettext.translation') + def test_translate_multiple_languages(self, mock_translation): + en_message = 'A message in the default locale' + es_translation = 'A message in Spanish' + zh_translation = 'A message in Chinese' + message = gettextutils.Message(en_message) + + es_translations = {en_message: es_translation} + zh_translations = {en_message: zh_translation} + translations_map = {'es': es_translations, + 'zh': zh_translations} + translator = fakes.FakeTranslations.translator(translations_map) + mock_translation.side_effect = translator + + self.assertEqual(es_translation, message.translate('es')) + self.assertEqual(zh_translation, message.translate('zh')) + self.assertEqual(en_message, message.translate(None)) + self.assertEqual(en_message, message.translate('en')) + self.assertEqual(en_message, message.translate('XX')) + + @mock.patch('gettext.translation') + def test_translate_message_with_param(self, mock_translation): + message_with_params = 'A message: %s' + es_translation = 'A message in Spanish: %s' + param = 'A Message param' + + translations = {message_with_params: es_translation} + translator = fakes.FakeTranslations.translator({'es': translations}) + mock_translation.side_effect = translator + + msg = gettextutils.Message(message_with_params) + msg = msg % param + + default_translation = message_with_params % param + expected_translation = es_translation % param + self.assertEqual(expected_translation, msg.translate('es')) + self.assertEqual(default_translation, msg.translate('XX')) + + @mock.patch('gettext.translation') + def test_translate_message_with_object_param(self, mock_translation): + message_with_params = 'A message: %s' + es_translation = 'A message in Spanish: %s' + param = 'A Message param' + param_translation = 'A Message param in Spanish' + + translations = {message_with_params: es_translation, + param: param_translation} + translator = fakes.FakeTranslations.translator({'es': translations}) + mock_translation.side_effect = translator + + msg = gettextutils.Message(message_with_params) + param_msg = gettextutils.Message(param) + + # Here we are testing translation of a Message with another object + # that can be translated via its unicode() representation, this is + # very common for instance when modding an Exception with a Message + obj = utils.SomeObject(param_msg) + msg = msg % obj + + default_translation = message_with_params % param + expected_translation = es_translation % param_translation + + self.assertEqual(expected_translation, msg.translate('es')) + self.assertEqual(default_translation, msg.translate('XX')) + + @mock.patch('gettext.translation') + def test_translate_message_with_param_from_unicoded_obj(self, + mock_translation): + message_with_params = 'A message: %s' + es_translation = 'A message in Spanish: %s' + param = 'A Message param' + + translations = {message_with_params: es_translation} + translator = fakes.FakeTranslations.translator({'es': translations}) + mock_translation.side_effect = translator + + msg = gettextutils.Message(message_with_params) + msg = msg % param + + default_translation = message_with_params % param + expected_translation = es_translation % param + + obj = utils.SomeObject(msg) + unicoded_obj = six.text_type(obj) + + self.assertEqual(expected_translation, unicoded_obj.translate('es')) + self.assertEqual(default_translation, unicoded_obj.translate('XX')) + + @mock.patch('gettext.translation') + def test_translate_message_with_message_parameter(self, mock_translation): + message_with_params = 'A message with param: %s' + es_translation = 'A message with param in Spanish: %s' + message_param = 'A message param' + es_param_translation = 'A message param in Spanish' + + translations = {message_with_params: es_translation, + message_param: es_param_translation} + translator = fakes.FakeTranslations.translator({'es': translations}) + mock_translation.side_effect = translator + + msg = gettextutils.Message(message_with_params) + msg_param = gettextutils.Message(message_param) + msg = msg % msg_param + + default_translation = message_with_params % message_param + expected_translation = es_translation % es_param_translation + self.assertEqual(expected_translation, msg.translate('es')) + self.assertEqual(default_translation, msg.translate('XX')) + + @mock.patch('gettext.translation') + def test_translate_message_with_message_parameters(self, mock_translation): + message_with_params = 'A message with params: %s %s' + es_translation = 'A message with params in Spanish: %s %s' + message_param = 'A message param' + es_param_translation = 'A message param in Spanish' + another_message_param = 'Another message param' + another_es_param_translation = 'Another message param in Spanish' + + translations = {message_with_params: es_translation, + message_param: es_param_translation, + another_message_param: another_es_param_translation} + translator = fakes.FakeTranslations.translator({'es': translations}) + mock_translation.side_effect = translator + + msg = gettextutils.Message(message_with_params) + param_1 = gettextutils.Message(message_param) + param_2 = gettextutils.Message(another_message_param) + msg = msg % (param_1, param_2) + + default_translation = message_with_params % (message_param, + another_message_param) + expected_translation = es_translation % (es_param_translation, + another_es_param_translation) + self.assertEqual(expected_translation, msg.translate('es')) + self.assertEqual(default_translation, msg.translate('XX')) + + @mock.patch('gettext.translation') + def test_translate_message_with_named_parameters(self, mock_translation): + message_with_params = 'A message with params: %(param)s' + es_translation = 'A message with params in Spanish: %(param)s' + message_param = 'A Message param' + es_param_translation = 'A message param in Spanish' + + translations = {message_with_params: es_translation, + message_param: es_param_translation} + translator = fakes.FakeTranslations.translator({'es': translations}) + mock_translation.side_effect = translator + + msg = gettextutils.Message(message_with_params) + msg_param = gettextutils.Message(message_param) + msg = msg % {'param': msg_param} + + default_translation = message_with_params % {'param': message_param} + expected_translation = es_translation % {'param': es_param_translation} + self.assertEqual(expected_translation, msg.translate('es')) + self.assertEqual(default_translation, msg.translate('XX')) + + @mock.patch('locale.getdefaultlocale') + @mock.patch('gettext.translation') + def test_translate_message_non_default_locale(self, + mock_translation, + mock_getdefaultlocale): + message_with_params = 'A message with params: %(param)s' + es_translation = 'A message with params in Spanish: %(param)s' + zh_translation = 'A message with params in Chinese: %(param)s' + fr_translation = 'A message with params in French: %(param)s' + + message_param = 'A Message param' + es_param_translation = 'A message param in Spanish' + zh_param_translation = 'A message param in Chinese' + fr_param_translation = 'A message param in French' + + es_translations = {message_with_params: es_translation, + message_param: es_param_translation} + zh_translations = {message_with_params: zh_translation, + message_param: zh_param_translation} + fr_translations = {message_with_params: fr_translation, + message_param: fr_param_translation} + + translator = fakes.FakeTranslations.translator({'es': es_translations, + 'zh': zh_translations, + 'fr': fr_translations}) + mock_translation.side_effect = translator + mock_getdefaultlocale.return_value = ('es',) + + msg = gettextutils.Message(message_with_params) + msg_param = gettextutils.Message(message_param) + msg = msg % {'param': msg_param} + + es_translation = es_translation % {'param': es_param_translation} + zh_translation = zh_translation % {'param': zh_param_translation} + fr_translation = fr_translation % {'param': fr_param_translation} + + # Because sys.getdefaultlocale() was Spanish, + # the default translation will be to Spanish + self.assertEqual(es_translation, msg) + self.assertEqual(es_translation, msg.translate()) + self.assertEqual(es_translation, msg.translate('es')) + + # Translation into other locales still works + self.assertEqual(zh_translation, msg.translate('zh')) + self.assertEqual(fr_translation, msg.translate('fr')) diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 0000000..a6ad6c3 --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,42 @@ +# All Rights Reserved. +# +# 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 + + +class SomeObject(object): + + def __init__(self, message): + self.message = message + + def __unicode__(self): + return self.message + # alias for Python 3 + __str__ = __unicode__ + + +class NoDeepCopyObject(object): + + def __init__(self, value): + self.value = value + + if six.PY3: + def __str__(self): + return str(self.value) + else: + def __unicode__(self): + return unicode(self.value) + + def __deepcopy__(self, memo): + raise TypeError('Deep Copy not supported') |