This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: asyncio.Controller
Type: Stage: resolved
Components: Library (Lib) Versions: Python 3.7
process
Status: closed Resolution: wont fix
Dependencies: Superseder:
Assigned To: Nosy List: barry, giampaolo.rodola, njs, pitrou, vstinner, yselivanov
Priority: normal Keywords:

Created on 2017-05-07 18:24 by barry, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 1492 closed barry, 2017-05-07 18:26
Messages (17)
msg293205 - (view) Author: Barry A. Warsaw (barry) * (Python committer) Date: 2017-05-07 18:24
Over in https://github.com/aio-libs/aiosmtpd we have a Controller class which is very handy for testing and other cases.  I realized that this isn't really aiosmtpd specific, and with just a few tweaks it could be appropriate for the stdlib.

I have a branch ready for a pull request.  This is the tracking/discussion issue.
msg293260 - (view) Author: Nathaniel Smith (njs) * (Python committer) Date: 2017-05-08 23:06
Looks interesting! What's the advantage over running the server and the test in the same loop? The ability to use blocking operations in the tests, and to re-use an expensive-to-start server over multiple tests? (I've mostly used threads in tests to run blocking code for interoperability testing, and kept the async code in the main thread, so this is a bit novel to me.)
msg293264 - (view) Author: Barry A. Warsaw (barry) * (Python committer) Date: 2017-05-09 00:44
On May 08, 2017, at 11:06 PM, Nathaniel Smith wrote:

>Looks interesting! What's the advantage over running the server and the test
>in the same loop? The ability to use blocking operations in the tests, and to
>re-use an expensive-to-start server over multiple tests?

So, the ability to re-use expensive-to-start servers is definitely one of the
advantages.  I use nose2's layers, but test fixtures would fall into the same
category.

As for running the server and tests in the same loop; I haven't tried that,
but it seems like it would be more complicated to set up (maybe that's
dependent on the code under test).  More important is that I want to block the
tests until the server starts up.  I'm not sure (haven't tried) whether that's
possible when running them all in the same loop.

One other use case I have is for the LMTP server in Mailman 3.  The controller
turns out to be useful based on the start/stop framework for MM3 "runners".
That's probably strictly doable without the controller, but it's convenient,
readable, and a nice reuse.
msg293464 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2017-05-11 00:09
I'm not sure that Controller is generic enough to be part of asyncio.

I'm not sure about the cancellation of all pending tasks on stop().

Why not starting by putting this class in a library to mature its API?
msg293465 - (view) Author: Barry A. Warsaw (barry) * (Python committer) Date: 2017-05-11 00:26
On May 11, 2017, at 12:09 AM, STINNER Victor wrote:

>Why not starting by putting this class in a library to mature its API?

It's already part of aiosmtpd although not with the small amount of
generic-ness included here.  It's been useful and stable.  So this is just
refactoring out the aiosmtpd-ness of it.  It doesn't seem big enough to put
into yet another separate library.
msg294638 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2017-05-28 11:07
I think the API is too specific.  Instead of requiring hostname and port, why not let the user override setup and teardown coroutines?

In your case, this could be:

async def setup(self):
    self.server = await self.loop.create_server(...)

async def teardown(self):
    await self.server.wait_closed()
msg294666 - (view) Author: Barry A. Warsaw (barry) * (Python committer) Date: 2017-05-29 00:47
Hi Antoine,

On May 28, 2017, at 11:07 AM, Antoine Pitrou wrote:

>I think the API is too specific.

Can you elaborate?  What's too specific about it?  Do you have in mind a use
case where you wouldn't need to provide hostname and port?

>Instead of requiring hostname and port, why not let the user override setup
>and teardown coroutines?
>
>In your case, this could be:
>
>async def setup(self):
>    self.server = await self.loop.create_server(...)
>
>async def teardown(self):
>    await self.server.wait_closed()

It's certainly possible to factor those out so they could be overridden, I'm
just not sure why that's needed.
msg294675 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2017-05-29 07:07
> Can you elaborate?  What's too specific about it?  Do you have in mind a use case where you wouldn't need to provide hostname and port?

Any use case where setup is more elaborate than calling create_server(...).  For example I might write a UDP server.  Or a distributed system that listens to several ports at once, or launches a thread pool. etc.
msg294698 - (view) Author: Barry A. Warsaw (barry) * (Python committer) Date: 2017-05-29 15:23
On May 29, 2017, at 07:07 AM, Antoine Pitrou wrote:

>For example I might write a UDP server.  Or a distributed system that listens
>to several ports at once, or launches a thread pool. etc.

Thanks, those are nice motivational examples.
msg294725 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2017-05-29 23:42
I'm not sure we want this to be in asyncio: it's a very high-level object somewhere in between the low-level and the application level.  Some things off the top of my head that users will want from this API:

- detailed logging or hooks to implement it
- hooks on thread start / stop
- coroutines to run before starting the server
- coroutines to run before stopping the loop
- custom undhandled exceptions handlers
- type of the server created: TCP/UDP/Unix
- ability to configure SSL
- etc

Since asyncio is no longer provisional, it won't be possible to evolve the API in bugfix releases, which will likely make it impossible to use for many users until 3.8.

In general, the advice for things like this is to put them on PyPI, gather some feedback, and sort out the API details.

-1 to add this in 3.7 in its current state.

P.S. It would be interesting to try to evolve the idea a bit further: it would be interesting if Controller was a high-level description of a service and we had a separate concept of ControllerRunner to run Controllers in threads, processes and corotuines.  Maybe build something like erlang supervisor trees out of them.
msg294767 - (view) Author: Barry A. Warsaw (barry) * (Python committer) Date: 2017-05-30 16:56
On May 29, 2017, at 11:42 PM, Yury Selivanov wrote:

>- detailed logging or hooks to implement it
>- hooks on thread start / stop
>- coroutines to run before starting the server
>- coroutines to run before stopping the loop
>- custom undhandled exceptions handlers
>- type of the server created: TCP/UDP/Unix
>- ability to configure SSL
>- etc
>
>P.S. It would be interesting to try to evolve the idea a bit further: it
>would be interesting if Controller was a high-level description of a service
>and we had a separate concept of ControllerRunner to run Controllers in
>threads, processes and corotuines.  Maybe build something like erlang
>supervisor trees out of them.

There's also value in doing one simple thing that adds convenience for users.
I don't personally have any interest in building something as elaborate as the
above, but I've used the simple idea here several times in different projects.
msg294790 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2017-05-30 21:57
Barry: would you be ok to start by adding Controller to asyncio.test_utils,
and wait later to expose it in the public API?
msg294794 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2017-05-30 22:36
> STINNER Victor added the comment:
> 
> Barry: would you be ok to start by adding Controller to asyncio.test_utils,
> and wait later to expose it in the public API?

Sorry, but we are going to deprecate and remove test_utils soon. It's a bunch of internal unit test helpers used privately by asyncio. They are not documented and not supported. Now that asyncio repo is in CPython repo and we don't release it on its own, i see no reason to keep test_utils (we can move it to 'Lib/test'.

Again, the natural way of something like Controller to end up in asyncio is to either go through full PEP process, or live some time on PyPI and prove to be useful.
msg294798 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2017-05-30 23:10
> There's also value in doing one simple thing that adds convenience for users.
> I don't personally have any interest in building something as elaborate as the
> above, but I've used the simple idea here several times in different projects.

Yeah, I understand.  I totally see how Controller can be useful, moreover,
I've implemented something like it bunch of times (usually for running
tests).  But with Guido no longer actively involved with asyncio, and me
being the only one who is actively working on/supporting asyncio, I 
think we need to go through the full PEP process when we want to add 
new APIs.  bpo simply does not provide enough exposure: guys from
Twisted and Tornado, for instance, won't see this discussion.

I'd be glad to assist you with the PEP though!
msg294801 - (view) Author: Barry A. Warsaw (barry) * (Python committer) Date: 2017-05-30 23:55
On May 30, 2017, at 10:36 PM, Yury Selivanov wrote:

>Again, the natural way of something like Controller to end up in asyncio is
>to either go through full PEP process, or live some time on PyPI and prove to
>be useful.

A PEP feels like overkill; we don't require PEPs for every addition to an
existing stdlib module or package.  My worry too is that a PEP tends to evoke
endless bikeshedding.

I appreciate that you want to be careful not to saddle asyncio with too much
baggage, or that you don't feel Controller is significant enough to generalize
and put in the package.  Perhaps a middle ground would be to label parts of
the asyncio API provisional, and add Controller to that?
msg294803 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2017-05-31 00:19
> I appreciate that you want to be careful not to saddle asyncio with too much
> baggage, or that you don't feel Controller is significant enough to generalize
> and put in the package.  Perhaps a middle ground would be to label parts of
> the asyncio API provisional, and add Controller to that?

Thing is, when asyncio was provisional, we still couldn't significantly 
change it or break it.  Never in asyncio stdlib era had we removed or 
redesigned some APIs.  Only small additions and bug fixes.  And honestly,
maintaining something provisional and changing it in bugfix releases
is too much stress: we managed to break `loop.connect_socket` once
because nobody tests bugfix RCs.  It was broken for ~6 months.

IMHO: the design of Controller is currently incomplete (see one of my
previous comments).  Even in this thread two other core devs raised a 
question that the API isn't generic enough to be part of asyncio.
Right now it's not flexible and tailored for one specific use case. 
Should the user need slightly more, they will have to copy/paste it, 
or, worse, inherit from it and use its private APIs.
msg311558 - (view) Author: Barry A. Warsaw (barry) * (Python committer) Date: 2018-02-03 14:48
There doesn't seem to be much appetite for this in the stdlib, so closing.  It'll just have to live in a third party module.
History
Date User Action Args
2022-04-11 14:58:46adminsetgithub: 74486
2018-02-03 14:48:37barrysetstatus: open -> closed
resolution: wont fix
messages: + msg311558

stage: resolved
2017-05-31 00:19:50yselivanovsetmessages: + msg294803
2017-05-30 23:55:19barrysetmessages: + msg294801
2017-05-30 23:10:09yselivanovsetmessages: + msg294798
2017-05-30 22:36:38yselivanovsetmessages: + msg294794
2017-05-30 21:57:00vstinnersetmessages: + msg294790
2017-05-30 16:56:07barrysetmessages: + msg294767
2017-05-29 23:42:32yselivanovsetmessages: + msg294725
2017-05-29 15:23:56barrysetmessages: + msg294698
2017-05-29 07:07:38pitrousetmessages: + msg294675
2017-05-29 00:47:21barrysetmessages: + msg294666
2017-05-28 11:07:59pitrousetnosy: + pitrou, yselivanov, giampaolo.rodola
messages: + msg294638
2017-05-11 00:26:59barrysetmessages: + msg293465
2017-05-11 00:09:41vstinnersetnosy: + vstinner
messages: + msg293464
2017-05-09 00:44:57barrysetmessages: + msg293264
2017-05-08 23:06:28njssetnosy: + njs
messages: + msg293260
2017-05-07 18:26:25barrysetpull_requests: + pull_request1594
2017-05-07 18:24:57barrycreate