Skip to content

Commit 042011a

Browse files
committed
Pin dev deps to latest, expand typecheck tests, simplify tox to testing-only
- Update dev dep versions: ty>=0.0.20, ruff>=0.15.4, mypy>=1.19.1, tox>=4.47.1, tox-uv>=1.33.1, ipykernel>=7.2.0 - Restructure tox.ini: remove lint/typecheck envs (redundant with pre-commit + CI), focus on runtime testing only - Cross beartype compat with torch/jax backends (bt{020,021,022}×{torch,jax}) - Expand typecheck tests from 3 to 5 sample files covering all public API edge cases: imports, decorator signatures, Like types, make_array_type, and pyright-only annotations - Update CI beartype-compat matrix to include backend combinations - Fix missing B import in test_numpy.py (needed for ~B with PEP 563)
1 parent 4bd50d5 commit 042011a

File tree

11 files changed

+665
-89
lines changed

11 files changed

+665
-89
lines changed

.github/workflows/ci.yml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
steps:
4040
- uses: actions/checkout@v6
4141
- uses: astral-sh/setup-uv@v7
42-
- run: uv run tox -e typecheck-compat
42+
- run: uv run pytest tests/test_typecheck.py -v
4343

4444
test:
4545
runs-on: ubuntu-latest
@@ -52,16 +52,19 @@ jobs:
5252
steps:
5353
- uses: actions/checkout@v6
5454
- uses: astral-sh/setup-uv@v7
55-
- run: uv run tox -e "${{ matrix.python }}${{ matrix.suffix }}"
55+
- run: uv run tox -e "py${{ matrix.python }}${{ matrix.suffix }}"
56+
env:
57+
TOX_PYTHON: python${{ matrix.python }}
5658

5759
beartype-compat:
5860
runs-on: ubuntu-latest
5961
strategy:
6062
fail-fast: false
6163
matrix:
6264
version: ["bt020", "bt021", "bt022"]
63-
name: beartype (${{ matrix.version }})
65+
suffix: ["", "-torch", "-jax"]
66+
name: beartype (${{ matrix.version }}${{ matrix.suffix }})
6467
steps:
6568
- uses: actions/checkout@v6
6669
- uses: astral-sh/setup-uv@v7
67-
- run: uv run tox -e ${{ matrix.version }}
70+
- run: uv run tox -e "${{ matrix.version }}${{ matrix.suffix }}"

pyproject.toml

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,22 @@ requires-python = ">=3.12"
88
dependencies = ["numpy>=2.2.0", "beartype>=0.22.9"]
99

1010
[dependency-groups]
11+
# CPU-only variants for dev/CI testing. Shapix is device-agnostic — it checks
12+
# .shape and .dtype only, so GPU/TPU builds (torch+cuda, jax[cuda12], etc.)
13+
# work without any extra configuration. Users install their own backend.
1114
torch = ["torch>=2.6.0"]
1215
jax = ["jax[cpu]>=0.5.0"]
1316
dev = [
14-
"ruff>=0.14.11",
17+
"ruff>=0.15.4",
1518
"pyright>=1.1.408",
16-
"mypy>=1.15",
17-
"ty>=0.0.1a7",
19+
"mypy>=1.19.1",
20+
"ty>=0.0.20",
1821
"pytest>=9.0.2",
1922
"pre-commit>=4.5.1",
20-
"tox>=4.19",
21-
"tox-uv>=1",
23+
"tox>=4.47.1",
24+
"tox-uv>=1.33.1",
2225
"codespell>=2.4.1",
23-
"ipykernel>=7.1.0",
26+
"ipykernel>=7.2.0",
2427
"matplotlib>=3.10.8",
2528
]
2629

tests/test_numpy.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -773,15 +773,15 @@ def f(x: F32[3, N]) -> F32[3, N]:
773773
return x
774774

775775
with pytest.raises(BeartypeCallHintParamViolation):
776-
f(np.ones((4, 5), dtype=np.int32))
776+
f(np.ones((4, 5), dtype=np.int32)) # type: ignore
777777

778778
def test_both_wrong(self) -> None:
779779
@beartype
780780
def f(x: F32[N]) -> F32[N]:
781781
return x
782782

783783
with pytest.raises(BeartypeCallHintParamViolation):
784-
f(np.ones(3, dtype=np.int64))
784+
f(np.ones(3, dtype=np.int64)) # type: ignore
785785

786786

787787
# =====================================================================
@@ -824,7 +824,7 @@ def test_f32like_accepted(self, value: object) -> None:
824824
def f(x: F32Like) -> float:
825825
return float(np.asarray(x).sum())
826826

827-
f(value)
827+
f(value) # type: ignore
828828

829829

830830
class TestArrayLikeRejection:

tests/test_typecheck.py

Lines changed: 44 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
Runs pyright, mypy, and ty on sample files under ``tests/typing/`` to verify
44
that shapix's public API type-checks cleanly across multiple checkers.
55
6+
Test matrix:
7+
- **Common files** (all checkers): imports, decorator, like types, make_array_type
8+
- **Pyright-only files**: full F32[N, C] annotations (TYPE_CHECKING stubs)
9+
610
These tests require ``pyright``, ``mypy``, and ``ty`` to be installed.
711
"""
812

@@ -16,6 +20,22 @@
1620

1721
TYPING_DIR = Path(__file__).parent / "typing"
1822

23+
# ---------------------------------------------------------------------------
24+
# File categories
25+
# ---------------------------------------------------------------------------
26+
27+
# Files that should pass cleanly on ALL type checkers
28+
COMMON_FILES = [
29+
"check_imports.py",
30+
"check_decorator.py",
31+
"check_like_types.py",
32+
"check_make_array_type.py",
33+
]
34+
35+
# Files that only pyright supports (F32[N, C] subscript annotations)
36+
PYRIGHT_ONLY_FILES = ["check_annotations.py"]
37+
38+
1939
# ---------------------------------------------------------------------------
2040
# Helpers
2141
# ---------------------------------------------------------------------------
@@ -24,7 +44,7 @@
2444
def _run(
2545
cmd: list[str], *, cwd: Path | None = None
2646
) -> subprocess.CompletedProcess[str]:
27-
return subprocess.run(cmd, capture_output=True, text=True, timeout=60, cwd=cwd)
47+
return subprocess.run(cmd, capture_output=True, text=True, timeout=120, cwd=cwd)
2848

2949

3050
def _skip_if_missing(tool: str) -> None:
@@ -33,50 +53,48 @@ def _skip_if_missing(tool: str) -> None:
3353

3454

3555
# ---------------------------------------------------------------------------
36-
# Tests — common (all checkers should pass)
56+
# Pyright — common + pyright-only files
3757
# ---------------------------------------------------------------------------
3858

39-
COMMON_FILES = ["check_imports.py", "check_decorator.py"]
40-
4159

4260
@pytest.mark.typecheck
43-
@pytest.mark.parametrize("filename", COMMON_FILES)
4461
class TestPyright:
62+
@pytest.mark.parametrize("filename", COMMON_FILES + PYRIGHT_ONLY_FILES)
4563
def test_pyright(self, filename: str) -> None:
4664
_skip_if_missing("pyright")
4765
result = _run(["pyright", str(TYPING_DIR / filename)])
48-
assert result.returncode == 0, f"pyright failed:\n{result.stdout}\n{result.stderr}"
66+
assert result.returncode == 0, (
67+
f"pyright failed on {filename}:\n{result.stdout}\n{result.stderr}"
68+
)
69+
70+
71+
# ---------------------------------------------------------------------------
72+
# Mypy — common files only
73+
# ---------------------------------------------------------------------------
4974

5075

5176
@pytest.mark.typecheck
52-
@pytest.mark.parametrize("filename", COMMON_FILES)
5377
class TestMypy:
78+
@pytest.mark.parametrize("filename", COMMON_FILES)
5479
def test_mypy(self, filename: str) -> None:
5580
_skip_if_missing("mypy")
5681
result = _run(["mypy", str(TYPING_DIR / filename), "--ignore-missing-imports"])
57-
assert result.returncode == 0, f"mypy failed:\n{result.stdout}\n{result.stderr}"
58-
59-
60-
@pytest.mark.typecheck
61-
@pytest.mark.parametrize("filename", COMMON_FILES)
62-
class TestTy:
63-
def test_ty(self, filename: str) -> None:
64-
_skip_if_missing("ty")
65-
result = _run(["ty", "check", str(TYPING_DIR / filename)])
66-
assert result.returncode == 0, f"ty failed:\n{result.stdout}\n{result.stderr}"
82+
assert result.returncode == 0, (
83+
f"mypy failed on {filename}:\n{result.stdout}\n{result.stderr}"
84+
)
6785

6886

6987
# ---------------------------------------------------------------------------
70-
# Testspyright-only (full F32[N, C] annotation pattern)
88+
# tycommon files only
7189
# ---------------------------------------------------------------------------
7290

73-
PYRIGHT_FILES = ["check_annotations.py"]
74-
7591

7692
@pytest.mark.typecheck
77-
@pytest.mark.parametrize("filename", PYRIGHT_FILES)
78-
class TestPyrightAnnotations:
79-
def test_pyright_annotations(self, filename: str) -> None:
80-
_skip_if_missing("pyright")
81-
result = _run(["pyright", str(TYPING_DIR / filename)])
82-
assert result.returncode == 0, f"pyright failed:\n{result.stdout}\n{result.stderr}"
93+
class TestTy:
94+
@pytest.mark.parametrize("filename", COMMON_FILES)
95+
def test_ty(self, filename: str) -> None:
96+
_skip_if_missing("ty")
97+
result = _run(["ty", "check", str(TYPING_DIR / filename)])
98+
assert result.returncode == 0, (
99+
f"ty failed on {filename}:\n{result.stdout}\n{result.stderr}"
100+
)

0 commit comments

Comments
 (0)