-
Notifications
You must be signed in to change notification settings - Fork 765
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Wrapping class with icontract invariant decorator causes PyLance to incorrectly detect type. #1667
Comments
This looks like it's a bug in the type annotations within the The decorator def __call__(self, cls: type) -> type: The correct type annotation for this method is: def __call__(self, cls: _T) -> _T: where `_T is defined as: _T = TypeVar("_T", bound=type) This preserves the type of the decorated class. If I make this change, it type checks fine in pylance and in mypy. I recommend filing a bug or submitting a PR in the |
I worked with the developer behind icontract and he and I verified a fix on his side is possible (see his pull request #227). I still question why Python itself plus mypy had no issue with the looser typing but PyLance did. Either way, this may be solved by a change in PyLance. |
Python doesn't care about annotations at runtime, so you could put anything there and it will run. As for mypy, that's hard to say. It could be a bug they haven't fixed or had reported to them. |
This appears to be a bug in mypy. They are not honoring the |
It looks like this is a known bug in mypy. It has been open for more than four years: python/mypy#3135. Here's an sample that demonstrates how pyright (the type checker that underlies pylance) applies the class decorator whereas mypy does not. from typing import Callable, TYPE_CHECKING, Type
class replace_with_int1:
def __init__(self):
...
def __call__(self, cls: type) -> Type[int]:
return int
@replace_with_int1()
class NotInt1:
...
if TYPE_CHECKING:
reveal_type(NotInt1) # pyright: "Type[int]"", mypy: "def () -> NotInt1"
print(NotInt1) # <class 'int'>
def replace_with_int2() -> Callable[..., Type[int]]:
return lambda x: int
@replace_with_int2()
class NotInt2:
...
if TYPE_CHECKING:
reveal_type(NotInt2) # pyright: "Type[int]"", mypy: "def () -> NotInt2"
print(NotInt2) # <class 'int'> |
I'm going to close this as needing a fix upstream; we're doing the right thing according to the type annotations here, so there's no work to do. |
Environment data
Expected behaviour
When using icontract to add an invariant to a class, both mypy and Python's own type() command show the type of a class wrapped by icontract to be the same as one that was never wrapped. PyLance shouldn't have an issue with using a wrapped class in place of the bare class for type hinting.
Actual behaviour
TestClass1 is a bare class.
TestClass2 is a wrapped class.
(see below for the actual code)
PyLance states that TestClass1 is of type "Type[TestClass1]" while it shows that TestClass2 is of type "type".
PyLance states that TestClass2 can't be used to type annotate a dataclass field because it expected a Class but got a type instead. Further, because of that issue, PyLance states that variables within TestClass2 are of unknown type and can't be referenced. The code works fine.
Mypy states:
..and printing the type of each from Python shows:
Logs
Python Language Server Log
Code Snippet / Additional information
The text was updated successfully, but these errors were encountered: