PyCharm's type checker works well with this:
from functools import cache
class MyType:
pass
@cache
def f() -> MyType:
...
v = f() # v: MyType
...but not this:
class C:
@cache
def m(self) -> MyType:
...
v = C().m() # v: Any
What should I do to get good autocompletion?
This is YouTrack/PY-49635 (open as of November 2023).
My current workaround is the following:
from functools import cache
from typing import TypeVar
if TYPE_CHECKING:
# To be read by mypy and other proper typecheckers
_cache = cache
else:
# To be read by PyCharm
# This is not actually type-safe since T is unbound, but whatever.
T = TypeVar('T')
def _cache(function: T) -> T:
'''
Workaround for `PY-49635\
<https://youtrack.jetbrains.com/issue/PY-49635>`_.
:param function: A callable.
:return: The same callable, wrapped in :func:`functools.cache`.
'''
return cache(function)
Anything in the else
block is supposed to be ignored by typecheckers and always run by the interpreter. However, PyCharm doesn't honor that. In a way, that's good: it allows us to write code that doesn't cause any unnecessary warnings. Mypy/Pyright/Your-decent-typechecker is smart enough to turn a blind eye to our workaround function and PyCharm is lenient enough not to warn us.
This is very fragile and may break in future PyCharm releases. That is, if the JetBrains guys ever "fix" this behaviour.
Usage:
@_cache
def f() -> YourType:
...
class C:
@_cache
def m(self) -> YourType:
...
v = f() # v: YourType
v = C().m() # v: YourType
The workaround above works in PyCharm 2023.2.5. I haven't tested with other versions.