Skip to content

Commit 3bcc669

Browse files
committed
Defer loading entry-point themes until needed
1 parent fa4563f commit 3bcc669

File tree

2 files changed

+33
-4
lines changed

2 files changed

+33
-4
lines changed

CHANGES.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ Release 7.3.7 (in development)
44
Bugs fixed
55
----------
66

7+
* #12299: Defer loading themes defined via entry points until
8+
their explicit use by the user or a child theme.
9+
Patch by Adam Turner.
10+
711

812
Release 7.3.6 (released Apr 17, 2024)
913
=====================================

sphinx/theming.py

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from importlib_metadata import entry_points
3333

3434
if TYPE_CHECKING:
35+
from collections.abc import Callable
3536
from typing import TypedDict
3637

3738
from typing_extensions import Required
@@ -156,6 +157,7 @@ class HTMLThemeFactory:
156157
def __init__(self, app: Sphinx) -> None:
157158
self._app = app
158159
self._themes = app.registry.html_themes
160+
self._entry_point_themes: dict[str, Callable[[], None]] = {}
159161
self._load_builtin_themes()
160162
if getattr(app.config, 'html_theme_path', None):
161163
self._load_additional_themes(app.config.html_theme_path)
@@ -183,8 +185,16 @@ def _load_entry_point_themes(self) -> None:
183185
for entry_point in entry_points(group='sphinx.html_themes'):
184186
if entry_point.name in self._themes:
185187
continue # don't overwrite loaded themes
186-
self._app.registry.load_extension(self._app, entry_point.module)
187-
_config_post_init(self._app, self._app.config)
188+
189+
def _load_theme_closure(
190+
# bind variables in the function definition
191+
app: Sphinx = self._app,
192+
theme_module: str = entry_point.module,
193+
) -> None:
194+
app.setup_extension(theme_module)
195+
_config_post_init(app, app.config)
196+
197+
self._entry_point_themes[entry_point.name] = _load_theme_closure
188198

189199
@staticmethod
190200
def _find_themes(theme_path: str) -> dict[str, str]:
@@ -217,10 +227,18 @@ def _find_themes(theme_path: str) -> dict[str, str]:
217227

218228
def create(self, name: str) -> Theme:
219229
"""Create an instance of theme."""
230+
if name in self._entry_point_themes:
231+
# Load a deferred theme from an entry point
232+
entry_point_loader = self._entry_point_themes[name]
233+
entry_point_loader()
220234
if name not in self._themes:
221235
raise ThemeError(__('no theme named %r found (missing theme.toml?)') % name)
222236

223-
themes, theme_dirs, tmp_dirs = _load_theme_with_ancestors(self._themes, name)
237+
themes, theme_dirs, tmp_dirs = _load_theme_with_ancestors(
238+
name,
239+
self._themes,
240+
self._entry_point_themes,
241+
)
224242
return Theme(name, configs=themes, paths=theme_dirs, tmp_dirs=tmp_dirs)
225243

226244

@@ -235,7 +253,10 @@ def _is_archived_theme(filename: str, /) -> bool:
235253

236254

237255
def _load_theme_with_ancestors(
238-
theme_paths: dict[str, str], name: str, /
256+
name: str,
257+
theme_paths: dict[str, str],
258+
entry_point_themes: dict[str, Callable[[], None]],
259+
/,
239260
) -> tuple[dict[str, _ConfigFile], list[str], list[str]]:
240261
themes: dict[str, _ConfigFile] = {}
241262
theme_dirs: list[str] = []
@@ -253,6 +274,10 @@ def _load_theme_with_ancestors(
253274
if inherit in themes:
254275
msg = __('The %r theme has circular inheritance') % name
255276
raise ThemeError(msg)
277+
if inherit in entry_point_themes and inherit not in theme_paths:
278+
# Load a deferred theme from an entry point
279+
entry_point_loader = entry_point_themes[inherit]
280+
entry_point_loader()
256281
if inherit not in theme_paths:
257282
msg = __(
258283
'The %r theme inherits from %r, which is not a loaded theme. '

0 commit comments

Comments
 (0)