Skip to content

Commit 972dd70

Browse files
authored
Pin a dev version of the mypy-typemap prototype, run some tests against it (#111)
1 parent f452084 commit 972dd70

18 files changed

Lines changed: 184 additions & 60 deletions

README.md

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,19 @@ This is the development repository for
55
which proposes TypeScript-inspired type-level introspection and construction
66
facilities for the Python type system.
77

8+
This repository contains an implementation of the proposed additions
9+
to ``typing`` ([typemap/typing.py](typemap/typing.py)), exported as
10+
the module ``typemap_extensions``.
11+
12+
It also contains a **prototype** runtime evaluator
13+
([typemap/type_eval](typemap/type_eval)).
14+
815
Discussion of the PEP is at the
916
[PEP 827 discussion thread](https://discuss.python.org/t/pep-827-type-manipulation/106353).
1017

11-
This repository also contains an implementation of the proposed
12-
additions to ``typing`` ([typemap/typing.py](typemap/typing.py)), as well as a
13-
**prototype** runtime evaluator ([typemap/type_eval](typemap/type_eval)).
18+
A prototype typechecker implementation lives at
19+
https://github.com/msullivan/mypy-typemap and is a test dependency of
20+
this repo.
1421

1522
## Development
1623

@@ -21,9 +28,11 @@ additions to ``typing`` ([typemap/typing.py](typemap/typing.py)), as well as a
2128

2229
## Running the typechecker
2330

24-
If you have https://github.com/msullivan/mypy/tree/typemap active in a
25-
venv, you can run it against at least some of the tests with
26-
invocations like:
27-
`mypy --python-version=3.14 tests/test_qblike_2.py`
31+
The prototype mypy can be run from this repo with `uv run mypy`.
32+
Stubs are set up so that importing ``typemap_extensions`` will do the
33+
right thing.
34+
35+
`uv run pytest tests/test_mypy_proto.py` will run the mypy prototype
36+
against a supported subset of test files.
2837

29-
Not all of them run cleanly yet though.
38+
You can also run the prototype mypy directly on a file with `uv run mypy <file>`

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ include = ["typemap", "typemap.*", "typemap_extensions"]
1313
test = [
1414
"pytest>=7.0",
1515
"ruff",
16-
"mypy==1.18.1",
16+
"mypy @ git+https://github.com/msullivan/mypy-typemap@f49083e5cd7124df93ea1a0a844d60adf901c250",
1717
]
1818

1919
[tool.uv]

scripts/update-mypy-pin

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/bin/bash
2+
set -e
3+
4+
MYPY_DIR="${1:-../mypy}"
5+
6+
if [ ! -d "$MYPY_DIR/.git" ]; then
7+
echo "error: $MYPY_DIR is not a git repository" >&2
8+
exit 1
9+
fi
10+
11+
REV=$(git -C "$MYPY_DIR" rev-parse HEAD)
12+
echo "Pinning mypy to $REV (from $MYPY_DIR)"
13+
14+
sed -i "s|mypy @ git+https://github.com/msullivan/mypy-typemap@[a-f0-9]*|mypy @ git+https://github.com/msullivan/mypy-typemap@$REV|" pyproject.toml
15+
16+
uv sync
17+
18+
git add pyproject.toml uv.lock
19+
git commit -m "Pin mypy to mypy-typemap@${REV:0:12}"

tests/test_astlike_1.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# SKIP MYPY: runs forever! must debug
2+
13
import pytest
24
import typing
35

tests/test_call.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
def func[*T, K: BaseTypedDict](
1818
*args: Unpack[T],
1919
**kwargs: Unpack[K],
20-
) -> NewProtocol[*[Member[c.name, int] for c in Iter[Attrs[K]]]]: ...
20+
) -> NewProtocol[*[Member[c.name, int] for c in Iter[Attrs[K]]]]:
21+
raise NotImplementedError
2122

2223

2324
def test_call_1():

tests/test_cqa.py

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ def project_root() -> pathlib.Path:
1515

1616
def test_cqa_ruff_check(project_root):
1717
"""Test that code passes ruff linting checks."""
18-
# Ruff respects pyproject.toml configuration and exclusions
1918
result = subprocess.run(
2019
[sys.executable, "-m", "ruff", "check", "."],
2120
capture_output=True,
@@ -32,7 +31,6 @@ def test_cqa_ruff_check(project_root):
3231

3332
def test_cqa_ruff_format_check(project_root):
3433
"""Test that code is properly formatted according to ruff."""
35-
# Ruff format respects pyproject.toml exclusions
3634
result = subprocess.run(
3735
[sys.executable, "-m", "ruff", "format", "--check", "."],
3836
capture_output=True,
@@ -48,26 +46,23 @@ def test_cqa_ruff_format_check(project_root):
4846

4947

5048
def test_cqa_mypy(project_root):
51-
"""Test that code passes mypy type checking."""
52-
# Mypy uses configuration from pyproject.toml
53-
# Run on typemap -- tests not ready yet
54-
for subdir in ["typemap"]:
55-
result = subprocess.run(
56-
[
57-
sys.executable,
58-
"-m",
59-
"mypy",
60-
"--config-file",
61-
project_root / "pyproject.toml",
62-
subdir,
63-
],
64-
capture_output=True,
65-
text=True,
66-
cwd=project_root,
67-
)
49+
"""Test that typemap/ passes mypy type checking."""
50+
result = subprocess.run(
51+
[
52+
sys.executable,
53+
"-m",
54+
"mypy",
55+
"--config-file",
56+
project_root / "pyproject.toml",
57+
"typemap",
58+
],
59+
capture_output=True,
60+
text=True,
61+
cwd=project_root,
62+
)
6863

69-
if result.returncode != 0:
70-
output = result.stdout
71-
if result.stderr:
72-
output += "\n\n" + result.stderr
73-
pytest.fail(f"mypy validation failed:\n{output}", pytrace=False)
64+
if result.returncode != 0:
65+
output = result.stdout
66+
if result.stderr:
67+
output += "\n\n" + result.stderr
68+
pytest.fail(f"mypy validation failed:\n{output}", pytrace=False)

tests/test_dataclass_like.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
ReadOnly,
55
TypedDict,
66
Never,
7-
Self,
87
)
98

109
import typemap_extensions as typing
@@ -54,7 +53,7 @@ class Field[T: FieldArgs](typing.InitField[T]):
5453
Literal["__init__"],
5554
Callable[
5655
typing.Params[
57-
typing.Param[Literal["self"], Self],
56+
typing.Param[Literal["self"], T],
5857
*[
5958
typing.Param[
6059
p.name,
@@ -80,7 +79,6 @@ class Field[T: FieldArgs](typing.InitField[T]):
8079
*[x for x in typing.Iter[typing.Members[T]]],
8180
]
8281

83-
8482
"""
8583
8684
``UpdateClass`` can then be used to create a class decorator (a la
@@ -95,7 +93,7 @@ def dataclass_ish[T](
9593
# Add the computed __init__ function
9694
InitFnType[T],
9795
]:
98-
pass
96+
raise NotImplementedError
9997

10098

10199
"""
@@ -112,7 +110,7 @@ def __init_subclass__[T](
112110
# Add the computed __init__ function
113111
InitFnType[T],
114112
]:
115-
super().__init_subclass__()
113+
pass
116114

117115

118116
# End PEP section

tests/test_eval_call_with_types.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# SKIP MYPY
2+
13
import pytest
24

35
from typing import Callable, Generic, Literal, Self, TypeVar

tests/test_fastapilike_1.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# SKIP MYPY: Invalid use of Self
2+
# TODO: resolve
3+
14
# We should have at least *one* test with this...
25
from __future__ import annotations
36

@@ -71,7 +74,9 @@ class _Default:
7174

7275
type AddInit[T] = NewProtocol[
7376
InitFnType[T],
74-
*Members[T],
77+
# TODO: mypy rejects this -- should it work?
78+
# *Members[T],
79+
*[t for t in Iter[Members[T]]],
7580
]
7681

7782
# Strip `| None` from a type by iterating over its union components

tests/test_fastapilike_2.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# SKIP MYPY: Invalid use of Self
2+
# TODO: resolve
3+
14
from typing import (
25
Callable,
36
Literal,

0 commit comments

Comments
 (0)