build: typecheck the python code (#5269)
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
add pyright to project, add it to pre-commit fix all errors it detects Co-authored-by: Darragh Elliott <me@delliott.net> Reviewed-on: #5269
This commit was merged in pull request #5269.
This commit is contained in:
@@ -14,7 +14,7 @@ from fsfe_website_build.lib.misc import get_basename, get_version, lang_from_fil
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _get_xmls(file: Path, parser: etree.XMLParser) -> etree.Element:
|
||||
def _get_xmls(file: Path, parser: etree.XMLParser) -> list:
|
||||
"""
|
||||
include second level elements of a given XML file
|
||||
this emulates the behaviour of the original
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import logging
|
||||
import multiprocessing
|
||||
import multiprocessing.pool
|
||||
from itertools import product
|
||||
from pathlib import Path
|
||||
|
||||
@@ -24,7 +24,7 @@ def _do_symlinking(link_type: str, lang: str) -> None:
|
||||
source.symlink_to(target.relative_to(source.parent))
|
||||
|
||||
|
||||
def global_symlinks(languages: list[str], pool: multiprocessing.Pool) -> None:
|
||||
def global_symlinks(languages: list[str], pool: multiprocessing.pool.Pool) -> None:
|
||||
"""
|
||||
After this step, the following symlinks will exist:
|
||||
* global/data/texts/.texts.<lang>.xml for each language
|
||||
|
||||
@@ -19,7 +19,7 @@ def prepare_early_subdirectories(source_dir: Path, processes: int) -> None:
|
||||
sys.path.append(str(subdir_path.resolve()))
|
||||
# Ignore this very sensible warning, as we do evil things
|
||||
# here for out subdir scripts
|
||||
import early_subdir # noqa: PLC0415
|
||||
import early_subdir # noqa: PLC0415 # pyright: ignore [reportMissingImports]
|
||||
|
||||
early_subdir.run(processes, subdir_path)
|
||||
# Remove its path from where things can be imported
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import json
|
||||
import logging
|
||||
import multiprocessing
|
||||
import multiprocessing.pool
|
||||
from pathlib import Path
|
||||
|
||||
import iso639
|
||||
@@ -74,7 +74,7 @@ def _process_file(file: Path, stopwords: set[str]) -> dict:
|
||||
def index_websites(
|
||||
source_dir: Path,
|
||||
languages: list[str],
|
||||
pool: multiprocessing.Pool,
|
||||
pool: multiprocessing.pool.Pool,
|
||||
) -> None:
|
||||
"""
|
||||
Generate a search index for all sites that have a search/search.js file
|
||||
|
||||
@@ -23,7 +23,7 @@ def prepare_subdirectories(
|
||||
sys.path.append(str(subdir_path.resolve()))
|
||||
# Ignore this very sensible warning, as we do evil things
|
||||
# here for out subdir scripts
|
||||
import subdir # noqa: PLC0415
|
||||
import subdir # noqa: PLC0415 # pyright: ignore [reportMissingImports]
|
||||
|
||||
subdir.run(languages, processes, subdir_path)
|
||||
# Remove its path from where things can be imported
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
# directory tree and does not touch the target directory tree at all.
|
||||
# -----------------------------------------------------------------------------
|
||||
import logging
|
||||
import multiprocessing
|
||||
import multiprocessing.pool
|
||||
from pathlib import Path
|
||||
|
||||
from .index_website import index_websites
|
||||
@@ -28,9 +28,9 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
def phase1_run(
|
||||
source_dir: Path,
|
||||
languages: list[str] or None,
|
||||
languages: list[str],
|
||||
processes: int,
|
||||
pool: multiprocessing.Pool,
|
||||
pool: multiprocessing.pool.Pool,
|
||||
) -> None:
|
||||
"""
|
||||
Run all the necessary sub functions for phase1.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import logging
|
||||
import multiprocessing
|
||||
import multiprocessing.pool
|
||||
from pathlib import Path
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -22,7 +22,7 @@ def _do_symlinking(directory: Path) -> None:
|
||||
)
|
||||
|
||||
|
||||
def update_defaultxsls(source_dir: Path, pool: multiprocessing.Pool) -> None:
|
||||
def update_defaultxsls(source_dir: Path, pool: multiprocessing.pool.Pool) -> None:
|
||||
"""
|
||||
Place a .default.xsl into each directory containing source files for
|
||||
HTML pages (*.xhtml). These .default.xsl are symlinks to the first
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import logging
|
||||
import multiprocessing
|
||||
import multiprocessing.pool
|
||||
from pathlib import Path
|
||||
|
||||
from lxml import etree
|
||||
@@ -32,9 +32,9 @@ def _write_localmenus(
|
||||
version = etree.SubElement(page, "version")
|
||||
version.text = "1"
|
||||
|
||||
for source_file in filter(
|
||||
lambda path: path is not None,
|
||||
(
|
||||
for source_file in [
|
||||
path
|
||||
for path in (
|
||||
base_file.with_suffix(f".{lang}.xhtml")
|
||||
if base_file.with_suffix(f".{lang}.xhtml").exists()
|
||||
else (
|
||||
@@ -43,8 +43,9 @@ def _write_localmenus(
|
||||
else None
|
||||
)
|
||||
for base_file in base_files
|
||||
),
|
||||
):
|
||||
)
|
||||
if path is not None
|
||||
]:
|
||||
for localmenu in etree.parse(source_file).xpath("//localmenu"):
|
||||
etree.SubElement(
|
||||
page,
|
||||
@@ -77,7 +78,7 @@ def _write_localmenus(
|
||||
def update_localmenus(
|
||||
source_dir: Path,
|
||||
languages: list[str],
|
||||
pool: multiprocessing.Pool,
|
||||
pool: multiprocessing.pool.Pool,
|
||||
) -> None:
|
||||
"""
|
||||
Update all the .localmenu.*.xml files containing the local menus.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import logging
|
||||
import multiprocessing
|
||||
import multiprocessing.pool
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
@@ -19,17 +19,16 @@ def _update_sheet(file: Path) -> None:
|
||||
Update a given xsl file if any of its dependant xsl files have been updated
|
||||
"""
|
||||
xslt_root = etree.parse(file)
|
||||
imports = (
|
||||
imports = [
|
||||
file.parent.joinpath(imp.get("href")).resolve().relative_to(Path.cwd())
|
||||
for imp in xslt_root.xpath(
|
||||
"//xsl:import",
|
||||
namespaces={"xsl": "http://www.w3.org/1999/XSL/Transform"},
|
||||
"//xsl:import", namespaces={"xsl": "http://www.w3.org/1999/XSL/Transform"}
|
||||
)
|
||||
)
|
||||
]
|
||||
touch_if_newer_dep(file, imports)
|
||||
|
||||
|
||||
def update_stylesheets(source_dir: Path, pool: multiprocessing.Pool) -> None:
|
||||
def update_stylesheets(source_dir: Path, pool: multiprocessing.pool.Pool) -> None:
|
||||
"""
|
||||
This script is called from the phase 1 Makefile and touches all XSL files
|
||||
which depend on another XSL file that has changed since the last build run.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import logging
|
||||
import multiprocessing
|
||||
import multiprocessing.pool
|
||||
from pathlib import Path
|
||||
from xml.sax.saxutils import escape
|
||||
|
||||
@@ -77,7 +77,7 @@ def _update_tag_sets(
|
||||
def update_tags(
|
||||
source_dir: Path,
|
||||
languages: list[str],
|
||||
pool: multiprocessing.Pool,
|
||||
pool: multiprocessing.pool.Pool,
|
||||
) -> None:
|
||||
"""
|
||||
Update Tag pages, xmllists and xmls
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import datetime
|
||||
import fnmatch
|
||||
import logging
|
||||
import multiprocessing
|
||||
import multiprocessing.pool
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
@@ -48,10 +48,9 @@ def _update_for_base(
|
||||
if len(pattern) <= 0:
|
||||
logger.debug("Pattern too short, continue!")
|
||||
continue
|
||||
search_result = re.search(r":\[(.*)\]", line)
|
||||
tag = (
|
||||
re.search(r":\[(.*)\]", line).group(1).strip()
|
||||
if re.search(r":\[(.*)\]", line) is not None
|
||||
else ""
|
||||
search_result.group(1).strip() if search_result is not None else ""
|
||||
)
|
||||
|
||||
for xml_file in filter(
|
||||
@@ -94,7 +93,7 @@ def _update_for_base(
|
||||
def _update_module_xmllists(
|
||||
source_dir: Path,
|
||||
languages: list[str],
|
||||
pool: multiprocessing.Pool,
|
||||
pool: multiprocessing.pool.Pool,
|
||||
) -> None:
|
||||
"""
|
||||
Update .xmllist files for .sources and .xhtml containing <module>s
|
||||
@@ -142,7 +141,7 @@ def _check_xmllist_deps(file: Path) -> None:
|
||||
|
||||
def _touch_xmllists_with_updated_deps(
|
||||
source_dir: Path,
|
||||
pool: multiprocessing.Pool,
|
||||
pool: multiprocessing.pool.Pool,
|
||||
) -> None:
|
||||
"""
|
||||
Touch all .xmllist files where one of the contained files has changed
|
||||
@@ -154,7 +153,7 @@ def _touch_xmllists_with_updated_deps(
|
||||
def update_xmllists(
|
||||
source_dir: Path,
|
||||
languages: list[str],
|
||||
pool: multiprocessing.Pool,
|
||||
pool: multiprocessing.pool.Pool,
|
||||
) -> None:
|
||||
"""
|
||||
Update XML filelists (*.xmllist)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import logging
|
||||
import multiprocessing
|
||||
import multiprocessing.pool
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
@@ -23,7 +23,7 @@ def _copy_file(target: Path, source_dir: Path, source_file: Path) -> None:
|
||||
shutil.copymode(source_file, target_file)
|
||||
|
||||
|
||||
def copy_files(source_dir: Path, pool: multiprocessing.Pool, target: Path) -> None:
|
||||
def copy_files(source_dir: Path, pool: multiprocessing.pool.Pool, target: Path) -> None:
|
||||
"""
|
||||
Copy images, docments etc
|
||||
"""
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import logging
|
||||
import multiprocessing
|
||||
import multiprocessing.pool
|
||||
from pathlib import Path
|
||||
|
||||
from fsfe_website_build.lib.misc import get_basename
|
||||
@@ -20,7 +20,7 @@ def _do_symlinking(target: Path) -> None:
|
||||
|
||||
|
||||
def create_index_symlinks(
|
||||
pool: multiprocessing.Pool,
|
||||
pool: multiprocessing.pool.Pool,
|
||||
target: Path,
|
||||
) -> None:
|
||||
"""
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import logging
|
||||
import multiprocessing
|
||||
import multiprocessing.pool
|
||||
from pathlib import Path
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -16,7 +16,7 @@ def _do_symlinking(target: Path) -> None:
|
||||
|
||||
|
||||
def create_language_symlinks(
|
||||
pool: multiprocessing.Pool,
|
||||
pool: multiprocessing.pool.Pool,
|
||||
target: Path,
|
||||
) -> None:
|
||||
"""
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import logging
|
||||
import multiprocessing
|
||||
import multiprocessing.pool
|
||||
from pathlib import Path
|
||||
|
||||
from fsfe_website_build.lib.misc import get_basepath
|
||||
@@ -89,7 +89,7 @@ def _process_stylesheet(
|
||||
def process_files(
|
||||
source_dir: Path,
|
||||
languages: list[str],
|
||||
pool: multiprocessing.Pool,
|
||||
pool: multiprocessing.pool.Pool,
|
||||
target: Path,
|
||||
) -> None:
|
||||
"""
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# script for FSFE website build, phase 2
|
||||
# -----------------------------------------------------------------------------
|
||||
import logging
|
||||
import multiprocessing
|
||||
import multiprocessing.pool
|
||||
from pathlib import Path
|
||||
|
||||
from .copy_files import copy_files
|
||||
@@ -20,7 +20,7 @@ logger = logging.getLogger(__name__)
|
||||
def phase2_run(
|
||||
source_dir: Path,
|
||||
languages: list[str],
|
||||
pool: multiprocessing.Pool,
|
||||
pool: multiprocessing.pool.Pool,
|
||||
target: Path,
|
||||
) -> None:
|
||||
"""
|
||||
|
||||
@@ -27,7 +27,7 @@ def _run_webserver(path: str, port: int) -> None:
|
||||
httpd.serve_forever()
|
||||
|
||||
|
||||
def serve_websites(serve_dir: str, base_port: int, increment_number: int) -> None:
|
||||
def serve_websites(serve_dir: Path, base_port: int, increment_number: int) -> None:
|
||||
"""
|
||||
Takes a target directory, a base port and a number to increment port by per dir
|
||||
It then serves all directories over http on localhost
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import logging
|
||||
import multiprocessing
|
||||
import multiprocessing.pool
|
||||
from pathlib import Path
|
||||
|
||||
from fsfe_website_build.lib.misc import run_command
|
||||
@@ -32,7 +32,9 @@ def _rsync(stagedir: Path, target: str, port: int) -> None:
|
||||
)
|
||||
|
||||
|
||||
def stage_to_target(stagedir: Path, targets: str, pool: multiprocessing.Pool) -> None:
|
||||
def stage_to_target(
|
||||
stagedir: Path, targets: str, pool: multiprocessing.pool.Pool
|
||||
) -> None:
|
||||
"""
|
||||
Use a multithreaded rsync to copy the stage dir to all targets.
|
||||
"""
|
||||
|
||||
@@ -24,8 +24,6 @@ def keys_exists_test() -> None:
|
||||
|
||||
|
||||
def keys_exists_bad_input_test() -> None:
|
||||
with pytest.raises(TypeError):
|
||||
keys_exists([], "a")
|
||||
assert keys_exists({}, "a") is False
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,9 @@ pre-commit:
|
||||
glob: "*.py"
|
||||
run: ruff format {staged_files}
|
||||
stage_fixed: true
|
||||
pyright:
|
||||
glob: "*.py"
|
||||
run: pyright {staged_files}
|
||||
pytest:
|
||||
glob:
|
||||
- "*.py"
|
||||
|
||||
@@ -6,23 +6,25 @@ readme = "README.md"
|
||||
# Pinned to 3.12 as a known working version, that also has a prebuild wheel for tdewolff-minify
|
||||
requires-python = "==3.13.*"
|
||||
dependencies = [
|
||||
# XML parser
|
||||
"lxml",
|
||||
# For getting english language names of languages from two letter codes.
|
||||
"python-iso639",
|
||||
# For stopwords for the search index
|
||||
"nltk",
|
||||
# For minification html css and js
|
||||
"tdewolff-minify",
|
||||
# For HTTP requests
|
||||
"requests",
|
||||
"lxml", # XML parser
|
||||
"nltk", # For stopwords for the search index
|
||||
"python-iso639", # For getting english language names of languages from two letter codes.
|
||||
"requests", # For HTTP requests
|
||||
"tdewolff-minify", # For minification html css and js
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
build = "fsfe_website_build:main"
|
||||
|
||||
[dependency-groups]
|
||||
dev = ["ruff", "lefthook", "taplo", "pytest"]
|
||||
dev = [
|
||||
"lefthook", # pre-commit hook
|
||||
"pyright", # python typechecker
|
||||
"pytest", # python test runner
|
||||
"ruff", # python formatter and linter
|
||||
"taplo", # toml formatter
|
||||
"types-lxml", # type stubs for lxml
|
||||
]
|
||||
|
||||
[build-system]
|
||||
requires = ["uv_build"]
|
||||
|
||||
@@ -18,7 +18,7 @@ from lxml import etree
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _generate_translation_data(lang: str, file: Path) -> dict:
|
||||
def _generate_translation_data(lang: str, file: Path) -> dict[str, str] | None:
|
||||
page = get_basepath(file)
|
||||
ext = file.suffix.removeprefix(".")
|
||||
working_file = file.with_suffix("").with_suffix(f".{lang}.{ext}")
|
||||
@@ -72,17 +72,13 @@ def _generate_translation_data(lang: str, file: Path) -> dict:
|
||||
def _get_text_ids(file: Path) -> list[str]:
|
||||
texts_tree = etree.parse(file)
|
||||
root = texts_tree.getroot()
|
||||
return list(
|
||||
filter(
|
||||
lambda text_id: text_id is not None,
|
||||
(elem.get("id") for elem in root.iter()),
|
||||
),
|
||||
)
|
||||
elements = (elem.get("id") for elem in root.iter())
|
||||
return [i for i in elements if i is not None]
|
||||
|
||||
|
||||
def _create_overview(
|
||||
target_dir: Path,
|
||||
data: dict[str : dict[int : list[dict]]],
|
||||
data: dict[str, dict[int, list[dict]]],
|
||||
) -> None:
|
||||
work_file = target_dir.joinpath("langs.en.xml")
|
||||
if not target_dir.exists():
|
||||
@@ -118,7 +114,7 @@ def _create_overview(
|
||||
def _create_translation_file(
|
||||
target_dir: Path,
|
||||
lang: str,
|
||||
data: dict[int : list[dict]],
|
||||
data: dict[int, list[dict]],
|
||||
) -> None:
|
||||
work_file = target_dir.joinpath(f"translations.{lang}.xml")
|
||||
page = etree.Element("translation-status")
|
||||
|
||||
89
uv.lock
generated
89
uv.lock
generated
@@ -2,6 +2,19 @@ version = 1
|
||||
revision = 3
|
||||
requires-python = "==3.13.*"
|
||||
|
||||
[[package]]
|
||||
name = "beautifulsoup4"
|
||||
version = "4.13.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "soupsieve" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/85/2e/3e5079847e653b1f6dc647aa24549d68c6addb4c595cc0d902d1b19308ad/beautifulsoup4-4.13.5.tar.gz", hash = "sha256:5e70131382930e7c3de33450a2f54a63d5e4b19386eab43a5b34d594268f3695", size = 622954, upload-time = "2025-08-24T14:06:13.168Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/04/eb/f4151e0c7377a6e08a38108609ba5cede57986802757848688aeedd1b9e8/beautifulsoup4-4.13.5-py3-none-any.whl", hash = "sha256:642085eaa22233aceadff9c69651bc51e8bf3f874fb6d7104ece2beb24b47c4a", size = 105113, upload-time = "2025-08-24T14:06:14.884Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2025.8.3"
|
||||
@@ -74,6 +87,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cssselect"
|
||||
version = "1.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/72/0a/c3ea9573b1dc2e151abfe88c7fe0c26d1892fe6ed02d0cdb30f0d57029d5/cssselect-1.3.0.tar.gz", hash = "sha256:57f8a99424cfab289a1b6a816a43075a4b00948c86b4dcf3ef4ee7e15f7ab0c7", size = 42870, upload-time = "2025-03-10T09:30:29.638Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/58/257350f7db99b4ae12b614a36256d9cc870d71d9e451e79c2dc3b23d7c3c/cssselect-1.3.0-py3-none-any.whl", hash = "sha256:56d1bf3e198080cc1667e137bc51de9cadfca259f03c2d4e09037b3e01e30f0d", size = 18786, upload-time = "2025-03-10T09:30:28.048Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fsfe-website-build"
|
||||
version = "0.0.0"
|
||||
@@ -89,9 +111,11 @@ dependencies = [
|
||||
[package.dev-dependencies]
|
||||
dev = [
|
||||
{ name = "lefthook" },
|
||||
{ name = "pyright" },
|
||||
{ name = "pytest" },
|
||||
{ name = "ruff" },
|
||||
{ name = "taplo" },
|
||||
{ name = "types-lxml" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
@@ -106,9 +130,11 @@ requires-dist = [
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
{ name = "lefthook" },
|
||||
{ name = "pyright" },
|
||||
{ name = "pytest" },
|
||||
{ name = "ruff" },
|
||||
{ name = "taplo" },
|
||||
{ name = "types-lxml" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -188,6 +214,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/4d/66/7d9e26593edda06e8cb531874633f7c2372279c3b0f46235539fe546df8b/nltk-3.9.1-py3-none-any.whl", hash = "sha256:4fa26829c5b00715afe3061398a8989dc643b92ce7dd93fb4585a70930d168a1", size = 1505442, upload-time = "2024-08-18T19:48:21.909Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nodeenv"
|
||||
version = "1.9.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "25.0"
|
||||
@@ -224,6 +259,19 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyright"
|
||||
version = "1.1.404"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "nodeenv" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e2/6e/026be64c43af681d5632722acd100b06d3d39f383ec382ff50a71a6d5bce/pyright-1.1.404.tar.gz", hash = "sha256:455e881a558ca6be9ecca0b30ce08aa78343ecc031d37a198ffa9a7a1abeb63e", size = 4065679, upload-time = "2025-08-20T18:46:14.029Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/84/30/89aa7f7d7a875bbb9a577d4b1dc5a3e404e3d2ae2657354808e905e358e0/pyright-1.1.404-py3-none-any.whl", hash = "sha256:c7b7ff1fdb7219c643079e4c3e7d4125f0dafcc19d253b47e898d130ea426419", size = 5902951, upload-time = "2025-08-20T18:46:12.096Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "8.4.1"
|
||||
@@ -312,6 +360,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/84/a8/001d4a7c2b37623a3fd7463208267fb906df40ff31db496157549cfd6e72/ruff-0.12.11-py3-none-win_arm64.whl", hash = "sha256:bae4d6e6a2676f8fb0f98b74594a048bae1b944aab17e9f5d504062303c6dbea", size = 12135290, upload-time = "2025-08-28T13:59:06.933Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "soupsieve"
|
||||
version = "2.8"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6d/e6/21ccce3262dd4889aa3332e5a119a3491a95e8f60939870a3a035aabac0d/soupsieve-2.8.tar.gz", hash = "sha256:e2dd4a40a628cb5f28f6d4b0db8800b8f581b65bb380b97de22ba5ca8d72572f", size = 103472, upload-time = "2025-08-27T15:39:51.78Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c", size = 36679, upload-time = "2025-08-27T15:39:50.179Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "taplo"
|
||||
version = "0.9.3"
|
||||
@@ -355,6 +412,38 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "types-html5lib"
|
||||
version = "1.1.11.20250809"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/70/ab/6aa4c487ae6f4f9da5153143bdc9e9b4fbc2b105df7ef8127fb920dc1f21/types_html5lib-1.1.11.20250809.tar.gz", hash = "sha256:7976ec7426bb009997dc5e072bca3ed988dd747d0cbfe093c7dfbd3d5ec8bf57", size = 16793, upload-time = "2025-08-09T03:14:20.819Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/05/328a2d6ecbd8aa3e16512600da78b1fe4605125896794a21824f3cac6f14/types_html5lib-1.1.11.20250809-py3-none-any.whl", hash = "sha256:e5f48ab670ae4cdeafd88bbc47113d8126dcf08318e0b8d70df26ecc13eca9b6", size = 22867, upload-time = "2025-08-09T03:14:20.048Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "types-lxml"
|
||||
version = "2025.8.25"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "beautifulsoup4" },
|
||||
{ name = "cssselect" },
|
||||
{ name = "types-html5lib" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e0/3e/a545ece610c1bd9699addd887edfe9477a8f647c4336ba75cfb0561d197c/types_lxml-2025.8.25.tar.gz", hash = "sha256:79b9f5b1f236f937f14fe3add9dc687bd8d4111ca5df58eb9f1bde1a3b032fd5", size = 156126, upload-time = "2025-08-26T06:28:56.793Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/29/c45f567b4142288b8184f073af8f659abd134c21de055f971c65f2d755bd/types_lxml-2025.8.25-py3-none-any.whl", hash = "sha256:d61340e5329e102d3f8d64124e90d50c12c0bfeaa9088d65558279ef4e7138ac", size = 95318, upload-time = "2025-08-26T06:28:54.066Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.15.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.5.0"
|
||||
|
||||
Reference in New Issue
Block a user