From 3fbbb6ccfb836a55b11af8153be17fcc017a1a53 Mon Sep 17 00:00:00 2001 From: milde Date: Mon, 21 Jan 2013 15:19:18 +0000 Subject: [PATCH] Fix [ 3601607 ] node.__repr__() must return `str` instance. git-svn-id: http://svn.code.sf.net/p/docutils/code/trunk/docutils@7592 929543f6-e4f2-0310-98a6-ba3bd3dd1d04 --- docutils/nodes.py | 19 +++++++++++++++---- test/test_nodes.py | 49 +++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 60 insertions(+), 8 deletions(-) diff --git a/docutils/nodes.py b/docutils/nodes.py index f88de28ab..86d113624 100644 --- a/docutils/nodes.py +++ b/docutils/nodes.py @@ -299,15 +299,26 @@ class Node(object): if sys.version_info < (3,): class reprunicode(unicode): """ - A class that removes the initial u from unicode's repr. + A unicode sub-class that removes the initial u from unicode's repr. """ def __repr__(self): return unicode.__repr__(self)[1:] + + else: reprunicode = unicode +def ensure_str(s): + """ + Failsave conversion of `unicode` to `str`. + """ + if sys.version_info < (3,) and isinstance(s, unicode): + return s.encode('ascii', 'backslashreplace') + return s + + class Text(Node, reprunicode): """ @@ -341,7 +352,7 @@ class Text(Node, reprunicode): data = self if len(data) > maxlen: data = data[:maxlen-4] + ' ...' - return '<%s: %s>' % (self.tagname, repr(reprunicode(data))) + return '<%s: %r>' % (self.tagname, reprunicode(data)) def __repr__(self): return self.shortrepr(maxlen=68) @@ -478,14 +489,14 @@ class Element(Node): break if self['names']: return '<%s "%s": %s>' % (self.__class__.__name__, - '; '.join(self['names']), data) + '; '.join([ensure_str(n) for n in self['names']]), data) else: return '<%s: %s>' % (self.__class__.__name__, data) def shortrepr(self): if self['names']: return '<%s "%s"...>' % (self.__class__.__name__, - '; '.join(self['names'])) + '; '.join([ensure_str(n) for n in self['names']])) else: return '<%s...>' % self.tagname diff --git a/test/test_nodes.py b/test/test_nodes.py index ef423b4c4..6a785b516 100755 --- a/test/test_nodes.py +++ b/test/test_nodes.py @@ -33,6 +33,11 @@ class TextTests(unittest.TestCase): self.assertEqual(repr(self.text), r"<#text: 'Line 1.\nLine 2.'>") self.assertEqual(self.text.shortrepr(), r"<#text: 'Line 1.\nLine 2.'>") + self.assertEqual(nodes.reprunicode('foo'), u'foo') + if sys.version_info < (3,): + self.assertEqual(repr(self.unicode_text), r"<#text: 'M\xf6hren'>") + else: + self.assertEqual(repr(self.unicode_text), u"<#text: 'Möhren'>") def test_str(self): self.assertEqual(str(self.text), 'Line 1.\nLine 2.') @@ -42,10 +47,13 @@ class TextTests(unittest.TestCase): self.assertEqual(str(self.unicode_text), 'M\xf6hren') def test_astext(self): - self.assertEqual(self.text.astext(), 'Line 1.\nLine 2.') + self.assertTrue(isinstance(self.text.astext(), unicode)) + self.assertEqual(self.text.astext(), u'Line 1.\nLine 2.') + self.assertEqual(self.unicode_text.astext(), u'Möhren') def test_pformat(self): - self.assertEqual(self.text.pformat(), 'Line 1.\nLine 2.\n') + self.assertTrue(isinstance(self.text.pformat(), unicode)) + self.assertEqual(self.text.pformat(), u'Line 1.\nLine 2.\n') def test_asciirestriction(self): if sys.version_info < (3,): @@ -88,11 +96,25 @@ class ElementTests(unittest.TestCase): dom = element.asdom() self.assertEqual(dom.toxml(), u'') dom.unlink() + element['names'] = ['nobody', u'имя', u'näs'] + if sys.version_info < (3,): + self.assertEqual(repr(element), + '') + else: + self.assertEqual(repr(element), u'') + self.assertTrue(isinstance(repr(element), str)) def test_withtext(self): element = nodes.Element('text\nmore', nodes.Text('text\nmore')) + uelement = nodes.Element(u'grün', nodes.Text(u'grün')) self.assertEqual(repr(element), r">") + if sys.version_info < (3,): + self.assertEqual(repr(uelement), ">") + else: + self.assertEqual(repr(uelement), u">") + self.assertTrue(isinstance(repr(uelement),str)) self.assertEqual(str(element), 'text\nmore') + self.assertEqual(str(uelement), 'gr\xfcn') dom = element.asdom() self.assertEqual(dom.toxml(), 'text\nmore') dom.unlink() @@ -194,6 +216,25 @@ class ElementTests(unittest.TestCase): class MiscTests(unittest.TestCase): + def test_reprunicode(self): + # return `unicode` instance + self.assertTrue(isinstance(nodes.reprunicode('foo'), unicode)) + self.assertEqual(nodes.reprunicode('foo'), u'foo') + self.assertEqual(nodes.reprunicode(u'Möhre'), u'Möhre') + if sys.version_info < (3,): # strip leading "u" from representation + self.assertEqual(repr(nodes.reprunicode(u'Möhre')), + repr(u'Möhre')[1:]) + else: # no change to `unicode` under Python 3k + self.assertEqual(repr(nodes.reprunicode(u'Möhre')), repr(u'Möhre')) + + def test_ensure_str(self): + self.assertTrue(isinstance(nodes.ensure_str(u'über'), str)) + self.assertEqual(nodes.ensure_str('over'), 'over') + if sys.version_info < (3,): # strip leading "u" from representation + self.assertEqual(nodes.ensure_str(u'über'), r'\xfcber') + else: + self.assertEqual(nodes.ensure_str(u'über'), r'über') + def test_node_class_names(self): node_class_names = [] for x in dir(nodes): @@ -484,7 +525,7 @@ class TreeCopyVisitorTests(unittest.TestCase): def compare_trees(self, one, two): self.assertEqual(one.__class__, two.__class__) - self.assertNotEquals(id(one), id(two)) + self.assertNotEqual(id(one), id(two)) self.assertEqual(len(one.children), len(two.children)) for i in range(len(one.children)): self.compare_trees(one.children[i], two.children[i]) @@ -534,7 +575,7 @@ class MiscFunctionTests(unittest.TestCase): element = nodes.Element() document.set_id(element) self.assertEqual(element['ids'], ['prefixauto1']) - + if __name__ == '__main__': unittest.main() -- 2.11.4.GIT