Skip to content

Commit df8c5a9

Browse files
committed
Better documentation of type test.
Thanks @bprobert97.
1 parent 55dcb00 commit df8c5a9

File tree

2 files changed

+32
-6
lines changed

2 files changed

+32
-6
lines changed

typing_tests/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
The codebase is type-checked with `mypy src/` and tested with `pytest`, however neither of these explicitly check that `mypy` can infer the correct types for `Thing` attributes like properties and actions. The Python files in this folder are intended to be checked using:
44

55
```terminal
6-
mypy --warn-unused-ignores .\typing_tests\
6+
mypy --warn-unused-ignores typing_tests
77
```
88

99
The files include valid code that's accompanied by `assert_type` statements (which check the inferred types are what we expect them to be) as well as invalid code where the expected `mypy` errors are ignored. This tests for expected errors - if an expected error is not thrown, it will cause an `unused-ignore` error.

typing_tests/thing_definitions.py

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
This module checks that code defining a Thing may be type checked using
44
mypy.
55
6-
See README.md for more details.
6+
See README.md for how it's run.
77
"""
88

99
import labthings_fastapi as lt
@@ -27,12 +27,19 @@ def int_factory() -> int:
2727
"""A property that is not bound to a Thing.
2828
2929
This will go wrong at runtime if we access its ``model`` but it should
30-
have its type inferred as an `int`.
30+
have its type inferred as an `int`. This is intended to let mypy check
31+
the default is of the correct type when used with dataclass-style syntax
32+
(``prop: int = lt.property(default=0)`` ).
3133
"""
3234
assert_type(unbound_prop, int)
3335

3436
unbound_prop_2 = lt.property(default_factory=int_factory)
35-
"""A property that is not bound to a Thing, with a factory."""
37+
"""A property that is not bound to a Thing, with a factory.
38+
39+
As with `.unbound_prop` this won't work at runtime, but its type should
40+
be inferred as `int` (which allows checking the default type matches
41+
the attribute type annotation, when used on a class).
42+
"""
3643

3744
assert_type(unbound_prop_2, int)
3845

@@ -73,7 +80,16 @@ class TestPropertyDefaultsMatch(lt.Thing):
7380
"This property should cause mypy to throw an error, as it has no default."
7481

7582
listprop: list[int] = lt.property(default_factory=list)
76-
"A list property with a default factory. Note the default factory is a less specific type."
83+
"""A list property with a default factory.
84+
85+
Note the default factory is a less specific type.
86+
87+
Default types must be compatible with the attribute type, but not
88+
necessarily the same. This tests a common scenario, where the default (an
89+
empty list) is compatible, but not the same as ``list[int]`` .
90+
91+
Note this is "tested" simply by the absence of `mypy` errors.
92+
"""
7793

7894

7995
# Check that the type hints on an instance of the class are correct.
@@ -84,6 +100,7 @@ class TestPropertyDefaultsMatch(lt.Thing):
84100
assert_type(test_defaults_match.optionalintprop, int | None)
85101
assert_type(test_defaults_match.optionalintprop2, int | None)
86102
assert_type(test_defaults_match.optionalintprop3, int | None)
103+
assert_type(test_defaults_match.optionalintprop4, int | None)
87104

88105
# NB the types of the class attributes will be the same as the instance attributes
89106
# because of the type hint on `lt.property`. This is incorrect (the class attributes
@@ -93,7 +110,16 @@ class TestPropertyDefaultsMatch(lt.Thing):
93110

94111

95112
class TestExplicitDescriptor(lt.Thing):
96-
"""A Thing that checks our explicit descriptor type hints are working."""
113+
r"""A Thing that checks our explicit descriptor type hints are working.
114+
115+
This tests `.DataProperty` descriptors work as intended when used directly,
116+
rather than via ``lt.property``\ .
117+
118+
``lt.property`` has a "white lie" on its return type, which makes it
119+
work with dataclass-style syntax (type annotation on the class attribute
120+
rather than part of the descriptor). It's therefore useful to test
121+
the underlying class as well.
122+
"""
97123

98124
intprop1 = lt.DataProperty[int](default=0)
99125
"""A DataProperty that should not cause mypy errors."""

0 commit comments

Comments
 (0)