Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/18772.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Make `.sleep(..)` return a coroutine, so that mypy can catch places where we don't await on it.
Comment thread
erikjohnston marked this conversation as resolved.
Outdated
2 changes: 1 addition & 1 deletion synapse/state/v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class Clock(Protocol):
# This is usually synapse.util.Clock, but it's replaced with a FakeClock in tests.
# We only ever sleep(0) though, so that other async functions can make forward
# progress without waiting for stateres to complete.
def sleep(self, duration_ms: float) -> Awaitable[None]: ...
async def sleep(self, duration_ms: float) -> None: ...


class StateResolutionStore(Protocol):
Expand Down
8 changes: 2 additions & 6 deletions synapse/util/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
Any,
Callable,
Dict,
Generator,
Iterator,
Mapping,
Optional,
Expand All @@ -42,7 +41,6 @@
from typing_extensions import ParamSpec

from twisted.internet import defer, task
from twisted.internet.defer import Deferred
from twisted.internet.interfaces import IDelayedCall, IReactorTime
from twisted.internet.task import LoopingCall
from twisted.python.failure import Failure
Expand Down Expand Up @@ -121,13 +119,11 @@ class Clock:

_reactor: IReactorTime = attr.ib()

@defer.inlineCallbacks
def sleep(self, seconds: float) -> "Generator[Deferred[float], Any, Any]":
async def sleep(self, seconds: float) -> None:
Comment thread
erikjohnston marked this conversation as resolved.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this other spot doesn't have types and is in contrib/ so we don't necessarily need to change it but we could also align

def sleep(self, seconds):
d = defer.Deferred()
reactor.callLater(seconds, d.callback, seconds)
return d

Copy link
Copy Markdown
Member Author

@erikjohnston erikjohnston Aug 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, mmm good spot. Looking at it though I'm disinclined to try and move it to async/await as it still users generators and @defer.inlineCallbacks everywhere else. If anything I'd be inclined to remove the cmdclient package entirely

d: defer.Deferred[float] = defer.Deferred()
with context.PreserveLoggingContext():
self._reactor.callLater(seconds, d.callback, seconds)
res = yield d
return res
await d

def time(self) -> float:
"""Returns the current system time in seconds since epoch."""
Expand Down
2 changes: 1 addition & 1 deletion tests/rest/client/test_transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def test_logcontexts_with_async_result(
) -> Generator["defer.Deferred[Any]", object, None]:
@defer.inlineCallbacks
def cb() -> Generator["defer.Deferred[object]", object, Tuple[int, JsonDict]]:
yield Clock(reactor).sleep(0)
yield defer.ensureDeferred(Clock(reactor).sleep(0))
return 1, {}

@defer.inlineCallbacks
Expand Down
2 changes: 1 addition & 1 deletion tests/server_notices/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ def _check_user_received_server_notice(
break

# Sleep and try again.
self.clock.sleep(0.1)
self.get_success(self.clock.sleep(0.1))
else:
self.fail(
f"Failed to join the server notices room. No 'join' field in sync_body['rooms']: {sync_body['rooms']}"
Expand Down
4 changes: 2 additions & 2 deletions tests/state/test_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@


class FakeClock:
def sleep(self, msec: float) -> "defer.Deferred[None]":
return defer.succeed(None)
async def sleep(self, msec: float) -> None:
return None


class FakeEvent:
Expand Down
19 changes: 8 additions & 11 deletions tests/util/test_logcontext.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,18 @@ def test_with_context(self) -> None:
with LoggingContext("test"):
self._check_test_key("test")

@defer.inlineCallbacks
def test_sleep(self) -> Generator["defer.Deferred[object]", object, None]:
async def test_sleep(self) -> None:
clock = Clock(reactor)

@defer.inlineCallbacks
def competing_callback() -> Generator["defer.Deferred[object]", object, None]:
async def competing_callback() -> None:
with LoggingContext("competing"):
yield clock.sleep(0)
await clock.sleep(0)
self._check_test_key("competing")

reactor.callLater(0, competing_callback)
reactor.callLater(0, lambda: defer.ensureDeferred(competing_callback()))

with LoggingContext("one"):
yield clock.sleep(0)
await clock.sleep(0)
self._check_test_key("one")

def _test_run_in_background(self, function: Callable[[], object]) -> defer.Deferred:
Expand Down Expand Up @@ -108,9 +106,8 @@ def check_logcontext() -> None:
return d2

def test_run_in_background_with_blocking_fn(self) -> defer.Deferred:
@defer.inlineCallbacks
def blocking_function() -> Generator["defer.Deferred[object]", object, None]:
yield Clock(reactor).sleep(0)
async def blocking_function() -> None:
await Clock(reactor).sleep(0)

return self._test_run_in_background(blocking_function)

Expand All @@ -133,7 +130,7 @@ def testfunc() -> defer.Deferred:
def test_run_in_background_with_coroutine(self) -> defer.Deferred:
async def testfunc() -> None:
self._check_test_key("one")
d = Clock(reactor).sleep(0)
d = defer.ensureDeferred(Clock(reactor).sleep(0))
self.assertIs(current_context(), SENTINEL_CONTEXT)
await d
self._check_test_key("one")
Expand Down
Loading