SF.net SVN: docutils:[9809 ] trunk/docutils/tools

aa-turner--- via Docutils-checkins <[email protected]>
Newsgroups gmane.text.docutils.cvs
Message-ID <[email protected]>
Revision: 9809
          http://sourceforge.net/p/docutils/code/9809
Author:   aa-turner
Date:     2024-08-01 06:49:33 +0000 (Thu, 01 Aug 2024)
Log Message:
-----------
Add type hints to tools/

Modified Paths:
--------------
    trunk/docutils/tools/buildhtml.py
    trunk/docutils/tools/dev/create_unimap.py
    trunk/docutils/tools/dev/generate_punctuation_chars.py
    trunk/docutils/tools/dev/quicktest.py
    trunk/docutils/tools/dev/unicode2rstsubs.py
    trunk/docutils/tools/test/test_buildhtml.py

Modified: trunk/docutils/tools/buildhtml.py
===================================================================
--- trunk/docutils/tools/buildhtml.py	2024-07-31 19:42:20 UTC (rev 9808)
+++ trunk/docutils/tools/buildhtml.py	2024-08-01 06:49:33 UTC (rev 9809)
@@ -11,8 +11,11 @@
 Files with names starting ``pep-`` are interpreted as reStructuredText PEPs.
 """
 
+from __future__ import annotations
+
 __docformat__ = 'reStructuredText'
 
+from pathlib import Path
 
 try:
     import locale
@@ -20,11 +23,13 @@
 except Exception:
     pass
 
-from fnmatch import fnmatch
 import os
 import os.path
 import sys
 import warnings
+from fnmatch import fnmatch
+from types import SimpleNamespace
+from typing import TYPE_CHECKING
 
 import docutils
 import docutils.io
@@ -34,7 +39,11 @@
 from docutils.readers import standalone, pep
 from docutils.writers import html4css1, html5_polyglot, pep_html
 
+if TYPE_CHECKING:
+    from typing import Literal
 
+    from docutils.frontend import Values
+
 usage = '%prog [options] [<directory> ...]'
 description = ('Generate .html from all reStructuredText files '
                'in each <directory> (default is the current directory).')
@@ -114,52 +123,73 @@
     Command-line option processing for the ``buildhtml.py`` front end.
     """
 
-    def check_values(self, values, args):
+    def check_values(self, values: Values, args: list[str]) -> Values:
         super().check_values(values, args)
         values._source = None
         return values
 
-    def check_args(self, args):
+    def check_args(self, args: list[str]) -> tuple[None, None]:
         self.values._directories = args or [os.getcwd()]
         # backwards compatibility:
         return None, None
 
 
-class Struct:
+class Struct(SimpleNamespace):
+    components: tuple[docutils.SettingsSpec, ...]
+    reader: str
+    writer: str
+    option_parser: OptionParser
+    setting_defaults: Values
+    config_settings: Values
 
-    """Stores data attributes for dotted-attribute access."""
 
-    def __init__(self, **keywordargs):
-        self.__dict__.update(keywordargs)
-
-
 class Builder:
+    publishers: dict[str, Struct] = {
+        '': Struct(
+            components=(
+                pep.Reader, rst.Parser, pep_html.Writer, SettingsSpec,
+            ),
+        ),
+        'html4': Struct(
+            components=(
+                rst.Parser, standalone.Reader, html4css1.Writer, SettingsSpec,
+            ),
+            reader='standalone',
+            writer='html4',
+        ),
+        'html5': Struct(
+            components=(
+                rst.Parser, standalone.Reader, html5_polyglot.Writer,
+                SettingsSpec,
+            ),
+            reader='standalone',
+            writer='html5',
+        ),
+        'PEPs': Struct(
+            components=(
+                rst.Parser, pep.Reader, pep_html.Writer, SettingsSpec,
+            ),
+            reader='pep',
+            writer='pep_html',
+        ),
+    }
+    """Publisher-specific settings.  Key '' is for the front-end script
+    itself.  ``self.publishers[''].components`` must contain a superset of
+    all components used by individual publishers."""
 
-    def __init__(self):
-        self.publishers = {
-            '': Struct(components=(pep.Reader, rst.Parser, pep_html.Writer,
-                                   SettingsSpec)),
-            'html4': Struct(components=(rst.Parser, standalone.Reader,
-                                        html4css1.Writer, SettingsSpec),
-                            reader='standalone',
-                            writer='html4'),
-            'html5': Struct(components=(rst.Parser, standalone.Reader,
-                                        html5_polyglot.Writer, SettingsSpec),
-                            reader='standalone',
-                            writer='html5'),
-            'PEPs': Struct(components=(rst.Parser, pep.Reader,
-                                       pep_html.Writer, SettingsSpec),
-                           reader='pep',
-                           writer='pep_html')}
-        """Publisher-specific settings.  Key '' is for the front-end script
-        itself.  ``self.publishers[''].components`` must contain a superset of
-        all components used by individual publishers."""
-
+    def __init__(self) -> None:
+        self.publishers = self.publishers.copy()
         self.setup_publishers()
         # default html writer (may change to html5 some time):
         self.publishers['html'] = self.publishers['html4']
 
-    def setup_publishers(self):
+        with warnings.catch_warnings():
+            warnings.filterwarnings('ignore', category=DeprecationWarning)
+            self.settings_spec = frontend.Values()
+            self.initial_settings = frontend.Values()
+        self.directories = []
+
+    def setup_publishers(self) -> None:
         """
         Manage configurations for individual publishers.
 
@@ -179,7 +209,7 @@
                 publisher.setting_defaults = option_parser.get_default_values()
                 frontend.make_paths_absolute(
                     publisher.setting_defaults.__dict__,
-                    option_parser.relative_path_settings)
+                    list(option_parser.relative_path_settings))
                 publisher.config_settings = (
                     option_parser.get_standard_config_settings())
             self.settings_spec = self.publishers[''].option_parser.parse_args(
@@ -195,7 +225,11 @@
             self.initial_settings.writer = (self.initial_settings.html_writer
                                             or 'html')
 
-    def get_settings(self, publisher_name, directory=None):
+    def get_settings(
+        self,
+        publisher_name: Literal['', 'html', 'html5', 'html4', 'PEPs'],
+        directory: str | os.PathLike[str] | None = None,
+    ) -> Values:
         """
         Return a settings object, from multiple sources.
 
@@ -216,7 +250,8 @@
             local_config = publisher.option_parser.get_config_file_settings(
                 os.path.join(directory, 'docutils.conf'))
             frontend.make_paths_absolute(
-                local_config, publisher.option_parser.relative_path_settings,
+                local_config,
+                list(publisher.option_parser.relative_path_settings),
                 directory)
             settings.update(local_config, publisher.option_parser)
         settings.update(self.settings_spec.__dict__, publisher.option_parser)
@@ -225,7 +260,11 @@
         settings.prune = list(set(settings.prune))
         return settings
 
-    def run(self, directory=None, recurse=True):
+    def run(
+        self,
+        directory: str | os.PathLike[str] | None = None,
+        recurse: bool = True,
+    ) -> None:
         recurse = recurse and self.initial_settings.recurse
         if directory:
             self.directories = [directory]
@@ -234,8 +273,8 @@
         else:
             self.directories = [os.getcwd()]
         for directory in self.directories:
-            directory = os.path.abspath(directory)
-            for dirpath, dirnames, filenames in os.walk(directory):
+            dir_abs = Path(directory).resolve()
+            for dirpath, dirnames, filenames in os.walk(dir_abs):
                 # `os.walk()` by default recurses down the tree,
                 # we modify `dirnames` in-place to control the behaviour.
                 if recurse:
@@ -242,9 +281,14 @@
                     dirnames.sort()
                 else:
                     del dirnames[:]
-                self.visit(dirpath, dirnames, filenames)
+                self.visit(Path(dirpath), dirnames, filenames)
 
-    def visit(self, dirpath, dirnames, filenames):
+    def visit(
+        self,
+        dirpath: Path,
+        dirnames: list[str],
+        filenames: list[str],
+    ) -> None:
         settings = self.get_settings('', dirpath)
         errout = docutils.io.ErrorOutput(encoding=settings.error_encoding)
         if match_patterns(dirpath, settings.prune):
@@ -261,10 +305,9 @@
             if match_patterns(name, settings.ignore):
                 continue
             if match_patterns(name, settings.sources):
-                self.process_txt(dirpath, name)
+                self.process_rst_source_file(dirpath, name)
 
-    def process_txt(self, directory, name):
-        # TODO change name to `process_rst_source_file()`?
+    def process_rst_source_file(self, directory: Path, name: str) -> None:
         if name.startswith('pep-'):
             publisher = 'PEPs'
         else:
@@ -272,7 +315,7 @@
         settings = self.get_settings(publisher, directory)
         errout = docutils.io.ErrorOutput(encoding=settings.error_encoding)
         pub_struct = self.publishers[publisher]
-        settings._source = os.path.normpath(os.path.join(directory, name))
+        settings._source = str(directory / name)
         settings._destination = os.path.splitext(settings._source)[0] + '.html'
         if not self.initial_settings.silent:
             errout.write('    ::: Processing: %s\n' % name)
@@ -289,7 +332,7 @@
                 errout.write(f'        {type(err).__name__}: {err}\n')
 
 
-def match_patterns(name, patterns):
+def match_patterns(name: str | os.PathLike[str], patterns: str) -> bool:
     """Return True, if `name` matches any item of the sequence `patterns`.
 
     Matching is done with `fnmatch.fnmatch`. It resembles shell-style
@@ -300,6 +343,7 @@
     PROVISIONAL.
     TODO: use `pathlib.PurePath.match()` once this supports "**".
     """
+    name = os.fspath(name)
     for pattern in patterns:
         if fnmatch(name, pattern):
             return True

Modified: trunk/docutils/tools/dev/create_unimap.py
===================================================================
--- trunk/docutils/tools/dev/create_unimap.py	2024-07-31 19:42:20 UTC (rev 9808)
+++ trunk/docutils/tools/dev/create_unimap.py	2024-08-01 06:49:33 UTC (rev 9809)
@@ -9,19 +9,20 @@
 # Get unicode.xml from
 # <https://www.w3.org/2003/entities/xml/unicode.xml>.
 
+from __future__ import annotations
+
+import pprint
+import sys
 from xml.dom import minidom
-import sys
-import pprint
 
+text_map: dict[str, str] = {}
+math_map: dict[str, str] = {}
 
-text_map = {}
-math_map = {}
 
-
 class Visitor:
     """Node visitor for contents of unicode.xml."""
 
-    def visit_character(self, node):
+    def visit_character(self, node: minidom.Element) -> None:
         for n in node.childNodes:
             if n.nodeName == 'latex':
                 code = node.attributes['dec'].value
@@ -40,7 +41,10 @@
                     text_map[chr(int(code))] = '{%s}' % latex_code
 
 
-def call_visitor(node, visitor=Visitor()):
+def call_visitor(
+    node: minidom.Document | minidom.Element | minidom.Text,
+    visitor: Visitor = Visitor(),
+) -> None:
     if isinstance(node, minidom.Text):
         name = 'Text'
     else:
@@ -56,7 +60,7 @@
 document = minidom.parse(sys.stdin)
 call_visitor(document)
 
-unicode_map = math_map
+unicode_map: dict[str, str] = math_map
 unicode_map.update(text_map)
 # Now unicode_map contains the text entries plus dollar-enclosed math
 # entries for those chars for which no text entry exists.

Modified: trunk/docutils/tools/dev/generate_punctuation_chars.py
===================================================================
--- trunk/docutils/tools/dev/generate_punctuation_chars.py	2024-07-31 19:42:20 UTC (rev 9808)
+++ trunk/docutils/tools/dev/generate_punctuation_chars.py	2024-08-01 06:49:33 UTC (rev 9809)
@@ -33,10 +33,16 @@
       #inline-markup-recognition-rules
 """
 
+from __future__ import annotations
+
 import sys
 import unicodedata
+from typing import TYPE_CHECKING
 
+if TYPE_CHECKING:
+    from collections.abc import Iterable
 
+
 # Template for utils.punctuation_chars
 # ------------------------------------
 
@@ -147,7 +153,11 @@
 #
 # ::
 
-def unicode_charlists(categories, cp_min=0, cp_max=sys.maxunicode):
+def unicode_charlists(
+    categories: Iterable[str],
+    cp_min: int = 0,
+    cp_max: int = sys.maxunicode,
+) -> dict[str, list[str]]:
     """Return dictionary of Unicode character lists.
 
     For each of the `catagories`, an item contains a list with all Unicode
@@ -168,7 +178,7 @@
 #
 # ::
 
-def character_category_patterns():
+def character_category_patterns() -> tuple[str, str, str, str]:
 
     """Docutils character category patterns.
 
@@ -247,16 +257,20 @@
     # non-matching, after markup
     closing_delimiters = [r'\\.,;!?']
 
-    return [''.join(chars) for chars in (openers, closers, delimiters,
-                                         closing_delimiters)]
+    return (
+        ''.join(openers),
+        ''.join(closers),
+        ''.join(delimiters),
+        ''.join(closing_delimiters),
+    )
 
 
-def mark_intervals(s):
+def mark_intervals(s: str) -> str:
     """Return s with shortcut notation for runs of consecutive characters
 
     Sort string and replace 'cdef' by 'c-f' and similar.
     """
-    lst = []
+    lst: list[list[int]] = []
     s = sorted(ord(ch) for ch in s)
     for n in s:
         try:
@@ -267,7 +281,7 @@
         except IndexError:
             lst.append([n])
 
-    lst2 = []
+    lst2: list[str] = []
     for i in lst:
         i = [chr(n) for n in i]
         if len(i) > 2:
@@ -277,7 +291,12 @@
     return ''.join(lst2)
 
 
-def wrap_string(s, startstring="(", endstring="    )", wrap=71):
+def wrap_string(
+    s: str,
+    startstring: str = "(",
+    endstring: str = "    )",
+    wrap: int = 71,
+) -> str:
     """Line-wrap a unicode string literal definition."""
     s = s.encode('unicode-escape').decode()
     c = len(startstring)
@@ -295,7 +314,7 @@
     return ''.join(lst)
 
 
-def print_differences(old, new, name):
+def print_differences(old: str, new: str, name: str) -> bool:
     """List characters missing in old/new."""
     if old != new:
         print(f'"{name}" changed')
@@ -392,7 +411,7 @@
 
 # Replacements::
 
-    substitutions = {
+    substitutions: dict[str, str] = {
         'python_version': sys.version.split()[0],
         'unidata_version': unicodedata.unidata_version,
         'openers': wrap_string(o, startstring="openers = ("),

Modified: trunk/docutils/tools/dev/quicktest.py
===================================================================
--- trunk/docutils/tools/dev/quicktest.py	2024-07-31 19:42:20 UTC (rev 9808)
+++ trunk/docutils/tools/dev/quicktest.py	2024-08-01 06:49:33 UTC (rev 9809)
@@ -26,6 +26,8 @@
    (cf. :PEP:`540`, :PEP:`538`, :PEP:`597`, and :PEP:`686`).
 """
 
+from __future__ import annotations
+
 try:
     import locale
     locale.setlocale(locale.LC_ALL, '')
@@ -32,14 +34,29 @@
 except Exception:
     pass
 
+import getopt
 import sys
-import getopt
+from typing import TYPE_CHECKING
+
 import docutils
 from docutils import frontend
 from docutils.utils import new_document
 from docutils.parsers.rst import Parser
 
+if TYPE_CHECKING:
+    from collections.abc import Callable
+    from typing import TextIO, TypedDict
 
+    from docutils import nodes
+
+    class _OptArgs(TypedDict):
+        debug: bool
+        attributes: bool
+        styledxml: str
+
+    _FormatFunc = Callable[[str, nodes.document, _OptArgs], str]
+
+
 usage_header = """\
 quicktest.py: Quickly test the reStructuredText parser.  This is not an
 interface to the full functionality of Docutils.  Use one of the ``rst2*.py``
@@ -72,7 +89,7 @@
 the data structure: (long option, short option, description)."""
 
 
-def usage():
+def usage() -> None:
     print(usage_header)
     for longopt, shortopt, description in options:
         if longopt[-1:] == '=':
@@ -90,15 +107,21 @@
         print(description)
 
 
-def _pretty(input, document, optargs):
+def _pretty(
+    input: str, document: nodes.document, optargs: _OptArgs,
+) -> str:
     return document.pformat()
 
 
-def _rawxml(input, document, optargs):
+def _rawxml(
+    input: str, document: nodes.document, optargs: _OptArgs,
+) -> str:
     return document.asdom().toxml()
 
 
-def _styledxml(input, document, optargs):
+def _styledxml(
+    input: str, document: nodes.document, optargs: _OptArgs,
+) -> str:
     docnode = document.asdom().childNodes[0]
     return '\n'.join(('<?xml version="1.0" encoding="ISO-8859-1"?>',
                       '<?xml-stylesheet type="text/xsl" href="%s"?>'
@@ -106,11 +129,15 @@
                       docnode.toxml()))
 
 
-def _prettyxml(input, document, optargs):
+def _prettyxml(
+    input: str, document: nodes.document, optargs: _OptArgs,
+) -> str:
     return document.asdom().toprettyxml('    ', '\n')
 
 
-def _test(input, document, optargs):
+def _test(
+    input: str, document: nodes.document, optargs: _OptArgs,
+) -> str:
     tq = '"""'
     output = document.pformat()         # same as _pretty()
     return """\
@@ -125,7 +152,7 @@
 """ % (tq, escape(input.rstrip()), tq, tq, escape(output.rstrip()), tq)
 
 
-def escape(text):
+def escape(text: str) -> str:
     """
     Return `text` in triple-double-quoted Python string form.
     """
@@ -135,21 +162,27 @@
     return text
 
 
-_outputFormatters = {
+_output_formatters: dict[str, _FormatFunc] = {
     'rawxml': _rawxml,
     'styledxml': _styledxml,
     'xml': _prettyxml,
     'pretty': _pretty,
-    'test': _test}
+    'test': _test,
+}
 
 
-def format(outputFormat, input, document, optargs):
-    formatter = _outputFormatters[outputFormat]
+def format(
+    output_format: str,
+    input: str,
+    document: nodes.document,
+    optargs: _OptArgs,
+) -> str:
+    formatter = _output_formatters[output_format]
     return formatter(input, document, optargs)
 
 
-def posixGetArgs(argv):
-    outputFormat = 'pretty'
+def posix_get_args(argv: list[str]) -> tuple[TextIO, TextIO, str, _OptArgs]:
+    output_format = 'pretty'
     # convert fancy_getopt style option list to getopt.getopt() arguments
     shortopts = ''.join(option[1] + ':' * (option[0][-1:] == '=')
                         for option in options if option[1])
@@ -159,7 +192,7 @@
     except getopt.GetoptError:
         usage()
         sys.exit(2)
-    optargs = {'debug': 0, 'attributes': 0}
+    optargs = {'debug': False, 'attributes': False}
     for o, a in opts:
         if o in ['-h', '--help']:
             usage()
@@ -171,20 +204,20 @@
                               and ' [%s]'%docutils.__version_details__ or ''))
             sys.exit()
         elif o in ['-r', '--rawxml']:
-            outputFormat = 'rawxml'
+            output_format = 'rawxml'
         elif o in ['-s', '--styledxml']:
-            outputFormat = 'styledxml'
+            output_format = 'styledxml'
             optargs['styledxml'] = a
         elif o in ['-x', '--xml']:
-            outputFormat = 'xml'
+            output_format = 'xml'
         elif o in ['-p', '--pretty']:
-            outputFormat = 'pretty'
+            output_format = 'pretty'
         elif o in ['-t', '--test']:
-            outputFormat = 'test'
+            output_format = 'test'
         elif o in ['--attributes', '-A']:
-            optargs['attributes'] = 1
+            optargs['attributes'] = True
         elif o in ['-d', '--debug']:
-            optargs['debug'] = 1
+            optargs['debug'] = True
         else:
             raise getopt.GetoptError("getopt should have saved us!")
     if len(args) > 2:
@@ -191,26 +224,28 @@
         print('Maximum 2 arguments allowed.')
         usage()
         sys.exit(1)
-    inputFile = sys.stdin
-    outputFile = sys.stdout
+    input_file = sys.stdin
+    output_file = sys.stdout
     if args:
-        inputFile = open(args.pop(0))
+        input_file = open(args.pop(0))
     if args:
-        outputFile = open(args.pop(0), 'w')
-    return inputFile, outputFile, outputFormat, optargs
+        output_file = open(args.pop(0), 'w')
+    return input_file, output_file, output_format, optargs
 
 
-def main():
+def main() -> None:
     # process cmdline arguments:
-    inputFile, outputFile, outputFormat, optargs = posixGetArgs(sys.argv[1:])
+    (
+        input_file, output_file, output_format, optargs,
+    ) = posix_get_args(sys.argv[1:])
     settings = frontend.get_default_settings(Parser)
     settings.debug = optargs['debug']
     parser = Parser()
-    input = inputFile.read()
-    document = new_document(inputFile.name, settings)
+    input = input_file.read()
+    document = new_document(input_file.name, settings)
     parser.parse(input, document)
-    output = format(outputFormat, input, document, optargs)
-    outputFile.write(output)
+    output = format(output_format, input, document, optargs)
+    output_file.write(output)
     if optargs['attributes']:
         import pprint
         pprint.pprint(document.__dict__)

Modified: trunk/docutils/tools/dev/unicode2rstsubs.py
===================================================================
--- trunk/docutils/tools/dev/unicode2rstsubs.py	2024-07-31 19:42:20 UTC (rev 9808)
+++ trunk/docutils/tools/dev/unicode2rstsubs.py	2024-08-01 06:49:33 UTC (rev 9809)
@@ -19,16 +19,23 @@
 <https://www.w3.org/2003/entities/xml/>.
 """
 
-import sys
+from __future__ import annotations
+
 import os
 import re
+import sys
+from typing import TYPE_CHECKING, TextIO
 from xml.parsers.expat import ParserCreate
 
+if TYPE_CHECKING:
+    from typing import BinaryIO, NoReturn
+    from xml.parsers.expat import XMLParserType
 
+
 usage_msg = """Usage: %s [unicode.xml]\n"""
 
 
-def usage(prog, status=0, msg=None):
+def usage(prog: str, status: int = 0, msg: str | None = None) -> NoReturn:
     sys.stderr.write(usage_msg % prog)
     if msg:
         sys.stderr.write(msg + '\n')
@@ -35,7 +42,7 @@
     sys.exit(status)
 
 
-def main(argv=None):
+def main(argv: list[str] | None = None) -> None:
     if argv is None:
         argv = sys.argv
     if len(argv) == 2:
@@ -51,7 +58,7 @@
     process(infile)
 
 
-def process(infile):
+def process(infile: BinaryIO) -> None:
     grouper = CharacterEntitySetExtractor(infile)
     grouper.group()
     grouper.write_sets()
@@ -75,26 +82,26 @@
    <https://docutils.sourceforge.io>.
 """
 
-    def __init__(self, infile):
+    def __init__(self, infile: BinaryIO) -> None:
         self.infile = infile
         """Input unicode.xml file."""
 
-        self.parser = self.setup_parser()
+        self.parser: XMLParserType = self.setup_parser()
         """XML parser."""
 
-        self.elements = []
+        self.elements: list[str] = []
         """Stack of element names.  Last is current element."""
 
-        self.sets = {}
+        self.sets: dict[str, dict[str, str]] = {}
         """Mapping of charent set name to set dict."""
 
-        self.charid = None
+        self.charid: str | None = None
         """Current character's "id" attribute value."""
 
-        self.descriptions = {}
+        self.descriptions: dict[str, str] = {}
         """Mapping of character ID to description."""
 
-    def setup_parser(self):
+    def setup_parser(self) -> XMLParserType:
         parser = ParserCreate()
         parser.StartElementHandler = self.StartElementHandler
         parser.EndElementHandler = self.EndElementHandler
@@ -101,16 +108,16 @@
         parser.CharacterDataHandler = self.CharacterDataHandler
         return parser
 
-    def group(self):
+    def group(self) -> None:
         self.parser.ParseFile(self.infile)
 
-    def StartElementHandler(self, name, attributes):
+    def StartElementHandler(self, name: str, attributes) -> None:
         self.elements.append(name)
         handler = name + '_start'
         if hasattr(self, handler):
             getattr(self, handler)(name, attributes)
 
-    def EndElementHandler(self, name):
+    def EndElementHandler(self, name: str) -> None:
         assert self.elements[-1] == name, \
                'unknown end-tag %r (%r)' % (name, self.element)
         self.elements.pop()
@@ -118,15 +125,15 @@
         if hasattr(self, handler):
             getattr(self, handler)(name)
 
-    def CharacterDataHandler(self, data):
+    def CharacterDataHandler(self, data) -> None:
         handler = self.elements[-1] + '_data'
         if hasattr(self, handler):
             getattr(self, handler)(data)
 
-    def character_start(self, name, attributes):
+    def character_start(self, name: str, attributes) -> None:
         self.charid = attributes['id']
 
-    def entity_start(self, name, attributes):
+    def entity_start(self, name, attributes) -> None:
         set = self.entity_set_name(attributes['set'])
         if not set:
             return
@@ -140,7 +147,7 @@
                     % (set, entity, self.sets[set][entity], self.charid))
         self.sets[set][entity] = self.charid
 
-    def description_data(self, data):
+    def description_data(self, data) -> None:
         self.descriptions.setdefault(self.charid, '')
         self.descriptions[self.charid] += data
 
@@ -147,7 +154,7 @@
     entity_set_name_pat = re.compile(r'[0-9-]*(.+)$')
     """Pattern to strip ISO numbers off the beginning of set names."""
 
-    def entity_set_name(self, name):
+    def entity_set_name(self, name: str) -> str | None:
         """
         Return lowcased and standard-number-free entity set name.
         Return ``None`` for unwanted entity sets.
@@ -159,12 +166,12 @@
         self.sets.setdefault(name, {})
         return name
 
-    def write_sets(self):
+    def write_sets(self) -> None:
         sets = sorted(self.sets.keys())
         for set_name in sets:
             self.write_set(set_name)
 
-    def write_set(self, set_name, wide=None):
+    def write_set(self, set_name: str, wide: bool = False) -> None:
         if wide:
             outname = set_name + '-wide.txt'
         else:
@@ -177,24 +184,32 @@
         longest = 0
         for _, entity_name in entities:
             longest = max(longest, len(entity_name))
-        has_wide = None
+        has_wide = False
         for _, entity_name in entities:
             has_wide = self.write_entity(
                 set, set_name, entity_name, outfile, longest, wide) or has_wide
         if has_wide and not wide:
-            self.write_set(set_name, 1)
+            self.write_set(set_name, wide=True)
 
-    def write_entity(self, set, set_name, entity_name, outfile, longest,
-                     wide=None):
+    def write_entity(
+        self,
+        set: dict[str, str],
+        set_name: str,
+        entity_name: str,
+        outfile: TextIO,
+        longest: int,
+        wide: bool = False,
+    ) -> bool:
         charid = set[entity_name]
         if not wide:
             for code in charid[1:].split('-'):
                 if int(code, 16) > 0xFFFF:
-                    return 1            # wide-Unicode character
+                    return True         # wide-Unicode character
         codes = ' '.join('U+%s' % code for code in charid[1:].split('-'))
         outfile.write('.. %-*s unicode:: %s .. %s\n'
                       % (longest + 2, '|' + entity_name + '|',
                          codes, self.descriptions[charid]))
+        return False
 
 
 if __name__ == '__main__':

Modified: trunk/docutils/tools/test/test_buildhtml.py
===================================================================
--- trunk/docutils/tools/test/test_buildhtml.py	2024-07-31 19:42:20 UTC (rev 9808)
+++ trunk/docutils/tools/test/test_buildhtml.py	2024-08-01 06:49:33 UTC (rev 9809)
@@ -22,6 +22,8 @@
                         "--quiet".
 """
 
+from __future__ import annotations
+
 import shutil
 import subprocess
 import sys
@@ -69,7 +71,7 @@
         "_tmp_test_tree/dir2/sub/two.txt",
     )
 
-    def setUp(self):
+    def setUp(self) -> None:
         self.root = Path(tempfile.mkdtemp()).resolve()
 
         for file in self.tree:
@@ -77,15 +79,15 @@
             path.parent.mkdir(parents=True, exist_ok=True)
             path.write_text('dummy', encoding='utf-8')
 
-    def tearDown(self):
+    def tearDown(self) -> None:
         shutil.rmtree(self.root)
 
-    def test_1(self):
+    def test_1(self) -> None:
         opts = ["--dry-run", str(self.root)]
         _dirs, files = process_and_return_filelist(opts)
         self.assertEqual(files.count("one.txt"), 4)
 
-    def test_local(self):
+    def test_local(self) -> None:
         opts = ["--dry-run", "--local", str(self.root)]
         dirs, files = process_and_return_filelist(opts)
         self.assertEqual(len(dirs), 1)

This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.