Skip to content

Commit 63192a5

Browse files
authored
typing (#74)
1 parent 821d36e commit 63192a5

24 files changed

Lines changed: 544 additions & 252 deletions

.github/workflows/publish.yml

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,9 @@ jobs:
1111
permissions:
1212
id-token: write # to authenticate as Trusted Publisher to pypi.org
1313
steps:
14-
- uses: actions/checkout@v5
15-
- uses: actions/setup-python@v6
14+
- uses: actions/checkout@v7
15+
- uses: astral-sh/setup-uv@v9.0.0
1616
with:
17-
python-version: "3.x"
18-
cache: pip
19-
- run: pip install build
20-
- run: python -m build
17+
enable-cache: false
18+
- run: uv build
2119
- uses: pypa/gh-action-pypi-publish@release/v1

.github/workflows/pytest.yml

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,51 +10,56 @@ jobs:
1010
test:
1111
strategy:
1212
matrix:
13-
python-version: ["3.9", "3.13"]
13+
python-version: ["3.10", "3.14"]
1414
runs-on: ubuntu-latest
1515
timeout-minutes: 60
16+
env:
17+
UV_PYTHON: ${{ matrix.python-version }}
1618
steps:
17-
- name: Checkout
18-
uses: actions/checkout@v5
19+
- &clone
20+
uses: actions/checkout@v7
1921
with:
2022
filter: blob:none
2123
fetch-depth: 0
2224

23-
- name: Setup Python
24-
uses: actions/setup-python@v6
25+
- &uv
26+
uses: astral-sh/setup-uv@v9.0.0
2527
with:
26-
python-version: ${{ matrix.python-version }}
27-
cache: pip
28+
enable-cache: true
2829

29-
- name: Install project and dependencies
30-
run: pip install .[annlibs,tests]
30+
- &hatch
31+
name: Install Hatch
32+
# https://github.com/pypa/hatch/pull/2351
33+
run: uv tool install git+https://github.com/pypa/hatch.git@af5339a1a5bb8e6d4b659c4c1bfff8e7e37cdaff
3134

3235
- name: Run test suite
33-
run: pytest -v --color=yes
36+
run: hatch test -py ${{ matrix.python-version }} -v --color=yes
37+
38+
types:
39+
runs-on: ubuntu-latest
40+
steps:
41+
- *clone
42+
- *uv
43+
- *hatch
44+
- name: Type check
45+
run: hatch check types
3446

3547
build:
3648
runs-on: ubuntu-latest
3749
steps:
38-
- uses: actions/checkout@v5
39-
with:
40-
filter: blob:none
41-
fetch-depth: 0
42-
- uses: actions/setup-python@v6
43-
with:
44-
python-version: "3.x"
45-
cache: pip
46-
- name: Install tools
47-
run: pip install twine build
50+
- *clone
51+
- *uv
4852
- name: Build and check
4953
run: |
50-
python -m build
51-
twine check dist/*.whl
54+
uv build
55+
uvx twine check dist/*.whl
5256
5357
check:
5458
if: always()
5559
needs:
5660
- build
5761
- test
62+
- types
5863
runs-on: ubuntu-latest
5964
steps:
6065
# https://github.com/marketplace/actions/alls-green#why

.pre-commit-config.yaml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
ci:
2+
skip: [mypy] # needs hatch to build an env with the ANN libs
13
repos:
24
- repo: https://github.com/pre-commit/pre-commit-hooks
35
rev: v6.0.0
@@ -15,9 +17,19 @@ repos:
1517
- repo: https://github.com/astral-sh/ruff-pre-commit
1618
rev: v0.16.1
1719
hooks:
18-
- id: ruff
20+
- id: ruff-check
1921
args: ["--fix"]
2022
- id: ruff-format
23+
- repo: local
24+
hooks:
25+
- id: mypy
26+
name: mypy
27+
entry: hatch check types
28+
language: system
29+
types: [python]
30+
files: ^(src|tests)/
31+
require_serial: true
32+
pass_filenames: false
2133
- repo: https://github.com/pre-commit/mirrors-prettier
2234
rev: v4.0.0-alpha.8
2335
hooks:

.readthedocs.yaml

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

.readthedocs.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# https://docs.readthedocs.io/en/stable/config-file/v2.html
2+
version: 2
3+
build:
4+
os: ubuntu-26.04
5+
tools:
6+
python: "3.14"
7+
jobs:
8+
post_checkout:
9+
# unshallow so version can be derived from tag
10+
- git fetch --unshallow || true
11+
create_environment:
12+
- asdf plugin add uv
13+
- asdf install uv latest
14+
- asdf global uv latest
15+
# https://github.com/pypa/hatch/pull/2351
16+
- UV_TOOL_BIN_DIR=$HOME/.asdf/bin uv tool install git+https://github.com/pypa/hatch.git@af5339a1a5bb8e6d4b659c4c1bfff8e7e37cdaff
17+
build:
18+
html:
19+
- hatch run docs:build
20+
- mv docs/_build $READTHEDOCS_OUTPUT

docs/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# This file only contains a selection of the most common options. For a full
44
# list see the documentation:
55
# https://www.sphinx-doc.org/en/master/usage/configuration.html
6+
from __future__ import annotations
67

78
import os
89

examples/rnn_dbscan_big.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,38 @@
77
88
"""
99

10+
from __future__ import annotations
11+
12+
from typing import TYPE_CHECKING
13+
1014
import numpy as np
1115
from joblib import Memory
1216
from sklearn import metrics
1317
from sklearn.datasets import fetch_openml
1418

1519
from sklearn_ann.cluster.rnn_dbscan import simple_rnn_dbscan_pipeline
1620

21+
if TYPE_CHECKING:
22+
from collections.abc import Callable
23+
from typing import Any
24+
1725

1826
# #############################################################################
1927
# Generate sample data
20-
def fetch_mnist():
28+
def fetch_mnist() -> tuple[Any, Any]:
2129
print("Downloading mnist_784")
22-
mnist = fetch_openml("mnist_784")
23-
return mnist.data / 255, mnist.target
30+
data, target = fetch_openml("mnist_784", return_X_y=True)
31+
return data / 255, target
2432

2533

2634
memory = Memory("./mnist")
2735

2836
X, y = memory.cache(fetch_mnist)()
2937

3038

31-
def run_rnn_dbscan(neighbor_transformer, n_neighbors, **kwargs):
39+
def run_rnn_dbscan(
40+
neighbor_transformer: Callable[..., Any], n_neighbors: int, **kwargs: Any
41+
) -> None:
3242
# #############################################################################
3343
# Compute RnnDBSCAN
3444

examples/rnn_dbscan_simple.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
1010
"""
1111

12+
from __future__ import annotations
13+
1214
import numpy as np
1315
from sklearn import metrics
1416
from sklearn.datasets import make_blobs
@@ -34,7 +36,7 @@
3436

3537
# Number of clusters in labels, ignoring noise if present.
3638
n_clusters_ = len(set(labels)) - (1 if -1 in labels else 0)
37-
n_noise_ = list(labels).count(-1)
39+
n_noise_ = int((labels == -1).sum())
3840

3941
print(f"""\
4042
Estimated number of clusters: {n_clusters_}

pyproject.toml

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,13 @@ license = "BSD-3-Clause"
99
urls.Source = "https://github.com/scikit-learn-contrib/sklearn-ann"
1010
urls.Documentation = "https://sklearn-ann.readthedocs.io/"
1111
dynamic = ["version", "readme"]
12-
requires-python = ">=3.9"
12+
requires-python = ">=3.10"
1313
dependencies = [
1414
"scikit-learn>=1.6.0",
1515
"scipy>=1.11.1,<2.0.0",
1616
]
1717

1818
[project.optional-dependencies]
19-
tests = [
20-
"pytest>=6.2.1",
21-
"pytest-cov>=2.10.1",
22-
]
23-
docs = [
24-
"sphinx>=7",
25-
"sphinx-gallery>=0.8.2",
26-
"sphinx-book-theme>=1.1.0",
27-
"sphinx-issues>=1.2.0",
28-
"numpydoc>=1.1.0",
29-
"matplotlib>=3.3.3",
30-
"scanpydoc",
31-
]
3219
annoy = [
3320
"annoy>=1.17.0,<2.0.0",
3421
]
@@ -46,6 +33,26 @@ annlibs = [
4633
"sklearn-ann[annoy,faiss,pynndescent,nmslib]",
4734
]
4835

36+
[dependency-groups]
37+
docs = [
38+
"sphinx>=7",
39+
"sphinx-gallery>=0.8.2",
40+
"sphinx-book-theme>=1.1.0",
41+
"sphinx-issues>=1.2.0",
42+
"numpydoc>=1.1.0",
43+
"matplotlib>=3.3.3",
44+
"scanpydoc",
45+
]
46+
tests = [
47+
"pytest>=6.2.1",
48+
"pytest-cov>=2.10.1",
49+
]
50+
typing = [
51+
"mypy",
52+
"matplotlib", # examples are type checked, too
53+
"scipy-stubs",
54+
]
55+
4956
[tool.hatch.version]
5057
source = "vcs"
5158

@@ -74,6 +81,7 @@ select = [
7481
"PTH", # Pathlib
7582
"RUF", # Ruff’s own rules
7683
"T20", # print statements
84+
"TC", # type checking
7785
]
7886
ignore = [
7987
# Don’t complain about “confusables”
@@ -84,19 +92,51 @@ ignore = [
8492
"tests/*.py" = ["T20"]
8593
[tool.ruff.lint.isort]
8694
known-first-party = ["sklearn_ann"]
95+
required-imports = ["from __future__ import annotations"]
96+
[tool.ruff.lint.flake8-type-checking]
97+
exempt-modules = []
98+
strict = true
8799

88100
[tool.hatch.envs.docs]
89101
installer = "uv"
90-
features = ["docs", "annlibs"]
102+
features = ["annlibs"]
103+
dependency-groups = ["docs"]
91104
scripts.build = "sphinx-build -M html docs docs/_build"
92105

106+
[tool.hatch.envs.hatch-check-types]
107+
features = ["annlibs"]
108+
dependency-groups = ["tests", "typing"]
109+
scripts.check = "mypy {args:.}"
110+
93111
[tool.hatch.envs.hatch-test]
94112
default-args = []
95-
features = ["tests", "annlibs"]
113+
features = ["annlibs"]
114+
dependency-groups = ["tests"]
96115

97116
[tool.hatch.build.targets.wheel]
98117
packages = ["src/sklearn_ann"]
99118

119+
[tool.mypy]
120+
python_version = "3.12"
121+
mypy_path = ["src", "tests"]
122+
strict = true
123+
explicit_package_bases = true # pytest doesn’t do __init__.py
124+
no_implicit_optional = true
125+
disallow_untyped_decorators = false # e.g. pytest.mark.parametrize
126+
follow_untyped_imports = true
127+
# follow_untyped_imports makes these readable, but not annotated
128+
untyped_calls_exclude = ["sklearn", "joblib", "pynndescent"]
129+
130+
[[tool.mypy.overrides]]
131+
# pynndescent has no __all__, so its re-exports are implicit
132+
module = ["pynndescent"]
133+
no_implicit_reexport = false
134+
135+
[[tool.mypy.overrides]]
136+
# nmslib is a compiled extension module without stubs
137+
module = ["nmslib"]
138+
ignore_missing_imports = true
139+
100140
[build-system]
101141
requires = ["hatchling", "hatch-vcs", "hatch-fancy-pypi-readme"]
102142
build-backend = "hatchling.build"

0 commit comments

Comments
 (0)