On Jun 6, 2016, at 08:29, Guido van Rossum <guido@python.org> wrote:

On Sun, Jun 5, 2016 at 10:16 PM, Glyph <glyph@twistedmatrix.com> wrote:

On Jun 4, 2016, at 13:25, Ben Darnell <ben@bendarnell.com> wrote:

If things are so sensitive to minor changes in timing, doesn't that set the
bar impossibly high for interoperability?


The sensitivity is not to changes in timing - i.e. when the wall-clock runs,
or ordering of non-deterministically ordered events - but rather to
reentrancy - whether certain things complete synchronously while the caller
is still on the stack and can depend on them having done so upon return.

The recommended way of writing tests within Twisted these days depends
heavily on `.callback´ synchronously resolving a Deferred, which is what
adding a call_soon breaks.

That's interesting, and also potentially worrisome (for interop, I'm
not saying Twisted is wrong here).

I think asyncio depends on the opposite: that if you add a callback to
a Future that's ready it does *not* immediately run. Asyncio's promise
is pretty strongly that callbacks are serialized (no callbacks running
inside other callbacks). IIRC we experimented with other semantics and
found that it was harder to reason about. (IMO if you *know* a Future
is ready why add a callback to it rather than just calling the damn
thing if that's what you want?)

I don't think Twisted is necessarily right here either.  You're absolutely right that it's easier to reason about reentrancy in some cases if you have a might-be-fired-might-not Future vs. the same sort of Deferred.  I like the property where you can do:

def test(test_case):
    a = asynchronously_something()
    test_case.this_must_not_have_a_result(a)
    cause_a_to_fire()
    test_case.assertEqual(test_case.this_must_have_a_result(a), something)

but this is (somewhat) opposed to the fact that call_soon means you never get the nasty surprise where the callback added in the middle of a function gets run before the rest of it does.  So I think there are good properties in both cases and given some thought it is probably possible to map between them, but Deferred is lower-level here in the sense that it provides a way to do this both with the event loop and without.  You can always call_soon(deferred.callback) but you can't go the other way and force a Future to resolve synchronously - right?

-glyph