I'm trying to use Jedi with a codebase roughly equivalent to
import jedi
from typing import Generic, TypeVar
T = TypeVar("T")
class Reader(Generic[T]):
@classmethod
def read(cls) -> T:
return cls()
class Foo(Reader["Foo"]):
def transform(self) -> int:
return 42
I'd expect that once I call Foo.read() and follow with .t Jedi will give transform as possible completion (in other words will correctly infer type of Foo.read() as Foo).
Actual behavior:
jedi.Interpreter("Foo.read().t", [globals()]).completions()
## []
Expected behavior:
expected = jedi.Interpreter("Foo().t", [globals()]).completions()
actual = jedi.Interpreter("Foo.read().t", [globals()]).completions()
assert actual == expected
Tested with:
jedi==0.16.0 (installed from 0c56aa4d4be211b2fd691dcd68fc1b06de2c6c52)Thanks for reproducing this!
@zero323 BTW, there's a mistake in your example, it should be Reader[str].
I don't think there is. The runtime behavior is:
>>> class Reader:
... @classmethod
... def read(cls):
... return cls()
...
... class Foo(Reader):
... def transform(self):
... return 42
...
>>> type(Foo.read())
<class '__main__.Foo'>
which is what I am trying to express.
"Foo" is used in Generic because you cannot use type in forward references in py file. If I used stubs we'd have
class Reader:
@classmethod
def read(cls):
return cls()
class Foo(Reader):
def transform(self):
return 42
in .py and
from typing import Generic, TypeVar
T = TypeVar("T")
class Reader(Generic[T]):
@classmethod
def read(cls) -> T: ...
class Foo(Reader[Foo]):
def transform(self) -> int: ...
in .pyi.
If we append
reveal_type(Foo.read())
to the latter and run it through mypy
mypy __init__.pyi
it yields expected output
__init__.pyi:12: note: Revealed type is '__main__.Foo*'
In such simple case roughly equivalent would be
from typing import Type, TypeVar
T = TypeVar("T")
class Reader:
@classmethod
def read(cls: Type[T]) -> T:
return cls()
class Foo(Reader):
def transform(self) -> int:
return 42
This works better in Pycharm, but doesn't resolve the problem here.
"Foo" is used in Generic because you cannot use type in forward references in py file. If I used stubs we'd have
My bad. You're right of course. I have almost forgotten about these again (even though I implemented it in Jedi).
This was a lot more work than I initially thought it would be. I thought a lot about how we could get this to work perfectly, but I also realized that it's not going to be possible.
So now if you have a file that contains the code you posted, it works well. If you copy paste your code into IPython, it won't work.
This is because of a limitation in Python: It's not really possible to work with runtime information of TypeVars. typing.py doesn't really have an helpers that would make it clear what generic class actually implies. There are just a lot of limitations that runtime Python has (that static analysis hasn't) in this case. So I'm closing, because I feel like the important parts have been fixed.
Sorry, this message might be a bit incomprehensible.
Thank you so much for your work. I've tested with pyspark-stubs and it works perfectly.
I think it is perfectly satisfying resolution - I realistically don't see much place for writing generic annotations inside interactive sessions anyway.