Coverage for an_website/utils/fix_static_path_impl.py: 93.750%
48 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-01 14:47 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-01 14:47 +0000
1# This program is free software: you can redistribute it and/or modify
2# it under the terms of the GNU Affero General Public License as
3# published by the Free Software Foundation, either version 3 of the
4# License, or (at your option) any later version.
5#
6# This program is distributed in the hope that it will be useful,
7# but WITHOUT ANY WARRANTY; without even the implied warranty of
8# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9# GNU Affero General Public License for more details.
10#
11# You should have received a copy of the GNU Affero General Public License
12# along with this program. If not, see <https://www.gnu.org/licenses/>.
14"""The module containing the impl of the fix_static_path function."""
16import logging
17import os
18from collections.abc import Callable, Iterable, Mapping
19from importlib.resources.abc import Traversable
20from pathlib import Path
21from typing import Final
23from blake3 import blake3
24from openmoji_dist import VERSION as OPENMOJI_VERSION
26if "STATIC_DIR" not in locals():
27 from .. import STATIC_DIR
29LOGGER: Final = logging.getLogger(__name__)
32def recurse_directory(
33 root: Traversable,
34 # pylint: disable-next=redefined-builtin
35 filter: Callable[[Traversable], bool] = lambda _: True,
36) -> Iterable[str]:
37 """Recursively iterate over entries in a directory."""
38 dirs: list[str] = ["."]
39 while dirs: # pylint: disable=while-used
40 curr_dir = dirs.pop()
41 for path in (root if curr_dir == "." else root / curr_dir).iterdir():
42 current: str = (
43 path.name
44 if curr_dir == "."
45 else os.path.join(curr_dir, path.name)
46 )
47 if path.is_dir():
48 dirs.append(current)
49 if filter(path):
50 yield current
53def hash_file(path: Traversable) -> str:
54 """Hash a file with BLAKE3."""
55 hasher = blake3()
56 with path.open("rb") as file:
57 for data in file:
58 hasher.update(data)
59 return hasher.hexdigest(8)
62def create_file_hashes_dict(
63 filter_path_fun: Callable[[str], bool] | None = None
64) -> dict[str, str]:
65 """Create a dict of file hashes."""
66 static = Path("/static")
67 file_hashes_dict = {
68 f"{(static / path).as_posix()}": hash_file(STATIC_DIR / path)
69 for path in recurse_directory(STATIC_DIR, lambda path: path.is_file())
70 if not path.endswith((".map", ".gz", ".zst"))
71 if filter_path_fun is None or filter_path_fun(path)
72 }
73 if filter_path_fun is None:
74 file_hashes_dict["/favicon.png"] = file_hashes_dict[
75 "/static/favicon.png"
76 ]
77 file_hashes_dict["/favicon.jxl"] = file_hashes_dict[
78 "/static/favicon.jxl"
79 ]
80 file_hashes_dict["/humans.txt"] = file_hashes_dict["/static/humans.txt"]
81 return file_hashes_dict
84def fix_static_path_impl(path: str, file_hashes_dict: Mapping[str, str]) -> str:
85 """Fix the path for static files."""
86 if not path.startswith("/"):
87 path = f"/static/{path}"
88 if "?" in path:
89 path = path.split("?")[0]
90 if path.startswith("/static/openmoji/"):
91 return f"{path}?v={OPENMOJI_VERSION}"
92 path = path.lower()
93 if path in file_hashes_dict:
94 hash_ = file_hashes_dict[path]
95 return f"{path}?v={hash_}"
96 LOGGER.warning("%s not in FILE_HASHES_DICT", path)
97 return path