Sphinx 7.4.6__py3-none-any.whl → 8.0.0rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of Sphinx might be problematic. Click here for more details.
- sphinx/__init__.py +2 -2
- sphinx/_cli/__init__.py +4 -4
- sphinx/application.py +7 -7
- sphinx/builders/__init__.py +2 -3
- sphinx/builders/_epub_base.py +33 -12
- sphinx/builders/changes.py +13 -5
- sphinx/builders/epub3.py +6 -2
- sphinx/builders/html/__init__.py +90 -59
- sphinx/builders/latex/__init__.py +38 -12
- sphinx/builders/latex/transforms.py +1 -1
- sphinx/builders/linkcheck.py +8 -49
- sphinx/builders/texinfo.py +12 -6
- sphinx/builders/text.py +7 -3
- sphinx/builders/xml.py +7 -3
- sphinx/cmd/quickstart.py +10 -20
- sphinx/config.py +12 -12
- sphinx/deprecation.py +8 -8
- sphinx/directives/__init__.py +14 -9
- sphinx/directives/other.py +2 -3
- sphinx/directives/patches.py +2 -2
- sphinx/domains/__init__.py +4 -2
- sphinx/domains/c/__init__.py +2 -2
- sphinx/domains/c/_ast.py +3 -2
- sphinx/domains/c/_parser.py +4 -3
- sphinx/domains/cpp/__init__.py +2 -2
- sphinx/domains/cpp/_ast.py +1 -2
- sphinx/domains/cpp/_parser.py +2 -2
- sphinx/domains/cpp/_symbol.py +2 -2
- sphinx/domains/javascript.py +1 -1
- sphinx/domains/math.py +1 -1
- sphinx/domains/python/__init__.py +1 -1
- sphinx/domains/python/_annotations.py +23 -1
- sphinx/domains/python/_object.py +0 -1
- sphinx/domains/std/__init__.py +7 -8
- sphinx/environment/__init__.py +14 -32
- sphinx/environment/adapters/indexentries.py +4 -6
- sphinx/environment/adapters/toctree.py +4 -4
- sphinx/environment/collectors/title.py +1 -1
- sphinx/environment/collectors/toctree.py +1 -1
- sphinx/events.py +3 -1
- sphinx/ext/autodoc/__init__.py +25 -67
- sphinx/ext/autodoc/directive.py +7 -5
- sphinx/ext/autodoc/importer.py +2 -1
- sphinx/ext/autodoc/preserve_defaults.py +2 -2
- sphinx/ext/autosummary/__init__.py +15 -7
- sphinx/ext/autosummary/generate.py +5 -4
- sphinx/ext/doctest.py +5 -5
- sphinx/ext/graphviz.py +1 -1
- sphinx/ext/imgmath.py +1 -1
- sphinx/ext/inheritance_diagram.py +1 -1
- sphinx/ext/intersphinx/__init__.py +25 -5
- sphinx/ext/intersphinx/_cli.py +7 -6
- sphinx/ext/intersphinx/_load.py +240 -115
- sphinx/ext/intersphinx/_resolve.py +12 -11
- sphinx/ext/intersphinx/_shared.py +102 -9
- sphinx/ext/mathjax.py +1 -1
- sphinx/ext/napoleon/docstring.py +2 -2
- sphinx/ext/todo.py +2 -2
- sphinx/ext/viewcode.py +2 -1
- sphinx/highlighting.py +3 -3
- sphinx/io.py +2 -2
- sphinx/jinja2glue.py +13 -6
- sphinx/locale/__init__.py +4 -3
- sphinx/locale/ta/LC_MESSAGES/sphinx.js +54 -54
- sphinx/locale/ta/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ta/LC_MESSAGES/sphinx.po +1578 -1843
- sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po +496 -704
- sphinx/project.py +23 -19
- sphinx/pycode/ast.py +2 -2
- sphinx/pycode/parser.py +2 -2
- sphinx/pygments_styles.py +3 -3
- sphinx/registry.py +3 -8
- sphinx/search/__init__.py +1 -1
- sphinx/testing/path.py +2 -1
- sphinx/testing/util.py +1 -1
- sphinx/texinputs/Makefile.jinja +2 -1
- sphinx/texinputs_win/Makefile.jinja +2 -1
- sphinx/theming.py +3 -12
- sphinx/transforms/__init__.py +5 -5
- sphinx/transforms/references.py +1 -1
- sphinx/util/__init__.py +11 -35
- sphinx/util/_timestamps.py +12 -0
- sphinx/util/cfamily.py +5 -5
- sphinx/util/console.py +4 -3
- sphinx/util/display.py +3 -3
- sphinx/util/docfields.py +1 -1
- sphinx/util/docutils.py +44 -10
- sphinx/util/fileutil.py +41 -9
- sphinx/util/i18n.py +9 -4
- sphinx/util/images.py +3 -2
- sphinx/util/inspect.py +29 -44
- sphinx/util/inventory.py +2 -2
- sphinx/util/matching.py +2 -2
- sphinx/util/math.py +1 -1
- sphinx/util/nodes.py +8 -8
- sphinx/util/osutil.py +46 -23
- sphinx/util/parallel.py +2 -2
- sphinx/util/requests.py +1 -1
- sphinx/util/template.py +3 -3
- sphinx/util/typing.py +67 -70
- sphinx/writers/html.py +1 -1
- sphinx/writers/html5.py +1 -1
- sphinx/writers/latex.py +4 -4
- sphinx/writers/manpage.py +2 -2
- sphinx/writers/texinfo.py +5 -5
- sphinx/writers/text.py +4 -4
- sphinx/writers/xml.py +2 -2
- {sphinx-7.4.6.dist-info → sphinx-8.0.0rc1.dist-info}/METADATA +10 -9
- {sphinx-7.4.6.dist-info → sphinx-8.0.0rc1.dist-info}/RECORD +112 -114
- sphinx/templates/quickstart/Makefile.jinja +0 -98
- sphinx/templates/quickstart/make.bat.jinja +0 -110
- sphinx/util/_pathlib.py +0 -120
- {sphinx-7.4.6.dist-info → sphinx-8.0.0rc1.dist-info}/LICENSE.rst +0 -0
- {sphinx-7.4.6.dist-info → sphinx-8.0.0rc1.dist-info}/WHEEL +0 -0
- {sphinx-7.4.6.dist-info → sphinx-8.0.0rc1.dist-info}/entry_points.txt +0 -0
sphinx/__init__.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""The Sphinx documentation toolchain."""
|
|
2
2
|
|
|
3
|
-
__version__ = '
|
|
3
|
+
__version__ = '8.0.0rc1'
|
|
4
4
|
__display_version__ = __version__ # used for command line version
|
|
5
5
|
|
|
6
6
|
# Keep this file executable as-is in Python 3!
|
|
@@ -27,7 +27,7 @@ warnings.filterwarnings(
|
|
|
27
27
|
#:
|
|
28
28
|
#: .. versionadded:: 1.2
|
|
29
29
|
#: Before version 1.2, check the string ``sphinx.__version__``.
|
|
30
|
-
version_info = (
|
|
30
|
+
version_info = (8, 0, 0, 'candidate', 1)
|
|
31
31
|
|
|
32
32
|
package_dir = os.path.abspath(os.path.dirname(__file__))
|
|
33
33
|
|
sphinx/_cli/__init__.py
CHANGED
|
@@ -36,10 +36,10 @@ from sphinx.locale import __, init_console
|
|
|
36
36
|
|
|
37
37
|
if TYPE_CHECKING:
|
|
38
38
|
from collections.abc import Callable, Iterable, Iterator, Sequence
|
|
39
|
-
from typing import NoReturn
|
|
39
|
+
from typing import NoReturn, TypeAlias
|
|
40
40
|
|
|
41
|
-
_PARSER_SETUP = Callable[[argparse.ArgumentParser], argparse.ArgumentParser]
|
|
42
|
-
_RUNNER = Callable[[argparse.Namespace], int]
|
|
41
|
+
_PARSER_SETUP: TypeAlias = Callable[[argparse.ArgumentParser], argparse.ArgumentParser]
|
|
42
|
+
_RUNNER: TypeAlias = Callable[[argparse.Namespace], int]
|
|
43
43
|
|
|
44
44
|
from typing import Protocol
|
|
45
45
|
|
|
@@ -79,7 +79,7 @@ class _RootArgumentParser(argparse.ArgumentParser):
|
|
|
79
79
|
]
|
|
80
80
|
|
|
81
81
|
if commands := list(_load_subcommand_descriptions()):
|
|
82
|
-
command_max_length = min(max(map(len, next(zip(*commands), ()))), 22)
|
|
82
|
+
command_max_length = min(max(map(len, next(zip(*commands, strict=True), ()))), 22)
|
|
83
83
|
help_fragments += [
|
|
84
84
|
'\n',
|
|
85
85
|
bold(underline(__('Commands:'))),
|
sphinx/application.py
CHANGED
|
@@ -10,10 +10,11 @@ import os
|
|
|
10
10
|
import pickle
|
|
11
11
|
import sys
|
|
12
12
|
from collections import deque
|
|
13
|
-
from collections.abc import Collection, Sequence # NoQA: TCH003
|
|
13
|
+
from collections.abc import Callable, Collection, Sequence # NoQA: TCH003
|
|
14
14
|
from io import StringIO
|
|
15
15
|
from os import path
|
|
16
|
-
from
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import IO, TYPE_CHECKING, Any, Literal
|
|
17
18
|
|
|
18
19
|
from docutils.nodes import TextElement # NoQA: TCH002
|
|
19
20
|
from docutils.parsers.rst import Directive, roles
|
|
@@ -31,7 +32,6 @@ from sphinx.locale import __
|
|
|
31
32
|
from sphinx.project import Project
|
|
32
33
|
from sphinx.registry import SphinxComponentRegistry
|
|
33
34
|
from sphinx.util import docutils, logging
|
|
34
|
-
from sphinx.util._pathlib import _StrPath
|
|
35
35
|
from sphinx.util.build_phase import BuildPhase
|
|
36
36
|
from sphinx.util.console import bold
|
|
37
37
|
from sphinx.util.display import progress_message
|
|
@@ -173,9 +173,9 @@ class Sphinx:
|
|
|
173
173
|
self.registry = SphinxComponentRegistry()
|
|
174
174
|
|
|
175
175
|
# validate provided directories
|
|
176
|
-
self.srcdir =
|
|
177
|
-
self.outdir =
|
|
178
|
-
self.doctreedir =
|
|
176
|
+
self.srcdir = Path(srcdir).resolve()
|
|
177
|
+
self.outdir = Path(outdir).resolve()
|
|
178
|
+
self.doctreedir = Path(doctreedir).resolve()
|
|
179
179
|
|
|
180
180
|
if not path.isdir(self.srcdir):
|
|
181
181
|
raise ApplicationError(__('Cannot find source directory (%s)') %
|
|
@@ -231,7 +231,7 @@ class Sphinx:
|
|
|
231
231
|
self.confdir = self.srcdir
|
|
232
232
|
self.config = Config({}, confoverrides or {})
|
|
233
233
|
else:
|
|
234
|
-
self.confdir =
|
|
234
|
+
self.confdir = Path(confdir).resolve()
|
|
235
235
|
self.config = Config.read(self.confdir, confoverrides or {}, self.tags)
|
|
236
236
|
|
|
237
237
|
# set up translation infrastructure
|
sphinx/builders/__init__.py
CHANGED
|
@@ -38,7 +38,6 @@ if TYPE_CHECKING:
|
|
|
38
38
|
from sphinx.config import Config
|
|
39
39
|
from sphinx.events import EventManager
|
|
40
40
|
from sphinx.util.tags import Tags
|
|
41
|
-
from sphinx.util.typing import NoneType
|
|
42
41
|
|
|
43
42
|
|
|
44
43
|
logger = logging.getLogger(__name__)
|
|
@@ -520,7 +519,7 @@ class Builder:
|
|
|
520
519
|
if path.isfile(docutilsconf):
|
|
521
520
|
self.env.note_dependency(docutilsconf)
|
|
522
521
|
|
|
523
|
-
filename = self.env.doc2path(docname)
|
|
522
|
+
filename = str(self.env.doc2path(docname))
|
|
524
523
|
filetype = get_filetype(self.app.config.source_suffix, filename)
|
|
525
524
|
publisher = self.app.registry.get_publisher(self.app, filetype)
|
|
526
525
|
self.env.temp_data['_parser'] = publisher.parser
|
|
@@ -645,7 +644,7 @@ class Builder:
|
|
|
645
644
|
progress = status_iterator(chunks, __('writing output... '), "darkgreen",
|
|
646
645
|
len(chunks), self.app.verbosity)
|
|
647
646
|
|
|
648
|
-
def on_chunk_done(args: list[tuple[str,
|
|
647
|
+
def on_chunk_done(args: list[tuple[str, nodes.document]], result: None) -> None:
|
|
649
648
|
next(progress)
|
|
650
649
|
|
|
651
650
|
self.app.phase = BuildPhase.RESOLVING
|
sphinx/builders/_epub_base.py
CHANGED
|
@@ -27,8 +27,9 @@ if TYPE_CHECKING:
|
|
|
27
27
|
|
|
28
28
|
try:
|
|
29
29
|
from PIL import Image
|
|
30
|
+
PILLOW_AVAILABLE = True
|
|
30
31
|
except ImportError:
|
|
31
|
-
|
|
32
|
+
PILLOW_AVAILABLE = False
|
|
32
33
|
|
|
33
34
|
|
|
34
35
|
logger = logging.getLogger(__name__)
|
|
@@ -107,8 +108,8 @@ class NavPoint(NamedTuple):
|
|
|
107
108
|
|
|
108
109
|
def sphinx_smarty_pants(t: str, language: str = 'en') -> str:
|
|
109
110
|
t = t.replace('"', '"')
|
|
110
|
-
t = smartquotes.educateDashesOldSchool(t)
|
|
111
|
-
t = smartquotes.educateQuotes(t, language)
|
|
111
|
+
t = smartquotes.educateDashesOldSchool(t) # type: ignore[no-untyped-call]
|
|
112
|
+
t = smartquotes.educateQuotes(t, language) # type: ignore[no-untyped-call]
|
|
112
113
|
t = t.replace('"', '"')
|
|
113
114
|
return t
|
|
114
115
|
|
|
@@ -411,8 +412,11 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
|
|
411
412
|
logger.warning(__('cannot read image file %r: copying it instead'),
|
|
412
413
|
path.join(self.srcdir, src))
|
|
413
414
|
try:
|
|
414
|
-
copyfile(
|
|
415
|
-
|
|
415
|
+
copyfile(
|
|
416
|
+
self.srcdir / src,
|
|
417
|
+
self.outdir / self.imagedir / dest,
|
|
418
|
+
force=True,
|
|
419
|
+
)
|
|
416
420
|
except OSError as err:
|
|
417
421
|
logger.warning(__('cannot copy image file %r: %s'),
|
|
418
422
|
path.join(self.srcdir, src), err)
|
|
@@ -440,7 +444,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
|
|
440
444
|
"""
|
|
441
445
|
if self.images:
|
|
442
446
|
if self.config.epub_fix_images or self.config.epub_max_image_width:
|
|
443
|
-
if not
|
|
447
|
+
if not PILLOW_AVAILABLE:
|
|
444
448
|
logger.warning(__('Pillow not found - copying image files'))
|
|
445
449
|
super().copy_image_files()
|
|
446
450
|
else:
|
|
@@ -474,14 +478,22 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
|
|
474
478
|
def build_mimetype(self) -> None:
|
|
475
479
|
"""Write the metainfo file mimetype."""
|
|
476
480
|
logger.info(__('writing mimetype file...'))
|
|
477
|
-
|
|
481
|
+
copyfile(
|
|
482
|
+
path.join(self.template_dir, 'mimetype'),
|
|
483
|
+
self.outdir / 'mimetype',
|
|
484
|
+
force=True,
|
|
485
|
+
)
|
|
478
486
|
|
|
479
487
|
def build_container(self, outname: str = 'META-INF/container.xml') -> None:
|
|
480
488
|
"""Write the metainfo file META-INF/container.xml."""
|
|
481
489
|
logger.info(__('writing META-INF/container.xml file...'))
|
|
482
|
-
outdir =
|
|
490
|
+
outdir = self.outdir / 'META-INF'
|
|
483
491
|
ensuredir(outdir)
|
|
484
|
-
|
|
492
|
+
copyfile(
|
|
493
|
+
path.join(self.template_dir, 'container.xml'),
|
|
494
|
+
outdir / 'container.xml',
|
|
495
|
+
force=True,
|
|
496
|
+
)
|
|
485
497
|
|
|
486
498
|
def content_metadata(self) -> dict[str, Any]:
|
|
487
499
|
"""Create a dictionary with all metadata for the content.opf
|
|
@@ -621,7 +633,12 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
|
|
621
633
|
html.escape(self.refnodes[0]['refuri'])))
|
|
622
634
|
|
|
623
635
|
# write the project file
|
|
624
|
-
copy_asset_file(
|
|
636
|
+
copy_asset_file(
|
|
637
|
+
path.join(self.template_dir, 'content.opf.jinja'),
|
|
638
|
+
self.outdir,
|
|
639
|
+
context=metadata,
|
|
640
|
+
force=True,
|
|
641
|
+
)
|
|
625
642
|
|
|
626
643
|
def new_navpoint(self, node: dict[str, Any], level: int, incr: bool = True) -> NavPoint:
|
|
627
644
|
"""Create a new entry in the toc from the node at given level."""
|
|
@@ -704,8 +721,12 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
|
|
704
721
|
navpoints = self.build_navpoints(refnodes)
|
|
705
722
|
level = max(item['level'] for item in self.refnodes)
|
|
706
723
|
level = min(level, self.config.epub_tocdepth)
|
|
707
|
-
copy_asset_file(
|
|
708
|
-
|
|
724
|
+
copy_asset_file(
|
|
725
|
+
path.join(self.template_dir, 'toc.ncx.jinja'),
|
|
726
|
+
self.outdir,
|
|
727
|
+
context=self.toc_metadata(level, navpoints),
|
|
728
|
+
force=True,
|
|
729
|
+
)
|
|
709
730
|
|
|
710
731
|
def build_epub(self) -> None:
|
|
711
732
|
"""Write the epub file.
|
sphinx/builders/changes.py
CHANGED
|
@@ -134,16 +134,24 @@ class ChangesBuilder(Builder):
|
|
|
134
134
|
with open(targetfn, 'w', encoding='utf-8') as f:
|
|
135
135
|
text = ''.join(hl(i + 1, line) for (i, line) in enumerate(lines))
|
|
136
136
|
ctx = {
|
|
137
|
-
'filename': self.env.doc2path(docname, False),
|
|
137
|
+
'filename': str(self.env.doc2path(docname, False)),
|
|
138
138
|
'text': text,
|
|
139
139
|
}
|
|
140
140
|
f.write(self.templates.render('changes/rstsource.html', ctx))
|
|
141
141
|
themectx = {'theme_' + key: val for (key, val) in
|
|
142
142
|
self.theme.get_options({}).items()}
|
|
143
|
-
copy_asset_file(
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
143
|
+
copy_asset_file(
|
|
144
|
+
path.join(package_dir, 'themes', 'default', 'static', 'default.css.jinja'),
|
|
145
|
+
self.outdir,
|
|
146
|
+
context=themectx,
|
|
147
|
+
renderer=self.templates,
|
|
148
|
+
force=True,
|
|
149
|
+
)
|
|
150
|
+
copy_asset_file(
|
|
151
|
+
path.join(package_dir, 'themes', 'basic', 'static', 'basic.css'),
|
|
152
|
+
self.outdir / 'basic.css',
|
|
153
|
+
force=True,
|
|
154
|
+
)
|
|
147
155
|
|
|
148
156
|
def hl(self, text: str, version: str) -> str:
|
|
149
157
|
text = html.escape(text)
|
sphinx/builders/epub3.py
CHANGED
|
@@ -194,8 +194,12 @@ class Epub3Builder(_epub_base.EpubBuilder):
|
|
|
194
194
|
# 'includehidden'
|
|
195
195
|
refnodes = self.refnodes
|
|
196
196
|
navlist = self.build_navlist(refnodes)
|
|
197
|
-
copy_asset_file(
|
|
198
|
-
|
|
197
|
+
copy_asset_file(
|
|
198
|
+
path.join(self.template_dir, 'nav.xhtml.jinja'),
|
|
199
|
+
self.outdir,
|
|
200
|
+
context=self.navigation_doc_metadata(navlist),
|
|
201
|
+
force=True,
|
|
202
|
+
)
|
|
199
203
|
|
|
200
204
|
# Add nav.xhtml to epub file
|
|
201
205
|
if 'nav.xhtml' not in self.files:
|
sphinx/builders/html/__init__.py
CHANGED
|
@@ -9,10 +9,10 @@ import os
|
|
|
9
9
|
import posixpath
|
|
10
10
|
import re
|
|
11
11
|
import sys
|
|
12
|
-
import time
|
|
13
12
|
import types
|
|
14
13
|
import warnings
|
|
15
14
|
from os import path
|
|
15
|
+
from pathlib import Path
|
|
16
16
|
from typing import IO, TYPE_CHECKING, Any
|
|
17
17
|
from urllib.parse import quote
|
|
18
18
|
|
|
@@ -39,18 +39,27 @@ from sphinx.locale import _, __
|
|
|
39
39
|
from sphinx.search import js_index
|
|
40
40
|
from sphinx.theming import HTMLThemeFactory
|
|
41
41
|
from sphinx.util import isurl, logging
|
|
42
|
+
from sphinx.util._timestamps import _format_rfc3339_microseconds
|
|
42
43
|
from sphinx.util.display import progress_message, status_iterator
|
|
43
44
|
from sphinx.util.docutils import new_document
|
|
44
45
|
from sphinx.util.fileutil import copy_asset
|
|
45
46
|
from sphinx.util.i18n import format_date
|
|
46
47
|
from sphinx.util.inventory import InventoryFile
|
|
47
48
|
from sphinx.util.matching import DOTFILES, Matcher, patmatch
|
|
48
|
-
from sphinx.util.osutil import
|
|
49
|
+
from sphinx.util.osutil import (
|
|
50
|
+
SEP,
|
|
51
|
+
_last_modified_time,
|
|
52
|
+
copyfile,
|
|
53
|
+
ensuredir,
|
|
54
|
+
os_path,
|
|
55
|
+
relative_uri,
|
|
56
|
+
)
|
|
49
57
|
from sphinx.writers.html import HTMLWriter
|
|
50
58
|
from sphinx.writers.html5 import HTML5Translator
|
|
51
59
|
|
|
52
60
|
if TYPE_CHECKING:
|
|
53
61
|
from collections.abc import Iterable, Iterator, Set
|
|
62
|
+
from typing import TypeAlias
|
|
54
63
|
|
|
55
64
|
from docutils.nodes import Node
|
|
56
65
|
from docutils.readers import Reader
|
|
@@ -67,7 +76,7 @@ INVENTORY_FILENAME = 'objects.inv'
|
|
|
67
76
|
logger = logging.getLogger(__name__)
|
|
68
77
|
return_codes_re = re.compile('[\r\n]+')
|
|
69
78
|
|
|
70
|
-
DOMAIN_INDEX_TYPE = tuple[
|
|
79
|
+
DOMAIN_INDEX_TYPE: TypeAlias = tuple[
|
|
71
80
|
# Index name (e.g. py-modindex)
|
|
72
81
|
str,
|
|
73
82
|
# Index class
|
|
@@ -87,9 +96,9 @@ def _stable_hash(obj: Any) -> str:
|
|
|
87
96
|
"""
|
|
88
97
|
if isinstance(obj, dict):
|
|
89
98
|
obj = sorted(map(_stable_hash, obj.items()))
|
|
90
|
-
if isinstance(obj,
|
|
99
|
+
if isinstance(obj, list | tuple | set | frozenset):
|
|
91
100
|
obj = sorted(map(_stable_hash, obj))
|
|
92
|
-
elif isinstance(obj,
|
|
101
|
+
elif isinstance(obj, type | types.FunctionType):
|
|
93
102
|
# The default repr() of functions includes the ID, which is not ideal.
|
|
94
103
|
# We use the fully qualified name instead.
|
|
95
104
|
obj = f'{obj.__module__}.{obj.__qualname__}'
|
|
@@ -395,7 +404,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
395
404
|
pass
|
|
396
405
|
|
|
397
406
|
if self.templates:
|
|
398
|
-
template_mtime = self.templates.newest_template_mtime()
|
|
407
|
+
template_mtime = int(self.templates.newest_template_mtime() * 10**6)
|
|
399
408
|
else:
|
|
400
409
|
template_mtime = 0
|
|
401
410
|
for docname in self.env.found_docs:
|
|
@@ -405,19 +414,19 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
405
414
|
continue
|
|
406
415
|
targetname = self.get_outfilename(docname)
|
|
407
416
|
try:
|
|
408
|
-
targetmtime =
|
|
417
|
+
targetmtime = _last_modified_time(targetname)
|
|
409
418
|
except Exception:
|
|
410
419
|
targetmtime = 0
|
|
411
420
|
try:
|
|
412
|
-
srcmtime = max(
|
|
421
|
+
srcmtime = max(_last_modified_time(self.env.doc2path(docname)), template_mtime)
|
|
413
422
|
if srcmtime > targetmtime:
|
|
414
423
|
logger.debug(
|
|
415
424
|
'[build target] targetname %r(%s), template(%s), docname %r(%s)',
|
|
416
425
|
targetname,
|
|
417
|
-
|
|
418
|
-
|
|
426
|
+
_format_rfc3339_microseconds(targetmtime),
|
|
427
|
+
_format_rfc3339_microseconds(template_mtime),
|
|
419
428
|
docname,
|
|
420
|
-
|
|
429
|
+
_format_rfc3339_microseconds(_last_modified_time(self.env.doc2path(docname))),
|
|
421
430
|
)
|
|
422
431
|
yield docname
|
|
423
432
|
except OSError:
|
|
@@ -609,7 +618,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
609
618
|
title = self.render_partial(title_node)['title'] if title_node else ''
|
|
610
619
|
|
|
611
620
|
# Suffix for the document
|
|
612
|
-
source_suffix = self.env.doc2path(docname, False)[len(docname):]
|
|
621
|
+
source_suffix = str(self.env.doc2path(docname, False))[len(docname):]
|
|
613
622
|
|
|
614
623
|
# the name for the copied source
|
|
615
624
|
if self.config.html_copy_source:
|
|
@@ -734,7 +743,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
734
743
|
'genindex-split.html')
|
|
735
744
|
self.handle_page('genindex-all', genindexcontext,
|
|
736
745
|
'genindex.html')
|
|
737
|
-
for (key, entries), count in zip(genindex, indexcounts):
|
|
746
|
+
for (key, entries), count in zip(genindex, indexcounts, strict=True):
|
|
738
747
|
ctx = {'key': key, 'entries': entries, 'count': count,
|
|
739
748
|
'genindexentries': genindex}
|
|
740
749
|
self.handle_page('genindex-' + key, ctx,
|
|
@@ -755,17 +764,20 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
755
764
|
def copy_image_files(self) -> None:
|
|
756
765
|
if self.images:
|
|
757
766
|
stringify_func = ImageAdapter(self.app.env).get_original_image_uri
|
|
758
|
-
ensuredir(
|
|
767
|
+
ensuredir(self.outdir / self.imagedir)
|
|
759
768
|
for src in status_iterator(self.images, __('copying images... '), "brown",
|
|
760
769
|
len(self.images), self.app.verbosity,
|
|
761
770
|
stringify_func=stringify_func):
|
|
762
771
|
dest = self.images[src]
|
|
763
772
|
try:
|
|
764
|
-
copyfile(
|
|
765
|
-
|
|
773
|
+
copyfile(
|
|
774
|
+
self.srcdir / src,
|
|
775
|
+
self.outdir / self.imagedir / dest,
|
|
776
|
+
force=True,
|
|
777
|
+
)
|
|
766
778
|
except Exception as err:
|
|
767
|
-
logger.warning(__(
|
|
768
|
-
|
|
779
|
+
logger.warning(__("cannot copy image file '%s': %s"),
|
|
780
|
+
self.srcdir / src, err)
|
|
769
781
|
|
|
770
782
|
def copy_download_files(self) -> None:
|
|
771
783
|
def to_relpath(f: str) -> str:
|
|
@@ -773,17 +785,17 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
773
785
|
|
|
774
786
|
# copy downloadable files
|
|
775
787
|
if self.env.dlfiles:
|
|
776
|
-
ensuredir(
|
|
788
|
+
ensuredir(self.outdir / '_downloads')
|
|
777
789
|
for src in status_iterator(self.env.dlfiles, __('copying downloadable files... '),
|
|
778
790
|
"brown", len(self.env.dlfiles), self.app.verbosity,
|
|
779
791
|
stringify_func=to_relpath):
|
|
780
792
|
try:
|
|
781
|
-
dest =
|
|
782
|
-
ensuredir(
|
|
783
|
-
copyfile(
|
|
793
|
+
dest = self.outdir / '_downloads' / self.env.dlfiles[src][1]
|
|
794
|
+
ensuredir(dest.parent)
|
|
795
|
+
copyfile(self.srcdir / src, dest, force=True)
|
|
784
796
|
except OSError as err:
|
|
785
797
|
logger.warning(__('cannot copy downloadable file %r: %s'),
|
|
786
|
-
|
|
798
|
+
self.srcdir / src, err)
|
|
787
799
|
|
|
788
800
|
def create_pygments_style_file(self) -> None:
|
|
789
801
|
"""Create a style file for pygments."""
|
|
@@ -800,30 +812,45 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
800
812
|
"""Copy a JavaScript file for translations."""
|
|
801
813
|
jsfile = self._get_translations_js()
|
|
802
814
|
if jsfile:
|
|
803
|
-
copyfile(
|
|
815
|
+
copyfile(
|
|
816
|
+
jsfile,
|
|
817
|
+
self.outdir / '_static' / 'translations.js',
|
|
818
|
+
force=True,
|
|
819
|
+
)
|
|
804
820
|
|
|
805
821
|
def copy_stemmer_js(self) -> None:
|
|
806
822
|
"""Copy a JavaScript file for stemmer."""
|
|
807
823
|
if self.indexer is not None:
|
|
808
824
|
if hasattr(self.indexer, 'get_js_stemmer_rawcodes'):
|
|
809
825
|
for jsfile in self.indexer.get_js_stemmer_rawcodes():
|
|
810
|
-
|
|
826
|
+
js_path = Path(jsfile)
|
|
827
|
+
copyfile(
|
|
828
|
+
js_path,
|
|
829
|
+
self.outdir / '_static' / js_path.name,
|
|
830
|
+
force=True,
|
|
831
|
+
)
|
|
811
832
|
else:
|
|
812
833
|
if js_stemmer_rawcode := self.indexer.get_js_stemmer_rawcode():
|
|
813
|
-
copyfile(
|
|
814
|
-
|
|
834
|
+
copyfile(
|
|
835
|
+
js_stemmer_rawcode,
|
|
836
|
+
self.outdir / '_static' / '_stemmer.js',
|
|
837
|
+
force=True,
|
|
838
|
+
)
|
|
815
839
|
|
|
816
840
|
def copy_theme_static_files(self, context: dict[str, Any]) -> None:
|
|
817
841
|
def onerror(filename: str, error: Exception) -> None:
|
|
818
|
-
|
|
819
|
-
|
|
842
|
+
msg = __("Failed to copy a file in the theme's 'static' directory: %s: %r")
|
|
843
|
+
logger.warning(msg, filename, error)
|
|
820
844
|
|
|
821
845
|
if self.theme:
|
|
822
846
|
for entry in reversed(self.theme.get_theme_dirs()):
|
|
823
|
-
copy_asset(
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
847
|
+
copy_asset(
|
|
848
|
+
Path(entry) / 'static',
|
|
849
|
+
self.outdir / '_static',
|
|
850
|
+
excluded=DOTFILES, context=context,
|
|
851
|
+
renderer=self.templates, onerror=onerror,
|
|
852
|
+
force=True,
|
|
853
|
+
)
|
|
827
854
|
|
|
828
855
|
def copy_html_static_files(self, context: dict[str, Any]) -> None:
|
|
829
856
|
def onerror(filename: str, error: Exception) -> None:
|
|
@@ -832,24 +859,36 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
832
859
|
|
|
833
860
|
excluded = Matcher([*self.config.exclude_patterns, '**/.*'])
|
|
834
861
|
for entry in self.config.html_static_path:
|
|
835
|
-
copy_asset(
|
|
836
|
-
|
|
837
|
-
|
|
862
|
+
copy_asset(
|
|
863
|
+
self.confdir / entry,
|
|
864
|
+
self.outdir / '_static',
|
|
865
|
+
excluded=excluded, context=context,
|
|
866
|
+
renderer=self.templates, onerror=onerror,
|
|
867
|
+
force=True,
|
|
868
|
+
)
|
|
838
869
|
|
|
839
870
|
def copy_html_logo(self) -> None:
|
|
840
871
|
if self.config.html_logo and not isurl(self.config.html_logo):
|
|
841
|
-
|
|
842
|
-
|
|
872
|
+
source_path = self.confdir / self.config.html_logo
|
|
873
|
+
copyfile(
|
|
874
|
+
source_path,
|
|
875
|
+
self.outdir / '_static' / source_path.name,
|
|
876
|
+
force=True,
|
|
877
|
+
)
|
|
843
878
|
|
|
844
879
|
def copy_html_favicon(self) -> None:
|
|
845
880
|
if self.config.html_favicon and not isurl(self.config.html_favicon):
|
|
846
|
-
|
|
847
|
-
|
|
881
|
+
source_path = self.confdir / self.config.html_favicon
|
|
882
|
+
copyfile(
|
|
883
|
+
source_path,
|
|
884
|
+
self.outdir / '_static' / source_path.name,
|
|
885
|
+
force=True,
|
|
886
|
+
)
|
|
848
887
|
|
|
849
888
|
def copy_static_files(self) -> None:
|
|
850
889
|
try:
|
|
851
890
|
with progress_message(__('copying static files')):
|
|
852
|
-
ensuredir(
|
|
891
|
+
ensuredir(self.outdir / '_static')
|
|
853
892
|
|
|
854
893
|
# prepare context for templates
|
|
855
894
|
context = self.globalcontext.copy()
|
|
@@ -872,8 +911,12 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
872
911
|
with progress_message(__('copying extra files')):
|
|
873
912
|
excluded = Matcher(self.config.exclude_patterns)
|
|
874
913
|
for extra_path in self.config.html_extra_path:
|
|
875
|
-
|
|
876
|
-
|
|
914
|
+
copy_asset(
|
|
915
|
+
self.confdir / extra_path,
|
|
916
|
+
self.outdir,
|
|
917
|
+
excluded=excluded,
|
|
918
|
+
force=True,
|
|
919
|
+
)
|
|
877
920
|
except OSError as err:
|
|
878
921
|
logger.warning(__('cannot copy extra file %r'), err)
|
|
879
922
|
|
|
@@ -940,7 +983,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
940
983
|
def index_page(self, pagename: str, doctree: nodes.document, title: str) -> None:
|
|
941
984
|
# only index pages with title
|
|
942
985
|
if self.indexer is not None and title:
|
|
943
|
-
filename = self.env.doc2path(pagename, base=False)
|
|
986
|
+
filename = str(self.env.doc2path(pagename, base=False))
|
|
944
987
|
metadata = self.env.metadata.get(pagename, {})
|
|
945
988
|
if 'no-search' in metadata or 'nosearch' in metadata:
|
|
946
989
|
self.indexer.feed(pagename, filename, '', new_document(''))
|
|
@@ -982,10 +1025,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
982
1025
|
matched = pattern
|
|
983
1026
|
sidebars = pat_sidebars
|
|
984
1027
|
|
|
985
|
-
|
|
986
|
-
# Replace with simple list coercion in Sphinx 8.0
|
|
987
|
-
# xref: RemovedInSphinx80Warning
|
|
988
|
-
ctx['sidebars'] = sidebars
|
|
1028
|
+
ctx['sidebars'] = list(sidebars)
|
|
989
1029
|
|
|
990
1030
|
# --------- these are overwritten by the serialization builder
|
|
991
1031
|
|
|
@@ -1142,7 +1182,7 @@ class StandaloneHTMLBuilder(Builder):
|
|
|
1142
1182
|
source_name = path.join(self.outdir, '_sources',
|
|
1143
1183
|
os_path(ctx['sourcename']))
|
|
1144
1184
|
ensuredir(path.dirname(source_name))
|
|
1145
|
-
copyfile(self.env.doc2path(pagename), source_name)
|
|
1185
|
+
copyfile(self.env.doc2path(pagename), source_name, force=True)
|
|
1146
1186
|
|
|
1147
1187
|
def update_page_context(self, pagename: str, templatename: str,
|
|
1148
1188
|
ctx: dict[str, Any], event_arg: Any) -> None:
|
|
@@ -1191,12 +1231,6 @@ def convert_html_css_files(app: Sphinx, config: Config) -> None:
|
|
|
1191
1231
|
config.html_css_files = html_css_files
|
|
1192
1232
|
|
|
1193
1233
|
|
|
1194
|
-
def _format_modified_time(timestamp: float) -> str:
|
|
1195
|
-
"""Return an RFC 3339 formatted string representing the given timestamp."""
|
|
1196
|
-
seconds, fraction = divmod(timestamp, 1)
|
|
1197
|
-
return time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(seconds)) + f'.{fraction:.3f}'
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
1234
|
def convert_html_js_files(app: Sphinx, config: Config) -> None:
|
|
1201
1235
|
"""Convert string styled html_js_files to tuple styled one."""
|
|
1202
1236
|
html_js_files: list[tuple[str, dict[str, str]]] = []
|
|
@@ -1299,10 +1333,7 @@ def error_on_html_sidebars_string_values(app: Sphinx, config: Config) -> None:
|
|
|
1299
1333
|
"Change to `html_sidebars = %r`.")
|
|
1300
1334
|
bad_patterns = ', '.join(map(repr, errors))
|
|
1301
1335
|
fixed = config.html_sidebars | errors
|
|
1302
|
-
|
|
1303
|
-
# Enable hard error in next major version.
|
|
1304
|
-
# xref: RemovedInSphinx80Warning
|
|
1305
|
-
# raise ConfigError(msg % (bad_patterns, fixed))
|
|
1336
|
+
raise ConfigError(msg % (bad_patterns, fixed))
|
|
1306
1337
|
|
|
1307
1338
|
|
|
1308
1339
|
def error_on_html_4(_app: Sphinx, config: Config) -> None:
|