agronholm / agronholm/typeguard
Subscripted PEP 695 generic type alias skips the check entirely, so any value passes
- 主要语言
- Python
- 星标
- 1.8k
- 派生
- 145
- 平均合并
- 8 天 12 小时
- 30 天内合并 PR
- 1
描述
### Things to check first
- [x] I have searched the existing issues and didn't find my bug already reported there
- [x] I have checked that my bug is still present in the latest release
### Typeguard version
4.6.0 (also present on `main` at 9f289c7fca68097542d3bde9d59496ad42e58251)
### Python version
3.12.14, 3.13.15 and 3.14.7 (all three behave the same)
### What happened?
When an annotation is a PEP 695 generic type alias with its type parameters filled in, such as `Boxed[int]` for `type Boxed[T] = list[T]`, no check is performed at all. Every value passes, including values of a completely unrelated type, and no error or warning is emitted.
A type alias without type parameters is checked correctly, so the two sit side by side in the same module and only one of them catches anything:
```
check_type(["a"], Plain) # type Plain = list[int] -> TypeCheckError, correct
check_type("hello", Boxed[int]) # type Boxed[T] = list[T] -> returns "hello"
```
I expected `check_type(["a"], Boxed[int])` to raise `TypeCheckError` the same way `check_type(["a"], list[int])` does, and I expected `@typechecked` to reject the call in the example below.
The same silence shows up wherever the parameterized alias appears, including nested inside another annotation:
```
check_type([["a"]], list[Boxed[int]]) # returns [['a']]
check_type({"k": ["a"]}, dict[str, Boxed[int]]) # returns {'k': ['a']}
check_type(["a"], Boxed[int] | None) # returns ['a']
```
This is a silent false negative rather than a crash, which is what makes it awkward in practice: a codebase that annotates with its own parameterized aliases gets no runtime checking on those annotations and nothing says so.
Possibly related: #541, where a type alias without parameters was not being resolved. That one is fixed and the non-parameterized case above confirms it.
### How can we reproduce the bug?
No third party libraries. `pip install typeguard` in a clean Python 3.12, 3.13 or 3.14 environment, then run:
```python
from typeguard import TypeCheckError, check_type, typechecked
type Plain = list[int]
type Boxed[T] = list[T]
def show(label, fn):
try:
fn()
print("no error |", label)
except TypeCheckError as exc:
print("TypeCheckError|", label, "|", exc)
# control: an alias with no type parameters is checked
show("Plain <- ['a'] ", lambda: check_type(["a"], Plain))
# the bug: the same list is accepted against a parameterized alias
show("Boxed[int] <- ['a'] ", lambda: check_type(["a"], Boxed[int]))
show("Boxed[int] <- 'hello'", lambda: check_type("hello", Boxed[int]))
@typechecked
def f(x: Boxed[int]) -> Boxed[int]:
return x
show("f('hello') ", lambda: f("hello"))
```
Output on typeguard 4.6.0 / Python 3.13.15:
```
TypeCheckError| Plain <- ['a'] | item 0 of list is not an instance of int
no error | Boxed[int] <- ['a']
no error | Boxed[int] <- 'hello'
no error | f('hello')
```
Only the first line raises. The last three all pass.
贡献指南
评估
这个 Issue 还没有评估数据。