build(deps): refresh dependencies and clean up test warnings (#1243)

Consolidates the currently applicable dependency updates into one PR, including the closed Dependabot backlog such as #1241, plus dependency surfaces that were not covered by the repository's previous pip-only Dependabot configuration.

Cleanup of test warnings.

Most importent:

* GitPython + pypdf security hardening.
* Uvicorn WebSocket close/backpressure/header fixes for server/dashboard reliability.
* FastAPI dependency-memory/OpenAPI improvements for the API process.
* Bokeh WebSocket/resource-leak/prefix fixes for EOSdash and proxied deployments.
* cachebox cancellation/lock cleanup fixes for long-running/concurrent work.
* pandas 3.0.5 avoiding the yanked 3.0.4 datetime/segfault build.
* Ruff security-lint and pydocstyle correctness fixes, plus faster release builds via PGO.
* platformdirs malformed-XDG and duplicate-directory fixes for deployment portability.
* CI action modernization, regenerated uv.lock, and expanded Dependabot coverage.

Runtime dependencies

    cachebox: 6.1.2 → 6.2.2
    fastapi: 0.139.2 → 0.141.1
    python-fasthtml: 0.14.9 → 0.14.11
    MonsterUI: 1.0.46 → 1.0.47
    bokeh: 3.9.1 → 3.9.2
    uvicorn: 0.51.0 → 0.52.4 (build(deps): bump uvicorn from 0.51.0 to 0.52.3 #1241, refreshed to latest patch)
    pandas: 3.0.3 → 3.0.5
    platformdirs: 4.11.0 → 4.11.3

Development/test dependencies

    pandas-stubs: 3.0.3.260530 → 3.0.5.260730
    types-PyYAML: 6.0.12.20260518 → 6.0.12.20260724
    GitPython: 3.1.53 → 3.1.58 (security/fix releases)
    coverage: 7.15.2 → 7.15.4
    pypdf: 6.14.2 → 6.16.1 (includes security fixes)

Pre-commit/tooling

    ruff-pre-commit: v0.15.21 → v0.16.3
    synchronize pandas-stubs, types-docutils, and types-PyYAML pins with pyproject.toml

CI / repository dependencies

    Python 3.13.9 → 3.13.15 in CI, Docker, .env, and local Docker Make targets
    actions/checkout → v7 in pytest, pre-commit, CodeQL, and release workflows
    actions/setup-python → v7 in pytest, pre-commit, and release workflows
    actions/upload-artifact → v7 in pytest workflow
    actions/stale: v9.1.0 → v11.0.0 (SHA-pinned)
    regenerate uv.lock from the final dependency pins so locked/frozen installs match pyproject.toml

Future update coverage

Expand Dependabot from pip-only to also monitor:

    GitHub Actions
    Docker

The existing open docutils 0.23 update (#1085) is intentionally excluded because it has separate compatibility/ignore handling and should remain isolated.

docker-build.yml was audited and is already using the newer action generations, so no changes were needed there.

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Normann
2026-08-21 07:07:34 +02:00
committed by GitHub
co-authored by github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
parent 230698a70d
commit 9eb3c7e483
14 changed files with 391 additions and 443 deletions
+31 -20
View File
@@ -6,10 +6,11 @@ import re
import sys
from difflib import SequenceMatcher
from pathlib import Path
from typing import cast
from docutils import nodes
from docutils import SettingsSpec, nodes
from docutils.core import publish_parts
from docutils.frontend import OptionParser
from docutils.frontend import get_default_settings
from docutils.parsers.rst import Directive, Parser, directives
from docutils.utils import Reporter, new_document
from sphinx.ext.napoleon import Config as NapoleonConfig
@@ -230,6 +231,7 @@ def prepare_docutils_for_sphinx():
required_arguments = 0
optional_arguments = 100
final_argument_whitespace = True
def run(self):
return []
@@ -249,13 +251,16 @@ def validate_rst(text: str) -> list[tuple[int, str]]:
class RecordingReporter(Reporter):
"""Capture warnings/errors instead of halting."""
def system_message(self, level, message, *children, **kwargs):
line = kwargs.get("line", None)
warnings.append((line or 0, message))
return nodes.system_message(message, level=level, type=self.levels[level], *children, **kwargs)
# Create default settings
settings = OptionParser(components=(Parser,)).get_default_values()
# Docutils expects the SettingsSpec subclass itself here. The stubs bundled with
# our current docutils/types-docutils pins still describe this argument as an
# instance; upstream fixed that annotation in 2026.
settings = get_default_settings(cast(SettingsSpec, Parser))
document = new_document("<docstring>", settings=settings)
@@ -265,7 +270,7 @@ def validate_rst(text: str) -> list[tuple[int, str]]:
report_level=1, # capture warnings and above
halt_level=100, # never halt
stream=None,
debug=False
debug=False,
)
parser = Parser()
@@ -275,7 +280,7 @@ def validate_rst(text: str) -> list[tuple[int, str]]:
def iter_docstrings(package_name: str):
"""Yield docstrings of modules, classes, functions in the given package."""
"""Yield project-owned docstrings of modules, classes, and functions in a package."""
package = importlib.import_module(package_name)
@@ -286,20 +291,26 @@ def iter_docstrings(package_name: str):
if module.__doc__:
yield f"Module {module.__name__}", inspect.getdoc(module)
# Classes + methods
# Classes + functions defined by this module. Imported objects and inherited
# methods are validated where they are defined, not repeatedly under every alias.
for _, obj in inspect.getmembers(module):
if inspect.isclass(obj) or inspect.isfunction(obj):
if obj.__doc__:
yield f"{module.__name__}.{obj.__name__}", inspect.getdoc(obj)
if not (inspect.isclass(obj) or inspect.isfunction(obj)):
continue
if getattr(obj, "__module__", None) != module.__name__:
continue
# Methods of classes
if inspect.isclass(obj):
for _, meth in inspect.getmembers(obj, inspect.isfunction):
if meth.__doc__:
yield f"{module.__name__}.{obj.__name__}.{meth.__name__}", inspect.getdoc(meth)
if obj.__doc__:
yield f"{module.__name__}.{obj.__name__}", inspect.getdoc(obj)
if inspect.isclass(obj):
for _, meth in inspect.getmembers(obj, inspect.isfunction):
if meth.__name__ not in obj.__dict__:
continue
if meth.__doc__:
yield f"{module.__name__}.{obj.__name__}.{meth.__name__}", inspect.getdoc(meth)
def map_converted_to_original(orig: str, conv: str) -> dict[int,int]:
def map_converted_to_original(orig: str, conv: str) -> dict[int, int]:
"""Map original docstring line to converted docstring line.
Returns:
@@ -351,10 +362,10 @@ def test_all_docstrings_rst_compliant():
ignore_msg_patterns.extend(patterns)
for conv_line, msg_text in messages:
orig_line = line_map.get(conv_line - 1, conv_line - 1) + 1
if any(re.search(pat, msg_text) for pat in ignore_msg_patterns):
continue
filtered_messages.append((orig_line, msg_text))
orig_line = line_map.get(conv_line - 1, conv_line - 1) + 1
if any(re.search(pat, msg_text) for pat in ignore_msg_patterns):
continue
filtered_messages.append((orig_line, msg_text))
if filtered_messages:
failures.append((location, filtered_messages, doc, doc_converted))
+2 -2
View File
@@ -83,7 +83,7 @@ class TestEOSdashConfig:
"""Test extracting EOS configuration details from EOS config based on provided values."""
with FILE_TESTDATA_EOSSERVER_CONFIG_1.open("r", encoding="utf-8", newline=None) as fd:
values = json.load(fd)
config_details = create_config_details(config_eos, values)
config_details = create_config_details(type(config_eos), values)
assert any(
item["name"] == "server.eosdash_port" and item["value"] == "8504"
for key, item in config_details.items()
@@ -98,7 +98,7 @@ class TestEOSdashConfig:
with FILE_TESTDATA_EOSSERVER_CONFIG_1.open("r", encoding="utf-8", newline=None) as fd:
values = json.load(fd)
config_details = create_config_details(
PVForecastPlaneSetting(), values, values_prefix=["pvforecast", "planes", "0"]
PVForecastPlaneSetting, values, values_prefix=["pvforecast", "planes", "0"]
)
assert any(
item["name"] == "pvforecast.planes.0.surface_azimuth" and item["value"] == "170"