Skip to content

Commit 1b48569

Browse files
feat!: add ty type checking (ap check), rename ruff's ap check to ap lint
- `ap check` runs `ty check` with afterpython/ty.toml (ty has no `extend` yet) - `ap init` asks to create afterpython/ty.toml, `ap init ty` re-runs the setup - ruff/ty hooks and CI checks only run when their config exists in afterpython/ - local `ty` pre-commit hook and CI `typecheck` job (uv or pixi environment) - fix type errors found by ty, incl. pcu treating a failed PyPI request as a version - docs: type checking, editor setup, `ap lint` Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 4b1fd67 commit 1b48569

33 files changed

Lines changed: 818 additions & 232 deletions

‎.github/workflows/ci.yml‎

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,13 @@ jobs:
3131

3232
- name: Run ruff
3333
# fails if ruff-check finds issues or ruff-format would change a file
34-
run: uvx prek run --config afterpython/.pre-commit-config.yaml --all-files --show-diff-on-failure ruff-check ruff-format
34+
# No afterpython/ruff.toml means ruff isn't set up (and there are no ruff hooks), pass green.
35+
run: |
36+
if [ ! -f afterpython/ruff.toml ]; then
37+
echo "afterpython/ruff.toml not found (ruff not set up); skipping."
38+
exit 0
39+
fi
40+
uvx prek run --config afterpython/.pre-commit-config.yaml --all-files --show-diff-on-failure ruff-check ruff-format
3541
3642
# Job 2: Detect Test Strategy
3743
# Determines whether to use pixi or uv based on pixi.toml presence
@@ -202,4 +208,44 @@ jobs:
202208
if-no-files-found: error
203209

204210
# Job 5: Type Check
205-
# typecheck:
211+
# Runs `ap check` like the ty hook does locally, in the project's own environment
212+
# (uv's .venv, or pixi's default environment when pixi.toml exists)
213+
typecheck:
214+
runs-on: ubuntu-latest
215+
steps:
216+
- name: Checkout code
217+
uses: actions/checkout@v7
218+
219+
- name: Check for ty setup
220+
# No afterpython/ty.toml means ty isn't set up (and there's no ty hook), pass green.
221+
id: ty
222+
run: |
223+
if [ -f afterpython/ty.toml ]; then
224+
echo "enabled=true" >> $GITHUB_OUTPUT
225+
else
226+
echo "afterpython/ty.toml not found (ty not set up); skipping."
227+
fi
228+
229+
- name: Install uv
230+
if: steps.ty.outputs.enabled == 'true' && hashFiles('pixi.toml') == ''
231+
uses: astral-sh/setup-uv@v10.2.0
232+
233+
- name: Run ty (uv)
234+
# the package + all [dependency-groups] (incl. afterpython itself), at the versions in uv.lock
235+
if: steps.ty.outputs.enabled == 'true' && hashFiles('pixi.toml') == ''
236+
run: |
237+
uv sync --locked --all-groups
238+
uv run --no-sync ap check
239+
240+
- name: Set up pixi
241+
if: steps.ty.outputs.enabled == 'true' && hashFiles('pixi.toml') != ''
242+
uses: prefix-dev/setup-pixi@v0.10.2
243+
with:
244+
pixi-version: v0.81.0
245+
cache: true
246+
environments: default
247+
248+
- name: Run ty (pixi)
249+
# ty uses the pixi environment it runs in (there's no .venv in CI)
250+
if: steps.ty.outputs.enabled == 'true' && hashFiles('pixi.toml') != ''
251+
run: pixi run ap check

‎.gitignore‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,3 +210,6 @@ tests/data/
210210
!afterpython/_website/src/lib/
211211

212212
afterpython/_website.backup/
213+
214+
# zed settings
215+
**/.zed/

‎README.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
[project-website-template]: https://github.com/AfterPythonOrg/project-website-template
2626
[uv]: https://docs.astral.sh/uv/
2727
[ruff]: https://docs.astral.sh/ruff/
28+
[ty]: https://docs.astral.sh/ty/
2829

2930

3031
## Problem
@@ -92,5 +93,5 @@ ap tui
9293
- [ruff]
9394
- [pagefind]
9495
- [WebLLM]
95-
<!-- - ty -->
96+
- [ty]
9697
<!-- - [pixi] -->

‎afterpython/.pre-commit-config.yaml‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,24 @@ repos:
3434
- id: ruff-format
3535
stages:
3636
- pre-commit
37+
- repo: local
38+
# not the official hook (astral-sh/ty-pre-commit), at least for now: it runs `uv check`,
39+
# which always type checks against its own environment built from pyproject.toml,
40+
# so packages only installed by e.g. pixi (like pytest in a pixi feature) can't be found.
41+
# `ap check` uses the project's own environment and ty version, like the editor does.
42+
# It type checks the whole project (like the official hook), not only the staged files,
43+
# since a change in one file can break types in another.
44+
hooks:
45+
- id: ty
46+
name: ty
47+
entry: sh -c 'PATH="$PWD/.venv/bin:$PWD/.venv/Scripts:$PATH" exec ap check'
48+
language: system
49+
types_or: [python, pyi, jupyter]
50+
pass_filenames: false
51+
always_run: true
52+
require_serial: true
53+
stages:
54+
- pre-commit
3755
- repo: local
3856
hooks:
3957
# same id twice so that SKIP=ap-commit skips both

‎afterpython/README_sample.py‎

Lines changed: 0 additions & 108 deletions
This file was deleted.

‎afterpython/doc/concepts.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
[mystmd]: https://mystmd.org
22
[pre-commit]: https://github.com/pre-commit/pre-commit
33
[ruff]: https://github.com/astral-sh/ruff
4+
[ty]: https://github.com/astral-sh/ty
45

56
# Concepts
67

@@ -16,6 +17,7 @@ The `afterpython/` folder serves as a centralized location for both your project
1617

1718
**Configuration files:**
1819
- `ruff.toml` for [ruff]
20+
- `ty.toml` for [ty]
1921
- `.pre-commit-config.yaml` for [pre-commit]
2022
- `authors.yml` for [mystmd]
2123
- `afterpython.toml` for `afterpython` itself (e.g. allowed commit types)
@@ -25,7 +27,7 @@ The `afterpython/` folder serves as a centralized location for both your project
2527
This structure serves two goals:
2628

2729
1. **Declutter the root directory** - Keeps maintenance-related configuration files separate from package code, making the project structure cleaner
28-
2. **Provide sane defaults** - Comes pre-configured with sensible defaults for common maintenance tools like [pre-commit] and [ruff], so you can start using them immediately
30+
2. **Provide sane defaults** - Comes pre-configured with sensible defaults for common maintenance tools like [pre-commit], [ruff] and [ty], so you can start using them immediately
2931

3032

3133
---

‎afterpython/doc/overview.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
[project-website-template]: https://github.com/AfterPythonOrg/project-website-template
1313
[uv]: https://docs.astral.sh/uv/
1414
[ruff]: https://docs.astral.sh/ruff/
15+
[ty]: https://docs.astral.sh/ty/
1516

1617
# Overview
1718

@@ -51,5 +52,5 @@ ap tui
5152
- [ruff]
5253
- [pagefind]
5354
- [WebLLM]
54-
<!-- - ty -->
55+
- [ty]
5556
<!-- - [pixi] -->

‎afterpython/doc/package_maintenance/ci_cd.md‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ Currently, `afterpython` only supports GitHub Actions for CI/CD pipelines.
77
Inside `.github/workflows/`, you'll find the following workflows:
88

99
### `ci.yml`
10-
Runs linting and formatting with ruff, runs tests with pytest and builds the package and verifies it can be installed with `uv build`.
10+
Runs linting and formatting with ruff, type checking with ty (`ap check`), runs tests with pytest and builds the package and verifies it can be installed with `uv build`.
11+
The ruff and ty checks only run if you set up `afterpython/ruff.toml` and `afterpython/ty.toml` respectively.
1112

1213
### `release.yml` (optional)
1314
Releases your package to [PyPI] and GitHub.
@@ -37,7 +38,7 @@ To create default branch protection rules:
3738
- No Branch Deletion
3839
- Protects the main branch from being deleted.
3940
- CI Status Checks (before the branch can be updated)
40-
- Requires all configured CI checks to pass before any update (push or PR merge) is allowed.
41+
- Requires the CI jobs `lint`, `test`, `build` and `typecheck` to pass before any update (push or PR merge) is allowed.
4142

4243
You can view them in **GitHub → Settings → Rules → Rulesets**
4344

‎afterpython/doc/package_maintenance/commit_workflow.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ After running `ap init`, if you agreed to create a `.pre-commit-config.yaml` fil
1212
### Commands
1313
- `ap prek` (or `ap pc`) — equivalent to `prek --config afterpython/.pre-commit-config.yaml`
1414

15+
### Default Hooks
16+
Besides basic file checks (e.g. trailing whitespace, YAML/TOML syntax), these hooks run on every commit:
17+
- `ruff-check` and `ruff-format` — lint and format with ruff (only if you set up `afterpython/ruff.toml`)
18+
- `ty` — type check the whole project with `ap check` (only if you set up `afterpython/ty.toml`)
19+
20+
See [Package Management](./package_management.md#type-checking) for details.
21+
1522

1623
---
1724
## Commit Messages
@@ -52,5 +59,6 @@ To opt out, remove the section or leave it empty. If you opted out during `ap in
5259

5360
### Bypassing Checks
5461
- `SKIP=ap-commit git commit` — skip the commit message check
62+
- `SKIP=ty git commit` — skip the type check, e.g. to commit work in progress (it still runs in CI)
5563
- `git commit --no-verify` — skip all pre-commit and commit message hooks
5664
- `git push --no-verify` — skip all pre-push hooks

‎afterpython/doc/package_maintenance/package_management.md‎

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
[poetry]: https://github.com/python-poetry/poetry
33
[pdm]: https://github.com/pdm-project/pdm
44
[ruff]: https://github.com/astral-sh/ruff
5+
[ty]: https://github.com/astral-sh/ty
56
[pixi]: https://github.com/prefix-dev/pixi
67
[npm-check-updates]: https://www.npmjs.com/package/npm-check-updates
78

@@ -30,17 +31,80 @@ Keep all your ruff settings in `afterpython/ruff.toml`, not in `pyproject.toml`
3031
If `ap init` finds an existing ruff config there, it skips the ruff setup; remove it and run `ap init ruff` to set it up again.
3132

3233
Wrappers (all arguments are passed through to ruff):
33-
- `ap check` (alias: `ap lint`) runs `ruff check`, e.g. `ap check --fix`.
34+
- `ap lint` runs `ruff check`, e.g. `ap lint --fix`.
3435
- `ap format` runs `ruff format`, e.g. `ap format --check`.
3536

3637
Or you can just directly use the `ruff` command (and ruff editor extensions) as usual, they use the same config.
3738

39+
The ruff hooks (`ruff-check` and `ruff-format`) and the ruff step of the CI `lint` job only run when `afterpython/ruff.toml` exists.
40+
If you declined ruff during `ap init` (or its setup was skipped), they are removed/skipped; if you use your own ruff config, set up its hooks and CI yourself.
41+
3842
:::{note}
3943
Relative paths in `afterpython/ruff.toml` are resolved from `afterpython/`, not the project root,
4044
e.g. use `"../tests/*"` in `per-file-ignores` to target your project's `tests/` folder.
4145
:::
4246

4347

48+
---
49+
## Type Checking
50+
`afterpython` uses [ty] for type checking, configured in `afterpython/ty.toml`.
51+
52+
- `ap check` runs `ty check` with `afterpython/ty.toml` (all arguments are passed through to ty), e.g. `ap check src/`.
53+
54+
Unlike ruff, ty doesn't support `extend` yet, so `pyproject.toml` can't point to `afterpython/ty.toml`, and ty only finds it when it's told to. So:
55+
- use `ap check` instead of `ty check` (or run `ty check --config-file afterpython/ty.toml` from the project root)
56+
- set up your editor as shown [below](#editor-setup)
57+
58+
Keep all your ty settings in `afterpython/ty.toml`, not in `pyproject.toml` (`[tool.ty]`) or a `ty.toml` at the project root.
59+
If `ap init` finds an existing ty config there, it skips the ty setup; remove it and run `ap init ty` to set it up again.
60+
61+
The `ty` hook (runs `ap check` on every commit) and the CI `typecheck` job only run when `afterpython/ty.toml` exists.
62+
If you declined ty during `ap init` (or its setup was skipped), they are removed/skipped.
63+
64+
:::{note}
65+
Unlike `afterpython/ruff.toml`, relative paths in `afterpython/ty.toml` are resolved from the project root,
66+
e.g. use `"tests/"` to target your project's `tests/` folder.
67+
:::
68+
69+
### Python Environment
70+
ty needs your project's Python environment to resolve imports of installed packages.
71+
It automatically uses the activated virtual environment, or `.venv` at the project root.
72+
For other environments (e.g. `pixi` or `conda`), or if your project has both `.venv` and a `pixi` environment and you want the latter, set it in `afterpython/ty.toml`:
73+
```toml
74+
[environment]
75+
python = ".pixi/envs/default"
76+
```
77+
Packages you only use in some files (e.g. `pytest` in `tests/`) must be installed in that environment too, otherwise ty reports them as unresolved imports.
78+
With `pixi`, that means adding the feature to your default environment, e.g. `default = ["dev", "test"]` in `pixi.toml`.
79+
80+
### Editor Setup
81+
ty's language server (used by editor extensions) doesn't find `afterpython/ty.toml` by itself, you need to set its `configurationFile` setting **in your project's settings**, e.g.
82+
83+
VS Code (`.vscode/settings.json`):
84+
```json
85+
{
86+
"ty.configurationFile": "./afterpython/ty.toml"
87+
}
88+
```
89+
90+
Zed (`.zed/settings.json`):
91+
```json
92+
{
93+
"lsp": {
94+
"ty": {
95+
"settings": {
96+
"configurationFile": "./afterpython/ty.toml"
97+
}
98+
}
99+
}
100+
}
101+
```
102+
For other editors, see ty's [editor settings](https://docs.astral.sh/ty/reference/editor-settings/#configurationfile).
103+
104+
:::{warning}
105+
Don't set it in your global (user) settings, ty fails to load projects that don't have `afterpython/ty.toml`.
106+
:::
107+
44108
---
45109
## Python Check Updates (`pcu`)
46110
As a package maintainer, you face a dilemma: your dependencies (e.g. `pandas`) release new versions with bug fixes and features, but updating the minimum versions (e.g. `pandas>=2.0.0`) in your `pyproject.toml` means dropping support for users with older versions.

0 commit comments

Comments
 (0)