Re: Asyncio -- delayed calculation

2016-12-02 Thread Chris Angelico
On Sat, Dec 3, 2016 at 1:26 AM, Frank Millman  wrote:
> Then Twisted made a strong case for an asynchronous approach. One of their
> claims (which I have no reason to doubt) was that, because each user
> 'session' spends most of its time waiting for something - keyboard input,
> reply from database, etc - their approach allows hundreds of concurrent
> users, something that I believe would not be possible with threading or
> multi-processing.

I'm not sure that "hundreds" would be a problem - I've had processes
with hundreds of threads before - but if you get up to hundreds of
*thousands* of threads, then yes, threads start to be a problem. When
you have that sort of traffic (that's multiple requests per
millisecond, or over a million requests per minute), you have to think
about throughput and efficiency, not just simplicity. But for low
traffic environments (single digits of requests per second), threads
work just fine. Actually, for traffic levels THAT low, you could
probably serialize your requests and nobody would notice. That's the
simplest of all, but it scales pretty terribly :)

Not everyone needs the full power of asyncio, so not everyone will
really see the point. I like that it's available to everyone, though.
If you need it, it's there.

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-12-02 Thread Marko Rauhamaa
"Frank Millman" :
> Then Twisted made a strong case for an asynchronous approach. One of
> their claims (which I have no reason to doubt) was that, because each
> user 'session' spends most of its time waiting for something -
> keyboard input, reply from database, etc - their approach allows
> hundreds of concurrent users, something that I believe would not be
> possible with threading or multi-processing.

You don't need asyncio to do event-driven programming in Python. I have
programmed several event-driven applications in Python (and even more of
them in other languages) without using asyncio (you only need
select.epoll()).

My favorite model is the so-called "callback hell" with explicit finite
state machines. That tried-and-true model has always been used with
parallel, concurrent and network programming as well as user-interface
programming. Your code should closely resemble this:

   http://www.bayfronttechnologies.com/capesdl.gif>


Multiprocessing is also an important tool for compartmentalizing and
parallelizing functionality. Threads are mainly suitable for turning
obnoxious blocking APIs into nonblocking ones, but even there, I would
prefer to give that job to separate processes.


Marko
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-12-02 Thread Frank Millman
"Steve D'Aprano"  wrote in message 
news:58417e2d$0$1612$c3e8da3$54964...@news.astraweb.com...


My first impressions on this is that we have a couple of good models for
preemptive parallelism, threads and processes, both of which can do
everything that concurrency can do, and more, and both of which are
significantly easier to understand too.

So why do we need asyncio? What is it actually good for?



As I have mentioned, my use-case is a multi-user client/server system.

The traditional approach used to be to use threads to allow multiple users 
to work concurrently.


Then Twisted made a strong case for an asynchronous approach. One of their 
claims (which I have no reason to doubt) was that, because each user 
'session' spends most of its time waiting for something - keyboard input, 
reply from database, etc - their approach allows hundreds of concurrent 
users, something that I believe would not be possible with threading or 
multi-processing.


Frank Millman


--
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-12-02 Thread Marko Rauhamaa
Steve D'Aprano :

> py> await x
>   File "", line 1
> await x
>   ^
> SyntaxError: invalid syntax

"await" is only allowed inside a coroutine.

> So why do we need asyncio? What is it actually good for?

Asyncio is a form of cooperative multitasking. It presents a framework
of "fake threads". The objective and programming model is identical to
that of threads.

As to why Python should need a coroutine framework, you can answer it
from two angles:

  1. Why does one need cooperative multitasking?

  2. Why should one use coroutines to implement cooperative
 multitasking?


1. Cooperative multitasking has made its way in Java ("NIO") and C#
(async/await). It has come about as enterprise computing realized the
multithreading model of the 1990's was shortsighted. In particular, it
wasn't scalable. Enterprise solutions collapsed under the weight of tens
of thousands of threads. Stack space ran out and schedulers became slow.
https://en.wikipedia.org/wiki/C10k_problem>

2. I have always been into asynchronous programming (cooperative
multitasking), but coroutines are far from my favorite programming
model. I am guessing Guido introduced them to Python because:

 * C# has them (me too!).

 * They have a glorious computer scientific past (CSP, emperor's new I/O
   framework).

 * They look like threads.

 * They were already there in the form of generators (low-hanging
   fruit).

And, maybe most importantly:

 * Twisted (et al) had needed an event-driven framework but Python
   didn't have one out of the box (https://lwn.net/Articles/692254/>).


Marko
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-12-02 Thread Steve D'Aprano
On Thu, 1 Dec 2016 06:53 pm, Christian Gollwitzer wrote:

> well that works - but I think it it is possible to explain it, without
> actually understanding what it does behind the scences:
> 
> x = foo()
> # schedule foo for execution, i.e. put it on a TODO list
> 
> await x
> # run the TODO list until foo has finished

Nope, sorry, that doesn't help even a little bit. For starters, it doesn't
work: it is a syntax error.

py> async def foo():
... return 1
...
py> x = foo()
py> await x
  File "", line 1
await x
  ^
SyntaxError: invalid syntax


But even if it did work, why am I waiting for the TODO list to finish?
Doesn't that mean I'm now blocking, which goes completely against the idea
of writing non-blocking asynchronous code? If I wanted to block waiting for
x, I'd just make it a regular, easy-to-understand, synchronous function.

Besides, where does x stash it's result? In a global variable? What if
another async routine messes with the same global?

My first impressions on this is that we have a couple of good models for
preemptive parallelism, threads and processes, both of which can do
everything that concurrency can do, and more, and both of which are
significantly easier to understand too.

So why do we need asyncio? What is it actually good for?




-- 
Steve
“Cheer up,” they said, “things could be worse.” So I cheered up, and sure
enough, things got worse.

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-12-01 Thread Ian Kelly
On Thu, Dec 1, 2016 at 12:53 AM, Christian Gollwitzer  wrote:
> well that works - but I think it it is possible to explain it, without
> actually understanding what it does behind the scences:
>
> x = foo()
> # schedule foo for execution, i.e. put it on a TODO list

This implies that if you never await foo it will still get done at
some point (e.g. when you await something else), which for coroutines
would be incorrect unless you call ensure_future() on it.

Come to think about it, it would probably not be a bad style rule to
consider that when you call something that returns an awaitable, you
should always either await or ensure_future it (or something else that
depends on it). Barring the unusual case where you want to create an
awaitable but *not* immediately schedule it.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-11-30 Thread Christian Gollwitzer

Am 30.11.16 um 22:07 schrieb Gregory Ewing:

Chris Angelico wrote:


That's because you're not actually running anything concurrently.


Yes, I know what happens and why. My point is that for
someone who *doesn't* know, simplistic attempts to
explain what "await" means can be very misleading.

There doesn't seem to be any accurate way of summarising
it in a few words. The best we can do seems to be to
just say "it's a magic word that you have to put in
front of any call to a function that you defined as
async".


well that works - but I think it it is possible to explain it, without 
actually understanding what it does behind the scences:


x = foo()
# schedule foo for execution, i.e. put it on a TODO list

await x
# run the TODO list until foo has finished

IMHO coroutines are a simple concept in itself, just that stackful 
programming (call/return) has tainted our minds so much that we have 
trouble figuring out a "function call" which does not "return" in the 
usual sense. The implementation is even more convoluted with the futures 
and promises and whatnot. For simply using that stuff it is not 
important to know how it works.



Christian
--
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-11-30 Thread Marko Rauhamaa
Gregory Ewing :
> My point is that for someone who *doesn't* know, simplistic attempts
> to explain what "await" means can be very misleading.
>
> There doesn't seem to be any accurate way of summarising it in a few
> words. The best we can do seems to be to just say "it's a magic word
> that you have to put in front of any call to a function that you
> defined as async".

I don't think it needs any other explanation. (Thousands of debugging
hours will likely be spent locating the missing "await" keywords.)

> I think it's possible to build a conceptual model that's easier to
> grasp and hides more of the underlying machinery.

I'm thinking the problem with asyncio is the very fact that it is hiding
the underlying callbacks. I'm also predicting there will be quite a bit
of asyncio code that ironically converts coroutines back into callbacks.

> PEP 3152 was my attempt at doing that. Unfortunately, we seem to have
> gone down a fork in the road that leads somewhere else and from which
> there's no getting back.

Alea jacta est.


Marko
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-11-30 Thread Marko Rauhamaa
Terry Reedy :

> On 11/30/2016 7:53 AM, Chris Angelico wrote:
>
>> I also think that everyone should spend some time writing
>> multithreaded code before switching to asyncio. It'll give you a
>> better appreciation for what's going on.
>
> I so disagree with this. I have written almost no thread code but have
> successfully written asyncio and async await code. Perhaps this is
> because I have written tkinter code, including scheduling code with
> root.after. The tk and asyncio event loops and scheduling are quite
> similar.

I kinda agree with Chris in that asyncio is a programming model
virtually identical with multithreading. Asyncio is sitting on an
event-driven machinery, but asyncio does its best to hide that fact.

The key similarity between coroutines and threads is concurrent linear
sequences of execution that encode states as blocking function calls.


Marko
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-11-30 Thread Gregory Ewing

Chris Angelico wrote:


That's because you're not actually running anything concurrently.


Yes, I know what happens and why. My point is that for
someone who *doesn't* know, simplistic attempts to
explain what "await" means can be very misleading.

There doesn't seem to be any accurate way of summarising
it in a few words. The best we can do seems to be to
just say "it's a magic word that you have to put in
front of any call to a function that you defined as
async".

A paraphrasing of the Zen comes to mind: "If the
semantics are hard to explain, it may be a bad idea."

> there are

complexities to the underlying concepts that can't be hidden by any
framework.


I don't entirely agree with that. I think it's possible
to build a conceptual model that's easier to grasp and
hides more of the underlying machinery. PEP 3152 was
my attempt at doing that. Unfortunately, we seem to
have gone down a fork in the road that leads somewhere
else and from which there's no getting back.

--
Greg
--
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-11-30 Thread Chris Kaynor
On Wed, Nov 30, 2016 at 10:58 AM, Chris Angelico  wrote:
> On Thu, Dec 1, 2016 at 5:54 AM, Terry Reedy  wrote:
>> On 11/30/2016 7:53 AM, Chris Angelico wrote:
>>
>>> I also think that everyone should spend some time writing
>>> multithreaded code before switching to asyncio. It'll give you a
>>> better appreciation for what's going on.
>>
>>
>> I so disagree with this.  I have written almost no thread code but have
>> successfully written asyncio and async await code.  Perhaps this is because
>> I have written tkinter code, including scheduling code with root.after.  The
>> tk and asyncio event loops and scheduling are quite similar.
>
> Okay, so maybe not everyone needs threading, but certainly having a
> background in threading can help a lot. It _is_ possible to jump
> straight into asyncio, but if you haven't gotten your head around
> concurrency in other forms, you're going to get very much confused.
>
> Or maybe it's that the benefits of threaded programming can also be
> gained by working with event driven code. That's also possible.

I've dealt with multi-process (Python; some in C++ and C#),
multi-thread (Python, C++, C#, and other), and co-routine concurrency
(C#/Unity; I haven't used asyncio yet), and co-routine is by far the
simplest, as you don't have to deal with locking or race conditions in
general.

If you understand threading/multi-process programming, co-routines
will be easier to understand, as some of the aspects still apply.
Learning threading/process-based will be easier in some ways if you
know co-routine. In other ways, it will be harder as you'll have to
learn to consider locking and race conditions in coding.

I think in a venn-diagram of the complexity, co-routines would exist
in a circle contained by multi-process (only explicit shared memory
simplifies things, but there are still issues), which is inside a
circle by threading.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-11-30 Thread Chris Angelico
On Thu, Dec 1, 2016 at 5:54 AM, Terry Reedy  wrote:
> On 11/30/2016 7:53 AM, Chris Angelico wrote:
>
>> I also think that everyone should spend some time writing
>> multithreaded code before switching to asyncio. It'll give you a
>> better appreciation for what's going on.
>
>
> I so disagree with this.  I have written almost no thread code but have
> successfully written asyncio and async await code.  Perhaps this is because
> I have written tkinter code, including scheduling code with root.after.  The
> tk and asyncio event loops and scheduling are quite similar.

Okay, so maybe not everyone needs threading, but certainly having a
background in threading can help a lot. It _is_ possible to jump
straight into asyncio, but if you haven't gotten your head around
concurrency in other forms, you're going to get very much confused.

Or maybe it's that the benefits of threaded programming can also be
gained by working with event driven code. That's also possible.

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-11-30 Thread Terry Reedy

On 11/30/2016 7:53 AM, Chris Angelico wrote:


I also think that everyone should spend some time writing
multithreaded code before switching to asyncio. It'll give you a
better appreciation for what's going on.


I so disagree with this.  I have written almost no thread code but have 
successfully written asyncio and async await code.  Perhaps this is 
because I have written tkinter code, including scheduling code with 
root.after.  The tk and asyncio event loops and scheduling are quite 
similar.


--
Terry Jan Reedy

--
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-11-30 Thread Chris Angelico
On Wed, Nov 30, 2016 at 11:40 PM, Gregory Ewing
 wrote:
> Chris Angelico wrote:
>>
>> From the point of view of
>> the rest of Python, no. It's a sign saying "Okay, Python, you can
>> alt-tab away from me now".
>
>
> The problem with that statement is it implies that if
> you omit the "await", then the thing you're calling
> will run uninterruptibly. Whereas what actually happens
> is that it doesn't get run at all.

That's because you're not actually running anything concurrently.
Until you wait for something to be done, it doesn't start happening.
Asynchronous I/O gives the illusion of concurrency, but actually,
everything's serialized.

I think a lot of the confusion around asyncio comes from people not
understanding the fundamentals of what's going on; there are
complexities to the underlying concepts that can't be hidden by any
framework. No matter what you do, you can't get away from them.
They're not asyncio's fault. They're not async/await's fault. They're
not the fault of having forty-two thousand different ways to do
things. They're fundamentals.

I also think that everyone should spend some time writing
multithreaded code before switching to asyncio. It'll give you a
better appreciation for what's going on.

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-11-30 Thread Gregory Ewing

Chris Angelico wrote:

From the point of view of
the rest of Python, no. It's a sign saying "Okay, Python, you can
alt-tab away from me now".


The problem with that statement is it implies that if
you omit the "await", then the thing you're calling
will run uninterruptibly. Whereas what actually happens
is that it doesn't get run at all.

--
Greg
--
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-11-30 Thread Gregory Ewing

On Tuesday 29 November 2016 14:21, Chris Angelico wrote:


"await" means "don't continue this function until that's done". It
blocks the function until a non-blocking operation is done.


That explanation gives the impression that it's some
kind of "join" operation on parallel tasks, i.e. if
you do

   x = foo()
   do_something_else()
   y = await x

then foo() somehow proceeds in the background while
do_something_else() is going on. But that's not the
way it works at all.

--
Greg
--
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-11-29 Thread Steve D'Aprano
On Wed, 30 Nov 2016 05:41 am, Ian Kelly wrote:

> You mean how do you create something that can be awaited that doesn't
> await something else in turn? With a Future.
> 
> import asyncio
> 
> class Awaitable(asyncio.Future):
>   def wake_up_later(self):
> asyncio.get_event_loop().call_later(3, self.set_result, 42)

Not to be confused with concurrent.Futures.

https://docs.python.org/3.5/library/concurrent.futures.html

https://docs.python.org/3.5/library/asyncio-task.html#asyncio.Future




-- 
Steve
“Cheer up,” they said, “things could be worse.” So I cheered up, and sure
enough, things got worse.

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-11-29 Thread Ian Kelly
On Mon, Nov 28, 2016 at 10:42 PM, Chris Angelico  wrote:
> On Tue, Nov 29, 2016 at 4:13 PM, Paul Rubin  wrote:
>>
>> I haven't gotten my head around Python asyncio and have been wanting
>> to read this:
>>
>>http://lucumr.pocoo.org/2016/10/30/i-dont-understand-asyncio/
>
> It's talking a lot about how we got here, which isn't all necessary if
> you just want to give asyncio a whirl. The conclusion at the end says
> that you should just use 'async def' and not bother with all the older
> forms, which I agree with (subject to the usual caveat that this
> implies no support for older Pythons).
>
> There's one thing that I really struggle with, though, and that's that
> there's no easy and obvious way to demonstrate the lowest level of
> operation. If "await x()" is like "yield from x()", how do you do the
> innermost "yield" that actually does something? I have the same
> confusion with Node.js, too. It's as if async primitives can't be
> implemented in application code at all, they just have to be given to
> you. Certainly it's not something made clear anywhere in the docs that
> I've found.

You mean how do you create something that can be awaited that doesn't
await something else in turn? With a Future.

import asyncio

class Awaitable(asyncio.Future):
  def wake_up_later(self):
asyncio.get_event_loop().call_later(3, self.set_result, 42)

async def main():
  awaitable = Awaitable()
  awaitable.wake_up_later()
  print(await awaitable)

asyncio.get_event_loop().run_until_complete(main())

The trick is in arranging for the future's result to be set. For I/O
events you would typically do that by associating a callback with a
file descriptor on the event loop:

https://docs.python.org/3/library/asyncio-eventloop.html#watch-file-descriptors

If you need to do something particulary abstruse you can always write
a custom Selector or event loop:

https://docs.python.org/3/library/selectors.html#module-selectors
https://docs.python.org/3/library/asyncio-eventloop.html#base-event-loop
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-11-28 Thread Marko Rauhamaa
Gregory Ewing :
> All the terminology around async/await is inherently confusing and
> counterintuitive, IMO. I'm disappointed that we've ended up here.

I think the conceptual mess can be clarified over time. Coroutines are
essentially threads. Why Python needs two threading implementations is
questionable. At least, with threads, you don't have to remember to tag
your function definitions with "async" and your functions calls with
"await".

Both threads and now coroutines were introduced as fig leafs to cover
the underlying finite state machines. Personally, I think it is a
mistake to hide the state machines. Instead, people should learn to
think in terms of state machines, which is the classic network protocol
model.


Marko
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-11-28 Thread Chris Angelico
On Tue, Nov 29, 2016 at 4:32 PM, Steven D'Aprano
 wrote:
> On Tuesday 29 November 2016 14:21, Chris Angelico wrote:
>
>> On Tue, Nov 29, 2016 at 1:23 PM, Steve D'Aprano
>>  wrote:
>>> This is confusing: why is this awaiting something inside an async function?
>>> Doesn't that mean that the await asyncio.gather(...) call is turned
>>> blocking?
>>
>> "await" means "don't continue this function until that's done". It
>> blocks the function until a non-blocking operation is done.
>
> So that would be... "yes"?

>From the point of view of the function, yes. From the point of view of
the rest of Python, no. It's a sign saying "Okay, Python, you can
alt-tab away from me now".

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-11-28 Thread Chris Angelico
On Tue, Nov 29, 2016 at 4:13 PM, Paul Rubin  wrote:
>
> I haven't gotten my head around Python asyncio and have been wanting
> to read this:
>
>http://lucumr.pocoo.org/2016/10/30/i-dont-understand-asyncio/

It's talking a lot about how we got here, which isn't all necessary if
you just want to give asyncio a whirl. The conclusion at the end says
that you should just use 'async def' and not bother with all the older
forms, which I agree with (subject to the usual caveat that this
implies no support for older Pythons).

There's one thing that I really struggle with, though, and that's that
there's no easy and obvious way to demonstrate the lowest level of
operation. If "await x()" is like "yield from x()", how do you do the
innermost "yield" that actually does something? I have the same
confusion with Node.js, too. It's as if async primitives can't be
implemented in application code at all, they just have to be given to
you. Certainly it's not something made clear anywhere in the docs that
I've found.

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-11-28 Thread Steven D'Aprano
On Tuesday 29 November 2016 14:21, Chris Angelico wrote:

> On Tue, Nov 29, 2016 at 1:23 PM, Steve D'Aprano
>  wrote:
>> This is confusing: why is this awaiting something inside an async function?
>> Doesn't that mean that the await asyncio.gather(...) call is turned
>> blocking?
> 
> "await" means "don't continue this function until that's done". It
> blocks the function until a non-blocking operation is done.

So that would be... "yes"?





-- 
Steven
"Ever since I learned about confirmation bias, I've been seeing 
it everywhere." - Jon Ronson

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-11-28 Thread Paul Rubin
Chris Angelico  writes:
> Asynchronous I/O is something to get your head around  I'd much
> rather work with generator-based async functions...

I haven't gotten my head around Python asyncio and have been wanting
to read this:

   http://lucumr.pocoo.org/2016/10/30/i-dont-understand-asyncio/

Is that picture too bleak?  I've used traditional cooperative
multitasking systems in the past, and the main hazard with them is to be
careful to not run for too long without yielding.  Python's stuff seems
much more complicated, with weirder hazards, some stemming from the
hidden mutable state in every generator.  I wonder whether a
stackless-based green thread approach might have been simpler.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-11-28 Thread Nathan Ernst
To be fair, in other languages, such as C# or C++ with similar mechanisms,
if you don't ask for the result from an async or future task, there's no
guarantee the async task will be executed at all unless (or until) you ask
for the result. C++'s futures even give an explicit flag indicating you
want the task to be executed on the current thread, but deferred until the
result is asked for. I'm sure the people on the standards committee had a
rationale for this, but I see only marginal utility for that use case
(maybe such as a cancellable expensive calc?). Seems like "I might need the
result of this calc down the road, but I don't need it currently, so I'll
just kick the can down the road.". I'm not aware of a mechanism to make C#
behave that way, but I know it's possible that tasks will not execute until
they're asked for their result, but I don't know that it is 100%
deterministic.

To be honest, I think async/await stile programming is so new that there's
not a lot of good material about how to properly use it (as far as both
when and how). The limit of my experience has been in C# dealing with web
requests, where the usage is basically, I'm going to need this data, so
I'll fire off the async request for it, and I've got a lot of work I can do
before I need the result, so lets do that, until I need to wait (await) for
the result of that request before I can proceed any further. It can be
useful, but it is a new, completely different paradigm and methodology to
wrap ones head around. I've used it for about 2 years in C# land, and I
still don't know that I'm doing it right. I've not done any async/await in
Python space, but I'm eager to learn and try, when it may be appropriate,
But, most of my current use cases don't require it.

On Mon, Nov 28, 2016 at 10:37 PM, Chris Angelico  wrote:

> On Tue, Nov 29, 2016 at 3:16 PM, Gregory Ewing
>  wrote:
> > Chris Angelico wrote:
> >>
> >> "await" means "don't continue this function until that's done". It
> >> blocks the function until a non-blocking operation is done.
> >
> >
> > However, *not* using 'await' doesn't mean the operation
> > will be done without blocking. Rather, it won't be done
> > at all (and is usually an error, but there's no way for
> > the implementation to detect it at the time).
> >
> > I don't blame Steve for being confused. All the
> > terminology around async/await is inherently confusing
> > and counterintuitive, IMO. I'm disappointed that we've
> > ended up here.
>
> Asynchronous I/O is something to get your head around. As someone who
> teaches both JavaScript and Python, I've run into this quite a bit,
> and frankly, callbacks may be easy to explain at a concrete level, but
> I'd much rather work with generator-based async functions (which JS is
> getting soon too). The best way to think about it IMO is a "process"
> that does blocking calls, and which the "system" can multitask away
> from any time it's blocked. Your function awaits something, but Python
> doesn't.
>
> ChrisA
> --
> https://mail.python.org/mailman/listinfo/python-list
>
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-11-28 Thread Chris Angelico
On Tue, Nov 29, 2016 at 3:16 PM, Gregory Ewing
 wrote:
> Chris Angelico wrote:
>>
>> "await" means "don't continue this function until that's done". It
>> blocks the function until a non-blocking operation is done.
>
>
> However, *not* using 'await' doesn't mean the operation
> will be done without blocking. Rather, it won't be done
> at all (and is usually an error, but there's no way for
> the implementation to detect it at the time).
>
> I don't blame Steve for being confused. All the
> terminology around async/await is inherently confusing
> and counterintuitive, IMO. I'm disappointed that we've
> ended up here.

Asynchronous I/O is something to get your head around. As someone who
teaches both JavaScript and Python, I've run into this quite a bit,
and frankly, callbacks may be easy to explain at a concrete level, but
I'd much rather work with generator-based async functions (which JS is
getting soon too). The best way to think about it IMO is a "process"
that does blocking calls, and which the "system" can multitask away
from any time it's blocked. Your function awaits something, but Python
doesn't.

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-11-28 Thread Gregory Ewing

Chris Angelico wrote:

"await" means "don't continue this function until that's done". It
blocks the function until a non-blocking operation is done.


However, *not* using 'await' doesn't mean the operation
will be done without blocking. Rather, it won't be done
at all (and is usually an error, but there's no way for
the implementation to detect it at the time).

I don't blame Steve for being confused. All the
terminology around async/await is inherently confusing
and counterintuitive, IMO. I'm disappointed that we've
ended up here.

--
Greg
--
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-11-28 Thread Zachary Ware
On Mon, Nov 28, 2016 at 6:48 AM, Steve D'Aprano
 wrote:
> What am I doing wrong?

Give yourself a bit more to debug with, since you're going to want to
do something with the result your expensive calculation anyway:

import asyncio

class Counter:
def __init__(self, i):
self.count = 10
self.i = i

async def count_down(self):
print(self, self.i, "starting")
while self.count > 0:
# simulate a computation
await asyncio.sleep(0.5)
self.count -= 1
print(self, self.i, "completed")
return self.i + self.count

async def main():
pool = [Counter(i) for i in range(5)]
results = []
for obj in pool:
results.append(obj.count_down())
return results

loop = asyncio.get_event_loop()
print(loop.run_until_complete(main()))


This gives you:

[, , , , ]
asynctest.py:25: RuntimeWarning: coroutine 'Counter.count_down' was
never awaited
  print(loop.run_until_complete(main()))

Ok, so let's fix that by adding an 'await' on line 21 (it's reported
at line 25 because that's when the unawaited coroutines are gc'd):

results.append(await obj.count_down())

Running that gives:

<__main__.Counter object at 0x10203f978> 0 starting
<__main__.Counter object at 0x10203f978> 0 completed
<__main__.Counter object at 0x1025af710> 1 starting
<__main__.Counter object at 0x1025af710> 1 completed
<__main__.Counter object at 0x1025b60b8> 2 starting
<__main__.Counter object at 0x1025b60b8> 2 completed
<__main__.Counter object at 0x1025b60f0> 3 starting
<__main__.Counter object at 0x1025b60f0> 3 completed
<__main__.Counter object at 0x1025b6128> 4 starting
<__main__.Counter object at 0x1025b6128> 4 completed
[0, 1, 2, 3, 4]

Still not right, only one count_down is run at a time.  But that's
because we're using a synchronous for loop to await our results and
populate the results list.  Naively, I tried an 'async for', but
that's trying to be asynchronous in the wrong place:

Traceback (most recent call last):
  File "asynctest.py", line 25, in 
print(loop.run_until_complete(main()))
  File "/usr/lib/python3.5/asyncio/base_events.py", line 387, in
run_until_complete
return future.result()
  File "/usr/lib/python3.5/asyncio/futures.py", line 274, in result
raise self._exception
  File "/usr/lib/python3.5/asyncio/tasks.py", line 239, in _step
result = coro.send(None)
  File "asynctest.py", line 20, in main
async for obj in pool:
TypeError: 'async for' requires an object with __aiter__ method, got list

So instead, checking the docs suggests using asyncio.gather for
parallel execution of tasks, which takes a variable number of
'coros_or_futures'.  On the first attempt, we saw that we had
accidentally created a list of "coroutine objects", so lets go back
and use that:

--- asynctest.py.orig 2016-11-28 21:03:04.0 -0600
+++ asynctest.py 2016-11-28 21:03:35.0 -0600
@@ -16,9 +16,10 @@

 async def main():
 pool = [Counter(i) for i in range(5)]
-results = []
+coros = []
 for obj in pool:
-results.append(await obj.count_down())
+coros.append(obj.count_down())
+results = asyncio.gather(*coros)
 return results

 loop = asyncio.get_event_loop()

Output:

<__main__.Counter object at 0x1026b6160> 4 starting
<__main__.Counter object at 0x10213f978> 0 starting
<__main__.Counter object at 0x1026b6128> 3 starting
<__main__.Counter object at 0x1026af748> 1 starting
<__main__.Counter object at 0x1026b60f0> 2 starting
<_GatheringFuture pending>

Now we've started everything asynchronously, but it exited way too
fast and never completed anything.  But instead of a list of results,
we got a _GatheringFuture at the end of everything.  So let's await
that:

results = await asyncio.gather(*coros)

And now we get:

<__main__.Counter object at 0x101eb6160> 4 starting
<__main__.Counter object at 0x10063f978> 0 starting
<__main__.Counter object at 0x101eb6128> 3 starting
<__main__.Counter object at 0x101eaf748> 1 starting
<__main__.Counter object at 0x101eb60f0> 2 starting
<__main__.Counter object at 0x101eb6160> 4 completed
<__main__.Counter object at 0x10063f978> 0 completed
<__main__.Counter object at 0x101eb6128> 3 completed
<__main__.Counter object at 0x101eaf748> 1 completed
<__main__.Counter object at 0x101eb60f0> 2 completed
[0, 1, 2, 3, 4]


And there we have it.  Our final script is:

import asyncio

class Counter:
def __init__(self, i):
self.count = 10
self.i = i

async def count_down(self):
print(self, self.i, "starting")
while self.count > 0:
# simulate a computation
await asyncio.sleep(0.5)
self.count -= 1
print(self, self.i, "completed")
return self.i + self.count

async def main():
pool = [Counter(i) for i in range(5)]
coros = []
for obj in pool:
coros.append(obj.count_down())
results = await asyncio.gather(*coros)

Re: Asyncio -- delayed calculation

2016-11-28 Thread Chris Angelico
On Tue, Nov 29, 2016 at 1:23 PM, Steve D'Aprano
 wrote:
> This is confusing: why is this awaiting something inside an async function?
> Doesn't that mean that the await asyncio.gather(...) call is turned
> blocking?

"await" means "don't continue this function until that's done". It
blocks the function until a non-blocking operation is done.

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-11-28 Thread Zentrader
Take a look at Doug Hellmann's example using multiprocessing at 
https://pymotw.com/2/multiprocessing/basics.html  You should be able to 
substitute the count down example directly into the first example.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-11-28 Thread Steve D'Aprano
On Tue, 29 Nov 2016 12:03 am, Chris Angelico wrote:

> On Mon, Nov 28, 2016 at 11:48 PM, Steve D'Aprano
>  wrote:
>> When I try running that, I get no output. No error, no exception, the
>> run_until_complete simply returns instantly.
> 
> When I do, I get this warning:
> 
> asynctest.py:17: RuntimeWarning: coroutine 'Counter.count_down' was
> never awaited
>   obj.count_down()

Ah yes, I saw that earlier, from an earlier version of the code that failed
with an exception. But it was enough to raise the warning, which then was
suppressed.


> Putting an 'await' in front of that call causes the tasks to be run
> consecutively, of course. The most similar code for running tasks
> concurrently seems to be this:
> 
> async def main():
> pool = [Counter() for i in range(5)]
> await asyncio.gather(*(obj.count_down() for obj in pool))
> 
> Taken from:
>
https://docs.python.org/3/library/asyncio-task.html#example-parallel-execution-of-tasks

This is confusing: why is this awaiting something inside an async function?
Doesn't that mean that the await asyncio.gather(...) call is turned
blocking?



-- 
Steve
“Cheer up,” they said, “things could be worse.” So I cheered up, and sure
enough, things got worse.

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-11-28 Thread Chris Angelico
On Tue, Nov 29, 2016 at 11:20 AM, Steve D'Aprano
 wrote:
> On Tue, 29 Nov 2016 02:53 am, Ian Kelly wrote:
>
>> In order for the coroutines to actually do anything, you need to
>> schedule them in some way with the event loop. That could take the
>> form of awaiting them from some other coroutine, or passing them
>> directly to loop.run_until_complete or event_loop.create_task, or as
>> Chris suggested awaiting them as an aggregate.
>
> I thought that's what I had done, by calling
>
> loop.run_until_complete(main())

That's invoking a single task, main(). If you were to write this code
using threads, main would need to be thread-aware so that it can fork
appropriately - it needs to start new threads for the subtasks. Tasks
in asyncio are like cooperatively-switched threads or threadlets, and
if you want them to run in parallel, you have to instruct them to run
as separate tasks.

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-11-28 Thread Steve D'Aprano
On Tue, 29 Nov 2016 02:53 am, Ian Kelly wrote:

> In order for the coroutines to actually do anything, you need to
> schedule them in some way with the event loop. That could take the
> form of awaiting them from some other coroutine, or passing them
> directly to loop.run_until_complete or event_loop.create_task, or as
> Chris suggested awaiting them as an aggregate.

I thought that's what I had done, by calling

loop.run_until_complete(main())



-- 
Steve
“Cheer up,” they said, “things could be worse.” So I cheered up, and sure
enough, things got worse.

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-11-28 Thread Ian Kelly
On Mon, Nov 28, 2016 at 5:48 AM, Steve D'Aprano
 wrote:
> Let's pretend that the computation can be performed asynchronously, so that
> I can have all five Counter objects counting down in parallel. I have this:
>
>
> import asyncio
>
> class Counter:
> def __init__(self):
> self.count = 10
> async def count_down(self):
> print(self, "starting")
> while self.count > 0:
> # simulate a computation
> await asyncio.sleep(0.5)
> self.count -= 1
> print(self, "completed")
>
> async def main():
> pool = [Counter() for i in range(5)]
> for obj in pool:
> obj.count_down()
>
> loop = asyncio.get_event_loop()
> loop.run_until_complete(main())
>
>
>
>
> When I try running that, I get no output. No error, no exception, the
> run_until_complete simply returns instantly.
>
> What am I doing wrong?

Remember that coroutines are basically generators. Native "async def"
coroutines are dressed up as something different, but they were still
designed as a drop-in replacement for generator coroutines. If
count_down were a generator function then simply calling it wouldn't
really do anything and as a native coroutine it still doesn't (other
than return a "coroutine object").

In order for the coroutines to actually do anything, you need to
schedule them in some way with the event loop. That could take the
form of awaiting them from some other coroutine, or passing them
directly to loop.run_until_complete or event_loop.create_task, or as
Chris suggested awaiting them as an aggregate.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Asyncio -- delayed calculation

2016-11-28 Thread Chris Angelico
On Mon, Nov 28, 2016 at 11:48 PM, Steve D'Aprano
 wrote:
> When I try running that, I get no output. No error, no exception, the
> run_until_complete simply returns instantly.

When I do, I get this warning:

asynctest.py:17: RuntimeWarning: coroutine 'Counter.count_down' was
never awaited
  obj.count_down()

Putting an 'await' in front of that call causes the tasks to be run
consecutively, of course. The most similar code for running tasks
concurrently seems to be this:

async def main():
pool = [Counter() for i in range(5)]
await asyncio.gather(*(obj.count_down() for obj in pool))

Taken from:
https://docs.python.org/3/library/asyncio-task.html#example-parallel-execution-of-tasks

There may be other ways, but that's the best I could find. It seems to
do what you want.

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Asyncio -- delayed calculation

2016-11-28 Thread Steve D'Aprano
I'm a complete and utter newbie when it comes to asynchronous programming,
so I may have the entire concept backwards here. But treat this as a
learning exercise rather than something I'd really do.

Suppose I have a bunch of calculations to do: count down from 10. So I have
a bunch of objects:

class Counter:
def __init__(self):
self.count = 10
def count_down(self):
print(self, "starting")
while self.count > 0:
# simulate a computation
time.sleep(0.5)
self.count -= 1
print(self, "completed")

pool = [Counter() for i in range(5)]

for obj in pool:
obj.count_down()



Since that's all blocking, I wait for the first Counter to count down to
zero before I move on to the second.

Let's pretend that the computation can be performed asynchronously, so that
I can have all five Counter objects counting down in parallel. I have this:


import asyncio

class Counter:
def __init__(self):
self.count = 10
async def count_down(self):
print(self, "starting")
while self.count > 0:
# simulate a computation
await asyncio.sleep(0.5)
self.count -= 1
print(self, "completed")

async def main():
pool = [Counter() for i in range(5)]
for obj in pool:
obj.count_down()

loop = asyncio.get_event_loop()
loop.run_until_complete(main())




When I try running that, I get no output. No error, no exception, the
run_until_complete simply returns instantly.

What am I doing wrong?

What I expected is that the pool of Counter objects would be created, each
one would have their count_down() method called without blocking, so I'd
have something like:

# IDs are simulated for ease of comprehension
<__main__.Counter object at 0x0123> starting
<__main__.Counter object at 0x0246> starting
<__main__.Counter object at 0x048c> starting
<__main__.Counter object at 0x0918> starting
<__main__.Counter object at 0x1230> starting
<__main__.Counter object at 0x0123> completed
<__main__.Counter object at 0x0246> completed
<__main__.Counter object at 0x048c> completed
<__main__.Counter object at 0x0918> completed
<__main__.Counter object at 0x1230> completed



-- 
Steve
“Cheer up,” they said, “things could be worse.” So I cheered up, and sure
enough, things got worse.

-- 
https://mail.python.org/mailman/listinfo/python-list