17db96d56Sopenharmony_ci# -*- coding: utf-8 -*-
27db96d56Sopenharmony_ci"""
37db96d56Sopenharmony_ci    pyspecific.py
47db96d56Sopenharmony_ci    ~~~~~~~~~~~~~
57db96d56Sopenharmony_ci
67db96d56Sopenharmony_ci    Sphinx extension with Python doc-specific markup.
77db96d56Sopenharmony_ci
87db96d56Sopenharmony_ci    :copyright: 2008-2014 by Georg Brandl.
97db96d56Sopenharmony_ci    :license: Python license.
107db96d56Sopenharmony_ci"""
117db96d56Sopenharmony_ci
127db96d56Sopenharmony_ciimport re
137db96d56Sopenharmony_ciimport io
147db96d56Sopenharmony_cifrom os import getenv, path
157db96d56Sopenharmony_cifrom time import asctime
167db96d56Sopenharmony_cifrom pprint import pformat
177db96d56Sopenharmony_cifrom docutils.io import StringOutput
187db96d56Sopenharmony_cifrom docutils.parsers.rst import Directive
197db96d56Sopenharmony_cifrom docutils.utils import new_document
207db96d56Sopenharmony_ci
217db96d56Sopenharmony_cifrom docutils import nodes, utils
227db96d56Sopenharmony_ci
237db96d56Sopenharmony_cifrom sphinx import addnodes
247db96d56Sopenharmony_cifrom sphinx.builders import Builder
257db96d56Sopenharmony_citry:
267db96d56Sopenharmony_ci    from sphinx.errors import NoUri
277db96d56Sopenharmony_ciexcept ImportError:
287db96d56Sopenharmony_ci    from sphinx.environment import NoUri
297db96d56Sopenharmony_cifrom sphinx.locale import _ as sphinx_gettext
307db96d56Sopenharmony_cifrom sphinx.util import status_iterator, logging
317db96d56Sopenharmony_cifrom sphinx.util.docutils import SphinxDirective
327db96d56Sopenharmony_cifrom sphinx.util.nodes import split_explicit_title
337db96d56Sopenharmony_cifrom sphinx.writers.text import TextWriter, TextTranslator
347db96d56Sopenharmony_cifrom sphinx.writers.latex import LaTeXTranslator
357db96d56Sopenharmony_ci
367db96d56Sopenharmony_citry:
377db96d56Sopenharmony_ci    from sphinx.domains.python import PyFunction, PyMethod
387db96d56Sopenharmony_ciexcept ImportError:
397db96d56Sopenharmony_ci    from sphinx.domains.python import PyClassmember as PyMethod
407db96d56Sopenharmony_ci    from sphinx.domains.python import PyModulelevel as PyFunction
417db96d56Sopenharmony_ci
427db96d56Sopenharmony_ci# Support for checking for suspicious markup
437db96d56Sopenharmony_ci
447db96d56Sopenharmony_ciimport suspicious
457db96d56Sopenharmony_ci
467db96d56Sopenharmony_ci
477db96d56Sopenharmony_ciISSUE_URI = 'https://bugs.python.org/issue?@action=redirect&bpo=%s'
487db96d56Sopenharmony_ciGH_ISSUE_URI = 'https://github.com/python/cpython/issues/%s'
497db96d56Sopenharmony_ciSOURCE_URI = 'https://github.com/python/cpython/tree/3.11/%s'
507db96d56Sopenharmony_ci
517db96d56Sopenharmony_ci# monkey-patch reST parser to disable alphabetic and roman enumerated lists
527db96d56Sopenharmony_cifrom docutils.parsers.rst.states import Body
537db96d56Sopenharmony_ciBody.enum.converters['loweralpha'] = \
547db96d56Sopenharmony_ci    Body.enum.converters['upperalpha'] = \
557db96d56Sopenharmony_ci    Body.enum.converters['lowerroman'] = \
567db96d56Sopenharmony_ci    Body.enum.converters['upperroman'] = lambda x: None
577db96d56Sopenharmony_ci
587db96d56Sopenharmony_ci
597db96d56Sopenharmony_ci# Support for marking up and linking to bugs.python.org issues
607db96d56Sopenharmony_ci
617db96d56Sopenharmony_cidef issue_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
627db96d56Sopenharmony_ci    issue = utils.unescape(text)
637db96d56Sopenharmony_ci    # sanity check: there are no bpo issues within these two values
647db96d56Sopenharmony_ci    if 47261 < int(issue) < 400000:
657db96d56Sopenharmony_ci        msg = inliner.reporter.error(f'The BPO ID {text!r} seems too high -- '
667db96d56Sopenharmony_ci                                     'use :gh:`...` for GitHub IDs', line=lineno)
677db96d56Sopenharmony_ci        prb = inliner.problematic(rawtext, rawtext, msg)
687db96d56Sopenharmony_ci        return [prb], [msg]
697db96d56Sopenharmony_ci    text = 'bpo-' + issue
707db96d56Sopenharmony_ci    refnode = nodes.reference(text, text, refuri=ISSUE_URI % issue)
717db96d56Sopenharmony_ci    return [refnode], []
727db96d56Sopenharmony_ci
737db96d56Sopenharmony_ci
747db96d56Sopenharmony_ci# Support for marking up and linking to GitHub issues
757db96d56Sopenharmony_ci
767db96d56Sopenharmony_cidef gh_issue_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
777db96d56Sopenharmony_ci    issue = utils.unescape(text)
787db96d56Sopenharmony_ci    # sanity check: all GitHub issues have ID >= 32426
797db96d56Sopenharmony_ci    # even though some of them are also valid BPO IDs
807db96d56Sopenharmony_ci    if int(issue) < 32426:
817db96d56Sopenharmony_ci        msg = inliner.reporter.error(f'The GitHub ID {text!r} seems too low -- '
827db96d56Sopenharmony_ci                                     'use :issue:`...` for BPO IDs', line=lineno)
837db96d56Sopenharmony_ci        prb = inliner.problematic(rawtext, rawtext, msg)
847db96d56Sopenharmony_ci        return [prb], [msg]
857db96d56Sopenharmony_ci    text = 'gh-' + issue
867db96d56Sopenharmony_ci    refnode = nodes.reference(text, text, refuri=GH_ISSUE_URI % issue)
877db96d56Sopenharmony_ci    return [refnode], []
887db96d56Sopenharmony_ci
897db96d56Sopenharmony_ci
907db96d56Sopenharmony_ci# Support for linking to Python source files easily
917db96d56Sopenharmony_ci
927db96d56Sopenharmony_cidef source_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
937db96d56Sopenharmony_ci    has_t, title, target = split_explicit_title(text)
947db96d56Sopenharmony_ci    title = utils.unescape(title)
957db96d56Sopenharmony_ci    target = utils.unescape(target)
967db96d56Sopenharmony_ci    refnode = nodes.reference(title, title, refuri=SOURCE_URI % target)
977db96d56Sopenharmony_ci    return [refnode], []
987db96d56Sopenharmony_ci
997db96d56Sopenharmony_ci
1007db96d56Sopenharmony_ci# Support for marking up implementation details
1017db96d56Sopenharmony_ci
1027db96d56Sopenharmony_ciclass ImplementationDetail(Directive):
1037db96d56Sopenharmony_ci
1047db96d56Sopenharmony_ci    has_content = True
1057db96d56Sopenharmony_ci    final_argument_whitespace = True
1067db96d56Sopenharmony_ci
1077db96d56Sopenharmony_ci    # This text is copied to templates/dummy.html
1087db96d56Sopenharmony_ci    label_text = 'CPython implementation detail:'
1097db96d56Sopenharmony_ci
1107db96d56Sopenharmony_ci    def run(self):
1117db96d56Sopenharmony_ci        self.assert_has_content()
1127db96d56Sopenharmony_ci        pnode = nodes.compound(classes=['impl-detail'])
1137db96d56Sopenharmony_ci        label = sphinx_gettext(self.label_text)
1147db96d56Sopenharmony_ci        content = self.content
1157db96d56Sopenharmony_ci        add_text = nodes.strong(label, label)
1167db96d56Sopenharmony_ci        self.state.nested_parse(content, self.content_offset, pnode)
1177db96d56Sopenharmony_ci        content = nodes.inline(pnode[0].rawsource, translatable=True)
1187db96d56Sopenharmony_ci        content.source = pnode[0].source
1197db96d56Sopenharmony_ci        content.line = pnode[0].line
1207db96d56Sopenharmony_ci        content += pnode[0].children
1217db96d56Sopenharmony_ci        pnode[0].replace_self(nodes.paragraph(
1227db96d56Sopenharmony_ci            '', '', add_text, nodes.Text(' '), content, translatable=False))
1237db96d56Sopenharmony_ci        return [pnode]
1247db96d56Sopenharmony_ci
1257db96d56Sopenharmony_ci
1267db96d56Sopenharmony_ci# Support for documenting platform availability
1277db96d56Sopenharmony_ci
1287db96d56Sopenharmony_ciclass Availability(SphinxDirective):
1297db96d56Sopenharmony_ci
1307db96d56Sopenharmony_ci    has_content = True
1317db96d56Sopenharmony_ci    required_arguments = 1
1327db96d56Sopenharmony_ci    optional_arguments = 0
1337db96d56Sopenharmony_ci    final_argument_whitespace = True
1347db96d56Sopenharmony_ci
1357db96d56Sopenharmony_ci    # known platform, libc, and threading implementations
1367db96d56Sopenharmony_ci    known_platforms = frozenset({
1377db96d56Sopenharmony_ci        "AIX", "Android", "BSD", "DragonFlyBSD", "Emscripten", "FreeBSD",
1387db96d56Sopenharmony_ci        "Linux", "NetBSD", "OpenBSD", "POSIX", "Solaris", "Unix", "VxWorks",
1397db96d56Sopenharmony_ci        "WASI", "Windows", "macOS",
1407db96d56Sopenharmony_ci        # libc
1417db96d56Sopenharmony_ci        "BSD libc", "glibc", "musl",
1427db96d56Sopenharmony_ci        # POSIX platforms with pthreads
1437db96d56Sopenharmony_ci        "pthreads",
1447db96d56Sopenharmony_ci    })
1457db96d56Sopenharmony_ci
1467db96d56Sopenharmony_ci    def run(self):
1477db96d56Sopenharmony_ci        availability_ref = ':ref:`Availability <availability>`: '
1487db96d56Sopenharmony_ci        avail_nodes, avail_msgs = self.state.inline_text(
1497db96d56Sopenharmony_ci            availability_ref + self.arguments[0],
1507db96d56Sopenharmony_ci            self.lineno)
1517db96d56Sopenharmony_ci        pnode = nodes.paragraph(availability_ref + self.arguments[0],
1527db96d56Sopenharmony_ci                                '', *avail_nodes, *avail_msgs)
1537db96d56Sopenharmony_ci        self.set_source_info(pnode)
1547db96d56Sopenharmony_ci        cnode = nodes.container("", pnode, classes=["availability"])
1557db96d56Sopenharmony_ci        self.set_source_info(cnode)
1567db96d56Sopenharmony_ci        if self.content:
1577db96d56Sopenharmony_ci            self.state.nested_parse(self.content, self.content_offset, cnode)
1587db96d56Sopenharmony_ci        self.parse_platforms()
1597db96d56Sopenharmony_ci
1607db96d56Sopenharmony_ci        return [cnode]
1617db96d56Sopenharmony_ci
1627db96d56Sopenharmony_ci    def parse_platforms(self):
1637db96d56Sopenharmony_ci        """Parse platform information from arguments
1647db96d56Sopenharmony_ci
1657db96d56Sopenharmony_ci        Arguments is a comma-separated string of platforms. A platform may
1667db96d56Sopenharmony_ci        be prefixed with "not " to indicate that a feature is not available.
1677db96d56Sopenharmony_ci
1687db96d56Sopenharmony_ci        Example::
1697db96d56Sopenharmony_ci
1707db96d56Sopenharmony_ci           .. availability:: Windows, Linux >= 4.2, not Emscripten, not WASI
1717db96d56Sopenharmony_ci
1727db96d56Sopenharmony_ci        Arguments like "Linux >= 3.17 with glibc >= 2.27" are currently not
1737db96d56Sopenharmony_ci        parsed into separate tokens.
1747db96d56Sopenharmony_ci        """
1757db96d56Sopenharmony_ci        platforms = {}
1767db96d56Sopenharmony_ci        for arg in self.arguments[0].rstrip(".").split(","):
1777db96d56Sopenharmony_ci            arg = arg.strip()
1787db96d56Sopenharmony_ci            platform, _, version = arg.partition(" >= ")
1797db96d56Sopenharmony_ci            if platform.startswith("not "):
1807db96d56Sopenharmony_ci                version = False
1817db96d56Sopenharmony_ci                platform = platform[4:]
1827db96d56Sopenharmony_ci            elif not version:
1837db96d56Sopenharmony_ci                version = True
1847db96d56Sopenharmony_ci            platforms[platform] = version
1857db96d56Sopenharmony_ci
1867db96d56Sopenharmony_ci        unknown = set(platforms).difference(self.known_platforms)
1877db96d56Sopenharmony_ci        if unknown:
1887db96d56Sopenharmony_ci            cls = type(self)
1897db96d56Sopenharmony_ci            logger = logging.getLogger(cls.__qualname__)
1907db96d56Sopenharmony_ci            logger.warn(
1917db96d56Sopenharmony_ci                f"Unknown platform(s) or syntax '{' '.join(sorted(unknown))}' "
1927db96d56Sopenharmony_ci                f"in '.. availability:: {self.arguments[0]}', see "
1937db96d56Sopenharmony_ci                f"{__file__}:{cls.__qualname__}.known_platforms for a set "
1947db96d56Sopenharmony_ci                "known platforms."
1957db96d56Sopenharmony_ci            )
1967db96d56Sopenharmony_ci
1977db96d56Sopenharmony_ci        return platforms
1987db96d56Sopenharmony_ci
1997db96d56Sopenharmony_ci
2007db96d56Sopenharmony_ci
2017db96d56Sopenharmony_ci# Support for documenting audit event
2027db96d56Sopenharmony_ci
2037db96d56Sopenharmony_cidef audit_events_purge(app, env, docname):
2047db96d56Sopenharmony_ci    """This is to remove from env.all_audit_events old traces of removed
2057db96d56Sopenharmony_ci    documents.
2067db96d56Sopenharmony_ci    """
2077db96d56Sopenharmony_ci    if not hasattr(env, 'all_audit_events'):
2087db96d56Sopenharmony_ci        return
2097db96d56Sopenharmony_ci    fresh_all_audit_events = {}
2107db96d56Sopenharmony_ci    for name, event in env.all_audit_events.items():
2117db96d56Sopenharmony_ci        event["source"] = [(d, t) for d, t in event["source"] if d != docname]
2127db96d56Sopenharmony_ci        if event["source"]:
2137db96d56Sopenharmony_ci            # Only keep audit_events that have at least one source.
2147db96d56Sopenharmony_ci            fresh_all_audit_events[name] = event
2157db96d56Sopenharmony_ci    env.all_audit_events = fresh_all_audit_events
2167db96d56Sopenharmony_ci
2177db96d56Sopenharmony_ci
2187db96d56Sopenharmony_cidef audit_events_merge(app, env, docnames, other):
2197db96d56Sopenharmony_ci    """In Sphinx parallel builds, this merges env.all_audit_events from
2207db96d56Sopenharmony_ci    subprocesses.
2217db96d56Sopenharmony_ci
2227db96d56Sopenharmony_ci    all_audit_events is a dict of names, with values like:
2237db96d56Sopenharmony_ci    {'source': [(docname, target), ...], 'args': args}
2247db96d56Sopenharmony_ci    """
2257db96d56Sopenharmony_ci    if not hasattr(other, 'all_audit_events'):
2267db96d56Sopenharmony_ci        return
2277db96d56Sopenharmony_ci    if not hasattr(env, 'all_audit_events'):
2287db96d56Sopenharmony_ci        env.all_audit_events = {}
2297db96d56Sopenharmony_ci    for name, value in other.all_audit_events.items():
2307db96d56Sopenharmony_ci        if name in env.all_audit_events:
2317db96d56Sopenharmony_ci            env.all_audit_events[name]["source"].extend(value["source"])
2327db96d56Sopenharmony_ci        else:
2337db96d56Sopenharmony_ci            env.all_audit_events[name] = value
2347db96d56Sopenharmony_ci
2357db96d56Sopenharmony_ci
2367db96d56Sopenharmony_ciclass AuditEvent(Directive):
2377db96d56Sopenharmony_ci
2387db96d56Sopenharmony_ci    has_content = True
2397db96d56Sopenharmony_ci    required_arguments = 1
2407db96d56Sopenharmony_ci    optional_arguments = 2
2417db96d56Sopenharmony_ci    final_argument_whitespace = True
2427db96d56Sopenharmony_ci
2437db96d56Sopenharmony_ci    _label = [
2447db96d56Sopenharmony_ci        "Raises an :ref:`auditing event <auditing>` {name} with no arguments.",
2457db96d56Sopenharmony_ci        "Raises an :ref:`auditing event <auditing>` {name} with argument {args}.",
2467db96d56Sopenharmony_ci        "Raises an :ref:`auditing event <auditing>` {name} with arguments {args}.",
2477db96d56Sopenharmony_ci    ]
2487db96d56Sopenharmony_ci
2497db96d56Sopenharmony_ci    @property
2507db96d56Sopenharmony_ci    def logger(self):
2517db96d56Sopenharmony_ci        cls = type(self)
2527db96d56Sopenharmony_ci        return logging.getLogger(cls.__module__ + "." + cls.__name__)
2537db96d56Sopenharmony_ci
2547db96d56Sopenharmony_ci    def run(self):
2557db96d56Sopenharmony_ci        name = self.arguments[0]
2567db96d56Sopenharmony_ci        if len(self.arguments) >= 2 and self.arguments[1]:
2577db96d56Sopenharmony_ci            args = (a.strip() for a in self.arguments[1].strip("'\"").split(","))
2587db96d56Sopenharmony_ci            args = [a for a in args if a]
2597db96d56Sopenharmony_ci        else:
2607db96d56Sopenharmony_ci            args = []
2617db96d56Sopenharmony_ci
2627db96d56Sopenharmony_ci        label = sphinx_gettext(self._label[min(2, len(args))])
2637db96d56Sopenharmony_ci        text = label.format(name="``{}``".format(name),
2647db96d56Sopenharmony_ci                            args=", ".join("``{}``".format(a) for a in args if a))
2657db96d56Sopenharmony_ci
2667db96d56Sopenharmony_ci        env = self.state.document.settings.env
2677db96d56Sopenharmony_ci        if not hasattr(env, 'all_audit_events'):
2687db96d56Sopenharmony_ci            env.all_audit_events = {}
2697db96d56Sopenharmony_ci
2707db96d56Sopenharmony_ci        new_info = {
2717db96d56Sopenharmony_ci            'source': [],
2727db96d56Sopenharmony_ci            'args': args
2737db96d56Sopenharmony_ci        }
2747db96d56Sopenharmony_ci        info = env.all_audit_events.setdefault(name, new_info)
2757db96d56Sopenharmony_ci        if info is not new_info:
2767db96d56Sopenharmony_ci            if not self._do_args_match(info['args'], new_info['args']):
2777db96d56Sopenharmony_ci                self.logger.warn(
2787db96d56Sopenharmony_ci                    "Mismatched arguments for audit-event {}: {!r} != {!r}"
2797db96d56Sopenharmony_ci                    .format(name, info['args'], new_info['args'])
2807db96d56Sopenharmony_ci                )
2817db96d56Sopenharmony_ci
2827db96d56Sopenharmony_ci        ids = []
2837db96d56Sopenharmony_ci        try:
2847db96d56Sopenharmony_ci            target = self.arguments[2].strip("\"'")
2857db96d56Sopenharmony_ci        except (IndexError, TypeError):
2867db96d56Sopenharmony_ci            target = None
2877db96d56Sopenharmony_ci        if not target:
2887db96d56Sopenharmony_ci            target = "audit_event_{}_{}".format(
2897db96d56Sopenharmony_ci                re.sub(r'\W', '_', name),
2907db96d56Sopenharmony_ci                len(info['source']),
2917db96d56Sopenharmony_ci            )
2927db96d56Sopenharmony_ci            ids.append(target)
2937db96d56Sopenharmony_ci
2947db96d56Sopenharmony_ci        info['source'].append((env.docname, target))
2957db96d56Sopenharmony_ci
2967db96d56Sopenharmony_ci        pnode = nodes.paragraph(text, classes=["audit-hook"], ids=ids)
2977db96d56Sopenharmony_ci        pnode.line = self.lineno
2987db96d56Sopenharmony_ci        if self.content:
2997db96d56Sopenharmony_ci            self.state.nested_parse(self.content, self.content_offset, pnode)
3007db96d56Sopenharmony_ci        else:
3017db96d56Sopenharmony_ci            n, m = self.state.inline_text(text, self.lineno)
3027db96d56Sopenharmony_ci            pnode.extend(n + m)
3037db96d56Sopenharmony_ci
3047db96d56Sopenharmony_ci        return [pnode]
3057db96d56Sopenharmony_ci
3067db96d56Sopenharmony_ci    # This list of sets are allowable synonyms for event argument names.
3077db96d56Sopenharmony_ci    # If two names are in the same set, they are treated as equal for the
3087db96d56Sopenharmony_ci    # purposes of warning. This won't help if number of arguments is
3097db96d56Sopenharmony_ci    # different!
3107db96d56Sopenharmony_ci    _SYNONYMS = [
3117db96d56Sopenharmony_ci        {"file", "path", "fd"},
3127db96d56Sopenharmony_ci    ]
3137db96d56Sopenharmony_ci
3147db96d56Sopenharmony_ci    def _do_args_match(self, args1, args2):
3157db96d56Sopenharmony_ci        if args1 == args2:
3167db96d56Sopenharmony_ci            return True
3177db96d56Sopenharmony_ci        if len(args1) != len(args2):
3187db96d56Sopenharmony_ci            return False
3197db96d56Sopenharmony_ci        for a1, a2 in zip(args1, args2):
3207db96d56Sopenharmony_ci            if a1 == a2:
3217db96d56Sopenharmony_ci                continue
3227db96d56Sopenharmony_ci            if any(a1 in s and a2 in s for s in self._SYNONYMS):
3237db96d56Sopenharmony_ci                continue
3247db96d56Sopenharmony_ci            return False
3257db96d56Sopenharmony_ci        return True
3267db96d56Sopenharmony_ci
3277db96d56Sopenharmony_ci
3287db96d56Sopenharmony_ciclass audit_event_list(nodes.General, nodes.Element):
3297db96d56Sopenharmony_ci    pass
3307db96d56Sopenharmony_ci
3317db96d56Sopenharmony_ci
3327db96d56Sopenharmony_ciclass AuditEventListDirective(Directive):
3337db96d56Sopenharmony_ci
3347db96d56Sopenharmony_ci    def run(self):
3357db96d56Sopenharmony_ci        return [audit_event_list('')]
3367db96d56Sopenharmony_ci
3377db96d56Sopenharmony_ci
3387db96d56Sopenharmony_ci# Support for documenting decorators
3397db96d56Sopenharmony_ci
3407db96d56Sopenharmony_ciclass PyDecoratorMixin(object):
3417db96d56Sopenharmony_ci    def handle_signature(self, sig, signode):
3427db96d56Sopenharmony_ci        ret = super(PyDecoratorMixin, self).handle_signature(sig, signode)
3437db96d56Sopenharmony_ci        signode.insert(0, addnodes.desc_addname('@', '@'))
3447db96d56Sopenharmony_ci        return ret
3457db96d56Sopenharmony_ci
3467db96d56Sopenharmony_ci    def needs_arglist(self):
3477db96d56Sopenharmony_ci        return False
3487db96d56Sopenharmony_ci
3497db96d56Sopenharmony_ci
3507db96d56Sopenharmony_ciclass PyDecoratorFunction(PyDecoratorMixin, PyFunction):
3517db96d56Sopenharmony_ci    def run(self):
3527db96d56Sopenharmony_ci        # a decorator function is a function after all
3537db96d56Sopenharmony_ci        self.name = 'py:function'
3547db96d56Sopenharmony_ci        return PyFunction.run(self)
3557db96d56Sopenharmony_ci
3567db96d56Sopenharmony_ci
3577db96d56Sopenharmony_ci# TODO: Use sphinx.domains.python.PyDecoratorMethod when possible
3587db96d56Sopenharmony_ciclass PyDecoratorMethod(PyDecoratorMixin, PyMethod):
3597db96d56Sopenharmony_ci    def run(self):
3607db96d56Sopenharmony_ci        self.name = 'py:method'
3617db96d56Sopenharmony_ci        return PyMethod.run(self)
3627db96d56Sopenharmony_ci
3637db96d56Sopenharmony_ci
3647db96d56Sopenharmony_ciclass PyCoroutineMixin(object):
3657db96d56Sopenharmony_ci    def handle_signature(self, sig, signode):
3667db96d56Sopenharmony_ci        ret = super(PyCoroutineMixin, self).handle_signature(sig, signode)
3677db96d56Sopenharmony_ci        signode.insert(0, addnodes.desc_annotation('coroutine ', 'coroutine '))
3687db96d56Sopenharmony_ci        return ret
3697db96d56Sopenharmony_ci
3707db96d56Sopenharmony_ci
3717db96d56Sopenharmony_ciclass PyAwaitableMixin(object):
3727db96d56Sopenharmony_ci    def handle_signature(self, sig, signode):
3737db96d56Sopenharmony_ci        ret = super(PyAwaitableMixin, self).handle_signature(sig, signode)
3747db96d56Sopenharmony_ci        signode.insert(0, addnodes.desc_annotation('awaitable ', 'awaitable '))
3757db96d56Sopenharmony_ci        return ret
3767db96d56Sopenharmony_ci
3777db96d56Sopenharmony_ci
3787db96d56Sopenharmony_ciclass PyCoroutineFunction(PyCoroutineMixin, PyFunction):
3797db96d56Sopenharmony_ci    def run(self):
3807db96d56Sopenharmony_ci        self.name = 'py:function'
3817db96d56Sopenharmony_ci        return PyFunction.run(self)
3827db96d56Sopenharmony_ci
3837db96d56Sopenharmony_ci
3847db96d56Sopenharmony_ciclass PyCoroutineMethod(PyCoroutineMixin, PyMethod):
3857db96d56Sopenharmony_ci    def run(self):
3867db96d56Sopenharmony_ci        self.name = 'py:method'
3877db96d56Sopenharmony_ci        return PyMethod.run(self)
3887db96d56Sopenharmony_ci
3897db96d56Sopenharmony_ci
3907db96d56Sopenharmony_ciclass PyAwaitableFunction(PyAwaitableMixin, PyFunction):
3917db96d56Sopenharmony_ci    def run(self):
3927db96d56Sopenharmony_ci        self.name = 'py:function'
3937db96d56Sopenharmony_ci        return PyFunction.run(self)
3947db96d56Sopenharmony_ci
3957db96d56Sopenharmony_ci
3967db96d56Sopenharmony_ciclass PyAwaitableMethod(PyAwaitableMixin, PyMethod):
3977db96d56Sopenharmony_ci    def run(self):
3987db96d56Sopenharmony_ci        self.name = 'py:method'
3997db96d56Sopenharmony_ci        return PyMethod.run(self)
4007db96d56Sopenharmony_ci
4017db96d56Sopenharmony_ci
4027db96d56Sopenharmony_ciclass PyAbstractMethod(PyMethod):
4037db96d56Sopenharmony_ci
4047db96d56Sopenharmony_ci    def handle_signature(self, sig, signode):
4057db96d56Sopenharmony_ci        ret = super(PyAbstractMethod, self).handle_signature(sig, signode)
4067db96d56Sopenharmony_ci        signode.insert(0, addnodes.desc_annotation('abstractmethod ',
4077db96d56Sopenharmony_ci                                                   'abstractmethod '))
4087db96d56Sopenharmony_ci        return ret
4097db96d56Sopenharmony_ci
4107db96d56Sopenharmony_ci    def run(self):
4117db96d56Sopenharmony_ci        self.name = 'py:method'
4127db96d56Sopenharmony_ci        return PyMethod.run(self)
4137db96d56Sopenharmony_ci
4147db96d56Sopenharmony_ci
4157db96d56Sopenharmony_ci# Support for documenting version of removal in deprecations
4167db96d56Sopenharmony_ci
4177db96d56Sopenharmony_ciclass DeprecatedRemoved(Directive):
4187db96d56Sopenharmony_ci    has_content = True
4197db96d56Sopenharmony_ci    required_arguments = 2
4207db96d56Sopenharmony_ci    optional_arguments = 1
4217db96d56Sopenharmony_ci    final_argument_whitespace = True
4227db96d56Sopenharmony_ci    option_spec = {}
4237db96d56Sopenharmony_ci
4247db96d56Sopenharmony_ci    _deprecated_label = 'Deprecated since version {deprecated}, will be removed in version {removed}'
4257db96d56Sopenharmony_ci    _removed_label = 'Deprecated since version {deprecated}, removed in version {removed}'
4267db96d56Sopenharmony_ci
4277db96d56Sopenharmony_ci    def run(self):
4287db96d56Sopenharmony_ci        node = addnodes.versionmodified()
4297db96d56Sopenharmony_ci        node.document = self.state.document
4307db96d56Sopenharmony_ci        node['type'] = 'deprecated-removed'
4317db96d56Sopenharmony_ci        version = (self.arguments[0], self.arguments[1])
4327db96d56Sopenharmony_ci        node['version'] = version
4337db96d56Sopenharmony_ci        env = self.state.document.settings.env
4347db96d56Sopenharmony_ci        current_version = tuple(int(e) for e in env.config.version.split('.'))
4357db96d56Sopenharmony_ci        removed_version = tuple(int(e) for e in self.arguments[1].split('.'))
4367db96d56Sopenharmony_ci        if current_version < removed_version:
4377db96d56Sopenharmony_ci            label = self._deprecated_label
4387db96d56Sopenharmony_ci        else:
4397db96d56Sopenharmony_ci            label = self._removed_label
4407db96d56Sopenharmony_ci
4417db96d56Sopenharmony_ci        label = sphinx_gettext(label)
4427db96d56Sopenharmony_ci        text = label.format(deprecated=self.arguments[0], removed=self.arguments[1])
4437db96d56Sopenharmony_ci        if len(self.arguments) == 3:
4447db96d56Sopenharmony_ci            inodes, messages = self.state.inline_text(self.arguments[2],
4457db96d56Sopenharmony_ci                                                      self.lineno+1)
4467db96d56Sopenharmony_ci            para = nodes.paragraph(self.arguments[2], '', *inodes, translatable=False)
4477db96d56Sopenharmony_ci            node.append(para)
4487db96d56Sopenharmony_ci        else:
4497db96d56Sopenharmony_ci            messages = []
4507db96d56Sopenharmony_ci        if self.content:
4517db96d56Sopenharmony_ci            self.state.nested_parse(self.content, self.content_offset, node)
4527db96d56Sopenharmony_ci        if len(node):
4537db96d56Sopenharmony_ci            if isinstance(node[0], nodes.paragraph) and node[0].rawsource:
4547db96d56Sopenharmony_ci                content = nodes.inline(node[0].rawsource, translatable=True)
4557db96d56Sopenharmony_ci                content.source = node[0].source
4567db96d56Sopenharmony_ci                content.line = node[0].line
4577db96d56Sopenharmony_ci                content += node[0].children
4587db96d56Sopenharmony_ci                node[0].replace_self(nodes.paragraph('', '', content, translatable=False))
4597db96d56Sopenharmony_ci            node[0].insert(0, nodes.inline('', '%s: ' % text,
4607db96d56Sopenharmony_ci                                           classes=['versionmodified']))
4617db96d56Sopenharmony_ci        else:
4627db96d56Sopenharmony_ci            para = nodes.paragraph('', '',
4637db96d56Sopenharmony_ci                                   nodes.inline('', '%s.' % text,
4647db96d56Sopenharmony_ci                                                classes=['versionmodified']),
4657db96d56Sopenharmony_ci                                   translatable=False)
4667db96d56Sopenharmony_ci            node.append(para)
4677db96d56Sopenharmony_ci        env = self.state.document.settings.env
4687db96d56Sopenharmony_ci        env.get_domain('changeset').note_changeset(node)
4697db96d56Sopenharmony_ci        return [node] + messages
4707db96d56Sopenharmony_ci
4717db96d56Sopenharmony_ci
4727db96d56Sopenharmony_ci# Support for including Misc/NEWS
4737db96d56Sopenharmony_ci
4747db96d56Sopenharmony_ciissue_re = re.compile('(?:[Ii]ssue #|bpo-)([0-9]+)', re.I)
4757db96d56Sopenharmony_cigh_issue_re = re.compile('(?:gh-issue-|gh-)([0-9]+)', re.I)
4767db96d56Sopenharmony_ciwhatsnew_re = re.compile(r"(?im)^what's new in (.*?)\??$")
4777db96d56Sopenharmony_ci
4787db96d56Sopenharmony_ci
4797db96d56Sopenharmony_ciclass MiscNews(Directive):
4807db96d56Sopenharmony_ci    has_content = False
4817db96d56Sopenharmony_ci    required_arguments = 1
4827db96d56Sopenharmony_ci    optional_arguments = 0
4837db96d56Sopenharmony_ci    final_argument_whitespace = False
4847db96d56Sopenharmony_ci    option_spec = {}
4857db96d56Sopenharmony_ci
4867db96d56Sopenharmony_ci    def run(self):
4877db96d56Sopenharmony_ci        fname = self.arguments[0]
4887db96d56Sopenharmony_ci        source = self.state_machine.input_lines.source(
4897db96d56Sopenharmony_ci            self.lineno - self.state_machine.input_offset - 1)
4907db96d56Sopenharmony_ci        source_dir = getenv('PY_MISC_NEWS_DIR')
4917db96d56Sopenharmony_ci        if not source_dir:
4927db96d56Sopenharmony_ci            source_dir = path.dirname(path.abspath(source))
4937db96d56Sopenharmony_ci        fpath = path.join(source_dir, fname)
4947db96d56Sopenharmony_ci        self.state.document.settings.record_dependencies.add(fpath)
4957db96d56Sopenharmony_ci        try:
4967db96d56Sopenharmony_ci            with io.open(fpath, encoding='utf-8') as fp:
4977db96d56Sopenharmony_ci                content = fp.read()
4987db96d56Sopenharmony_ci        except Exception:
4997db96d56Sopenharmony_ci            text = 'The NEWS file is not available.'
5007db96d56Sopenharmony_ci            node = nodes.strong(text, text)
5017db96d56Sopenharmony_ci            return [node]
5027db96d56Sopenharmony_ci        content = issue_re.sub(r':issue:`\1`', content)
5037db96d56Sopenharmony_ci        # Fallback handling for the GitHub issue
5047db96d56Sopenharmony_ci        content = gh_issue_re.sub(r':gh:`\1`', content)
5057db96d56Sopenharmony_ci        content = whatsnew_re.sub(r'\1', content)
5067db96d56Sopenharmony_ci        # remove first 3 lines as they are the main heading
5077db96d56Sopenharmony_ci        lines = ['.. default-role:: obj', ''] + content.splitlines()[3:]
5087db96d56Sopenharmony_ci        self.state_machine.insert_input(lines, fname)
5097db96d56Sopenharmony_ci        return []
5107db96d56Sopenharmony_ci
5117db96d56Sopenharmony_ci
5127db96d56Sopenharmony_ci# Support for building "topic help" for pydoc
5137db96d56Sopenharmony_ci
5147db96d56Sopenharmony_cipydoc_topic_labels = [
5157db96d56Sopenharmony_ci    'assert', 'assignment', 'async', 'atom-identifiers', 'atom-literals',
5167db96d56Sopenharmony_ci    'attribute-access', 'attribute-references', 'augassign', 'await',
5177db96d56Sopenharmony_ci    'binary', 'bitwise', 'bltin-code-objects', 'bltin-ellipsis-object',
5187db96d56Sopenharmony_ci    'bltin-null-object', 'bltin-type-objects', 'booleans',
5197db96d56Sopenharmony_ci    'break', 'callable-types', 'calls', 'class', 'comparisons', 'compound',
5207db96d56Sopenharmony_ci    'context-managers', 'continue', 'conversions', 'customization', 'debugger',
5217db96d56Sopenharmony_ci    'del', 'dict', 'dynamic-features', 'else', 'exceptions', 'execmodel',
5227db96d56Sopenharmony_ci    'exprlists', 'floating', 'for', 'formatstrings', 'function', 'global',
5237db96d56Sopenharmony_ci    'id-classes', 'identifiers', 'if', 'imaginary', 'import', 'in', 'integers',
5247db96d56Sopenharmony_ci    'lambda', 'lists', 'naming', 'nonlocal', 'numbers', 'numeric-types',
5257db96d56Sopenharmony_ci    'objects', 'operator-summary', 'pass', 'power', 'raise', 'return',
5267db96d56Sopenharmony_ci    'sequence-types', 'shifting', 'slicings', 'specialattrs', 'specialnames',
5277db96d56Sopenharmony_ci    'string-methods', 'strings', 'subscriptions', 'truth', 'try', 'types',
5287db96d56Sopenharmony_ci    'typesfunctions', 'typesmapping', 'typesmethods', 'typesmodules',
5297db96d56Sopenharmony_ci    'typesseq', 'typesseq-mutable', 'unary', 'while', 'with', 'yield'
5307db96d56Sopenharmony_ci]
5317db96d56Sopenharmony_ci
5327db96d56Sopenharmony_ci
5337db96d56Sopenharmony_ciclass PydocTopicsBuilder(Builder):
5347db96d56Sopenharmony_ci    name = 'pydoc-topics'
5357db96d56Sopenharmony_ci
5367db96d56Sopenharmony_ci    default_translator_class = TextTranslator
5377db96d56Sopenharmony_ci
5387db96d56Sopenharmony_ci    def init(self):
5397db96d56Sopenharmony_ci        self.topics = {}
5407db96d56Sopenharmony_ci        self.secnumbers = {}
5417db96d56Sopenharmony_ci
5427db96d56Sopenharmony_ci    def get_outdated_docs(self):
5437db96d56Sopenharmony_ci        return 'all pydoc topics'
5447db96d56Sopenharmony_ci
5457db96d56Sopenharmony_ci    def get_target_uri(self, docname, typ=None):
5467db96d56Sopenharmony_ci        return ''  # no URIs
5477db96d56Sopenharmony_ci
5487db96d56Sopenharmony_ci    def write(self, *ignored):
5497db96d56Sopenharmony_ci        writer = TextWriter(self)
5507db96d56Sopenharmony_ci        for label in status_iterator(pydoc_topic_labels,
5517db96d56Sopenharmony_ci                                     'building topics... ',
5527db96d56Sopenharmony_ci                                     length=len(pydoc_topic_labels)):
5537db96d56Sopenharmony_ci            if label not in self.env.domaindata['std']['labels']:
5547db96d56Sopenharmony_ci                self.env.logger.warn('label %r not in documentation' % label)
5557db96d56Sopenharmony_ci                continue
5567db96d56Sopenharmony_ci            docname, labelid, sectname = self.env.domaindata['std']['labels'][label]
5577db96d56Sopenharmony_ci            doctree = self.env.get_and_resolve_doctree(docname, self)
5587db96d56Sopenharmony_ci            document = new_document('<section node>')
5597db96d56Sopenharmony_ci            document.append(doctree.ids[labelid])
5607db96d56Sopenharmony_ci            destination = StringOutput(encoding='utf-8')
5617db96d56Sopenharmony_ci            writer.write(document, destination)
5627db96d56Sopenharmony_ci            self.topics[label] = writer.output
5637db96d56Sopenharmony_ci
5647db96d56Sopenharmony_ci    def finish(self):
5657db96d56Sopenharmony_ci        f = open(path.join(self.outdir, 'topics.py'), 'wb')
5667db96d56Sopenharmony_ci        try:
5677db96d56Sopenharmony_ci            f.write('# -*- coding: utf-8 -*-\n'.encode('utf-8'))
5687db96d56Sopenharmony_ci            f.write(('# Autogenerated by Sphinx on %s\n' % asctime()).encode('utf-8'))
5697db96d56Sopenharmony_ci            f.write(('topics = ' + pformat(self.topics) + '\n').encode('utf-8'))
5707db96d56Sopenharmony_ci        finally:
5717db96d56Sopenharmony_ci            f.close()
5727db96d56Sopenharmony_ci
5737db96d56Sopenharmony_ci
5747db96d56Sopenharmony_ci# Support for documenting Opcodes
5757db96d56Sopenharmony_ci
5767db96d56Sopenharmony_ciopcode_sig_re = re.compile(r'(\w+(?:\+\d)?)(?:\s*\((.*)\))?')
5777db96d56Sopenharmony_ci
5787db96d56Sopenharmony_ci
5797db96d56Sopenharmony_cidef parse_opcode_signature(env, sig, signode):
5807db96d56Sopenharmony_ci    """Transform an opcode signature into RST nodes."""
5817db96d56Sopenharmony_ci    m = opcode_sig_re.match(sig)
5827db96d56Sopenharmony_ci    if m is None:
5837db96d56Sopenharmony_ci        raise ValueError
5847db96d56Sopenharmony_ci    opname, arglist = m.groups()
5857db96d56Sopenharmony_ci    signode += addnodes.desc_name(opname, opname)
5867db96d56Sopenharmony_ci    if arglist is not None:
5877db96d56Sopenharmony_ci        paramlist = addnodes.desc_parameterlist()
5887db96d56Sopenharmony_ci        signode += paramlist
5897db96d56Sopenharmony_ci        paramlist += addnodes.desc_parameter(arglist, arglist)
5907db96d56Sopenharmony_ci    return opname.strip()
5917db96d56Sopenharmony_ci
5927db96d56Sopenharmony_ci
5937db96d56Sopenharmony_ci# Support for documenting pdb commands
5947db96d56Sopenharmony_ci
5957db96d56Sopenharmony_cipdbcmd_sig_re = re.compile(r'([a-z()!]+)\s*(.*)')
5967db96d56Sopenharmony_ci
5977db96d56Sopenharmony_ci# later...
5987db96d56Sopenharmony_ci# pdbargs_tokens_re = re.compile(r'''[a-zA-Z]+  |  # identifiers
5997db96d56Sopenharmony_ci#                                   [.,:]+     |  # punctuation
6007db96d56Sopenharmony_ci#                                   [\[\]()]   |  # parens
6017db96d56Sopenharmony_ci#                                   \s+           # whitespace
6027db96d56Sopenharmony_ci#                                   ''', re.X)
6037db96d56Sopenharmony_ci
6047db96d56Sopenharmony_ci
6057db96d56Sopenharmony_cidef parse_pdb_command(env, sig, signode):
6067db96d56Sopenharmony_ci    """Transform a pdb command signature into RST nodes."""
6077db96d56Sopenharmony_ci    m = pdbcmd_sig_re.match(sig)
6087db96d56Sopenharmony_ci    if m is None:
6097db96d56Sopenharmony_ci        raise ValueError
6107db96d56Sopenharmony_ci    name, args = m.groups()
6117db96d56Sopenharmony_ci    fullname = name.replace('(', '').replace(')', '')
6127db96d56Sopenharmony_ci    signode += addnodes.desc_name(name, name)
6137db96d56Sopenharmony_ci    if args:
6147db96d56Sopenharmony_ci        signode += addnodes.desc_addname(' '+args, ' '+args)
6157db96d56Sopenharmony_ci    return fullname
6167db96d56Sopenharmony_ci
6177db96d56Sopenharmony_ci
6187db96d56Sopenharmony_cidef process_audit_events(app, doctree, fromdocname):
6197db96d56Sopenharmony_ci    for node in doctree.traverse(audit_event_list):
6207db96d56Sopenharmony_ci        break
6217db96d56Sopenharmony_ci    else:
6227db96d56Sopenharmony_ci        return
6237db96d56Sopenharmony_ci
6247db96d56Sopenharmony_ci    env = app.builder.env
6257db96d56Sopenharmony_ci
6267db96d56Sopenharmony_ci    table = nodes.table(cols=3)
6277db96d56Sopenharmony_ci    group = nodes.tgroup(
6287db96d56Sopenharmony_ci        '',
6297db96d56Sopenharmony_ci        nodes.colspec(colwidth=30),
6307db96d56Sopenharmony_ci        nodes.colspec(colwidth=55),
6317db96d56Sopenharmony_ci        nodes.colspec(colwidth=15),
6327db96d56Sopenharmony_ci        cols=3,
6337db96d56Sopenharmony_ci    )
6347db96d56Sopenharmony_ci    head = nodes.thead()
6357db96d56Sopenharmony_ci    body = nodes.tbody()
6367db96d56Sopenharmony_ci
6377db96d56Sopenharmony_ci    table += group
6387db96d56Sopenharmony_ci    group += head
6397db96d56Sopenharmony_ci    group += body
6407db96d56Sopenharmony_ci
6417db96d56Sopenharmony_ci    row = nodes.row()
6427db96d56Sopenharmony_ci    row += nodes.entry('', nodes.paragraph('', nodes.Text('Audit event')))
6437db96d56Sopenharmony_ci    row += nodes.entry('', nodes.paragraph('', nodes.Text('Arguments')))
6447db96d56Sopenharmony_ci    row += nodes.entry('', nodes.paragraph('', nodes.Text('References')))
6457db96d56Sopenharmony_ci    head += row
6467db96d56Sopenharmony_ci
6477db96d56Sopenharmony_ci    for name in sorted(getattr(env, "all_audit_events", ())):
6487db96d56Sopenharmony_ci        audit_event = env.all_audit_events[name]
6497db96d56Sopenharmony_ci
6507db96d56Sopenharmony_ci        row = nodes.row()
6517db96d56Sopenharmony_ci        node = nodes.paragraph('', nodes.Text(name))
6527db96d56Sopenharmony_ci        row += nodes.entry('', node)
6537db96d56Sopenharmony_ci
6547db96d56Sopenharmony_ci        node = nodes.paragraph()
6557db96d56Sopenharmony_ci        for i, a in enumerate(audit_event['args']):
6567db96d56Sopenharmony_ci            if i:
6577db96d56Sopenharmony_ci                node += nodes.Text(", ")
6587db96d56Sopenharmony_ci            node += nodes.literal(a, nodes.Text(a))
6597db96d56Sopenharmony_ci        row += nodes.entry('', node)
6607db96d56Sopenharmony_ci
6617db96d56Sopenharmony_ci        node = nodes.paragraph()
6627db96d56Sopenharmony_ci        backlinks = enumerate(sorted(set(audit_event['source'])), start=1)
6637db96d56Sopenharmony_ci        for i, (doc, label) in backlinks:
6647db96d56Sopenharmony_ci            if isinstance(label, str):
6657db96d56Sopenharmony_ci                ref = nodes.reference("", nodes.Text("[{}]".format(i)), internal=True)
6667db96d56Sopenharmony_ci                try:
6677db96d56Sopenharmony_ci                    ref['refuri'] = "{}#{}".format(
6687db96d56Sopenharmony_ci                        app.builder.get_relative_uri(fromdocname, doc),
6697db96d56Sopenharmony_ci                        label,
6707db96d56Sopenharmony_ci                    )
6717db96d56Sopenharmony_ci                except NoUri:
6727db96d56Sopenharmony_ci                    continue
6737db96d56Sopenharmony_ci                node += ref
6747db96d56Sopenharmony_ci        row += nodes.entry('', node)
6757db96d56Sopenharmony_ci
6767db96d56Sopenharmony_ci        body += row
6777db96d56Sopenharmony_ci
6787db96d56Sopenharmony_ci    for node in doctree.traverse(audit_event_list):
6797db96d56Sopenharmony_ci        node.replace_self(table)
6807db96d56Sopenharmony_ci
6817db96d56Sopenharmony_ci
6827db96d56Sopenharmony_cidef patch_pairindextypes(app, _env) -> None:
6837db96d56Sopenharmony_ci    """Remove all entries from ``pairindextypes`` before writing POT files.
6847db96d56Sopenharmony_ci
6857db96d56Sopenharmony_ci    We want to run this just before writing output files, as the check to
6867db96d56Sopenharmony_ci    circumvent is in ``I18nBuilder.write_doc()``.
6877db96d56Sopenharmony_ci    As such, we link this to ``env-check-consistency``, even though it has
6887db96d56Sopenharmony_ci    nothing to do with the environment consistency check.
6897db96d56Sopenharmony_ci    """
6907db96d56Sopenharmony_ci    if app.builder.name != 'gettext':
6917db96d56Sopenharmony_ci        return
6927db96d56Sopenharmony_ci
6937db96d56Sopenharmony_ci    # allow translating deprecated index entries
6947db96d56Sopenharmony_ci    try:
6957db96d56Sopenharmony_ci        from sphinx.domains.python import pairindextypes
6967db96d56Sopenharmony_ci    except ImportError:
6977db96d56Sopenharmony_ci        pass
6987db96d56Sopenharmony_ci    else:
6997db96d56Sopenharmony_ci        # Sphinx checks if a 'pair' type entry on an index directive is one of
7007db96d56Sopenharmony_ci        # the Sphinx-translated pairindextypes values. As we intend to move
7017db96d56Sopenharmony_ci        # away from this, we need Sphinx to believe that these values don't
7027db96d56Sopenharmony_ci        # exist, by deleting them when using the gettext builder.
7037db96d56Sopenharmony_ci        pairindextypes.clear()
7047db96d56Sopenharmony_ci
7057db96d56Sopenharmony_ci
7067db96d56Sopenharmony_cidef setup(app):
7077db96d56Sopenharmony_ci    app.add_role('issue', issue_role)
7087db96d56Sopenharmony_ci    app.add_role('gh', gh_issue_role)
7097db96d56Sopenharmony_ci    app.add_role('source', source_role)
7107db96d56Sopenharmony_ci    app.add_directive('impl-detail', ImplementationDetail)
7117db96d56Sopenharmony_ci    app.add_directive('availability', Availability)
7127db96d56Sopenharmony_ci    app.add_directive('audit-event', AuditEvent)
7137db96d56Sopenharmony_ci    app.add_directive('audit-event-table', AuditEventListDirective)
7147db96d56Sopenharmony_ci    app.add_directive('deprecated-removed', DeprecatedRemoved)
7157db96d56Sopenharmony_ci    app.add_builder(PydocTopicsBuilder)
7167db96d56Sopenharmony_ci    app.add_builder(suspicious.CheckSuspiciousMarkupBuilder)
7177db96d56Sopenharmony_ci    app.add_object_type('opcode', 'opcode', '%s (opcode)', parse_opcode_signature)
7187db96d56Sopenharmony_ci    app.add_object_type('pdbcommand', 'pdbcmd', '%s (pdb command)', parse_pdb_command)
7197db96d56Sopenharmony_ci    app.add_object_type('2to3fixer', '2to3fixer', '%s (2to3 fixer)')
7207db96d56Sopenharmony_ci    app.add_directive_to_domain('py', 'decorator', PyDecoratorFunction)
7217db96d56Sopenharmony_ci    app.add_directive_to_domain('py', 'decoratormethod', PyDecoratorMethod)
7227db96d56Sopenharmony_ci    app.add_directive_to_domain('py', 'coroutinefunction', PyCoroutineFunction)
7237db96d56Sopenharmony_ci    app.add_directive_to_domain('py', 'coroutinemethod', PyCoroutineMethod)
7247db96d56Sopenharmony_ci    app.add_directive_to_domain('py', 'awaitablefunction', PyAwaitableFunction)
7257db96d56Sopenharmony_ci    app.add_directive_to_domain('py', 'awaitablemethod', PyAwaitableMethod)
7267db96d56Sopenharmony_ci    app.add_directive_to_domain('py', 'abstractmethod', PyAbstractMethod)
7277db96d56Sopenharmony_ci    app.add_directive('miscnews', MiscNews)
7287db96d56Sopenharmony_ci    app.connect('env-check-consistency', patch_pairindextypes)
7297db96d56Sopenharmony_ci    app.connect('doctree-resolved', process_audit_events)
7307db96d56Sopenharmony_ci    app.connect('env-merge-info', audit_events_merge)
7317db96d56Sopenharmony_ci    app.connect('env-purge-doc', audit_events_purge)
7327db96d56Sopenharmony_ci    return {'version': '1.0', 'parallel_read_safe': True}
733