I/O uses default encoding argument

Adam Turner <[email protected]> Thu, 9 Jun 2022 11:55:39 +0000
Newsgroups gmane.text.docutils.user
Message-ID <LO3P123MB26815A55AFB947524ED86027C2A79@LO3P123MB2681.GBRP123.PROD.OUTLOOK.COM>
Using Python 3.10's ``-X warn_default_encoding`` argument to Python, we can see a large number of places where the default encoding is used. On posix systems this is now UTF-8 following PEP 538 [1], but on Windows a non-unicode codepage can be used.

The attached patch fixes the majority of these instances.

A

[1]: https://peps.python.org/pep-0538/
0001-Add-encoding-arguments.patch (application/octet-stream, 15.1 KB)
From 40254ddd1606eaa3cb20d6b7ca7b07fd0086cd5a Mon Sep 17 00:00:00 2001
From: Adam Turner <[email protected]>
Date: Thu, 9 Jun 2022 12:21:16 +0100
Subject: [PATCH 1/2] Add encoding arguments

---
 docutils/docutils/io.py                                     | 6 +++---
 docutils/docutils/statemachine.py                           | 2 +-
 docutils/docutils/writers/latex2e/__init__.py               | 6 +++---
 docutils/docutils/writers/odf_odt/__init__.py               | 2 +-
 docutils/docutils/writers/s5_html/__init__.py               | 2 +-
 docutils/test/alltests.py                                   | 3 ++-
 docutils/test/functional/tests/footnotes_html5.py           | 2 +-
 .../test/functional/tests/standalone_rst_docutils_xml.py    | 2 +-
 docutils/test/functional/tests/standalone_rst_html4css1.py  | 2 +-
 docutils/test/functional/tests/standalone_rst_html5.py      | 2 +-
 docutils/test/functional/tests/standalone_rst_latex.py      | 2 +-
 docutils/test/functional/tests/standalone_rst_manpage.py    | 2 +-
 docutils/test/functional/tests/standalone_rst_pseudoxml.py  | 2 +-
 docutils/test/functional/tests/standalone_rst_s5_html_1.py  | 2 +-
 docutils/test/functional/tests/standalone_rst_s5_html_2.py  | 2 +-
 docutils/test/functional/tests/standalone_rst_xetex.py      | 2 +-
 docutils/test/test_CLI.py                                   | 2 +-
 docutils/test/test_functional.py                            | 4 ++--
 docutils/tools/dev/unicode2rstsubs.py                       | 2 +-
 docutils/tools/quicktest.py                                 | 4 ++--
 docutils/tools/test/test_buildhtml.py                       | 2 +-
 21 files changed, 28 insertions(+), 27 deletions(-)

diff --git a/docutils/docutils/io.py b/docutils/docutils/io.py
index 352d991fc..d4d542d31 100644
--- a/docutils/docutils/io.py
+++ b/docutils/docutils/io.py
@@ -193,7 +193,7 @@ class Output(TransformSpec):
     default_destination_path = None
 
     def __init__(self, destination=None, destination_path=None,
-                 encoding=None, error_handler='strict'):
+                 encoding='unicode', error_handler='strict'):
         self.encoding = encoding
         """Text encoding for the output destination."""
 
@@ -255,7 +255,7 @@ class ErrorOutput:
             destination = False
         # if `destination` is a file name, open it
         elif isinstance(destination, str):
-            destination = open(destination, 'w')
+            destination = open(destination, 'w', encoding='utf-8')
 
         self.destination = destination
         """Where warning output is sent."""
@@ -413,7 +413,7 @@ class FileOutput(Output):
     # conversion of newlines to the system specific default.)
 
     def __init__(self, destination=None, destination_path=None,
-                 encoding=None, error_handler='strict', autoclose=True,
+                 encoding='unicode', error_handler='strict', autoclose=True,
                  handle_io_errors=None, mode=None):
         """
         :Parameters:
diff --git a/docutils/docutils/statemachine.py b/docutils/docutils/statemachine.py
index 23b7eb015..d796e860b 100644
--- a/docutils/docutils/statemachine.py
+++ b/docutils/docutils/statemachine.py
@@ -92,7 +92,7 @@ How To Use This Module
    one-line strings. For example, to read text from a file called
    'inputfile'::
 
-       input_string = open('inputfile').read()
+       input_string = open('inputfile', encoding='utf-8').read()
        input_lines = statemachine.string2lines(input_string)
 
 5. Run the state machine on the input text and collect the results, a list::
diff --git a/docutils/docutils/writers/latex2e/__init__.py b/docutils/docutils/writers/latex2e/__init__.py
index d9065c3c3..8fe2bc301 100644
--- a/docutils/docutils/writers/latex2e/__init__.py
+++ b/docutils/docutils/writers/latex2e/__init__.py
@@ -271,12 +271,12 @@ class Writer(writers.Writer):
         # get template string from file
         templatepath = self.document.settings.template
         try:
-            with open(templatepath, encoding='utf8') as fp:
+            with open(templatepath, encoding='utf-8') as fp:
                 template = fp.read()
         except IOError:
             templatepath = os.path.join(self.default_template_path,
                                         templatepath)
-            with open(templatepath, encoding='utf8') as fp:
+            with open(templatepath, encoding='utf-8') as fp:
                 template = fp.read()
         # fill template
         self.assemble_parts()  # create dictionary of parts
@@ -597,7 +597,7 @@ def _read_block(fp):
 
 _docutils_sty = os.path.join(os.path.dirname(os.path.abspath(__file__)),
                              'docutils.sty')
-with open(_docutils_sty, encoding='utf8') as fp:
+with open(_docutils_sty, encoding='utf-8') as fp:
     for line in fp:
         line = line.strip('% \n')
         if not line.endswith('::'):
diff --git a/docutils/docutils/writers/odf_odt/__init__.py b/docutils/docutils/writers/odf_odt/__init__.py
index 4e0f71870..2fb6dbee8 100644
--- a/docutils/docutils/writers/odf_odt/__init__.py
+++ b/docutils/docutils/writers/odf_odt/__init__.py
@@ -936,7 +936,7 @@ class ODFTranslator(nodes.GenericNodeVisitor):
         stylespath = self.settings.stylesheet
         ext = os.path.splitext(stylespath)[1]
         if ext == '.xml':
-            with open(stylespath, 'r') as stylesfile:
+            with open(stylespath, 'r', encoding='utf-8') as stylesfile:
                 s1 = stylesfile.read()
         elif ext == extension:
             zfile = zipfile.ZipFile(stylespath, 'r')
diff --git a/docutils/docutils/writers/s5_html/__init__.py b/docutils/docutils/writers/s5_html/__init__.py
index 8a83db749..ccc03773f 100644
--- a/docutils/docutils/writers/s5_html/__init__.py
+++ b/docutils/docutils/writers/s5_html/__init__.py
@@ -216,7 +216,7 @@ class S5HTMLTranslator(html4css1.HTMLTranslator):
             base_theme_file = os.path.join(path, self.base_theme_file)
             # If it exists, read it and record the theme path:
             if os.path.isfile(base_theme_file):
-                with open(base_theme_file) as f:
+                with open(base_theme_file, encoding='utf-8') as f:
                     lines = f.readlines()
                 for line in lines:
                     line = line.strip()
diff --git a/docutils/test/alltests.py b/docutils/test/alltests.py
index ccb022da4..55787528b 100755
--- a/docutils/test/alltests.py
+++ b/docutils/test/alltests.py
@@ -30,7 +30,8 @@ class Tee:
     """Write to a file and a stream (default: stdout) simultaneously."""
 
     def __init__(self, filename, stream=sys.__stdout__):
-        self.file = open(filename, 'w', errors='backslashreplace')
+        self.file = open(filename, 'w', encoding='utf-8',
+                         errors='backslashreplace')
         atexit.register(self.close)
         self.stream = stream
         self.encoding = getattr(stream, 'encoding', None)
diff --git a/docutils/test/functional/tests/footnotes_html5.py b/docutils/test/functional/tests/footnotes_html5.py
index 4b7dc77d3..865bd4292 100644
--- a/docutils/test/functional/tests/footnotes_html5.py
+++ b/docutils/test/functional/tests/footnotes_html5.py
@@ -1,4 +1,4 @@
-with open('functional/tests/_standalone_rst_defaults.py') as _f:
+with open('functional/tests/_standalone_rst_defaults.py', encoding='utf-8') as _f:
     exec(_f.read())
 
 # Source and destination file names.
diff --git a/docutils/test/functional/tests/standalone_rst_docutils_xml.py b/docutils/test/functional/tests/standalone_rst_docutils_xml.py
index fbfc2a64d..7ba7b545c 100644
--- a/docutils/test/functional/tests/standalone_rst_docutils_xml.py
+++ b/docutils/test/functional/tests/standalone_rst_docutils_xml.py
@@ -1,4 +1,4 @@
-with open('functional/tests/_standalone_rst_defaults.py') as _f:
+with open('functional/tests/_standalone_rst_defaults.py', encoding='utf-8') as _f:
     exec(_f.read())
 
 # Source and destination file names.
diff --git a/docutils/test/functional/tests/standalone_rst_html4css1.py b/docutils/test/functional/tests/standalone_rst_html4css1.py
index 8b32fcc2e..dfa80bd08 100644
--- a/docutils/test/functional/tests/standalone_rst_html4css1.py
+++ b/docutils/test/functional/tests/standalone_rst_html4css1.py
@@ -1,4 +1,4 @@
-with open('functional/tests/_standalone_rst_defaults.py') as _f:
+with open('functional/tests/_standalone_rst_defaults.py', encoding='utf-8') as _f:
     exec(_f.read())
 
 # Source and destination file names.
diff --git a/docutils/test/functional/tests/standalone_rst_html5.py b/docutils/test/functional/tests/standalone_rst_html5.py
index 3035d21c0..67acd1cc3 100644
--- a/docutils/test/functional/tests/standalone_rst_html5.py
+++ b/docutils/test/functional/tests/standalone_rst_html5.py
@@ -1,4 +1,4 @@
-with open('functional/tests/_standalone_rst_defaults.py') as _f:
+with open('functional/tests/_standalone_rst_defaults.py', encoding='utf-8') as _f:
     exec(_f.read())
 
 # Source and destination file names.
diff --git a/docutils/test/functional/tests/standalone_rst_latex.py b/docutils/test/functional/tests/standalone_rst_latex.py
index 5f3de30d6..d9d793e15 100644
--- a/docutils/test/functional/tests/standalone_rst_latex.py
+++ b/docutils/test/functional/tests/standalone_rst_latex.py
@@ -1,4 +1,4 @@
-with open('functional/tests/_standalone_rst_defaults.py') as _f:
+with open('functional/tests/_standalone_rst_defaults.py', encoding='utf-8') as _f:
     exec(_f.read())
 
 # Source and destination file names.
diff --git a/docutils/test/functional/tests/standalone_rst_manpage.py b/docutils/test/functional/tests/standalone_rst_manpage.py
index 0f1d708c1..cccd73bf9 100644
--- a/docutils/test/functional/tests/standalone_rst_manpage.py
+++ b/docutils/test/functional/tests/standalone_rst_manpage.py
@@ -1,4 +1,4 @@
-with open('functional/tests/_standalone_rst_defaults.py') as _f:
+with open('functional/tests/_standalone_rst_defaults.py', encoding='utf-8') as _f:
     exec(_f.read())
 
 # Source and destination file names.
diff --git a/docutils/test/functional/tests/standalone_rst_pseudoxml.py b/docutils/test/functional/tests/standalone_rst_pseudoxml.py
index 75a5002ee..9728796d0 100644
--- a/docutils/test/functional/tests/standalone_rst_pseudoxml.py
+++ b/docutils/test/functional/tests/standalone_rst_pseudoxml.py
@@ -1,4 +1,4 @@
-with open('functional/tests/_standalone_rst_defaults.py') as _f:
+with open('functional/tests/_standalone_rst_defaults.py', encoding='utf-8') as _f:
     exec(_f.read())
 
 # Source and destination file names.
diff --git a/docutils/test/functional/tests/standalone_rst_s5_html_1.py b/docutils/test/functional/tests/standalone_rst_s5_html_1.py
index 6eb8264d5..2be8c06f9 100755
--- a/docutils/test/functional/tests/standalone_rst_s5_html_1.py
+++ b/docutils/test/functional/tests/standalone_rst_s5_html_1.py
@@ -1,7 +1,7 @@
 import filecmp as _filecmp
 
 
-with open('functional/tests/_standalone_rst_defaults.py') as _f:
+with open('functional/tests/_standalone_rst_defaults.py', encoding='utf-8') as _f:
     exec(_f.read())
 
 # Source and destination file names:
diff --git a/docutils/test/functional/tests/standalone_rst_s5_html_2.py b/docutils/test/functional/tests/standalone_rst_s5_html_2.py
index 2e87deaaf..333bb3c9d 100755
--- a/docutils/test/functional/tests/standalone_rst_s5_html_2.py
+++ b/docutils/test/functional/tests/standalone_rst_s5_html_2.py
@@ -1,5 +1,5 @@
 # initialize with the settings & definitions from test 1:
-with open('functional/tests/standalone_rst_s5_html_1.py') as _f:
+with open('functional/tests/standalone_rst_s5_html_1.py', encoding='utf-8') as _f:
     exec(_f.read())
 
 # overrides specific to this test:
diff --git a/docutils/test/functional/tests/standalone_rst_xetex.py b/docutils/test/functional/tests/standalone_rst_xetex.py
index db2c2e79e..a934824c0 100644
--- a/docutils/test/functional/tests/standalone_rst_xetex.py
+++ b/docutils/test/functional/tests/standalone_rst_xetex.py
@@ -1,4 +1,4 @@
-with open('functional/tests/_standalone_rst_defaults.py') as _f:
+with open('functional/tests/_standalone_rst_defaults.py', encoding='utf-8') as _f:
     exec(_f.read())
 
 # Source and destination file names.
diff --git a/docutils/test/test_CLI.py b/docutils/test/test_CLI.py
index 2dd6a8370..70274260b 100644
--- a/docutils/test/test_CLI.py
+++ b/docutils/test/test_CLI.py
@@ -63,7 +63,7 @@ class CliTests(unittest.TestCase):
         output = re.sub(r'default:[^)]*/[^)]*\)', 'default: [...])',
                         output, flags=re.DOTALL)
         # compare to stored version
-        with open('data/help/docutils.txt') as samplefile:
+        with open('data/help/docutils.txt', encoding='utf-8') as samplefile:
             expected = samplefile.read()
         if expected != output:
             print_mismatch(expected, output)
diff --git a/docutils/test/test_functional.py b/docutils/test/test_functional.py
index 60eb1bcc4..2420dbae3 100755
--- a/docutils/test/test_functional.py
+++ b/docutils/test/test_functional.py
@@ -117,10 +117,10 @@ expected output and check it in:
         namespace['settings_overrides'] = {'_disable_config': True}
         # Read the variables set in the default config file and in
         # the current config file into namespace:
-        with open(join_path(datadir, 'tests', '_default.py')) as f:
+        with open(join_path(datadir, 'tests', '_default.py'), encoding='utf-8') as f:
             defaultpy = f.read()
             exec(defaultpy, namespace)
-        with open(self.configfile) as f:
+        with open(self.configfile, encoding='utf-8') as f:
             exec(f.read(), namespace)
         # Check for required settings:
         assert 'test_source' in namespace,\
diff --git a/docutils/tools/dev/unicode2rstsubs.py b/docutils/tools/dev/unicode2rstsubs.py
index 1bc544df1..175d24d20 100755
--- a/docutils/tools/dev/unicode2rstsubs.py
+++ b/docutils/tools/dev/unicode2rstsubs.py
@@ -169,7 +169,7 @@ class CharacterEntitySetExtractor:
             outname = set_name + '-wide.txt'
         else:
             outname = set_name + '.txt'
-        outfile = open(outname, 'w')
+        outfile = open(outname, 'w', encoding='utf-8')
         print('writing file "%s"' % outname)
         outfile.write(self.header + '\n')
         set = self.sets[set_name]
diff --git a/docutils/tools/quicktest.py b/docutils/tools/quicktest.py
index 8093c0aba..5d8256e7f 100755
--- a/docutils/tools/quicktest.py
+++ b/docutils/tools/quicktest.py
@@ -181,9 +181,9 @@ def posixGetArgs(argv):
     inputFile = sys.stdin
     outputFile = sys.stdout
     if args:
-        inputFile = open(args.pop(0))
+        inputFile = open(args.pop(0), encoding='utf-8')
     if args:
-        outputFile = open(args.pop(0), 'w')
+        outputFile = open(args.pop(0), 'w', encoding='utf-8')
     return inputFile, outputFile, outputFormat, optargs
 
 
diff --git a/docutils/tools/test/test_buildhtml.py b/docutils/tools/test/test_buildhtml.py
index 9e95c8bb7..e90cec586 100644
--- a/docutils/tools/test/test_buildhtml.py
+++ b/docutils/tools/test/test_buildhtml.py
@@ -81,7 +81,7 @@ class BuildHtmlTests(unittest.TestCase):
             if "." not in s:
                 os.mkdir(s)
             else:
-                fd_s = open(s, "w")
+                fd_s = open(s, "w", encoding='utf-8')
                 fd_s.write("dummy")
                 fd_s.close()
 
-- 
2.34.1.windows.1