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.
"""
class StringWithAttrs(str):
import six
class StringWithAttrs(six.text_type):
"""A String that can have arbitrary attributes"""
pass

View File

@ -67,7 +67,7 @@ class ReportModel(col.MutableMapping):
self_cpy = copy.deepcopy(self)
for key in self_cpy:
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:
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.
"""
import six
from oslo_reports.views.text import header as header_views
@ -72,15 +74,15 @@ class BasicReport(object):
: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):
"""A Report Section
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
the generator and calls the view on the resulting model.
attempts to serialize the section by calling str() or unicode() on it, the
section runs the generator and calls the view on the resulting model.
.. seealso::

View File

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

View File

@ -19,6 +19,7 @@ import greenlet
import mock
from oslo_config import cfg
from oslotest import base
import six
from oslo_reports.generators import conf as os_cgen
from oslo_reports.generators import threading as os_tgen
@ -42,7 +43,7 @@ class TestOpenstackGenerators(base.BaseTestCase):
self.assertTrue(was_ok)
model.set_current_view_type('text')
self.assertIsNotNone(str(model))
self.assertIsNotNone(six.text_type(model))
def test_thread_generator_tb(self):
class FakeModel(object):
@ -72,7 +73,7 @@ class TestOpenstackGenerators(base.BaseTestCase):
self.assertTrue(was_ok)
model.set_current_view_type('text')
self.assertIsNotNone(str(model))
self.assertIsNotNone(six.text_type(model))
def test_config_model(self):
conf = cfg.ConfigOpts()
@ -102,7 +103,7 @@ class TestOpenstackGenerators(base.BaseTestCase):
'default: \n'
' crackers = triscuit\n'
' secrets = ***')
self.assertEqual(target_str, str(model))
self.assertEqual(target_str, six.text_type(model))
def test_package_report_generator(self):
class VersionObj(object):
@ -121,4 +122,4 @@ class TestOpenstackGenerators(base.BaseTestCase):
target_str = ('product = Sharp Cheddar\n'
'vendor = Cheese Shoppe\n'
'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
from oslotest import base
import six
from oslo_reports.models import base as base_model
from oslo_reports.models import with_default_views as mwdv
@ -34,15 +35,15 @@ class TestModelReportType(base.BaseTestCase):
model = mwdv_generator()
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')
self.assertEqual('{"int": 1, "string": "value"}', str(model))
self.assertEqual('{"int": 1, "string": "value"}', six.text_type(model))
model.set_current_view_type('xml')
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):
model = mwdv_generator()
@ -84,7 +85,7 @@ class TestModelReportType(base.BaseTestCase):
def test_report_of_type(self):
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())
@ -133,7 +134,7 @@ class TestGenericXMLView(base.BaseTestCase):
'<dt><a>1</a><b>2</b></dt>'
'<int>1</int>'
'<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):
self.model['lt'] = ['a', 'b']
@ -142,7 +143,7 @@ class TestGenericXMLView(base.BaseTestCase):
'<int>1</int>'
'<lt><item>a</item><item>b</item></lt>'
'<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):
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>'
'<int>1</int>'
'<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):
self.model['lt'] = [1, {'b': 2, 'c': 3}]
@ -162,7 +163,7 @@ class TestGenericXMLView(base.BaseTestCase):
'<lt><item>1</item>'
'<item><b>2</b><c>3</c></item></lt>'
'<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):
sm = mwdv_generator()
@ -177,7 +178,7 @@ class TestGenericXMLView(base.BaseTestCase):
'<model><int>1</int><string>value</string></model>'
'</submodel>'
'</model>')
self.assertEqual(target_str, str(self.model))
self.assertEqual(target_str, six.text_type(self.model))
def test_wrapper_name(self):
self.model.attached_view.wrapper_name = 'cheese'
@ -186,7 +187,7 @@ class TestGenericXMLView(base.BaseTestCase):
'<int>1</int>'
'<string>value</string>'
'</cheese>')
self.assertEqual(target_str, str(self.model))
self.assertEqual(target_str, six.text_type(self.model))
class TestGenericJSONViews(base.BaseTestCase):
@ -201,7 +202,8 @@ class TestGenericJSONViews(base.BaseTestCase):
self.model = base_model.ReportModel(data={'string': 'value', 'int': 1},
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):
self.model['dt'] = {'a': 1, 'b': 2}
@ -211,7 +213,7 @@ class TestGenericJSONViews(base.BaseTestCase):
'"int": 1, '
'"string": "value"'
'}')
self.assertEqual(target_str, str(self.model))
self.assertEqual(target_str, six.text_type(self.model))
def test_list_serialization(self):
self.model['lt'] = ['a', 'b']
@ -221,7 +223,7 @@ class TestGenericJSONViews(base.BaseTestCase):
'"lt": ["a", "b"], '
'"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):
self.model['dt'] = {'a': 1, 'b': [2, 3]}
@ -231,7 +233,7 @@ class TestGenericJSONViews(base.BaseTestCase):
'"int": 1, '
'"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):
self.model['lt'] = [1, {'b': 2, 'c': 3}]
@ -241,7 +243,7 @@ class TestGenericJSONViews(base.BaseTestCase):
'"lt": [1, {"b": 2, "c": 3}], '
'"string": "value"'
'}')
self.assertEqual(target_str, str(self.model))
self.assertEqual(target_str, six.text_type(self.model))
def test_submodel_serialization(self):
sm = mwdv_generator()
@ -254,7 +256,7 @@ class TestGenericJSONViews(base.BaseTestCase):
'"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):
@ -278,14 +280,15 @@ class TestGenericTextViews(base.BaseTestCase):
'string = value\n'
'int = 2\n'
'string = value')
self.assertEqual(target_str, str(self.model))
self.assertEqual(target_str, six.text_type(self.model))
def test_basic_kv_view(self):
attached_view = text_generic.BasicKeyValueView()
self.model = base_model.ReportModel(data={'string': 'value', 'int': 1},
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):
column_names = ['Column A', 'Column B']
@ -302,7 +305,7 @@ class TestGenericTextViews(base.BaseTestCase):
' 1 | 2 \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):
self.model['dt'] = {'a': 1, 'b': 2}
@ -312,7 +315,7 @@ class TestGenericTextViews(base.BaseTestCase):
' b = 2\n'
'int = 1\n'
'string = value')
self.assertEqual(target_str, str(self.model))
self.assertEqual(target_str, six.text_type(self.model))
def test_list_serialization(self):
self.model['lt'] = ['a', 'b']
@ -322,7 +325,7 @@ class TestGenericTextViews(base.BaseTestCase):
' a\n'
' b\n'
'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):
self.model['dt'] = {'a': 1, 'b': [2, 3]}
@ -335,7 +338,7 @@ class TestGenericTextViews(base.BaseTestCase):
'int = 1\n'
'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):
self.model['lt'] = [1, {'b': 2, 'c': 3}]
@ -347,7 +350,7 @@ class TestGenericTextViews(base.BaseTestCase):
' b = 2\n'
' c = 3\n'
'string = value')
self.assertEqual(target_str, str(self.model))
self.assertEqual(target_str, six.text_type(self.model))
def test_submodel_serialization(self):
sm = mwdv_generator()
@ -360,7 +363,7 @@ class TestGenericTextViews(base.BaseTestCase):
'submodel = \n'
' int = 1\n'
' string = value')
self.assertEqual(target_str, str(self.model))
self.assertEqual(target_str, six.text_type(self.model))
def test_custom_indent_string(self):
view = text_generic.KeyValueView(indent_str='~~')
@ -373,7 +376,7 @@ class TestGenericTextViews(base.BaseTestCase):
'~~a\n'
'~~b\n'
'string = value')
self.assertEqual(target_str, str(self.model))
self.assertEqual(target_str, six.text_type(self.model))
def get_open_mocks(rv):
@ -397,13 +400,15 @@ class TestJinjaView(base.BaseTestCase):
def test_load_from_file(self):
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')
def test_direct_pass(self):
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):
class TmpJinjaView(jv.JinjaView):
@ -411,7 +416,8 @@ class TestJinjaView(base.BaseTestCase):
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):
view_orig = jv.JinjaView(text=self.TEMPL_STR)

View File

@ -35,7 +35,7 @@ class MultiView(object):
"""
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)
@ -132,7 +132,7 @@ class KeyValueView(object):
for val in sorted(root, key=str):
res.extend(serialize(val, None, indent + 1))
else:
str_root = str(root)
str_root = six.text_type(root)
if '\n' in str_root:
# we are in a submodel
if rootkey is not None:
@ -175,7 +175,7 @@ class TableView(object):
self.column_width = (72 - len(column_names) + 1) // len(column_names)
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))
)
@ -188,14 +188,15 @@ class TableView(object):
self.header_fmt_str = column_headers + "\n" + vert_divider + "\n"
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))
)
def __call__(self, model):
res = self.header_fmt_str.format(ch=self.column_names)
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
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
"""
import six
class HeaderView(object):
"""A Text View With a Header
@ -31,7 +33,7 @@ class HeaderView(object):
self.header = header
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):

View File

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