Refactor `sphinx.theming` to prepare for iterative loading

This commit is contained in:
Adam Turner 2024-01-18 01:54:32 +00:00 committed by Adam Turner
parent 256c8f9de0
commit 99dd0cb4c6
2 changed files with 39 additions and 37 deletions

View File

@ -35,29 +35,6 @@ _NO_DEFAULT = object()
_THEME_CONF = 'theme.conf'
def _extract_zip(filename: str, target_dir: str, /) -> None:
"""Extract zip file to target directory."""
ensuredir(target_dir)
with ZipFile(filename) as archive:
for name in archive.namelist():
if name.endswith('/'):
continue
entry = path.join(target_dir, name)
ensuredir(path.dirname(entry))
with open(path.join(entry), 'wb') as fp:
fp.write(archive.read(name))
def _load_theme_conf(theme_dir: os.PathLike[str] | str) -> configparser.RawConfigParser:
c = configparser.RawConfigParser()
config_file_path = path.join(theme_dir, _THEME_CONF)
if not os.path.isfile(config_file_path):
raise ThemeError(__('theme configuration file %r not found') % config_file_path)
c.read(config_file_path, encoding='utf-8')
return c
class Theme:
"""A Theme is a set of HTML templates and configurations.
@ -169,15 +146,6 @@ class Theme:
self._base._cleanup()
def _is_archived_theme(filename: str, /) -> bool:
"""Check whether the specified file is an archived theme file or not."""
try:
with ZipFile(filename) as f:
return _THEME_CONF in f.namelist()
except Exception:
return False
class HTMLThemeFactory:
"""A factory class for HTML Themes."""
@ -214,9 +182,10 @@ class HTMLThemeFactory:
pass
else:
self._app.registry.load_extension(self._app, entry_point.module)
_config_post_init(None, self._app.config)
_config_post_init(self._app, self._app.config)
def _find_themes(self, theme_path: str) -> dict[str, str]:
@staticmethod
def _find_themes(theme_path: str) -> dict[str, str]:
"""Search themes from specified directory."""
themes: dict[str, str] = {}
if not path.isdir(theme_path):
@ -246,3 +215,35 @@ class HTMLThemeFactory:
raise ThemeError(__('no theme named %r found (missing theme.conf?)') % name)
return Theme(name, self._themes[name], self)
def _is_archived_theme(filename: str, /) -> bool:
"""Check whether the specified file is an archived theme file or not."""
try:
with ZipFile(filename) as f:
return _THEME_CONF in f.namelist()
except Exception:
return False
def _extract_zip(filename: str, target_dir: str, /) -> None:
"""Extract zip file to target directory."""
ensuredir(target_dir)
with ZipFile(filename) as archive:
for name in archive.namelist():
if name.endswith('/'):
continue
entry = path.join(target_dir, name)
ensuredir(path.dirname(entry))
with open(path.join(entry), 'wb') as fp:
fp.write(archive.read(name))
def _load_theme_conf(theme_dir: os.PathLike[str] | str, /) -> configparser.RawConfigParser:
c = configparser.RawConfigParser()
config_file_path = path.join(theme_dir, _THEME_CONF)
if not os.path.isfile(config_file_path):
raise ThemeError(__('theme configuration file %r not found') % config_file_path)
c.read(config_file_path, encoding='utf-8')
return c

View File

@ -5,7 +5,8 @@ import os
import pytest
import sphinx.builders.html
from sphinx.theming import Theme, ThemeError
from sphinx.errors import ThemeError
from sphinx.theming import Theme
@pytest.mark.sphinx(
@ -26,7 +27,7 @@ def test_theme_api(app, status, warning):
# test Theme instance API
theme = app.builder.theme
assert theme.name == 'ziptheme'
theme_dir = theme._theme_dir
tmp_dirs = (theme._theme_dir,)
assert theme._base.name == 'basic'
assert len(theme.get_theme_dirs()) == 2
@ -50,7 +51,7 @@ def test_theme_api(app, status, warning):
# cleanup temp directories
theme._cleanup()
assert not os.path.exists(theme_dir)
assert not any(map(os.path.exists, tmp_dirs))
def test_nonexistent_theme_conf(tmp_path):