Graduation Prep: Use six.text_type instead of str

Previously, `str()` was used to convert things into strings.
This will not work for unicode messages in Python 2, so
everything was converted to using `six.text_type` instead.
This commit is contained in:
Solly Ross 2015-06-03 13:39:58 -04:00
parent 4faad94ced
commit 6bfdb33cc9
9 changed files with 68 additions and 52 deletions

View File

@ -18,7 +18,10 @@ This module includes various utilities
used in generating reports. used in generating reports.
""" """
class StringWithAttrs(str): import six
class StringWithAttrs(six.text_type):
"""A String that can have arbitrary attributes""" """A String that can have arbitrary attributes"""
pass pass

View File

@ -67,7 +67,7 @@ class ReportModel(col.MutableMapping):
self_cpy = copy.deepcopy(self) self_cpy = copy.deepcopy(self)
for key in self_cpy: for key in self_cpy:
if getattr(self_cpy[key], 'attached_view', None) is not None: if getattr(self_cpy[key], 'attached_view', None) is not None:
self_cpy[key] = str(self_cpy[key]) self_cpy[key] = six.text_type(self_cpy[key])
if self.attached_view is not None: if self.attached_view is not None:
return self.attached_view(self_cpy) return self.attached_view(self_cpy)

View File

@ -19,6 +19,8 @@ All reports take the form of a report class containing various report
sections. sections.
""" """
import six
from oslo_reports.views.text import header as header_views from oslo_reports.views.text import header as header_views
@ -72,15 +74,15 @@ class BasicReport(object):
:returns: the serialized report :returns: the serialized report
""" """
return "\n".join(str(sect) for sect in self.sections) return "\n".join(six.text_type(sect) for sect in self.sections)
class ReportSection(object): class ReportSection(object):
"""A Report Section """A Report Section
A report section contains a generator and a top-level view. When something A report section contains a generator and a top-level view. When something
attempts to serialize the section by calling str() on it, the section runs attempts to serialize the section by calling str() or unicode() on it, the
the generator and calls the view on the resulting model. section runs the generator and calls the view on the resulting model.
.. seealso:: .. seealso::

View File

@ -16,6 +16,7 @@ import collections as col
import re import re
from oslotest import base from oslotest import base
import six
from oslo_reports.models import base as base_model from oslo_reports.models import base as base_model
from oslo_reports import report from oslo_reports import report
@ -25,7 +26,7 @@ class BasicView(object):
def __call__(self, model): def __call__(self, model):
res = "" res = ""
for k in sorted(model.keys()): for k in sorted(model.keys()):
res += str(k) + ": " + str(model[k]) + ";" res += six.text_type(k) + ": " + six.text_type(model[k]) + ";"
return res return res
@ -66,7 +67,7 @@ class TestBaseModel(base.BaseTestCase):
def test_submodel_attached_view(self): def test_submodel_attached_view(self):
class TmpView(object): class TmpView(object):
def __call__(self, model): def __call__(self, model):
return '{len: ' + str(len(model.c)) + '}' return '{len: ' + six.text_type(len(model.c)) + '}'
def generate_model_with_submodel(): def generate_model_with_submodel():
base_m = basic_generator() base_m = basic_generator()
@ -84,10 +85,10 @@ class TestBaseModel(base.BaseTestCase):
# ugly code for python 2.6 compat, since python 2.6 # ugly code for python 2.6 compat, since python 2.6
# does not have assertRaisesRegexp # does not have assertRaisesRegexp
try: try:
str(model) six.text_type(model)
except Exception as e: except Exception as e:
err_str = 'Cannot stringify model: no attached view' err_str = 'Cannot stringify model: no attached view'
self.assertEqual(str(e), err_str) self.assertEqual(six.text_type(e), err_str)
else: else:
self.assertTrue(False) self.assertTrue(False)
@ -95,7 +96,7 @@ class TestBaseModel(base.BaseTestCase):
model = base_model.ReportModel(data={'a': 1, 'b': 2}, model = base_model.ReportModel(data={'a': 1, 'b': 2},
attached_view=BasicView()) attached_view=BasicView())
self.assertEqual(str(model), 'a: 1;b: 2;') self.assertEqual(six.text_type(model), 'a: 1;b: 2;')
def test_model_repr(self): def test_model_repr(self):
model1 = base_model.ReportModel(data={'a': 1, 'b': 2}, model1 = base_model.ReportModel(data={'a': 1, 'b': 2},
@ -122,7 +123,7 @@ class TestBaseModel(base.BaseTestCase):
model.attached_view = BasicView() model.attached_view = BasicView()
# if we don't handle lists properly, we'll get a TypeError here # if we don't handle lists properly, we'll get a TypeError here
self.assertEqual('0: a;1: b;', str(model)) self.assertEqual('0: a;1: b;', six.text_type(model))
def test_immutable_mappings_produce_mutable_models(self): def test_immutable_mappings_produce_mutable_models(self):
class SomeImmutableMapping(col.Mapping): class SomeImmutableMapping(col.Mapping):
@ -142,9 +143,9 @@ class TestBaseModel(base.BaseTestCase):
model = base_model.ReportModel(data=mp) model = base_model.ReportModel(data=mp)
model.attached_view = BasicView() model.attached_view = BasicView()
self.assertEqual('a: 2;b: 4;c: 8;', str(model)) self.assertEqual('a: 2;b: 4;c: 8;', six.text_type(model))
model['d'] = 16 model['d'] = 16
self.assertEqual('a: 2;b: 4;c: 8;d: 16;', str(model)) self.assertEqual('a: 2;b: 4;c: 8;d: 16;', six.text_type(model))
self.assertEqual({'a': 2, 'b': 4, 'c': 8}, mp.data) self.assertEqual({'a': 2, 'b': 4, 'c': 8}, mp.data)

View File

@ -19,6 +19,7 @@ import greenlet
import mock import mock
from oslo_config import cfg from oslo_config import cfg
from oslotest import base from oslotest import base
import six
from oslo_reports.generators import conf as os_cgen from oslo_reports.generators import conf as os_cgen
from oslo_reports.generators import threading as os_tgen from oslo_reports.generators import threading as os_tgen
@ -42,7 +43,7 @@ class TestOpenstackGenerators(base.BaseTestCase):
self.assertTrue(was_ok) self.assertTrue(was_ok)
model.set_current_view_type('text') model.set_current_view_type('text')
self.assertIsNotNone(str(model)) self.assertIsNotNone(six.text_type(model))
def test_thread_generator_tb(self): def test_thread_generator_tb(self):
class FakeModel(object): class FakeModel(object):
@ -72,7 +73,7 @@ class TestOpenstackGenerators(base.BaseTestCase):
self.assertTrue(was_ok) self.assertTrue(was_ok)
model.set_current_view_type('text') model.set_current_view_type('text')
self.assertIsNotNone(str(model)) self.assertIsNotNone(six.text_type(model))
def test_config_model(self): def test_config_model(self):
conf = cfg.ConfigOpts() conf = cfg.ConfigOpts()
@ -102,7 +103,7 @@ class TestOpenstackGenerators(base.BaseTestCase):
'default: \n' 'default: \n'
' crackers = triscuit\n' ' crackers = triscuit\n'
' secrets = ***') ' secrets = ***')
self.assertEqual(target_str, str(model)) self.assertEqual(target_str, six.text_type(model))
def test_package_report_generator(self): def test_package_report_generator(self):
class VersionObj(object): class VersionObj(object):
@ -121,4 +122,4 @@ class TestOpenstackGenerators(base.BaseTestCase):
target_str = ('product = Sharp Cheddar\n' target_str = ('product = Sharp Cheddar\n'
'vendor = Cheese Shoppe\n' 'vendor = Cheese Shoppe\n'
'version = 1.0.0') 'version = 1.0.0')
self.assertEqual(target_str, str(model)) self.assertEqual(target_str, six.text_type(model))

View File

@ -16,6 +16,7 @@ import copy
import mock import mock
from oslotest import base from oslotest import base
import six
from oslo_reports.models import base as base_model from oslo_reports.models import base as base_model
from oslo_reports.models import with_default_views as mwdv from oslo_reports.models import with_default_views as mwdv
@ -34,15 +35,15 @@ class TestModelReportType(base.BaseTestCase):
model = mwdv_generator() model = mwdv_generator()
model.set_current_view_type('text') model.set_current_view_type('text')
self.assertEqual('int = 1\nstring = value', str(model)) self.assertEqual('int = 1\nstring = value', six.text_type(model))
model.set_current_view_type('json') model.set_current_view_type('json')
self.assertEqual('{"int": 1, "string": "value"}', str(model)) self.assertEqual('{"int": 1, "string": "value"}', six.text_type(model))
model.set_current_view_type('xml') model.set_current_view_type('xml')
self.assertEqual('<model><int>1</int><string>value</string></model>', self.assertEqual('<model><int>1</int><string>value</string></model>',
str(model)) six.text_type(model))
def test_recursive_type_propagation_with_nested_models(self): def test_recursive_type_propagation_with_nested_models(self):
model = mwdv_generator() model = mwdv_generator()
@ -84,7 +85,7 @@ class TestModelReportType(base.BaseTestCase):
def test_report_of_type(self): def test_report_of_type(self):
rep = report.ReportOfType('json') rep = report.ReportOfType('json')
rep.add_section(lambda x: str(x), mwdv_generator) rep.add_section(lambda x: six.text_type(x), mwdv_generator)
self.assertEqual('{"int": 1, "string": "value"}', rep.run()) self.assertEqual('{"int": 1, "string": "value"}', rep.run())
@ -133,7 +134,7 @@ class TestGenericXMLView(base.BaseTestCase):
'<dt><a>1</a><b>2</b></dt>' '<dt><a>1</a><b>2</b></dt>'
'<int>1</int>' '<int>1</int>'
'<string>value</string></model>') '<string>value</string></model>')
self.assertEqual(target_str, str(self.model)) self.assertEqual(target_str, six.text_type(self.model))
def test_list_serialization(self): def test_list_serialization(self):
self.model['lt'] = ['a', 'b'] self.model['lt'] = ['a', 'b']
@ -142,7 +143,7 @@ class TestGenericXMLView(base.BaseTestCase):
'<int>1</int>' '<int>1</int>'
'<lt><item>a</item><item>b</item></lt>' '<lt><item>a</item><item>b</item></lt>'
'<string>value</string></model>') '<string>value</string></model>')
self.assertEqual(target_str, str(self.model)) self.assertEqual(target_str, six.text_type(self.model))
def test_list_in_dict_serialization(self): def test_list_in_dict_serialization(self):
self.model['dt'] = {'a': 1, 'b': [2, 3]} self.model['dt'] = {'a': 1, 'b': [2, 3]}
@ -152,7 +153,7 @@ class TestGenericXMLView(base.BaseTestCase):
'<b><item>2</item><item>3</item></b></dt>' '<b><item>2</item><item>3</item></b></dt>'
'<int>1</int>' '<int>1</int>'
'<string>value</string></model>') '<string>value</string></model>')
self.assertEqual(target_str, str(self.model)) self.assertEqual(target_str, six.text_type(self.model))
def test_dict_in_list_serialization(self): def test_dict_in_list_serialization(self):
self.model['lt'] = [1, {'b': 2, 'c': 3}] self.model['lt'] = [1, {'b': 2, 'c': 3}]
@ -162,7 +163,7 @@ class TestGenericXMLView(base.BaseTestCase):
'<lt><item>1</item>' '<lt><item>1</item>'
'<item><b>2</b><c>3</c></item></lt>' '<item><b>2</b><c>3</c></item></lt>'
'<string>value</string></model>') '<string>value</string></model>')
self.assertEqual(target_str, str(self.model)) self.assertEqual(target_str, six.text_type(self.model))
def test_submodel_serialization(self): def test_submodel_serialization(self):
sm = mwdv_generator() sm = mwdv_generator()
@ -177,7 +178,7 @@ class TestGenericXMLView(base.BaseTestCase):
'<model><int>1</int><string>value</string></model>' '<model><int>1</int><string>value</string></model>'
'</submodel>' '</submodel>'
'</model>') '</model>')
self.assertEqual(target_str, str(self.model)) self.assertEqual(target_str, six.text_type(self.model))
def test_wrapper_name(self): def test_wrapper_name(self):
self.model.attached_view.wrapper_name = 'cheese' self.model.attached_view.wrapper_name = 'cheese'
@ -186,7 +187,7 @@ class TestGenericXMLView(base.BaseTestCase):
'<int>1</int>' '<int>1</int>'
'<string>value</string>' '<string>value</string>'
'</cheese>') '</cheese>')
self.assertEqual(target_str, str(self.model)) self.assertEqual(target_str, six.text_type(self.model))
class TestGenericJSONViews(base.BaseTestCase): class TestGenericJSONViews(base.BaseTestCase):
@ -201,7 +202,8 @@ class TestGenericJSONViews(base.BaseTestCase):
self.model = base_model.ReportModel(data={'string': 'value', 'int': 1}, self.model = base_model.ReportModel(data={'string': 'value', 'int': 1},
attached_view=attached_view) attached_view=attached_view)
self.assertEqual('{"int": 1, "string": "value"}', str(self.model)) self.assertEqual('{"int": 1, "string": "value"}',
six.text_type(self.model))
def test_dict_serialization(self): def test_dict_serialization(self):
self.model['dt'] = {'a': 1, 'b': 2} self.model['dt'] = {'a': 1, 'b': 2}
@ -211,7 +213,7 @@ class TestGenericJSONViews(base.BaseTestCase):
'"int": 1, ' '"int": 1, '
'"string": "value"' '"string": "value"'
'}') '}')
self.assertEqual(target_str, str(self.model)) self.assertEqual(target_str, six.text_type(self.model))
def test_list_serialization(self): def test_list_serialization(self):
self.model['lt'] = ['a', 'b'] self.model['lt'] = ['a', 'b']
@ -221,7 +223,7 @@ class TestGenericJSONViews(base.BaseTestCase):
'"lt": ["a", "b"], ' '"lt": ["a", "b"], '
'"string": "value"' '"string": "value"'
'}') '}')
self.assertEqual(target_str, str(self.model)) self.assertEqual(target_str, six.text_type(self.model))
def test_list_in_dict_serialization(self): def test_list_in_dict_serialization(self):
self.model['dt'] = {'a': 1, 'b': [2, 3]} self.model['dt'] = {'a': 1, 'b': [2, 3]}
@ -231,7 +233,7 @@ class TestGenericJSONViews(base.BaseTestCase):
'"int": 1, ' '"int": 1, '
'"string": "value"' '"string": "value"'
'}') '}')
self.assertEqual(target_str, str(self.model)) self.assertEqual(target_str, six.text_type(self.model))
def test_dict_in_list_serialization(self): def test_dict_in_list_serialization(self):
self.model['lt'] = [1, {'b': 2, 'c': 3}] self.model['lt'] = [1, {'b': 2, 'c': 3}]
@ -241,7 +243,7 @@ class TestGenericJSONViews(base.BaseTestCase):
'"lt": [1, {"b": 2, "c": 3}], ' '"lt": [1, {"b": 2, "c": 3}], '
'"string": "value"' '"string": "value"'
'}') '}')
self.assertEqual(target_str, str(self.model)) self.assertEqual(target_str, six.text_type(self.model))
def test_submodel_serialization(self): def test_submodel_serialization(self):
sm = mwdv_generator() sm = mwdv_generator()
@ -254,7 +256,7 @@ class TestGenericJSONViews(base.BaseTestCase):
'"string": "value", ' '"string": "value", '
'"submodel": {"int": 1, "string": "value"}' '"submodel": {"int": 1, "string": "value"}'
'}') '}')
self.assertEqual(target_str, str(self.model)) self.assertEqual(target_str, six.text_type(self.model))
class TestGenericTextViews(base.BaseTestCase): class TestGenericTextViews(base.BaseTestCase):
@ -278,14 +280,15 @@ class TestGenericTextViews(base.BaseTestCase):
'string = value\n' 'string = value\n'
'int = 2\n' 'int = 2\n'
'string = value') 'string = value')
self.assertEqual(target_str, str(self.model)) self.assertEqual(target_str, six.text_type(self.model))
def test_basic_kv_view(self): def test_basic_kv_view(self):
attached_view = text_generic.BasicKeyValueView() attached_view = text_generic.BasicKeyValueView()
self.model = base_model.ReportModel(data={'string': 'value', 'int': 1}, self.model = base_model.ReportModel(data={'string': 'value', 'int': 1},
attached_view=attached_view) attached_view=attached_view)
self.assertEqual('int = 1\nstring = value\n', str(self.model)) self.assertEqual('int = 1\nstring = value\n',
six.text_type(self.model))
def test_table_view(self): def test_table_view(self):
column_names = ['Column A', 'Column B'] column_names = ['Column A', 'Column B']
@ -302,7 +305,7 @@ class TestGenericTextViews(base.BaseTestCase):
' 1 | 2 \n' # noqa ' 1 | 2 \n' # noqa
' 3 | 4 \n') # noqa ' 3 | 4 \n') # noqa
self.assertEqual(target_str, str(self.model)) self.assertEqual(target_str, six.text_type(self.model))
def test_dict_serialization(self): def test_dict_serialization(self):
self.model['dt'] = {'a': 1, 'b': 2} self.model['dt'] = {'a': 1, 'b': 2}
@ -312,7 +315,7 @@ class TestGenericTextViews(base.BaseTestCase):
' b = 2\n' ' b = 2\n'
'int = 1\n' 'int = 1\n'
'string = value') 'string = value')
self.assertEqual(target_str, str(self.model)) self.assertEqual(target_str, six.text_type(self.model))
def test_list_serialization(self): def test_list_serialization(self):
self.model['lt'] = ['a', 'b'] self.model['lt'] = ['a', 'b']
@ -322,7 +325,7 @@ class TestGenericTextViews(base.BaseTestCase):
' a\n' ' a\n'
' b\n' ' b\n'
'string = value') 'string = value')
self.assertEqual(target_str, str(self.model)) self.assertEqual(target_str, six.text_type(self.model))
def test_list_in_dict_serialization(self): def test_list_in_dict_serialization(self):
self.model['dt'] = {'a': 1, 'b': [2, 3]} self.model['dt'] = {'a': 1, 'b': [2, 3]}
@ -335,7 +338,7 @@ class TestGenericTextViews(base.BaseTestCase):
'int = 1\n' 'int = 1\n'
'string = value') 'string = value')
self.assertEqual(target_str, str(self.model)) self.assertEqual(target_str, six.text_type(self.model))
def test_dict_in_list_serialization(self): def test_dict_in_list_serialization(self):
self.model['lt'] = [1, {'b': 2, 'c': 3}] self.model['lt'] = [1, {'b': 2, 'c': 3}]
@ -347,7 +350,7 @@ class TestGenericTextViews(base.BaseTestCase):
' b = 2\n' ' b = 2\n'
' c = 3\n' ' c = 3\n'
'string = value') 'string = value')
self.assertEqual(target_str, str(self.model)) self.assertEqual(target_str, six.text_type(self.model))
def test_submodel_serialization(self): def test_submodel_serialization(self):
sm = mwdv_generator() sm = mwdv_generator()
@ -360,7 +363,7 @@ class TestGenericTextViews(base.BaseTestCase):
'submodel = \n' 'submodel = \n'
' int = 1\n' ' int = 1\n'
' string = value') ' string = value')
self.assertEqual(target_str, str(self.model)) self.assertEqual(target_str, six.text_type(self.model))
def test_custom_indent_string(self): def test_custom_indent_string(self):
view = text_generic.KeyValueView(indent_str='~~') view = text_generic.KeyValueView(indent_str='~~')
@ -373,7 +376,7 @@ class TestGenericTextViews(base.BaseTestCase):
'~~a\n' '~~a\n'
'~~b\n' '~~b\n'
'string = value') 'string = value')
self.assertEqual(target_str, str(self.model)) self.assertEqual(target_str, six.text_type(self.model))
def get_open_mocks(rv): def get_open_mocks(rv):
@ -397,13 +400,15 @@ class TestJinjaView(base.BaseTestCase):
def test_load_from_file(self): def test_load_from_file(self):
self.model.attached_view = jv.JinjaView(path='a/b/c/d.jinja.txt') self.model.attached_view = jv.JinjaView(path='a/b/c/d.jinja.txt')
self.assertEqual('int is 1, string is value', str(self.model)) self.assertEqual('int is 1, string is value',
six.text_type(self.model))
self.MM_FILE.assert_called_with_once('a/b/c/d.jinja.txt') self.MM_FILE.assert_called_with_once('a/b/c/d.jinja.txt')
def test_direct_pass(self): def test_direct_pass(self):
self.model.attached_view = jv.JinjaView(text=self.TEMPL_STR) self.model.attached_view = jv.JinjaView(text=self.TEMPL_STR)
self.assertEqual('int is 1, string is value', str(self.model)) self.assertEqual('int is 1, string is value',
six.text_type(self.model))
def test_load_from_class(self): def test_load_from_class(self):
class TmpJinjaView(jv.JinjaView): class TmpJinjaView(jv.JinjaView):
@ -411,7 +416,8 @@ class TestJinjaView(base.BaseTestCase):
self.model.attached_view = TmpJinjaView() self.model.attached_view = TmpJinjaView()
self.assertEqual('int is 1, string is value', str(self.model)) self.assertEqual('int is 1, string is value',
six.text_type(self.model))
def test_is_deepcopiable(self): def test_is_deepcopiable(self):
view_orig = jv.JinjaView(text=self.TEMPL_STR) view_orig = jv.JinjaView(text=self.TEMPL_STR)

View File

@ -35,7 +35,7 @@ class MultiView(object):
""" """
def __call__(self, model): def __call__(self, model):
res = [str(model[key]) for key in model] res = [six.text_type(model[key]) for key in model]
return "\n".join(res) return "\n".join(res)
@ -132,7 +132,7 @@ class KeyValueView(object):
for val in sorted(root, key=str): for val in sorted(root, key=str):
res.extend(serialize(val, None, indent + 1)) res.extend(serialize(val, None, indent + 1))
else: else:
str_root = str(root) str_root = six.text_type(root)
if '\n' in str_root: if '\n' in str_root:
# we are in a submodel # we are in a submodel
if rootkey is not None: if rootkey is not None:
@ -175,7 +175,7 @@ class TableView(object):
self.column_width = (72 - len(column_names) + 1) // len(column_names) self.column_width = (72 - len(column_names) + 1) // len(column_names)
column_headers = "|".join( column_headers = "|".join(
"{ch[" + str(n) + "]: ^" + str(self.column_width) + "}" "{{ch[{n}]: ^{width}}}".format(n=n, width=self.column_width)
for n in range(len(column_names)) for n in range(len(column_names))
) )
@ -188,14 +188,15 @@ class TableView(object):
self.header_fmt_str = column_headers + "\n" + vert_divider + "\n" self.header_fmt_str = column_headers + "\n" + vert_divider + "\n"
self.row_fmt_str = "|".join( self.row_fmt_str = "|".join(
"{cv[" + str(n) + "]: ^" + str(self.column_width) + "}" "{{cv[{n}]: ^{width}}}".format(n=n, width=self.column_width)
for n in range(len(column_values)) for n in range(len(column_values))
) )
def __call__(self, model): def __call__(self, model):
res = self.header_fmt_str.format(ch=self.column_names) res = self.header_fmt_str.format(ch=self.column_names)
for raw_row in model[self.table_prop_name]: for raw_row in model[self.table_prop_name]:
row = [str(raw_row[prop_name]) for prop_name in self.column_values] row = [six.text_type(raw_row[prop_name])
for prop_name in self.column_values]
# double format is in case we have roundoff error # double format is in case we have roundoff error
res += '{0: <72}\n'.format(self.row_fmt_str.format(cv=row)) res += '{0: <72}\n'.format(self.row_fmt_str.format(cv=row))

View File

@ -17,6 +17,8 @@
This package defines several text views with headers This package defines several text views with headers
""" """
import six
class HeaderView(object): class HeaderView(object):
"""A Text View With a Header """A Text View With a Header
@ -31,7 +33,7 @@ class HeaderView(object):
self.header = header self.header = header
def __call__(self, model): def __call__(self, model):
return str(self.header) + "\n" + str(model) return six.text_type(self.header) + "\n" + six.text_type(model)
class TitledView(HeaderView): class TitledView(HeaderView):

View File

@ -75,7 +75,7 @@ class KeyValueView(object):
elif ET.iselement(rootmodel): elif ET.iselement(rootmodel):
res.append(rootmodel) res.append(rootmodel)
else: else:
res.text = str(rootmodel) res.text = six.text_type(rootmodel)
return res return res