-
Notifications
You must be signed in to change notification settings - Fork 216
Description
Summary
The ty type checker appears to incorrectly flag a type error when a module is used to satisfy a typing.Protocol. According to Python's official typing documentation, modules can serve as implementations for protocols, a feature that is correctly handled by other type checkers like Mypy and Pyright.
When running mypy, pyright or pyrefly on the project, type checkers pass without any errors, correctly identifying that the bar module is a valid implementation of the Proto protocol.
Code to Reproduce
Here is a minimal set of code that demonstrates the issue.
-
The Protocol Definition (
behavior.py):
A simple protocol that requires afoofunction.from typing import Protocol class Proto(Protocol): def foo(self, x: int) -> str: ...
-
The Module Implementation (
bar.py):
A module with a function that structurally conforms to the protocol.def foo(x: int) -> str: return f"6{x}9"
-
The Type Check (
main.py):
Thebarmodule is assigned to a variable annotated with thePrototype.import behavior import bar behavior.consume(bar) _: behavior.Proto = bar
Full Reproducible Example
You can reproduce it in playground
I have also uploaded the complete, self-contained project to a public GitHub repository. You can clone it and run the type checkers to see the issue firsthand: wvlab/example-python-module-protocols
To reproduce:
git clone https://github.com/wvlab/example-python-module-protocols.git mre && cd mre
uv sync --frozen
source .venv/bin/activate
./run_type_checkers.shty Output
> ty check
error[invalid-argument-type]: Argument to function `consume` is incorrect
--> src/example_module_protocols/main.py:9:28
|
8 | def main() -> None:
9 | print(behavior.consume(bar))
| ^^^ Expected `Proto`, found `<module 'example_module_protocols.implementations.bar'>`
10 | _: behavior.Proto = bar
11 | return
|
info: Function defined here
--> src/example_module_protocols/behavior.py:9:5
|
7 | def foo(self, x: int) -> str: ...
8 |
9 | def consume(proto: Proto) -> str:
| ^^^^^^^ ------------ Parameter declared here
10 | return proto.foo(5)
|
info: rule `invalid-argument-type` is enabled by default
error[invalid-assignment]: Object of type `<module 'example_module_protocols.implementations.bar'>` is not assignable to `Proto`
--> src/example_module_protocols/main.py:10:5
|
8 | def main() -> None:
9 | print(behavior.consume(bar))
10 | _: behavior.Proto = bar
| ^
11 | return
|
info: rule `invalid-assignment` is enabled by default
Version
ty 0.0.1-alpha.16 (e48b66f 2025-07-31)