|
From: <mi...@us...> - 2022-01-07 20:12:03
|
Revision: 8937
http://sourceforge.net/p/docutils/code/8937
Author: milde
Date: 2022-01-07 20:12:00 +0000 (Fri, 07 Jan 2022)
Log Message:
-----------
Drop use of utils.error_reporting module and deprecate it.
SafeString not required in Python 3
ErrorString obsoleted by new function io.error_string().
ErrorOutput and locale_encoding moved to docutils.io.
Modified Paths:
--------------
trunk/docutils/HISTORY.txt
trunk/docutils/docutils/core.py
trunk/docutils/docutils/frontend.py
trunk/docutils/docutils/io.py
trunk/docutils/docutils/parsers/rst/directives/misc.py
trunk/docutils/docutils/parsers/rst/directives/tables.py
trunk/docutils/docutils/statemachine.py
trunk/docutils/docutils/utils/__init__.py
trunk/docutils/docutils/utils/error_reporting.py
trunk/docutils/docutils/writers/_html_base.py
trunk/docutils/docutils/writers/latex2e/__init__.py
trunk/docutils/test/test_error_reporting.py
trunk/docutils/test/test_io.py
trunk/docutils/test/test_writers/test_latex2e.py
trunk/docutils/tools/buildhtml.py
trunk/docutils/tools/docutils-cli.py
Modified: trunk/docutils/HISTORY.txt
===================================================================
--- trunk/docutils/HISTORY.txt 2022-01-07 20:11:44 UTC (rev 8936)
+++ trunk/docutils/HISTORY.txt 2022-01-07 20:12:00 UTC (rev 8937)
@@ -46,7 +46,13 @@
* docutils/utils/__init__.py
- decode_path() returns `str` instance instead of `nodes.reprunicode`.
+ - new function error_string() obsoletes utils.error_reporting.ErrorString.
+ - class ErrorOutput moved here from docutils/utils/error_reporting.py
+* docutils/utils/error_reporting.py
+
+ - Add deprecation warning.
+
* test/DocutilsTestSupport.py
- exception_data() returns None if no exception was raised.
Modified: trunk/docutils/docutils/core.py
===================================================================
--- trunk/docutils/docutils/core.py 2022-01-07 20:11:44 UTC (rev 8936)
+++ trunk/docutils/docutils/core.py 2022-01-07 20:12:00 UTC (rev 8937)
@@ -20,7 +20,6 @@
from docutils import frontend, io, utils, readers, writers
from docutils.frontend import OptionParser
from docutils.transforms import Transformer
-from docutils.utils.error_reporting import ErrorOutput, ErrorString
import docutils.readers.doctree
class Publisher(object):
@@ -75,7 +74,7 @@
"""An object containing Docutils settings as instance attributes.
Set by `self.process_command_line()` or `self.get_settings()`."""
- self._stderr = ErrorOutput()
+ self._stderr = io.ErrorOutput()
def set_reader(self, reader_name, parser, parser_name):
"""Set `self.reader` by name."""
@@ -264,13 +263,13 @@
self.report_UnicodeError(error)
elif isinstance(error, io.InputError):
self._stderr.write(u'Unable to open source file for reading:\n'
- u' %s\n' % ErrorString(error))
+ u' %s\n' % io.error_string(error))
elif isinstance(error, io.OutputError):
self._stderr.write(
u'Unable to open destination file for writing:\n'
- u' %s\n' % ErrorString(error))
+ u' %s\n' % io.error_string(error))
else:
- print(u'%s' % ErrorString(error), file=self._stderr)
+ print(u'%s' % io.error_string(error), file=self._stderr)
print(("""\
Exiting due to error. Use "--traceback" to diagnose.
Please report errors to <doc...@li...>.
@@ -309,7 +308,7 @@
'Include "--traceback" output, Docutils version (%s),\n'
'Python version (%s), your OS type & version, and the\n'
'command line used.\n'
- % (ErrorString(error),
+ % (io.error_string(error),
self.settings.output_encoding,
data.encode('ascii', 'xmlcharrefreplace'),
data.encode('ascii', 'backslashreplace'),
Modified: trunk/docutils/docutils/frontend.py
===================================================================
--- trunk/docutils/docutils/frontend.py 2022-01-07 20:11:44 UTC (rev 8936)
+++ trunk/docutils/docutils/frontend.py 2022-01-07 20:12:00 UTC (rev 8937)
@@ -39,10 +39,7 @@
import warnings
import docutils
-import docutils.utils
-import docutils.nodes
-from docutils.utils.error_reporting import (locale_encoding, SafeString,
- ErrorOutput, ErrorString)
+from docutils import io
def store_multiple(option, opt, value, parser, *args, **kwargs):
@@ -63,8 +60,8 @@
"""
try:
new_settings = parser.get_config_file_settings(value)
- except ValueError as error:
- parser.error(error)
+ except ValueError as err:
+ parser.error(err)
parser.values.update(new_settings, parser)
def validate_encoding(setting, value, option_parser,
@@ -349,10 +346,10 @@
value = getattr(values, setting)
try:
new_value = self.validator(setting, value, parser)
- except Exception as error:
+ except Exception as err:
raise optparse.OptionValueError(
'Error in option "%s":\n %s'
- % (opt, ErrorString(error)))
+ % (opt, io.error_string(err)))
setattr(values, setting, new_value)
if self.overrides:
setattr(values, self.overrides, None)
@@ -607,8 +604,8 @@
if read_config_files and not self.defaults['_disable_config']:
try:
config_settings = self.get_standard_config_settings()
- except ValueError as error:
- self.error(SafeString(error))
+ except ValueError as err:
+ self.error(err)
self.set_defaults_from_dict(config_settings.__dict__)
def populate_from_components(self, components):
@@ -766,7 +763,7 @@
self._files = []
"""List of paths of configuration files read."""
- self._stderr = ErrorOutput()
+ self._stderr = io.ErrorOutput()
"""Wrapper around sys.stderr catching en-/decoding errors"""
def read(self, filenames, option_parser):
@@ -825,12 +822,12 @@
new_value = option.validator(
setting, value, option_parser,
config_parser=self, config_section=section)
- except Exception as error:
+ except Exception as err:
raise ValueError(
'Error in config file "%s", section "[%s]":\n'
' %s\n'
' %s = %s'
- % (filename, section, ErrorString(error),
+ % (filename, section, io.error_string(err),
setting, value))
self.set(section, setting, new_value)
if option.overrides:
Modified: trunk/docutils/docutils/io.py
===================================================================
--- trunk/docutils/docutils/io.py 2022-01-07 20:11:44 UTC (rev 8936)
+++ trunk/docutils/docutils/io.py 2022-01-07 20:12:00 UTC (rev 8937)
@@ -10,6 +10,10 @@
__docformat__ = 'reStructuredText'
import codecs
+try:
+ import locale # module missing in Jython
+except ImportError:
+ pass
import os
import re
import sys
@@ -16,12 +20,32 @@
import warnings
from docutils import TransformSpec
-from docutils.utils.error_reporting import locale_encoding, ErrorString, ErrorOutput
+# Guess the locale's encoding.
+# If no valid guess can be made, locale_encoding is set to `None`:
+try:
+ locale_encoding = locale.getlocale()[1] or locale.getdefaultlocale()[1]
+except ValueError as error: # OS X may set UTF-8 without language code
+ # See http://bugs.python.org/issue18378 fixed in 3.8
+ # and https://sourceforge.net/p/docutils/bugs/298/.
+ # Drop the special case after requiring Python >= 3.8
+ if "unknown locale: UTF-8" in error.args:
+ locale_encoding = "UTF-8"
+ else:
+ locale_encoding = None
+except: # any other problems determining the locale -> use None
+ locale_encoding = None
+try:
+ codecs.lookup(locale_encoding or '')
+except LookupError:
+ locale_encoding = None
+
+
class InputError(IOError): pass
class OutputError(IOError): pass
+
def check_encoding(stream, encoding):
"""Test, whether the encoding of `stream` matches `encoding`.
@@ -37,7 +61,12 @@
except (LookupError, AttributeError, TypeError):
return None
+def error_string(err):
+ """Return string representation of Exception `err`.
+ """
+ return f'{err.__class__.__name__}: {err}'
+
class Input(TransformSpec):
"""
@@ -120,7 +149,7 @@
raise UnicodeError(
'Unable to decode input data. Tried the following encodings: '
'%s.\n(%s)' % (', '.join([repr(enc) for enc in encodings]),
- ErrorString(error)))
+ error_string(error)))
coding_slug = re.compile(br"coding[:=]\s*([-\w.]+)")
"""Encoding declaration pattern."""
@@ -197,6 +226,86 @@
return data.encode(self.encoding, self.error_handler)
+class ErrorOutput(object):
+ """
+ Wrapper class for file-like error streams with
+ failsafe de- and encoding of `str`, `bytes`, `unicode` and
+ `Exception` instances.
+ """
+
+ def __init__(self, destination=None, encoding=None,
+ encoding_errors='backslashreplace',
+ decoding_errors='replace'):
+ """
+ :Parameters:
+ - `destination`: a file-like object,
+ a string (path to a file),
+ `None` (write to `sys.stderr`, default), or
+ evaluating to `False` (write() requests are ignored).
+ - `encoding`: `destination` text encoding. Guessed if None.
+ - `encoding_errors`: how to treat encoding errors.
+ """
+ if destination is None:
+ destination = sys.stderr
+ elif not destination:
+ destination = False
+ # if `destination` is a file name, open it
+ elif isinstance(destination, str):
+ destination = open(destination, 'w')
+
+ self.destination = destination
+ """Where warning output is sent."""
+
+ self.encoding = (encoding or getattr(destination, 'encoding', None) or
+ locale_encoding or 'ascii')
+ """The output character encoding."""
+
+ self.encoding_errors = encoding_errors
+ """Encoding error handler."""
+
+ self.decoding_errors = decoding_errors
+ """Decoding error handler."""
+
+ def write(self, data):
+ """
+ Write `data` to self.destination. Ignore, if self.destination is False.
+
+ `data` can be a `bytes`, `str`, or `Exception` instance.
+ """
+ if not self.destination:
+ return
+ if isinstance(data, Exception):
+ data = str(data)
+ try:
+ self.destination.write(data)
+ except UnicodeEncodeError:
+ self.destination.write(data.encode(self.encoding,
+ self.encoding_errors))
+ except TypeError:
+ if isinstance(data, str): # destination may expect bytes
+ self.destination.write(data.encode(self.encoding,
+ self.encoding_errors))
+ elif self.destination in (sys.stderr, sys.stdout):
+ self.destination.buffer.write(data) # write bytes to raw stream
+ else:
+ self.destination.write(str(data, self.encoding,
+ self.decoding_errors))
+
+ def close(self):
+ """
+ Close the error-output stream.
+
+ Ignored if the destination is` sys.stderr` or `sys.stdout` or has no
+ close() method.
+ """
+ if self.destination in (sys.stdout, sys.stderr):
+ return
+ try:
+ self.destination.close()
+ except AttributeError:
+ pass
+
+
class FileInput(Input):
"""
@@ -362,12 +471,12 @@
"""
if not self.opened:
self.open()
- if ('b' not in self.mode
+ if ('b' not in self.mode
and check_encoding(self.destination, self.encoding) is False):
data = self.encode(data)
if os.linesep != '\n':
# fix endings
- data = data.replace(b'\n', bytes(os.linesep, 'ascii'))
+ data = data.replace(b'\n', bytes(os.linesep, 'ascii'))
try:
self.destination.write(data)
except TypeError as err:
@@ -386,7 +495,7 @@
except (UnicodeError, LookupError) as err:
raise UnicodeError(
'Unable to encode output data. output-encoding is: '
- '%s.\n(%s)' % (self.encoding, ErrorString(err)))
+ '%s.\n(%s)' % (self.encoding, error_string(err)))
finally:
if self.autoclose:
self.close()
Modified: trunk/docutils/docutils/parsers/rst/directives/misc.py
===================================================================
--- trunk/docutils/docutils/parsers/rst/directives/misc.py 2022-01-07 20:11:44 UTC (rev 8936)
+++ trunk/docutils/docutils/parsers/rst/directives/misc.py 2022-01-07 20:12:00 UTC (rev 8937)
@@ -11,8 +11,6 @@
import re
import time
from docutils import io, nodes, statemachine, utils
-from docutils.utils.error_reporting import SafeString, ErrorString
-from docutils.utils.error_reporting import locale_encoding
from docutils.parsers.rst import Directive, convert_directive_function
from docutils.parsers.rst import directives, roles, states
from docutils.parsers.rst.directives.body import CodeBlock, NumberLines
@@ -81,10 +79,10 @@
raise self.severe(u'Problems with "%s" directive path:\n'
'Cannot encode input file path "%s" '
'(wrong locale?).' %
- (self.name, SafeString(path)))
+ (self.name, path))
except IOError as error:
raise self.severe(u'Problems with "%s" directive path:\n%s.' %
- (self.name, ErrorString(error)))
+ (self.name, io.error_string(error)))
# Get to-be-included content
startline = self.options.get('start-line', None)
@@ -97,7 +95,7 @@
rawtext = include_file.read()
except UnicodeError as error:
raise self.severe(u'Problem with "%s" directive:\n%s' %
- (self.name, ErrorString(error)))
+ (self.name, io.error_string(error)))
# start-after/end-before: no restrictions on newlines in match-text,
# and no restrictions on matching inside lines vs. line boundaries
after_text = self.options.get('start-after', None)
@@ -255,12 +253,12 @@
self.state.document.settings.record_dependencies.add(path)
except IOError as error:
raise self.severe(u'Problems with "%s" directive path:\n%s.'
- % (self.name, ErrorString(error)))
+ % (self.name, io.error_string(error)))
try:
text = raw_file.read()
except UnicodeError as error:
raise self.severe(u'Problem with "%s" directive:\n%s'
- % (self.name, ErrorString(error)))
+ % (self.name, io.error_string(error)))
attributes['source'] = path
elif 'url' in self.options:
source = self.options['url']
@@ -273,7 +271,7 @@
raw_text = urlopen(source).read()
except (URLError, IOError, OSError) as error:
raise self.severe(u'Problems with "%s" directive URL "%s":\n%s.'
- % (self.name, self.options['url'], ErrorString(error)))
+ % (self.name, self.options['url'], io.error_string(error)))
raw_file = io.StringInput(source=raw_text, source_path=source,
encoding=encoding,
error_handler=e_handler)
@@ -281,7 +279,7 @@
text = raw_file.read()
except UnicodeError as error:
raise self.severe(u'Problem with "%s" directive:\n%s'
- % (self.name, ErrorString(error)))
+ % (self.name, io.error_string(error)))
attributes['source'] = source
else:
# This will always fail because there is no content.
@@ -364,7 +362,7 @@
decoded = directives.unicode_code(code)
except ValueError as error:
raise self.error(u'Invalid character code: %s\n%s'
- % (code, ErrorString(error)))
+ % (code, io.error_string(error)))
element += nodes.Text(decoded)
return element.children
@@ -460,7 +458,7 @@
except ValueError as detail:
error = self.state_machine.reporter.error(
u'Invalid argument for "%s" directive:\n%s.'
- % (self.name, SafeString(detail)), nodes.literal_block(
+ % (self.name, detail), nodes.literal_block(
self.block_text, self.block_text), line=self.lineno)
return messages + [error]
role = roles.CustomRole(new_role_name, base_role, options, content)
Modified: trunk/docutils/docutils/parsers/rst/directives/tables.py
===================================================================
--- trunk/docutils/docutils/parsers/rst/directives/tables.py 2022-01-07 20:11:44 UTC (rev 8936)
+++ trunk/docutils/docutils/parsers/rst/directives/tables.py 2022-01-07 20:12:00 UTC (rev 8937)
@@ -15,7 +15,6 @@
import warnings
from docutils import io, nodes, statemachine, utils
-from docutils.utils.error_reporting import SafeString
from docutils.utils import SystemMessagePropagation
from docutils.parsers.rst import Directive
from docutils.parsers.rst import directives
@@ -324,7 +323,7 @@
except IOError as error:
severe = self.state_machine.reporter.severe(
u'Problems with "%s" directive path:\n%s.'
- % (self.name, SafeString(error)),
+ % (self.name, error),
nodes.literal_block(self.block_text, self.block_text),
line=self.lineno)
raise SystemMessagePropagation(severe)
@@ -342,7 +341,7 @@
except (URLError, IOError, OSError, ValueError) as error:
severe = self.state_machine.reporter.severe(
'Problems with "%s" directive URL "%s":\n%s.'
- % (self.name, self.options['url'], SafeString(error)),
+ % (self.name, self.options['url'], error),
nodes.literal_block(self.block_text, self.block_text),
line=self.lineno)
raise SystemMessagePropagation(severe)
Modified: trunk/docutils/docutils/statemachine.py
===================================================================
--- trunk/docutils/docutils/statemachine.py 2022-01-07 20:11:44 UTC (rev 8936)
+++ trunk/docutils/docutils/statemachine.py 2022-01-07 20:12:00 UTC (rev 8937)
@@ -110,8 +110,7 @@
import re
from unicodedata import east_asian_width
-from docutils import utils
-from docutils.utils.error_reporting import ErrorOutput
+from docutils import io, utils
class StateMachine(object):
@@ -171,7 +170,7 @@
line changes. Observers are called with one argument, ``self``.
Cleared at the end of `run()`."""
- self._stderr = ErrorOutput()
+ self._stderr = io.ErrorOutput()
"""Wrapper around sys.stderr catching en-/decoding errors"""
Modified: trunk/docutils/docutils/utils/__init__.py
===================================================================
--- trunk/docutils/docutils/utils/__init__.py 2022-01-07 20:11:44 UTC (rev 8936)
+++ trunk/docutils/docutils/utils/__init__.py 2022-01-07 20:12:00 UTC (rev 8937)
@@ -16,10 +16,8 @@
import warnings
import unicodedata
from docutils import ApplicationError, DataError, __version_info__
-from docutils import nodes
+from docutils import io, nodes
from docutils.nodes import unescape
-import docutils.io
-from docutils.utils.error_reporting import ErrorOutput, SafeString
class SystemMessage(ApplicationError):
@@ -108,8 +106,8 @@
"""The level at or above which `SystemMessage` exceptions
will be raised, halting execution."""
- if not isinstance(stream, ErrorOutput):
- stream = ErrorOutput(stream, encoding, error_handler)
+ if not isinstance(stream, io.ErrorOutput):
+ stream = io.ErrorOutput(stream, encoding, error_handler)
self.stream = stream
"""Where warning output is sent."""
@@ -131,8 +129,8 @@
DeprecationWarning, stacklevel=2)
self.report_level = report_level
self.halt_level = halt_level
- if not isinstance(stream, ErrorOutput):
- stream = ErrorOutput(stream, self.encoding, self.error_handler)
+ if not isinstance(stream, io.ErrorOutput):
+ stream = io.ErrorOutput(stream, self.encoding, self.error_handler)
self.stream = stream
self.debug_flag = debug
@@ -158,7 +156,7 @@
"""
# `message` can be a `str` or `Exception` instance.
if isinstance(message, Exception):
- message = SafeString(message)
+ message = str(message)
attributes = kwargs.copy()
if 'base_node' in kwargs:
@@ -717,8 +715,8 @@
of = None
else:
of = output_file
- self.file = docutils.io.FileOutput(destination_path=of,
- encoding='utf8', autoclose=False)
+ self.file = io.FileOutput(destination_path=of,
+ encoding='utf8', autoclose=False)
else:
self.file = None
Modified: trunk/docutils/docutils/utils/error_reporting.py
===================================================================
--- trunk/docutils/docutils/utils/error_reporting.py 2022-01-07 20:11:44 UTC (rev 8936)
+++ trunk/docutils/docutils/utils/error_reporting.py 2022-01-07 20:12:00 UTC (rev 8937)
@@ -11,15 +11,22 @@
# .. _2-Clause BSD license: https://opensource.org/licenses/BSD-2-Clause
"""
-Provisional module to handle Exceptions across Python versions.
+Deprecated module to handle Exceptions across Python versions.
-This module will be deprecated with the end of support for Python 2.7
-and be removed in Docutils 1.2.
+.. warning::
+ This module is deprecated with the end of support for Python 2.7
+ and will be removed in Docutils 1.2 or later.
+
+ Replacements:
+ | SafeString -> str
+ | ErrorString -> docutils.io.error_string()
+ | ErrorOutput -> docutils.io.ErrorOutput
+ | locale_encoding -> docutils.io.locale_encoding
Error reporting should be safe from encoding/decoding errors.
However, implicit conversions of strings and exceptions like
->>> u'%s world: %s' % ('H\xe4llo', Exception(u'H\xe4llo')
+>>> u'%s world: %s' % ('H\xe4llo', Exception(u'H\xe4llo'))
fail in some Python versions:
@@ -40,7 +47,13 @@
import codecs
import sys
+import warnings
+warnings.warn('The `docutils.utils.error_reporting` module is deprecated '
+ 'and will be removed in Docutils 1.2.\n'
+ 'Details with help("docutils.utils.error_reporting").',
+ DeprecationWarning, stacklevel=2)
+
# Guess the locale's encoding.
# If no valid guess can be made, locale_encoding is set to `None`:
try:
Modified: trunk/docutils/docutils/writers/_html_base.py
===================================================================
--- trunk/docutils/docutils/writers/_html_base.py 2022-01-07 20:11:44 UTC (rev 8936)
+++ trunk/docutils/docutils/writers/_html_base.py 2022-01-07 20:12:00 UTC (rev 8937)
@@ -28,7 +28,6 @@
from docutils import frontend, languages, nodes, utils, writers
from docutils.parsers.rst.directives import length_or_percentage_or_unitless
from docutils.parsers.rst.directives.images import PIL
-from docutils.utils.error_reporting import SafeString
from docutils.transforms import writer_aux
from docutils.utils.math import (unichar2tex, pick_math_environment,
math2html, latex2mathml, tex2mathml_extern)
@@ -395,8 +394,7 @@
encoding='utf-8').read()
self.settings.record_dependencies.add(path)
except IOError as err:
- msg = u"Cannot embed stylesheet '%r': %s." % (
- path, SafeString(err.strerror))
+ msg = f'Cannot embed stylesheet: {err}'
self.document.reporter.error(msg)
return '<--- %s --->\n' % msg
return self.embedded_stylesheet % content
Modified: trunk/docutils/docutils/writers/latex2e/__init__.py
===================================================================
--- trunk/docutils/docutils/writers/latex2e/__init__.py 2022-01-07 20:11:44 UTC (rev 8936)
+++ trunk/docutils/docutils/writers/latex2e/__init__.py 2022-01-07 20:12:00 UTC (rev 8937)
@@ -26,7 +26,6 @@
import docutils
from docutils import frontend, nodes, languages, writers, utils
-from docutils.utils.error_reporting import SafeString
from docutils.transforms import writer_aux
from docutils.utils.math import pick_math_environment, unichar2tex
@@ -1396,8 +1395,7 @@
encoding='utf-8').read()
self.settings.record_dependencies.add(path)
except IOError as err:
- msg = u'Cannot embed stylesheet %r:\n %s.' % (
- path, SafeString(err.strerror))
+ msg = f'Cannot embed stylesheet:\n {err}'
self.document.reporter.error(msg)
return '% ' + msg.replace('\n', '\n% ')
if is_package:
Modified: trunk/docutils/test/test_error_reporting.py
===================================================================
--- trunk/docutils/test/test_error_reporting.py 2022-01-07 20:11:44 UTC (rev 8936)
+++ trunk/docutils/test/test_error_reporting.py 2022-01-07 20:12:00 UTC (rev 8937)
@@ -24,10 +24,13 @@
unless the minimal required Python version has this problem fixed.
"""
+from io import StringIO, BytesIO
import os
import sys
import unittest
-from io import StringIO, BytesIO
+import warnings
+warnings.filterwarnings('ignore', category=DeprecationWarning,
+ message=r'.*utils\.error_reporting')
import DocutilsTestSupport # must be imported before docutils
from docutils import core, parsers, frontend, utils
@@ -34,7 +37,6 @@
from docutils.utils.error_reporting import SafeString, ErrorString, ErrorOutput
-
class SafeStringTests(unittest.TestCase):
# test data:
@@ -158,7 +160,6 @@
self.assertEqual(buf.getvalue(), u'b\ufffd u\xfc e\xfc b\xfc')
-
class SafeStringTests_locale(unittest.TestCase):
"""
Test docutils.SafeString with 'problematic' locales.
@@ -256,5 +257,6 @@
self.assertRaises(utils.SystemMessage,
self.parser.parse, source, self.document)
+
if __name__ == '__main__':
unittest.main()
Modified: trunk/docutils/test/test_io.py
===================================================================
--- trunk/docutils/test/test_io.py 2022-01-07 20:11:44 UTC (rev 8936)
+++ trunk/docutils/test/test_io.py 2022-01-07 20:12:00 UTC (rev 8937)
@@ -8,6 +8,7 @@
Test module for io.py.
"""
+from io import StringIO, BytesIO
import sys
import unittest
import warnings
@@ -14,9 +15,25 @@
import DocutilsTestSupport # must be imported before docutils
from docutils import io
-from docutils.utils.error_reporting import locale_encoding
-from test_error_reporting import BBuf, UBuf
+
+# Stub: Buffer with 'strict' auto-conversion of input to byte string:
+class BBuf(BytesIO):
+ def write(self, data):
+ if isinstance(data, str):
+ data.encode('ascii', 'strict')
+ super(BBuf, self).write(data)
+
+
+# Stub: Buffer expecting unicode string:
+class UBuf(StringIO):
+ def write(self, data):
+ # emulate Python 3 handling of stdout, stderr
+ if isinstance(data, bytes):
+ raise TypeError('must be unicode, not bytes')
+ super(UBuf, self).write(data)
+
+
class mock_stdout(UBuf):
encoding = 'utf8'
@@ -24,6 +41,7 @@
self.buffer = BBuf()
UBuf.__init__(self)
+
class HelperTests(unittest.TestCase):
def test_check_encoding_true(self):
@@ -47,8 +65,21 @@
self.assertEqual(io.check_encoding(mock_stdout, None), None)
# encoding is invalid
self.assertEqual(io.check_encoding(mock_stdout, 'UTF-9'), None)
+
+ def test_error_string(self):
+ us = '\xfc' # bytes(us) fails
+ bs = b'\xc3\xbc' # str(bs) returns repr(bs)
+ self.assertEqual('Exception: spam',
+ io.error_string(Exception('spam')))
+ self.assertEqual('IndexError: '+str(bs),
+ io.error_string(IndexError(bs)))
+ self.assertEqual('ImportError: %s' % us,
+ io.error_string(ImportError(us)))
+
+
+
class InputTests(unittest.TestCase):
def test_bom(self):
@@ -105,7 +136,7 @@
def test_heuristics_no_utf8(self):
# if no encoding is given and decoding with utf8 fails,
# use either the locale encoding (if specified) or latin-1:
- if locale_encoding != "utf8":
+ if io.locale_encoding != "utf8":
# in Py3k, the locale encoding is used without --input-encoding
# skipping the heuristic unless decoding fails.
return
@@ -190,5 +221,45 @@
self.assertRaises(ValueError, fo.write, self.udata)
+class ErrorOutputTests(unittest.TestCase):
+ def test_defaults(self):
+ e = io.ErrorOutput()
+ self.assertEqual(e.destination, sys.stderr)
+
+ def test_bbuf(self):
+ buf = BBuf() # buffer storing byte string
+ e = io.ErrorOutput(buf, encoding='ascii')
+ # write byte-string as-is
+ e.write(b'b\xfc')
+ self.assertEqual(buf.getvalue(), b'b\xfc')
+ # encode unicode data with backslashescape fallback replacement:
+ e.write(u' u\xfc')
+ self.assertEqual(buf.getvalue(), b'b\xfc u\\xfc')
+ # handle Exceptions with Unicode string args
+ # unicode(Exception(u'e\xfc')) # fails in Python < 2.6
+ e.write(AttributeError(u' e\xfc'))
+ self.assertEqual(buf.getvalue(), b'b\xfc u\\xfc e\\xfc')
+ # encode with `encoding` attribute
+ e.encoding = 'utf8'
+ e.write(u' u\xfc')
+ self.assertEqual(buf.getvalue(), b'b\xfc u\\xfc e\\xfc u\xc3\xbc')
+
+ def test_ubuf(self):
+ buf = UBuf() # buffer only accepting unicode string
+ # decode of binary strings
+ e = io.ErrorOutput(buf, encoding='ascii')
+ e.write(b'b\xfc')
+ self.assertEqual(buf.getvalue(), u'b\ufffd') # use REPLACEMENT CHARACTER
+ # write Unicode string and Exceptions with Unicode args
+ e.write(u' u\xfc')
+ self.assertEqual(buf.getvalue(), u'b\ufffd u\xfc')
+ e.write(AttributeError(u' e\xfc'))
+ self.assertEqual(buf.getvalue(), u'b\ufffd u\xfc e\xfc')
+ # decode with `encoding` attribute
+ e.encoding = 'latin1'
+ e.write(b' b\xfc')
+ self.assertEqual(buf.getvalue(), u'b\ufffd u\xfc e\xfc b\xfc')
+
+
if __name__ == '__main__':
unittest.main()
Modified: trunk/docutils/test/test_writers/test_latex2e.py
===================================================================
--- trunk/docutils/test/test_writers/test_latex2e.py 2022-01-07 20:11:44 UTC (rev 8936)
+++ trunk/docutils/test/test_writers/test_latex2e.py 2022-01-07 20:12:00 UTC (rev 8937)
@@ -1126,8 +1126,8 @@
# input
["""two stylesheets embedded in the header""",
head_template.substitute(dict(parts, stylesheet =
-r"""% Cannot embed stylesheet 'data/spam.sty':
-% No such file or directory.
+r"""% Cannot embed stylesheet:
+% [Errno 2] No such file or directory: 'data/spam.sty'
% embedded stylesheet: data/ham.tex
\newcommand{\ham}{wonderful ham}
Modified: trunk/docutils/tools/buildhtml.py
===================================================================
--- trunk/docutils/tools/buildhtml.py 2022-01-07 20:11:44 UTC (rev 8936)
+++ trunk/docutils/tools/buildhtml.py 2022-01-07 20:12:00 UTC (rev 8937)
@@ -30,8 +30,7 @@
import docutils
from docutils import ApplicationError
-from docutils import core, frontend, utils
-from docutils.utils.error_reporting import ErrorOutput, ErrorString
+from docutils import core, frontend, io, utils
from docutils.parsers import rst
from docutils.readers import standalone, pep
from docutils.writers import html4css1, html5_polyglot, pep_html
@@ -177,7 +176,7 @@
self.settings_spec = self.publishers[''].option_parser.parse_args(
values=frontend.Values()) # no defaults; just the cmdline opts
self.initial_settings = self.get_settings('')
-
+
if self.initial_settings.html_writer is not None:
warnings.warn('The configuration setting "html_writer" '
'will be removed in Docutils 1.2. '
@@ -209,7 +208,7 @@
def run(self, directory=None, recurse=1):
# if not self.initial_settings.silent:
- # errout = ErrorOutput(encoding=self.initial_settings.error_encoding)
+ # errout = io.ErrorOutput(encoding=self.initial_settings.error_encoding)
# errout.write('*** Using writer "%s"\n'
# % self.initial_settings.writer)
# sys.stderr.flush()
@@ -230,7 +229,7 @@
def visit(self, directory, names, subdirectories):
settings = self.get_settings('', directory)
- errout = ErrorOutput(encoding=settings.error_encoding)
+ errout = io.ErrorOutput(encoding=settings.error_encoding)
if settings.prune and (os.path.abspath(directory) in settings.prune):
errout.write('/// ...Skipping directory (pruned): %s\n' %
directory)
@@ -257,7 +256,7 @@
else:
publisher = self.initial_settings.writer
settings = self.get_settings(publisher, directory)
- errout = ErrorOutput(encoding=settings.error_encoding)
+ errout = io.ErrorOutput(encoding=settings.error_encoding)
pub_struct = self.publishers[publisher]
settings._source = os.path.normpath(os.path.join(directory, name))
settings._destination = settings._source[:-4]+'.html'
@@ -264,8 +263,8 @@
if not self.initial_settings.silent:
errout.write(' ::: Processing: %s\n' % name)
sys.stderr.flush()
- try:
- if not settings.dry_run:
+ if not settings.dry_run:
+ try:
core.publish_file(source_path=settings._source,
destination_path=settings._destination,
reader_name=pub_struct.reader_name,
@@ -272,9 +271,8 @@
parser_name='restructuredtext',
writer_name=pub_struct.writer_name,
settings=settings)
- except ApplicationError:
- error = sys.exc_info()[1] # get exception in Python 3.x
- errout.write(' %s\n' % ErrorString(error))
+ except ApplicationError as err:
+ errout.write(f' {type(err).__name__}: {err}\n')
if __name__ == "__main__":
Modified: trunk/docutils/tools/docutils-cli.py
===================================================================
--- trunk/docutils/tools/docutils-cli.py 2022-01-07 20:11:44 UTC (rev 8936)
+++ trunk/docutils/tools/docutils-cli.py 2022-01-07 20:12:00 UTC (rev 8937)
@@ -19,7 +19,7 @@
try:
import locale # module missing in Jython
locale.setlocale(locale.LC_ALL, '')
-except locale.Error:
+except:
pass
import argparse
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|