"""Record metadata for the build process."""
from __future__ import annotations
from typing import TYPE_CHECKING
from sphinx.locale import __
from sphinx.util._serialise import stable_hash
if TYPE_CHECKING:
from collections.abc import Set
from pathlib import Path
from sphinx.config import Config, _ConfigRebuild
from sphinx.util.tags import Tags
class BuildInfo:
"""buildinfo file manipulator.
HTMLBuilder and its family are storing their own envdata to ``.buildinfo``.
This class is a manipulator for the file.
"""
@classmethod
def load(cls: type[BuildInfo], filename: Path, /) -> BuildInfo:
content = filename.read_text(encoding='utf-8')
lines = content.splitlines()
version = lines[0].rstrip()
if version != '# Sphinx build info version 1':
msg = __('failed to read broken build info file (unknown version)')
raise ValueError(msg)
if not lines[2].startswith('config: '):
msg = __('failed to read broken build info file (missing config entry)')
raise ValueError(msg)
if not lines[3].startswith('tags: '):
msg = __('failed to read broken build info file (missing tags entry)')
raise ValueError(msg)
build_info = BuildInfo()
build_info.config_hash = lines[2].removeprefix('config: ').strip()
build_info.tags_hash = lines[3].removeprefix('tags: ').strip()
return build_info
def __init__(
self,
config: Config | None = None,
tags: Tags | None = None,
config_categories: Set[_ConfigRebuild] = frozenset(),
) -> None:
self.config_hash = ''
self.tags_hash = ''
if config:
values = {c.name: c.value for c in config.filter(config_categories)}
self.config_hash = stable_hash(values)
if tags:
self.tags_hash = stable_hash(sorted(tags))
def __eq__(self, other: BuildInfo) -> bool: # type: ignore[override]
return (
self.config_hash == other.config_hash and self.tags_hash == other.tags_hash
)
def __hash__(self) -> int:
return hash((self.config_hash, self.tags_hash))
def dump(self, filename: Path, /) -> None:
build_info = (
'# Sphinx build info version 1\n'
'# This file records the configuration used when building these files. '
'When it is not found, a full rebuild will be done.\n'
f'config: {self.config_hash}\n'
f'tags: {self.tags_hash}\n'
)
filename.write_text(build_info, encoding='utf-8')