File: builder.py

package info (click to toggle)
python-discord 2.6.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 8,476 kB
  • sloc: python: 49,910; javascript: 363; makefile: 154
file content (157 lines) | stat: -rw-r--r-- 6,014 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import datetime
import os
import re

from sphinx.builders.gettext import GettextRenderer, I18nBuilder, MessageCatalogBuilder, should_write
from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.environment.adapters.indexentries import IndexEntries
from sphinx.locale import __
from sphinx.util.osutil import ensuredir
from sphinx.writers.html5 import HTML5Translator

try:
    # Latest sphinx version lets you import ctime directly.
    from sphinx import version_info

    comp = version_info[:3]
    if comp >= (7, 2, 0):
        from sphinx.builders.gettext import ctime
        from sphinx.util.display import status_iterator
    else:
        from sphinx.builders.gettext import ltz, timestamp

        ctime = datetime.datetime.fromtimestamp(timestamp, ltz).strftime('%Y-%m-%d %H:%M%z')
        from sphinx.util import status_iterator

except Exception as exc:
    # Fallback
    import time

    if (source_date_epoch := os.getenv('SOURCE_DATE_EPOCH')) is not None:
        timestamp = time.gmtime(float(source_date_epoch))
    else:
        # determine timestamp once to remain unaffected by DST changes during build
        timestamp = time.localtime()
    ctime = time.strftime('%Y-%m-%d %H:%M%z', timestamp)

class DPYHTML5Translator(HTML5Translator):
    def visit_section(self, node):
        self.section_level += 1
        self.body.append(self.starttag(node, 'section'))

    def depart_section(self, node):
        self.section_level -= 1
        self.body.append('</section>\n')

    def visit_table(self, node):
        self.body.append('<div class="table-wrapper">')
        super().visit_table(node)

    def depart_table(self, node):
        super().depart_table(node)
        self.body.append('</div>')


class DPYStandaloneHTMLBuilder(StandaloneHTMLBuilder):
    # This is mostly copy pasted from Sphinx.
    def write_genindex(self) -> None:
        # the total count of lines for each index letter, used to distribute
        # the entries into two columns
        genindex = IndexEntries(self.env).create_index(self, group_entries=False)
        indexcounts = []
        for _k, entries in genindex:
            indexcounts.append(sum(1 + len(subitems) for _, (_, subitems, _) in entries))

        genindexcontext = {
            'genindexentries': genindex,
            'genindexcounts': indexcounts,
            'split_index': self.config.html_split_index,
        }

        if self.config.html_split_index:
            self.handle_page('genindex', genindexcontext, 'genindex-split.html')
            self.handle_page('genindex-all', genindexcontext, 'genindex.html')
            for (key, entries), count in zip(genindex, indexcounts):
                ctx = {'key': key, 'entries': entries, 'count': count, 'genindexentries': genindex}
                self.handle_page('genindex-' + key, ctx, 'genindex-single.html')
        else:
            self.handle_page('genindex', genindexcontext, 'genindex.html')


class DPYMessageCatalogBuilder(MessageCatalogBuilder):
    _ADMONITION_REGEX = re.compile(r'\.\.\s*[a-zA-Z\_-]+::')

    def finish(self) -> None:
        # Bypass MessageCatalogBuilder.finish
        I18nBuilder.finish(self)

        # This is mostly copy pasted from Sphinx
        # However, this allows
        context = {
            'version': self.config.version,
            'copyright': self.config.copyright,
            'project': self.config.project,
            'last_translator': self.config.gettext_last_translator,
            'language_team': self.config.gettext_language_team,
            'ctime': ctime,
            'display_location': self.config.gettext_location,
            'display_uuid': self.config.gettext_uuid,
        }

        REGEX = self._ADMONITION_REGEX
        for textdomain, catalog in status_iterator(
            self.catalogs.items(),
            __("writing message catalogs... "),
            "darkgreen",
            len(self.catalogs),
            self.app.verbosity,
            lambda textdomain__: textdomain__[0],
        ):
            # noop if config.gettext_compact is set
            ensuredir(os.path.join(self.outdir, os.path.dirname(textdomain)))

            # Due to a bug in Sphinx where messages contain admonitions, this code makes it
            # so they're suppressed from the output to prevent the output and CI from breaking
            # This is quite a bandaid fix but it seems to work ok
            # See https://github.com/sphinx-doc/sphinx/issues/10334
            context['messages'] = [msg for msg in catalog if REGEX.search(msg.text) is None]

            content = GettextRenderer(template_path='_templates/gettext', outdir=self.outdir).render(
                'message.pot_t', context
            )

            pofn = os.path.join(self.outdir, textdomain + '.pot')
            if should_write(pofn, content):
                with open(pofn, 'w', encoding='utf-8') as pofile:
                    pofile.write(content)


def add_custom_jinja2(app):
    env = app.builder.templates.environment
    env.tests['prefixedwith'] = str.startswith
    env.tests['suffixedwith'] = str.endswith


def add_builders(app):
    """This is necessary because RTD injects their own for some reason."""
    app.set_translator('html', DPYHTML5Translator, override=True)
    app.add_builder(DPYStandaloneHTMLBuilder, override=True)
    app.add_builder(DPYMessageCatalogBuilder, override=True)

    try:
        original = app.registry.builders['readthedocs']
    except KeyError:
        pass
    else:
        injected_mro = tuple(
            base if base is not StandaloneHTMLBuilder else DPYStandaloneHTMLBuilder for base in original.mro()[1:]
        )
        new_builder = type(original.__name__, injected_mro, {'name': 'readthedocs'})
        app.set_translator('readthedocs', DPYHTML5Translator, override=True)
        app.add_builder(new_builder, override=True)


def setup(app):
    add_builders(app)
    app.connect('builder-inited', add_custom_jinja2)
    return {'parallel_read_safe': True}