Skip to content
Snippets Groups Projects
Unverified Commit 9643dfde authored by Richard van der Hoff's avatar Richard van der Hoff Committed by GitHub
Browse files

improve typing annotations in CachedCall (#10450)

tighten up some of the typing in CachedCall, which is going to be needed when
Twisted 21.7 brings better typing on Deferred.
parent 752fe0cd
Branches
Tags
No related merge requests found
Update type annotations to work with forthcoming Twisted 21.7.0 release.
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import enum
from typing import Awaitable, Callable, Generic, Optional, TypeVar, Union from typing import Awaitable, Callable, Generic, Optional, TypeVar, Union
from twisted.internet.defer import Deferred from twisted.internet.defer import Deferred
...@@ -22,6 +22,10 @@ from synapse.logging.context import make_deferred_yieldable, run_in_background ...@@ -22,6 +22,10 @@ from synapse.logging.context import make_deferred_yieldable, run_in_background
TV = TypeVar("TV") TV = TypeVar("TV")
class _Sentinel(enum.Enum):
sentinel = object()
class CachedCall(Generic[TV]): class CachedCall(Generic[TV]):
"""A wrapper for asynchronous calls whose results should be shared """A wrapper for asynchronous calls whose results should be shared
...@@ -65,7 +69,7 @@ class CachedCall(Generic[TV]): ...@@ -65,7 +69,7 @@ class CachedCall(Generic[TV]):
""" """
self._callable: Optional[Callable[[], Awaitable[TV]]] = f self._callable: Optional[Callable[[], Awaitable[TV]]] = f
self._deferred: Optional[Deferred] = None self._deferred: Optional[Deferred] = None
self._result: Union[None, Failure, TV] = None self._result: Union[_Sentinel, TV, Failure] = _Sentinel.sentinel
async def get(self) -> TV: async def get(self) -> TV:
"""Kick off the call if necessary, and return the result""" """Kick off the call if necessary, and return the result"""
...@@ -78,8 +82,9 @@ class CachedCall(Generic[TV]): ...@@ -78,8 +82,9 @@ class CachedCall(Generic[TV]):
self._callable = None self._callable = None
# once the deferred completes, store the result. We cannot simply leave the # once the deferred completes, store the result. We cannot simply leave the
# result in the deferred, since if it's a Failure, GCing the deferred # result in the deferred, since `awaiting` a deferred destroys its result.
# would then log a critical error about unhandled Failures. # (Also, if it's a Failure, GCing the deferred would log a critical error
# about unhandled Failures)
def got_result(r): def got_result(r):
self._result = r self._result = r
...@@ -92,13 +97,15 @@ class CachedCall(Generic[TV]): ...@@ -92,13 +97,15 @@ class CachedCall(Generic[TV]):
# and any eventual exception may not be reported. # and any eventual exception may not be reported.
# we can now await the deferred, and once it completes, return the result. # we can now await the deferred, and once it completes, return the result.
await make_deferred_yieldable(self._deferred) if isinstance(self._result, _Sentinel):
await make_deferred_yieldable(self._deferred)
assert not isinstance(self._result, _Sentinel)
if isinstance(self._result, Failure):
self._result.raiseException()
raise AssertionError("unexpected return from Failure.raiseException")
# I *think* this is the easiest way to correctly raise a Failure without having return self._result
# to gut-wrench into the implementation of Deferred.
d = Deferred()
d.callback(self._result)
return await d
class RetryOnExceptionCachedCall(Generic[TV]): class RetryOnExceptionCachedCall(Generic[TV]):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment