Coverage for an_website / js_licenses / js_licenses.py: 88.571%
35 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-24 17:35 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-24 17:35 +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"""
15A page with all the JavaScript files and their licenses.
17This is used for LibreJS to make sure the extension knows the licenses.
18This isn't important, as the JS files should contain the licenses themselves.
20See: https://www.gnu.org/software/librejs/free-your-javascript.html#step3
21and https://www.gnu.org/licenses/javascript-labels.html
22"""
24import logging
25from collections.abc import Mapping
26from functools import cache
27from typing import Final
29from .. import STATIC_DIR, pytest_is_running
30from ..utils.fix_static_path_impl import recurse_directory
31from ..utils.request_handler import HTMLRequestHandler
32from ..utils.utils import ModuleInfo
34LOGGER: Final = logging.getLogger(__name__)
37def get_module_info() -> ModuleInfo:
38 """Create and return the ModuleInfo for this module."""
39 return ModuleInfo(
40 handlers=((r"/js-lizenzen", JSLicenses),),
41 name="JavaScript-Lizenzen",
42 description=(
43 "Informationen über die Lizenzen der JavaScript-Dateien "
44 "auf dieser Seite"
45 ),
46 path="/js-lizenzen",
47 aliases=("/js-licenses",),
48 keywords=("JavaScript", "License", "Lizenz"),
49 )
52LICENSE_COMMENT_START: Final = "// @license "
53LICENSES: Final[Mapping[str, str]] = {
54 "magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt": (
55 "https://www.gnu.org/licenses/agpl-3.0.html"
56 ),
57 "magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt": (
58 "https://www.jclark.com/xml/copying.txt"
59 ),
60}
63@cache
64def get_js_filenames_and_licenses() -> list[tuple[str, str, str]]:
65 """
66 Get the names of the JS files in this project.
68 Returns a list of tuples with filename, license and license URL.
69 """
70 js_files_dir = STATIC_DIR / "js"
71 licenses_list: list[tuple[str, str, str]] = []
72 for filename in recurse_directory(
73 js_files_dir, lambda path: path.name.endswith(".js")
74 ):
75 path = js_files_dir / filename
76 if not path.is_file():
77 continue
78 with path.open(encoding="UTF-8") as file:
79 license_line = file.readline()
80 if LICENSE_COMMENT_START not in license_line:
81 LOGGER.critical("%s has no license comment", filename)
82 if pytest_is_running():
83 raise AssertionError(f"Could not get license of: {filename}")
84 continue
85 magnet, name = (
86 license_line[
87 license_line.index(LICENSE_COMMENT_START)
88 + len(LICENSE_COMMENT_START) :
89 ]
90 .strip()
91 .split(" ")
92 )
93 licenses_list.append((filename, name.strip(), LICENSES[magnet.strip()]))
94 return licenses_list
97class JSLicenses(HTMLRequestHandler):
98 """The request handler for the JS licenses page."""
100 async def get(self, *, head: bool = False) -> None:
101 """Handle GET requests to the JS licenses page."""
102 if head:
103 return
104 await self.render(
105 "pages/js_licenses.html", js_files=get_js_filenames_and_licenses()
106 )