Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.5
rev: a56c0b927e6465d37cae3e97d35d4d18ab2b96cd # frozen: v0.16.9
hooks:
- id: ruff
exclude: ^benchmark/
- id: ruff-format
types_or: [python, pyi, jupyter]

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.11.1
rev: 7ff8d35ae36a7d2b968f2f90b4c723e292e594ee # frozen: v2.3.1
hooks:
- id: mypy
files: ^src/exstruct/
additional_dependencies:
- pydantic>=2.0.0
- types-PyYAML
Expand Down
35 changes: 21 additions & 14 deletions src/exstruct/core/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,8 +324,9 @@ def build_pre_com_pipeline(inputs: ExtractionInputs) -> list[ExtractionStep]:
StepConfig(
name="formulas_map_openpyxl",
step=step_extract_formulas_map_openpyxl,
enabled=lambda _inputs: _inputs.include_formulas_map
and not _inputs.use_com_for_formulas,
enabled=lambda _inputs: (
_inputs.include_formulas_map and not _inputs.use_com_for_formulas
),
),
StepConfig(
name="colors_map_openpyxl",
Expand All @@ -352,8 +353,9 @@ def build_pre_com_pipeline(inputs: ExtractionInputs) -> list[ExtractionStep]:
StepConfig(
name="formulas_map_openpyxl",
step=step_extract_formulas_map_openpyxl,
enabled=lambda _inputs: _inputs.include_formulas_map
and not _inputs.use_com_for_formulas,
enabled=lambda _inputs: (
_inputs.include_formulas_map and not _inputs.use_com_for_formulas
),
),
StepConfig(
name="colors_map_openpyxl",
Expand All @@ -380,14 +382,16 @@ def build_pre_com_pipeline(inputs: ExtractionInputs) -> list[ExtractionStep]:
StepConfig(
name="formulas_map_openpyxl",
step=step_extract_formulas_map_openpyxl,
enabled=lambda _inputs: _inputs.include_formulas_map
and not _inputs.use_com_for_formulas,
enabled=lambda _inputs: (
_inputs.include_formulas_map and not _inputs.use_com_for_formulas
),
),
StepConfig(
name="colors_map_openpyxl_if_skip_com",
step=step_extract_colors_map_openpyxl,
enabled=lambda _inputs: _inputs.include_colors_map
and bool(os.getenv("SKIP_COM_TESTS")),
enabled=lambda _inputs: (
_inputs.include_colors_map and bool(os.getenv("SKIP_COM_TESTS"))
),
),
StepConfig(
name="merged_cells_openpyxl",
Expand All @@ -409,14 +413,16 @@ def build_pre_com_pipeline(inputs: ExtractionInputs) -> list[ExtractionStep]:
StepConfig(
name="formulas_map_openpyxl",
step=step_extract_formulas_map_openpyxl,
enabled=lambda _inputs: _inputs.include_formulas_map
and not _inputs.use_com_for_formulas,
enabled=lambda _inputs: (
_inputs.include_formulas_map and not _inputs.use_com_for_formulas
),
),
StepConfig(
name="colors_map_openpyxl_if_skip_com",
step=step_extract_colors_map_openpyxl,
enabled=lambda _inputs: _inputs.include_colors_map
and bool(os.getenv("SKIP_COM_TESTS")),
enabled=lambda _inputs: (
_inputs.include_colors_map and bool(os.getenv("SKIP_COM_TESTS"))
),
),
StepConfig(
name="merged_cells_openpyxl",
Expand Down Expand Up @@ -467,8 +473,9 @@ def build_com_pipeline(inputs: ExtractionInputs) -> list[ComExtractionStep]:
ComStepConfig(
name="formulas_map_com",
step=step_extract_formulas_map_com,
enabled=lambda _inputs: _inputs.include_formulas_map
and _inputs.use_com_for_formulas,
enabled=lambda _inputs: (
_inputs.include_formulas_map and _inputs.use_com_for_formulas
),
),
ComStepConfig(
name="colors_map_com",
Expand Down
5 changes: 3 additions & 2 deletions src/exstruct/core/shapes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from __future__ import annotations

from collections.abc import Iterable, Iterator
from collections.abc import Callable, Iterable, Iterator
import math
from typing import Literal, Protocol, SupportsInt, cast, runtime_checkable

Expand Down Expand Up @@ -73,7 +73,8 @@ def find_index(edges: list[float], pos: float) -> int | None:
c = find_index(col_edges, x)
if r is None or c is None:
return None
return f"{xw.utils.col_name(c)}{r}"
column_name = cast(Callable[[int], str], xw.utils.col_name)(c)
return f"{column_name}{r}"


def has_arrow(style_val: object) -> bool:
Expand Down
7 changes: 3 additions & 4 deletions src/exstruct/edit/internal.py
Original file line number Diff line number Diff line change
Expand Up @@ -2065,7 +2065,7 @@ def _create_xls_seed_with_com(seed_path: Path, *, initial_sheet_name: str) -> No
app = xw.App(add_book=False, visible=False)
app.display_alerts = False
app.screen_updating = False
workbook = app.books.add()
workbook = cast(XlwingsWorkbookProtocol, app.books.add())
try:
workbook.sheets[0].name = initial_sheet_name
workbook.save(str(seed_path))
Expand Down Expand Up @@ -4459,8 +4459,7 @@ def _xlwings_add_list_object(list_objects: object, source_range_api: object) ->
errors.append(f"{attempt.signature} [{source_label}] -> {exc!r}")
tail = " | ".join(errors[-4:])
raise ValueError(
"apply_table_style failed to add table after COM Add signature retries. "
f"{tail}"
f"apply_table_style failed to add table after COM Add signature retries. {tail}"
)


Expand Down Expand Up @@ -4743,7 +4742,7 @@ def _xlwings_workbook(file_path: Path) -> Iterator[XlwingsWorkbookProtocol]:
app = xw.App(add_book=False, visible=False)
app.display_alerts = False
app.screen_updating = False
workbook = app.books.open(str(file_path))
workbook = cast(XlwingsWorkbookProtocol, app.books.open(str(file_path)))
try:
yield workbook
finally:
Expand Down
4 changes: 2 additions & 2 deletions src/exstruct/edit/specs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from __future__ import annotations

from typing import Final, cast
from typing import Final

from pydantic import BaseModel, Field

Expand Down Expand Up @@ -57,7 +57,7 @@ def get_alias_map_for_op(op_name: str) -> dict[str, str]:

if op_name not in PATCH_OP_SPECS:
return {}
spec = PATCH_OP_SPECS[cast(PatchOpType, op_name)]
spec = PATCH_OP_SPECS[op_name]
return dict(spec.aliases)


Expand Down
2 changes: 1 addition & 1 deletion src/exstruct/render/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -974,7 +974,7 @@ def _read_worker_result(result_path: Path) -> _RenderWorkerResult:
payload = json.loads(result_path.read_text(encoding="utf-8"))
except Exception as exc:
raise RenderError(
"Failed to render PDF pages: stage=result " f"invalid payload ({exc})."
f"Failed to render PDF pages: stage=result invalid payload ({exc})."
) from exc
if not isinstance(payload, dict):
raise RenderError(
Expand Down
27 changes: 27 additions & 0 deletions tasks/feature_spec.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,32 @@
# Feature Spec

## 2026-09-26 Dependabot alerts and Python dependency refresh

### Goal

- Resolve the currently open Dependabot alerts for `uv.lock` and refresh the other locked Python dependencies in the same dependency set.

### Dependency update contract

- Upgrade locked packages to the newest versions resolvable under the existing `pyproject.toml` dependency bounds and `requires-python = ">=3.11"`.
- Refresh the Ruff and mypy hook revisions in `.pre-commit-config.yaml` to match the updated locked tool versions.
- Preserve the existing direct dependency ranges, optional extras, and public package/API contracts unless a documented constraint prevents a patched version from resolving.
- Do not change runtime behavior or public APIs as part of this dependency refresh.
- Static typing compatibility edits are allowed when the updated package types expose existing annotation mismatches; they must not change runtime behavior.

### Scope and verification

- GitHub repository: `harumiWeb/exstruct`.
- Open Dependabot inventory at task start: 43 alerts, all reported against `uv.lock`; repeated alerts include multiple advisories and package-name casing variants.
- Update the workspace lockfile and include the `benchmark` workspace member in resolution.
- Verify with lockfile consistency, a locked full workspace sync, the repository pre-commit hooks, current Ruff/mypy checks, documentation build, diff review, and a fresh Dependabot alert query.
- Record local results separately from GitHub PR/check status.

### ADR verdict

- `not-needed`
- rationale: the lockfile/tool refresh and static typing compatibility edits do not change the public contract or introduce a lasting design policy.

## 2026-04-22 README English/Japanese parity refresh

### Goal
Expand Down
22 changes: 22 additions & 0 deletions tasks/todo.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
# Todo

## 2026-09-26 Dependabot alerts and Python dependency refresh

### Planning

- [x] Review all open GitHub Dependabot alerts and the dependency constraints in `pyproject.toml` / `uv.lock`.
- [x] Upgrade every locked Python dependency to the newest version allowed by the existing project and Python-version constraints.
- [x] Refresh the Ruff and mypy pre-commit hook revisions to match the updated toolchain.
- [x] Resolve static-analysis incompatibilities introduced by the newly locked tool and library versions without changing runtime behavior.
- [x] Check lockfile consistency, install the complete dependency set, run the configured static checks, and inspect the complete diff.
- [x] Re-query Dependabot alerts; all 43 remain open on the base branch until the updates merge.
- [x] Commit, push, and open a PR with the verified results.

### Review

- `uv lock --upgrade` resolved 128 packages across the root and `benchmark` workspace members; all 43 open lockfile alerts have patched versions in the updated lock.
- Refreshed pre-commit hooks to Ruff 0.16.9 and mypy 2.3.1, limited formatter inputs to Python-family files, and aligned mypy's file scope with `src/exstruct/`.
- Adjusted type annotations/casts at the xlwings boundary and removed a redundant type cast; no runtime or public API behavior changed.
- Verification passed: `uv lock --check`, `uv sync --locked --all-packages --all-groups --all-extras`, `uv run task ruff`, `uv run task mypy`, `uv run task precommit-run`, `uv run task build-docs`, and `git diff --check`.
- The documentation build succeeded with its existing `generated/models.md` navigation warning. The full pytest suite was not run.
Comment thread
harumiWeb marked this conversation as resolved.
- The 43 Dependabot alerts are still open on `main` pending merge of this PR.
- PR: [#140](https://github.com/harumiWeb/exstruct/pull/140), `fix(deps): refresh Python dependencies`.

## 2026-04-22 README English/Japanese parity refresh

### Planning
Expand Down
10 changes: 4 additions & 6 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,10 @@
_FixtureReturn = TypeVar("_FixtureReturn")


def _typed_autouse_fixture() -> (
Callable[
[Callable[_FixtureParam, _FixtureReturn]],
Callable[_FixtureParam, _FixtureReturn],
]
):
def _typed_autouse_fixture() -> Callable[
[Callable[_FixtureParam, _FixtureReturn]],
Callable[_FixtureParam, _FixtureReturn],
]:
"""Return a typed autouse fixture decorator for strict mypy runs."""

return cast(
Expand Down
6 changes: 3 additions & 3 deletions tests/mcp/patch/test_models_internal_coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -553,9 +553,9 @@ def test_internal_create_chart_allows_name_matching_new_default_name() -> None:
chart_collection.Count = 0
chart_collection.Add.return_value = chart_object
chart_objects = MagicMock(
side_effect=lambda index=None: chart_collection
if index is None
else chart_object
side_effect=lambda index=None: (
chart_collection if index is None else chart_object
)
)

anchor_range = MagicMock()
Expand Down
18 changes: 4 additions & 14 deletions tests/render/test_render_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -663,13 +663,8 @@ def test_run_render_worker_subprocess_success_when_join_timeout(
monkeypatch.setattr(
render,
"_wait_for_worker_result",
lambda proc,
*,
result_path,
join_timeout_deadline,
join_timeout_seconds,
post_exit_timeout_seconds: render._RenderWorkerResult.success(
[str(output_dir / "01_Sheet1.png")]
lambda proc, *, result_path, join_timeout_deadline, join_timeout_seconds, post_exit_timeout_seconds: (
render._RenderWorkerResult.success([str(output_dir / "01_Sheet1.png")])
),
)
result = render._run_render_worker_subprocess(
Expand Down Expand Up @@ -781,13 +776,8 @@ def test_run_render_worker_subprocess_uses_single_join_budget(
monkeypatch.setattr(
render,
"_wait_for_worker_result",
lambda proc,
*,
result_path,
join_timeout_deadline,
join_timeout_seconds,
post_exit_timeout_seconds: render._RenderWorkerResult.success(
[str(output_dir / "01_Sheet1.png")]
lambda proc, *, result_path, join_timeout_deadline, join_timeout_seconds, post_exit_timeout_seconds: (
render._RenderWorkerResult.success([str(output_dir / "01_Sheet1.png")])
),
)

Expand Down
Loading
Loading