Commit 94965d7
chore: pyright CI gate (public API) + Python 3.13 matrix + pyproject cleanup (Modernization Phase 3)
Issue: #29 (Phase 3 — dev-tooling)
Phases 1 (#39) and 2 (#43) shipped the API ergonomics and bug fixes from
issue #29. This PR covers the dev-tooling-modernization sub-bullets that
were tractable in a single PR — `pyright` strict-mode CI gate on the
public API, Python 3.13 in the test matrix, and cleanup of stale
`pyproject.toml` config.
CI changes
- New `typecheck` job in `.github/workflows/ci.yml` runs
`pyright` (installed alongside the editable package) on the public-API
surface: `src/pptx/{__init__,api,presentation,util,exc,types}.py`.
`pptx/__init__.py` is included because it's the literal entrypoint
resolved by `from pptx import Presentation`. Pyright runs in strict
mode (already configured in `pyproject.toml`'s `[tool.pyright]`
section) and the gate fails on any error, satisfying issue #29's
acceptance criterion of "zero errors on the public API".
- Test matrix extended to include Python 3.13 (was 3.9 through 3.12).
Public-API pyright fixes (zero errors after these)
- `src/pptx/api.py` `Presentation()`: replaced `if hasattr(p,
"__fspath__"): p = os.fspath(p)` (which doesn't narrow under pyright)
with explicit
`pkg_file: str | IO[bytes] = os.fspath(p) if isinstance(p, os.PathLike) else p`.
Identical runtime behavior; fully narrowed for the type checker.
- `src/pptx/presentation.py` `save()`: same shape change for the same
reason.
- `src/pptx/presentation.py`: added
`# pyright: ignore[reportPrivateUsage]` on the deferred imports of
`_Sections` (from `pptx.sections`) and `_PortContext` (from
`pptx.parts.slide`) — both legitimately consumed at this seam by
`Presentation.sections` and `Presentation.append_from`. The
leading-underscore convention is documented intent ("internal");
pyright sees the rule and complains regardless. Suppression is the
standard escape hatch.
- `src/pptx/presentation.py`: dropped unused
`duplicate_notes_slide_for` import from `append_from`. The
`noqa: F401` was hiding an actually-unused symbol.
`pyproject.toml` cleanup
- Bumped `requires-python` from `>=3.8` to `>=3.9`. Python 3.8 reached
end-of-life in 2024-10 and was never in the test matrix; this aligns
the floor with what is actually exercised. PyPI users pinned to 3.8
will see a clean "no compatible version" message via wheel metadata
(no runtime crash). Per Forge's NIT, the next release tag should
bump the minor version (e.g. `1.0.x` → `1.1.0`) and call out the
floor change in `HISTORY.rst`.
- Dropped the `Python :: 3.8` classifier; added `Python :: 3.13`.
- Removed the dead `[tool.black]` section. The fork standardized on
`ruff format` in v1.2.0; black is no longer used anywhere in the
toolchain (no `black` invocation in CI, in any Makefile, or in any
developer doc).
Skipped from issue #29 Phase 3 (deferred to separate PRs)
- `uv` migration. Replacing the setuptools build backend, adding a
uv lockfile, and reworking CI for uv is a significant standalone
change worth its own PR.
- Ruff selection strengthening (adding e.g. `B` flake8-bugbear or
`RUF` Ruff-specific rules). Trial runs surface 49 + 82 findings
respectively — most are real but each requires manual
resolution. Defer to a follow-up PR that pairs the rule addition
with the cleanup commit.
- `pytest-syrupy` snapshot tests for XML fixtures (issue marks this
optional).
- `unittest`-style test conversion: already done in this fork.
Verified by `grep -rln "import unittest|class.*TestCase" tests/`
— empty result.
Tests
- Full pytest: `3456 passed in 4.99s` (no regressions; +0 vs Phase 2).
- Full behave: `1041 scenarios passed, 0 failed` (no regressions).
- Ruff: `ruff check src tests` → All checks passed; `ruff format
--check` → no diff.
- Pyright on public API: `0 errors, 0 warnings, 0 informations`.
Refs #291 parent 5c26976 commit 94965d7
4 files changed
Lines changed: 43 additions & 19 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
36 | 36 | | |
37 | 37 | | |
38 | 38 | | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
39 | 63 | | |
40 | 64 | | |
41 | 65 | | |
42 | 66 | | |
43 | 67 | | |
44 | 68 | | |
45 | | - | |
| 69 | + | |
46 | 70 | | |
47 | 71 | | |
48 | 72 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
17 | | - | |
18 | 17 | | |
19 | 18 | | |
20 | 19 | | |
21 | 20 | | |
| 21 | + | |
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
| |||
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
36 | | - | |
| 36 | + | |
37 | 37 | | |
38 | 38 | | |
39 | 39 | | |
| |||
42 | 42 | | |
43 | 43 | | |
44 | 44 | | |
45 | | - | |
46 | | - | |
47 | | - | |
48 | 45 | | |
49 | 46 | | |
50 | 47 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
27 | 27 | | |
28 | 28 | | |
29 | 29 | | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
30 | 33 | | |
31 | | - | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
32 | 39 | | |
33 | | - | |
34 | | - | |
35 | | - | |
36 | | - | |
37 | | - | |
| 40 | + | |
38 | 41 | | |
39 | 42 | | |
40 | 43 | | |
41 | | - | |
| 44 | + | |
42 | 45 | | |
43 | 46 | | |
44 | 47 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
73 | 73 | | |
74 | 74 | | |
75 | 75 | | |
76 | | - | |
77 | | - | |
78 | | - | |
79 | | - | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
80 | 80 | | |
81 | 81 | | |
82 | 82 | | |
| |||
157 | 157 | | |
158 | 158 | | |
159 | 159 | | |
160 | | - | |
| 160 | + | |
161 | 161 | | |
162 | 162 | | |
163 | 163 | | |
| |||
202 | 202 | | |
203 | 203 | | |
204 | 204 | | |
205 | | - | |
| 205 | + | |
206 | 206 | | |
207 | 207 | | |
208 | 208 | | |
| |||
0 commit comments