Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit ce54477

Browse files
author
David Robertson
authored
Give PyCharm some help with @cache_in_self (#15238)
* Give PyCharm some help with `@cache_in_self` * Changelog * Fix import for old python versions
1 parent caf43c3 commit ce54477

File tree

2 files changed

+27
-3
lines changed

2 files changed

+27
-3
lines changed

changelog.d/15238.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improve type hints.

synapse/server.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import logging
2424
from typing import TYPE_CHECKING, Callable, Dict, List, Optional, TypeVar, cast
2525

26+
from typing_extensions import TypeAlias
27+
2628
from twisted.internet.interfaces import IOpenSSLContextFactory
2729
from twisted.internet.tcp import Port
2830
from twisted.web.iweb import IPolicyForHTTPS
@@ -142,10 +144,31 @@
142144
from synapse.handlers.saml import SamlHandler
143145

144146

145-
T = TypeVar("T")
147+
# The annotation for `cache_in_self` used to be
148+
# def (builder: Callable[["HomeServer"],T]) -> Callable[["HomeServer"],T]
149+
# which mypy was happy with.
150+
#
151+
# But PyCharm was confused by this. If `foo` was decorated by `@cache_in_self`, then
152+
# an expression like `hs.foo()`
153+
#
154+
# - would erroneously warn that we hadn't provided a `hs` argument to foo (PyCharm
155+
# confused about boundmethods and unbound methods?), and
156+
# - would be considered to have type `Any`, making for a poor autocomplete and
157+
# cross-referencing experience.
158+
#
159+
# Instead, use a typevar `F` to express that `@cache_in_self` returns exactly the
160+
# same type it receives. This isn't strictly true [*], but it's more than good
161+
# enough to keep PyCharm and mypy happy.
162+
#
163+
# [*]: (e.g. `builder` could be an object with a __call__ attribute rather than a
164+
# types.FunctionType instance, whereas the return value is always a
165+
# types.FunctionType instance.)
166+
167+
T: TypeAlias = object
168+
F = TypeVar("F", bound=Callable[["HomeServer"], T])
146169

147170

148-
def cache_in_self(builder: Callable[["HomeServer"], T]) -> Callable[["HomeServer"], T]:
171+
def cache_in_self(builder: F) -> F:
149172
"""Wraps a function called e.g. `get_foo`, checking if `self.foo` exists and
150173
returning if so. If not, calls the given function and sets `self.foo` to it.
151174
@@ -183,7 +206,7 @@ def _get(self: "HomeServer") -> T:
183206

184207
return dep
185208

186-
return _get
209+
return cast(F, _get)
187210

188211

189212
class HomeServer(metaclass=abc.ABCMeta):

0 commit comments

Comments
 (0)