Skip to content

Commit 8641f7f

Browse files
nstarmanjorenham
andauthored
✅ test: add tests & CI (#18)
Co-authored-by: Joren Hammudoglu <[email protected]>
1 parent 1d187c1 commit 8641f7f

File tree

8 files changed

+222
-8
lines changed

8 files changed

+222
-8
lines changed

.github/workflows/ci.yml

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
name: CI
2+
permissions: read-all
3+
4+
on:
5+
workflow_dispatch:
6+
pull_request:
7+
push:
8+
branches: [main]
9+
10+
concurrency:
11+
group: ${{ github.workflow }}-${{ github.ref }}
12+
cancel-in-progress: true
13+
14+
env:
15+
# Many color libraries just need this to be set to any value, but at least
16+
# one distinguishes color depth, where "3" -> "256-bit color".
17+
FORCE_COLOR: 3
18+
19+
jobs:
20+
format:
21+
name: Format
22+
runs-on: ubuntu-latest
23+
steps:
24+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
25+
26+
- name: Install uv
27+
uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb
28+
29+
- name: Install the project
30+
run: uv sync --locked --group test
31+
32+
- name: Run lefthook hooks
33+
run: uv run --frozen lefthook run pre-commit
34+
35+
checks:
36+
name: Check Python ${{ matrix.python-version }} on ${{ matrix.runs-on }}
37+
runs-on: ${{ matrix.runs-on }}
38+
needs: [format]
39+
strategy:
40+
fail-fast: false
41+
matrix:
42+
python-version: ["3.11", "3.12", "3.13"]
43+
runs-on: [ubuntu-latest, macos-latest, windows-latest]
44+
45+
steps:
46+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
47+
48+
- name: Install uv
49+
uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb
50+
with:
51+
python-version: ${{ matrix.python-version }}
52+
53+
- name: Install the project
54+
run: uv sync --locked --group test
55+
56+
- name: Test package
57+
run: >-
58+
uv run --frozen pytest
59+
--cov --cov-report=xml --cov-report=term --durations=20
60+
src docs tests
61+
62+
- name: Upload coverage report
63+
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24
64+
with:
65+
token: ${{ secrets.CODECOV_TOKEN }}
66+
67+
check_oldest:
68+
name: Check Oldest Dependencies
69+
runs-on: ${{ matrix.runs-on }}
70+
needs: [format]
71+
strategy:
72+
fail-fast: false
73+
matrix:
74+
python-version: ["3.11"]
75+
runs-on: [ubuntu-latest]
76+
77+
steps:
78+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
79+
80+
- name: Install uv
81+
uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb
82+
with:
83+
python-version: ${{ matrix.python-version }}
84+
- name: Install the project
85+
run: uv sync --locked --group test --resolution lowest-direct
86+
87+
- name: Test package
88+
run: >-
89+
uv run --frozen pytest
90+
--cov --cov-report=xml --cov-report=term --durations=20
91+
src docs tests
92+
93+
- name: Upload coverage report
94+
uses: codecov/codecov-action@v18283e04ce6e62d37312384ff67231eb8fd56d24
95+
with:
96+
token: ${{ secrets.CODECOV_TOKEN }}

conftest.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
"""Pytest configuration file."""
22

3+
from typing import Final
4+
35
from sybil import Sybil
46
from sybil.parsers.doctest import DocTestParser
57

6-
readme_tester = Sybil(
8+
readme_tester: Final = Sybil(
79
parsers=[DocTestParser()],
810
pattern="README.md",
911
)
1012

11-
python_file_tester = Sybil(
13+
python_file_tester: Final = Sybil(
1214
parsers=[DocTestParser()],
1315
pattern="src/**/*.py",
1416
)
1517

16-
pytest_collect_file = (readme_tester + python_file_tester).pytest()
18+
pytest_collect_file: Final = (readme_tester + python_file_tester).pytest()

lefthook.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ pre-commit:
1313
run: uv run ruff format {staged_files}
1414
- name: mypy
1515
glob: "*.py"
16-
run: uv run mypy {staged_files}
16+
run: uv run --group mypy mypy {staged_files}

pyproject.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@
5151
"pytest-github-actions-annotate-failures>=0.3.0",
5252
"sybil>=8.0.0",
5353
]
54+
mypy = [
55+
"mypy>=1.16.0"
56+
]
5457

5558

5659
[tool.hatch]
@@ -74,6 +77,7 @@ version_tuple = {version_tuple!r}
7477
[tool.mypy]
7578
files = ["src", "tests"]
7679
python_version = "3.10"
80+
mypy_path = "src"
7781

7882
strict = true
7983
disallow_incomplete_defs = true
@@ -90,6 +94,10 @@ version_tuple = {version_tuple!r}
9094
module = "sybil.*"
9195
ignore_missing_imports = true
9296

97+
[[tool.mypy.overrides]]
98+
module = "tests.*"
99+
disallow_untyped_defs = false
100+
93101

94102
[tool.pytest.ini_options]
95103
addopts = [
@@ -130,6 +138,9 @@ version_tuple = {version_tuple!r}
130138
"ISC001", # Conflicts with formatter
131139
]
132140

141+
[tool.ruff.lint.per-file-ignores]
142+
"tests/*.py" = ["ANN201", "D1", "S101"]
143+
133144
[tool.ruff.lint.flake8-import-conventions]
134145
banned-from = ["array_api_typing"]
135146

src/array_api_typing/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
"""Static typing support for the array API standard."""
22

3-
__all__ = ["HasArrayNamespace", "__version__", "__version_tuple__"]
3+
__all__ = (
4+
"HasArrayNamespace",
5+
"__version__",
6+
"__version_tuple__",
7+
)
48

59
from ._namespace import HasArrayNamespace
610
from ._version import version as __version__, version_tuple as __version_tuple__

src/array_api_typing/_namespace.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
"""Static typing support for the array API standard."""
2-
3-
__all__ = ["HasArrayNamespace"]
1+
__all__ = ("HasArrayNamespace",)
42

53
from types import ModuleType
64
from typing import Protocol, final

tests/test_namespace.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from types import SimpleNamespace
2+
from typing import Protocol, runtime_checkable
3+
4+
import array_api_typing as xpt
5+
6+
7+
@runtime_checkable
8+
class CheckableHasArrayNamespace(xpt.HasArrayNamespace, Protocol): # type: ignore[misc]
9+
"""Runtime checkable version of HasArrayNamespace."""
10+
11+
12+
class GoodArray:
13+
"""Example class that implements the HasArrayNamespace protocol."""
14+
15+
def __array_namespace__(self) -> object: # noqa: PLW3201
16+
return SimpleNamespace()
17+
18+
19+
class BadArray:
20+
"""Example class that does not implement the HasArrayNamespace protocol."""
21+
22+
23+
def test_has_namespace_class():
24+
"""Test that GoodArray is a subclass of HasArrayNamespace."""
25+
assert issubclass(GoodArray, CheckableHasArrayNamespace)
26+
27+
28+
def test_has_namespace_instance():
29+
"""Test that an instance of GoodArray is recognized as HasArrayNamespace."""
30+
x = GoodArray()
31+
assert isinstance(x, CheckableHasArrayNamespace)
32+
33+
34+
def test_not_has_namespace_class():
35+
"""Test that BadArray is not a subclass of HasArrayNamespace."""
36+
assert not issubclass(BadArray, CheckableHasArrayNamespace)
37+
38+
39+
def test_not_has_namespace_instance():
40+
"""Test that an instance of BadArray is not recognized as HasArrayNamespace."""
41+
y = BadArray()
42+
assert not isinstance(y, CheckableHasArrayNamespace)

uv.lock

Lines changed: 61 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)