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

Commit

Permalink
Merge pull request #6505 from matrix-org/erikj/make_deferred_yiedable
Browse files Browse the repository at this point in the history
Fix `make_deferred_yieldable` to work with coroutines
  • Loading branch information
erikjohnston authored Dec 10, 2019
2 parents e726e18 + ffeafad commit 35f3c36
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 1 deletion.
1 change: 1 addition & 0 deletions changelog.d/6505.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Make `make_deferred_yieldable` to work with async/await.
11 changes: 10 additions & 1 deletion synapse/logging/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
See doc/log_contexts.rst for details on how this works.
"""

import inspect
import logging
import threading
import types
Expand Down Expand Up @@ -612,7 +613,8 @@ def run_in_background(f, *args, **kwargs):


def make_deferred_yieldable(deferred):
"""Given a deferred, make it follow the Synapse logcontext rules:
"""Given a deferred (or coroutine), make it follow the Synapse logcontext
rules:
If the deferred has completed (or is not actually a Deferred), essentially
does nothing (just returns another completed deferred with the
Expand All @@ -624,6 +626,13 @@ def make_deferred_yieldable(deferred):
(This is more-or-less the opposite operation to run_in_background.)
"""
if inspect.isawaitable(deferred):
# If we're given a coroutine we convert it to a deferred so that we
# run it and find out if it immediately finishes, it it does then we
# don't need to fiddle with log contexts at all and can return
# immediately.
deferred = defer.ensureDeferred(deferred)

if not isinstance(deferred, defer.Deferred):
return deferred

Expand Down
24 changes: 24 additions & 0 deletions tests/util/test_logcontext.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,30 @@ def test_nested_logging_context(self):
nested_context = nested_logging_context(suffix="bar")
self.assertEqual(nested_context.request, "foo-bar")

@defer.inlineCallbacks
def test_make_deferred_yieldable_with_await(self):
# an async function which retuns an incomplete coroutine, but doesn't
# follow the synapse rules.

async def blocking_function():
d = defer.Deferred()
reactor.callLater(0, d.callback, None)
await d

sentinel_context = LoggingContext.current_context()

with LoggingContext() as context_one:
context_one.request = "one"

d1 = make_deferred_yieldable(blocking_function())
# make sure that the context was reset by make_deferred_yieldable
self.assertIs(LoggingContext.current_context(), sentinel_context)

yield d1

# now it should be restored
self._check_test_key("one")


# a function which returns a deferred which has been "called", but
# which had a function which returned another incomplete deferred on
Expand Down

0 comments on commit 35f3c36

Please sign in to comment.