Skip to content

Commit 2291028

Browse files
committed
Add static types for the HTML context dict
1 parent aeeaeaf commit 2291028

File tree

4 files changed

+112
-11
lines changed

4 files changed

+112
-11
lines changed

sphinx/builders/epub3.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,20 @@
2222

2323
if TYPE_CHECKING:
2424
from collections.abc import Set
25-
from typing import Any
25+
from typing import Any, Literal
2626

2727
from sphinx.application import Sphinx
28+
from sphinx.builders.html._ctx import _GlobalContextHTML
2829
from sphinx.config import Config
2930
from sphinx.util.typing import ExtensionMetadata
3031

32+
class _GlobalContextEpub3(_GlobalContextHTML):
33+
theme_writing_mode: str | None
34+
html_tag: str
35+
use_meta_charset: bool
36+
skip_ua_compatible: Literal[True]
37+
38+
3139
logger = logging.getLogger(__name__)
3240

3341

@@ -89,6 +97,8 @@ class Epub3Builder(_epub_base.EpubBuilder):
8997
html_tag = HTML_TAG
9098
use_meta_charset = True
9199

100+
globalcontext: _GlobalContextEpub3
101+
92102
# Finish by building the epub file
93103
def handle_finish(self) -> None:
94104
"""Create the metainfo files and finally the epub."""

sphinx/builders/html/__init__.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
from docutils.nodes import Node
7171

7272
from sphinx.application import Sphinx
73+
from sphinx.builders.html._ctx import _GlobalContextHTML, _PageContextHTML
7374
from sphinx.config import Config
7475
from sphinx.environment import BuildEnvironment
7576
from sphinx.util.typing import ExtensionMetadata
@@ -137,6 +138,8 @@ class StandaloneHTMLBuilder(Builder):
137138
imgpath: str = ''
138139
domain_indices: list[DOMAIN_INDEX_TYPE] = []
139140

141+
globalcontext: _GlobalContextHTML
142+
140143
def __init__(self, app: Sphinx, env: BuildEnvironment) -> None:
141144
super().__init__(app, env)
142145

@@ -565,7 +568,7 @@ def prepare_writing(self, docnames: Set[str]) -> None:
565568
'html5_doctype': True,
566569
}
567570
if self.theme:
568-
self.globalcontext |= {
571+
self.globalcontext |= { # type: ignore[typeddict-item]
569572
f'theme_{key}': val
570573
for key, val in self.theme.get_options(self.theme_options).items()
571574
}
@@ -580,7 +583,7 @@ def get_doc_context(self, docname: str, body: str, metatags: str) -> dict[str, A
580583
# find out relations
581584
prev = next = None
582585
parents = []
583-
rellinks = self.globalcontext['rellinks'][:]
586+
rellinks = list(self.globalcontext['rellinks'])
584587
related = self.relations.get(docname)
585588
titles = self.env.titles
586589
if related and related[2]:
@@ -921,7 +924,7 @@ def copy_static_files(self) -> None:
921924
self._static_dir.mkdir(parents=True, exist_ok=True)
922925

923926
# prepare context for templates
924-
context = self.globalcontext.copy()
927+
context: dict[str, Any] = self.globalcontext.copy() # type: ignore[assignment]
925928
if self.indexer is not None:
926929
context.update(self.indexer.context_for_searchtool())
927930

@@ -1040,7 +1043,7 @@ def get_output_path(self, page_name: str, /) -> Path:
10401043
def get_outfilename(self, pagename: str) -> _StrPath:
10411044
return _StrPath(self.get_output_path(pagename))
10421045

1043-
def add_sidebars(self, pagename: str, ctx: dict[str, Any]) -> None:
1046+
def add_sidebars(self, pagename: str, ctx: _PageContextHTML) -> None:
10441047
def has_wildcard(pattern: str) -> bool:
10451048
return any(char in pattern for char in '*?[')
10461049

@@ -1080,7 +1083,7 @@ def handle_page(
10801083
outfilename: Path | None = None,
10811084
event_arg: Any = None,
10821085
) -> None:
1083-
ctx = self.globalcontext.copy()
1086+
ctx: _PageContextHTML = self.globalcontext.copy() # type: ignore[assignment]
10841087
# current_page_name is backwards compatibility
10851088
ctx['pagename'] = ctx['current_page_name'] = pagename
10861089
ctx['encoding'] = self.config.html_output_encoding
@@ -1124,7 +1127,7 @@ def hasdoc(name: str) -> bool:
11241127

11251128
ctx['toctree'] = lambda **kwargs: self._get_local_toctree(pagename, **kwargs)
11261129
self.add_sidebars(pagename, ctx)
1127-
ctx.update(addctx)
1130+
ctx.update(addctx) # type: ignore[typeddict-item]
11281131

11291132
# 'blah.html' should have content_root = './' not ''.
11301133
ctx['content_root'] = (f'..{SEP}' * default_baseuri.count(SEP)) or f'.{SEP}'
@@ -1261,7 +1264,7 @@ def js_tag(js: _JavaScript | str) -> str:
12611264
copyfile(self.env.doc2path(pagename), source_file_path, force=True)
12621265

12631266
def update_page_context(
1264-
self, pagename: str, templatename: str, ctx: dict[str, Any], event_arg: Any
1267+
self, pagename: str, templatename: str, ctx: _PageContextHTML, event_arg: Any
12651268
) -> None:
12661269
pass
12671270

sphinx/builders/html/_ctx.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
from __future__ import annotations
2+
3+
from typing import TYPE_CHECKING
4+
5+
if TYPE_CHECKING:
6+
from collections.abc import Callable, Sequence
7+
from typing import Any, Literal, Protocol, TypedDict
8+
9+
from sphinx.builders.html._assets import _CascadingStyleSheet, _JavaScript
10+
11+
class _NavigationRelation(TypedDict):
12+
link: str
13+
title: str
14+
15+
class _GlobalContextHTML(TypedDict):
16+
embedded: bool
17+
project: str
18+
release: str
19+
version: str
20+
last_updated: str | None
21+
copyright: str
22+
master_doc: str
23+
root_doc: str
24+
use_opensearch: bool
25+
docstitle: str | None
26+
shorttitle: str
27+
show_copyright: bool
28+
show_search_summary: bool
29+
show_sphinx: bool
30+
has_source: bool
31+
show_source: bool
32+
sourcelink_suffix: str
33+
file_suffix: str
34+
link_suffix: str
35+
script_files: Sequence[_JavaScript]
36+
language: str | None
37+
css_files: Sequence[_CascadingStyleSheet]
38+
sphinx_version: str
39+
sphinx_version_tuple: tuple[int, int, int, str, int]
40+
docutils_version_info: tuple[int, int, int, str, int]
41+
styles: Sequence[str]
42+
rellinks: Sequence[tuple[str, str, str, str]]
43+
builder: str
44+
parents: Sequence[_NavigationRelation]
45+
logo_url: str
46+
logo_alt: str
47+
favicon_url: str
48+
html5_doctype: Literal[True]
49+
50+
class _PathtoCallable(Protocol):
51+
def __call__(
52+
self, otheruri: str, resource: bool = False, baseuri: str = ...
53+
) -> str: ...
54+
55+
class _ToctreeCallable(Protocol):
56+
def __call__(self, **kwargs: Any) -> str: ...
57+
58+
class _PageContextHTML(_GlobalContextHTML):
59+
# get_doc_context()
60+
prev: Sequence[_NavigationRelation]
61+
next: Sequence[_NavigationRelation]
62+
title: str
63+
meta: dict[str, Any] | None
64+
body: str
65+
metatags: str
66+
sourcename: str
67+
toc: str
68+
display_toc: bool
69+
page_source_suffix: str
70+
71+
# handle_page()
72+
pagename: str
73+
current_page_name: str
74+
encoding: str
75+
pageurl: str | None
76+
pathto: _PathtoCallable
77+
hasdoc: Callable[[str], bool]
78+
toctree: _ToctreeCallable
79+
content_root: str
80+
css_tag: Callable[[_CascadingStyleSheet], str]
81+
js_tag: Callable[[_JavaScript], str]
82+
83+
# add_sidebars()
84+
sidebars: Sequence[str] | None
85+
86+
else:
87+
_NavigationRelation = dict
88+
_GlobalContextHTML = dict
89+
_PageContextHTML = dict

tests/test_builders/test_build_html.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@
3030
from tests.test_builders.xpath_util import check_xpath
3131

3232
if TYPE_CHECKING:
33-
from typing import Any
34-
33+
from sphinx.builders.html._ctx import _PageContextHTML
3534
from sphinx.testing.util import SphinxTestApp
3635

3736

@@ -416,7 +415,7 @@ def test_html_style(app: SphinxTestApp) -> None:
416415
},
417416
)
418417
def test_html_sidebar(app: SphinxTestApp) -> None:
419-
ctx: dict[str, Any] = {}
418+
ctx: _PageContextHTML = {} # type: ignore[typeddict-item]
420419

421420
# default for alabaster
422421
app.build(force_all=True)

0 commit comments

Comments
 (0)