-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
ObservableDeferred: run observers in order #11229
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
`ObservableDeferred`: run registered observers in order. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,11 +22,11 @@ | |
Any, | ||
Awaitable, | ||
Callable, | ||
Collection, | ||
Dict, | ||
Generic, | ||
Hashable, | ||
Iterable, | ||
List, | ||
Optional, | ||
Set, | ||
TypeVar, | ||
|
@@ -76,12 +76,17 @@ class ObservableDeferred(Generic[_T]): | |
def __init__(self, deferred: "defer.Deferred[_T]", consumeErrors: bool = False): | ||
object.__setattr__(self, "_deferred", deferred) | ||
object.__setattr__(self, "_result", None) | ||
object.__setattr__(self, "_observers", set()) | ||
object.__setattr__(self, "_observers", list()) | ||
|
||
def callback(r): | ||
object.__setattr__(self, "_result", (True, r)) | ||
while self._observers: | ||
observer = self._observers.pop() | ||
|
||
# once we have set _result, no more entries will be added to _observers, | ||
# so it's safe to replace it with the empty tuple. | ||
observers = self._observers | ||
object.__setattr__(self, "_observers", tuple()) | ||
|
||
for observer in observers: | ||
try: | ||
observer.callback(r) | ||
except Exception as e: | ||
|
@@ -95,12 +100,16 @@ def callback(r): | |
|
||
def errback(f): | ||
object.__setattr__(self, "_result", (False, f)) | ||
while self._observers: | ||
|
||
# once we have set _result, no more entries will be added to _observers, | ||
# so it's safe to replace it with the empty tuple. | ||
observers = self._observers | ||
object.__setattr__(self, "_observers", tuple()) | ||
|
||
for observer in observers: | ||
# This is a little bit of magic to correctly propagate stack | ||
# traces when we `await` on one of the observer deferreds. | ||
f.value.__failure__ = f | ||
|
||
observer = self._observers.pop() | ||
try: | ||
observer.errback(f) | ||
except Exception as e: | ||
|
@@ -127,20 +136,13 @@ def observe(self) -> "defer.Deferred[_T]": | |
""" | ||
if not self._result: | ||
d: "defer.Deferred[_T]" = defer.Deferred() | ||
|
||
def remove(r): | ||
self._observers.discard(d) | ||
return r | ||
|
||
d.addBoth(remove) | ||
Comment on lines
-130
to
-135
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this code removed the observer from (it was added in #190 - no real clues there as to why.) |
||
|
||
self._observers.add(d) | ||
self._observers.append(d) | ||
return d | ||
else: | ||
success, res = self._result | ||
return defer.succeed(res) if success else defer.fail(res) | ||
|
||
def observers(self) -> "List[defer.Deferred[_T]]": | ||
def observers(self) -> "Collection[defer.Deferred[_T]]": | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A bit surprised that we're leaking internal state here! Could only see one use though. Probably fine as it is. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yup. Thought about changing it to a method that just returns the number of observers. Ran out of enthusiasm. |
||
return self._observers | ||
|
||
def has_called(self) -> bool: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,9 +47,7 @@ def check1(r): | |
self.assertTrue(set_d.called) | ||
return r | ||
|
||
# TODO: Actually ObservableDeferred *doesn't* run its tests in order on py3.8. | ||
# maybe we should fix that? | ||
# get_d.addCallback(check1) | ||
Comment on lines
-50
to
-52
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the comment isn't terribly clear, but I think this is what we're fixing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cf: #8572 (comment) |
||
get_d.addCallback(check1) | ||
|
||
# now fire off all the deferreds | ||
origin_d.callback(99) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the reason for this dance? Do we override
__setattr__
below? Oh yes, we do. I guess this is meant to be a fairly transparent wrapper around aDeferred
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, for some reason someone decided that making ObservableDeferred a transparent wrapper for a Deferred was a good idea. I hate it.