Skip to content

Commit

Permalink
Deprecate nodes.reprunicode and nodes.ensure_str().
Browse files Browse the repository at this point in the history
Drop uses of the deprecated constructs (not required with Python 3).

git-svn-id: svn://svn.code.sf.net/p/docutils/code/trunk@8931 929543f6-e4f2-0310-98a6-ba3bd3dd1d04
  • Loading branch information
milde committed Jan 4, 2022
1 parent 718a909 commit c8471ce
Show file tree
Hide file tree
Showing 8 changed files with 46 additions and 69 deletions.
9 changes: 7 additions & 2 deletions docutils/HISTORY.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ Changes Since 0.18.1
====================

* General

- Dropped support for Python 2.7, 3.5, and 3.6.

- Dropped support for Python 2.7, 3.5, and 3.6. and removed compatibility
hacks from code and tests.

* docutils/parsers/__init__.py

Expand All @@ -42,6 +43,10 @@ Changes Since 0.18.1

- Don't use mutable default values for function arguments. Fixes bug #430.

* docutils/utils/__init__.py

- decode_path() returns `str` instance instead of `nodes.reprunicode`.

* test/DocutilsTestSupport.py

- exception_data() returns None if no exception was raised.
Expand Down
3 changes: 3 additions & 0 deletions docutils/RELEASE-NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ Drop support for `old-format configuration files`_ in Docutils 1.2.

* Remove the "rawsource" argument from nodes.Text.__init__()
(deprecated and ignored since Docutils 0.18) in Docutils 1.3.

* Remove the compatibility hacks `nodes.reprunicode` and `nodes.ensure_str()`
in Docutils 1.2. They are not required with Python 3.x.

* Move math format conversion from docutils/utils/math (called from
docutils/writers/_html_base.py) to a transform__.
Expand Down
74 changes: 30 additions & 44 deletions docutils/docutils/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,6 @@ def __bool__(self):
"""
return True

if sys.version_info < (3, 0):
__nonzero__ = __bool__

if sys.version_info < (3, 0):
# on 2.x, str(node) will be a byte string with Unicode
# characters > 255 escaped; on 3.x this is no longer necessary
def __str__(self):
return unicode(self).encode('raw_unicode_escape')

def asdom(self, dom=None):
"""Return a DOM **fragment** representation of this Node."""
if dom is None:
Expand Down Expand Up @@ -346,24 +337,25 @@ def previous_sibling(self):
except (AttributeError, IndexError):
return None

if sys.version_info < (3, 0):
class reprunicode(unicode):
"""
A unicode sub-class that removes the initial u from unicode's repr.
"""

def __repr__(self):
return unicode.__repr__(self)[1:]
else:
reprunicode = unicode
class reprunicode(str):
"""
Deprecated backwards compatibility stub. Use the standard `str` instead.
"""
def __init__(self, s):
warnings.warn('nodes.reprunicode() is not required with Python 3'
' and will be removed in Docutils 1.2.',
DeprecationWarning, stacklevel=2)
super().__init__()


def ensure_str(s):
"""
Failsafe conversion of `unicode` to `str`.
Deprecated backwards compatibility stub returning `s`.
"""
if sys.version_info < (3, 0) and isinstance(s, unicode):
return s.encode('ascii', 'backslashreplace')
warnings.warn('nodes.ensure_str() is not required with Python 3'
' and will be removed in Docutils 1.2.',
DeprecationWarning, stacklevel=2)
return s

# definition moved here from `utils` to avoid circular import dependency
Expand All @@ -381,28 +373,25 @@ def unescape(text, restore_backslashes=False, respect_whitespace=False):
return text


class Text(Node, reprunicode):
class Text(Node, str):

"""
Instances are terminal nodes (leaves) containing text only; no child
nodes or attributes. Initialize by passing a string to the constructor.
Access the text itself with the `astext` method.
Access the raw (null-escaped) text with ``str(<instance>)``
and unescaped text with the `astext` method.
"""

tagname = '#text'

children = ()
"""Text nodes have no children, and cannot have children."""

if sys.version_info > (3, 0):
def __new__(cls, data, rawsource=None):
"""Assert that `data` is not an array of bytes."""
if isinstance(data, bytes):
raise TypeError('expecting str data, not bytes')
return reprunicode.__new__(cls, data)
else:
def __new__(cls, data, rawsource=None):
return reprunicode.__new__(cls, data)
def __new__(cls, data, rawsource=None):
"""Assert that `data` is not an array of bytes."""
if isinstance(data, bytes):
raise TypeError('expecting str data, not bytes')
return str.__new__(cls, data)

def __init__(self, data, rawsource=None):
"""The `rawsource` argument is ignored and deprecated."""
Expand All @@ -415,7 +404,7 @@ def shortrepr(self, maxlen=18):
data = self
if len(data) > maxlen:
data = data[:maxlen-4] + ' ...'
return '<%s: %r>' % (self.tagname, reprunicode(data))
return '<%s: %r>' % (self.tagname, str(data))

def __repr__(self):
return self.shortrepr(maxlen=68)
Expand All @@ -424,7 +413,7 @@ def _dom_node(self, domroot):
return domroot.createTextNode(unicode(self))

def astext(self):
return reprunicode(unescape(self))
return str(unescape(self))

# Note about __unicode__: The implementation of __unicode__ here,
# and the one raising NotImplemented in the superclass Node had
Expand All @@ -436,7 +425,7 @@ def astext(self):
# an infinite loop

def copy(self):
return self.__class__(reprunicode(self))
return self.__class__(str(self))

def deepcopy(self):
return self.copy()
Expand All @@ -445,7 +434,7 @@ def pformat(self, indent=' ', level=0):
try:
if self.document.settings.detailed:
lines = ['%s%s' % (indent*level, '<#text>')
] + [indent*(level+1) + repr(reprunicode(line))
] + [indent*(level+1) + repr(line)
for line in self.splitlines(True)]
return '\n'.join(lines) + '\n'
except AttributeError:
Expand All @@ -461,10 +450,10 @@ def pformat(self, indent=' ', level=0):
# taken care of by UserString.

def rstrip(self, chars=None):
return self.__class__(reprunicode.rstrip(self, chars))
return self.__class__(str.rstrip(self, chars))

def lstrip(self, chars=None):
return self.__class__(reprunicode.lstrip(self, chars))
return self.__class__(str.lstrip(self, chars))

class Element(Node):

Expand Down Expand Up @@ -589,28 +578,25 @@ def __repr__(self):
break
if self['names']:
return '<%s "%s": %s>' % (self.__class__.__name__,
'; '.join([ensure_str(n) for n in self['names']]), data)
'; '.join(self['names']), data)
else:
return '<%s: %s>' % (self.__class__.__name__, data)

def shortrepr(self):
if self['names']:
return '<%s "%s"...>' % (self.__class__.__name__,
'; '.join([ensure_str(n) for n in self['names']]))
'; '.join(self['names']))
else:
return '<%s...>' % self.tagname

def __unicode__(self):
def __str__(self):
if self.children:
return u'%s%s%s' % (self.starttag(),
''.join([unicode(c) for c in self.children]),
self.endtag())
else:
return self.emptytag()

if sys.version_info >= (3, 0):
__str__ = __unicode__

def starttag(self, quoteattr=None):
# the optional arg is used by the docutils_xml writer
if quoteattr is None:
Expand Down
1 change: 0 additions & 1 deletion docutils/docutils/parsers/rst/directives/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ def run(self):
path = os.path.join(self.standard_include_path, path[1:-1])
path = os.path.normpath(os.path.join(source_dir, path))
path = utils.relative_path(None, path)
path = nodes.reprunicode(path)
encoding = self.options.get(
'encoding', self.state.document.settings.input_encoding)
e_handler=self.state.document.settings.input_encoding_error_handler
Expand Down
6 changes: 3 additions & 3 deletions docutils/docutils/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ class NameValueError(DataError): pass

def decode_path(path):
"""
Ensure `path` is Unicode. Return `nodes.reprunicode` object.
Ensure `path` is Unicode. Return `str` instance.
Decode file/path string in a failsafe manner if not already done.
"""
Expand All @@ -348,15 +348,15 @@ def decode_path(path):
path = path.decode(sys.getfilesystemencoding(), 'strict')
except AttributeError: # default value None has no decode method
if not path:
return nodes.reprunicode('')
return ''
raise ValueError('`path` value must be a String or ``None``, not %r'
%path)
except UnicodeDecodeError:
try:
path = path.decode('utf-8', 'strict')
except UnicodeDecodeError:
path = path.decode('ascii', 'replace')
return nodes.reprunicode(path)
return path


def extract_name_value(line):
Expand Down
14 changes: 0 additions & 14 deletions docutils/test/test_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ def test_repr(self):
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')
self.assertEqual(repr(self.unicode_text), u"<#text: 'Möhren'>")

def test_str(self):
Expand Down Expand Up @@ -323,19 +322,6 @@ def test_unicode(self):

class MiscTests(unittest.TestCase):

def test_reprunicode(self):
# return `unicode` instance
self.assertTrue(isinstance(nodes.reprunicode('foo'), str))
self.assertEqual(nodes.reprunicode('foo'), u'foo')
self.assertEqual(nodes.reprunicode(u'Möhre'), u'Möhre')
# 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')
self.assertEqual(nodes.ensure_str(u'über'), r'über')

def test_node_class_names(self):
node_class_names = []
for x in dir(nodes):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
import __init__
from test_parsers import DocutilsTestSupport

from docutils.nodes import reprunicode


def suite():
s = DocutilsTestSupport.ParserTestSuite()
Expand Down Expand Up @@ -417,12 +415,12 @@ def suite():
<system_message level="3" line="1" source="test data" type="ERROR">
<paragraph>
Error in "image" directive:
invalid option value: (option: "align"; value: %s)
invalid option value: (option: "align"; value: 'ä')
"\xe4" unknown; choose from "top", "middle", "bottom", "left", "center", or "right".
<literal_block xml:space="preserve">
.. image:: picture.png
:align: \xe4
""" % repr(reprunicode(u'\xe4'))],
"""],
["""
.. image:: test.png
:target: Uppercase_
Expand Down
2 changes: 1 addition & 1 deletion docutils/test/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ def test_decode_path(self):
self.assertEqual(bytespath, u'späm')
self.assertEqual(unipath, u'späm')
self.assertEqual(defaultpath, u'')
self.assertTrue(isinstance(bytespath, nodes.reprunicode))
self.assertTrue(isinstance(bytespath, str))
self.assertTrue(isinstance(unipath, str))
self.assertTrue(isinstance(defaultpath, str))
self.assertRaises(ValueError, utils.decode_path, 13)
Expand Down

0 comments on commit c8471ce

Please sign in to comment.