From 42a654640444e0616557993ef043e0d0e892377d Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sat, 12 Aug 2023 00:20:00 +0100 Subject: [PATCH] Move ``_file_checksum()`` to ``sphinx.builders.html._assets`` --- sphinx/builders/html/__init__.py | 25 +------------------------ sphinx/builders/html/_assets.py | 32 ++++++++++++++++++++++++++++++++ tests/test_build_html.py | 6 +++--- 3 files changed, 36 insertions(+), 27 deletions(-) create mode 100644 sphinx/builders/html/_assets.py diff --git a/sphinx/builders/html/__init__.py b/sphinx/builders/html/__init__.py index 67b24b952..e153319fb 100644 --- a/sphinx/builders/html/__init__.py +++ b/sphinx/builders/html/__init__.py @@ -9,7 +9,6 @@ import posixpath import re import sys import warnings -import zlib from datetime import datetime, timezone from os import path from typing import IO, TYPE_CHECKING, Any @@ -27,6 +26,7 @@ from sphinx import __display_version__, package_dir from sphinx import version_info as sphinx_version from sphinx.application import Sphinx from sphinx.builders import Builder +from sphinx.builders.html._assets import _file_checksum from sphinx.config import ENUM, Config from sphinx.domains import Domain, Index, IndexEntry from sphinx.environment import BuildEnvironment @@ -1254,29 +1254,6 @@ def setup_js_tag_helper(app: Sphinx, pagename: str, templatename: str, context['js_tag'] = js_tag -def _file_checksum(outdir: str | os.PathLike[str], filename: str | os.PathLike[str]) -> str: - filename = os.fspath(filename) - # Don't generate checksums for HTTP URIs - if '://' in filename: - return '' - # Some themes and extensions have used query strings - # for a similar asset checksum feature. - # As we cannot safely strip the query string, - # raise an error to the user. - if '?' in filename: - msg = f'Local asset file paths must not contain query strings: {filename!r}' - raise ThemeError(msg) - try: - # Ensure universal newline mode is used to avoid checksum differences - with open(path.join(outdir, filename), encoding='utf-8') as f: - content = f.read().encode(encoding='utf-8') - except FileNotFoundError: - return '' - if not content: - return '' - return f'{zlib.crc32(content):08x}' - - def setup_resource_paths(app: Sphinx, pagename: str, templatename: str, context: dict, doctree: Node) -> None: """Set up relative resource paths.""" diff --git a/sphinx/builders/html/_assets.py b/sphinx/builders/html/_assets.py new file mode 100644 index 000000000..92bec213a --- /dev/null +++ b/sphinx/builders/html/_assets.py @@ -0,0 +1,32 @@ +from __future__ import annotations + +import os +import zlib +from typing import TYPE_CHECKING + +from sphinx.errors import ThemeError + +if TYPE_CHECKING: + from pathlib import Path + + +def _file_checksum(outdir: Path, filename: str | os.PathLike[str]) -> str: + filename = os.fspath(filename) + # Don't generate checksums for HTTP URIs + if '://' in filename: + return '' + # Some themes and extensions have used query strings + # for a similar asset checksum feature. + # As we cannot safely strip the query string, + # raise an error to the user. + if '?' in filename: + msg = f'Local asset file paths must not contain query strings: {filename!r}' + raise ThemeError(msg) + try: + # Remove all carriage returns to avoid checksum differences + content = outdir.joinpath(filename).read_bytes().translate(None, b'\r') + except FileNotFoundError: + return '' + if not content: + return '' + return f'{zlib.crc32(content):08x}' diff --git a/tests/test_build_html.py b/tests/test_build_html.py index c2f6987a6..10aa595ab 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -12,10 +12,10 @@ from html5lib import HTMLParser import sphinx.builders.html from sphinx.builders.html import ( - _file_checksum, validate_html_extra_path, validate_html_static_path, ) +from sphinx.builders.html._assets import _file_checksum from sphinx.errors import ConfigError, ThemeError from sphinx.testing.util import strip_escseq from sphinx.util.inventory import InventoryFile @@ -1248,10 +1248,10 @@ def test_file_checksum(app): def test_file_checksum_query_string(): with pytest.raises(ThemeError, match='Local asset file paths must not contain query strings'): - _file_checksum('', 'with_query_string.css?dead_parrots=1') + _file_checksum(Path(), 'with_query_string.css?dead_parrots=1') with pytest.raises(ThemeError, match='Local asset file paths must not contain query strings'): - _file_checksum('', 'with_query_string.js?dead_parrots=1') + _file_checksum(Path(), 'with_query_string.js?dead_parrots=1') with pytest.raises(ThemeError, match='Local asset file paths must not contain query strings'): _file_checksum(Path.cwd(), '_static/with_query_string.css?dead_parrots=1')