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.Server class documented but not exported
Type: behavior Stage:
Components: asyncio Versions: Python 3.8, Python 3.7
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: asvetlov Nosy List: Ron Frederick, asvetlov, gvanrossum, vstinner, yselivanov
Priority: normal Keywords:

Created on 2015-11-27 19:28 by Ron Frederick, last changed 2022-04-11 14:58 by admin.

Messages (14)
msg255476 - (view) Author: Ron Frederick (Ron Frederick) * Date: 2015-11-27 19:28
The asyncio documentation defines the class 'asyncio.Server' in section 18.5.1.15. However, this class is not exported by asyncio. It is defined in base_events.py but not in the __all__ list. The only class exported at the asyncio top level is asyncio.AbstractServer, defined in events.py.

I think the documentation should match the exports. Either Server should be exported out of base_events.py, or the documentation should only refer to asyncio.AbstractServer.
msg255477 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2015-11-27 19:32
Let's not export the Server class -- in uvloop, for instance, I have a completely different internal implementation of Server (I can't use the one from asyncio).
msg255497 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015-11-27 22:18
Yeah, I recall this point. The doc points to Server, but Guido didn't
want to expose "implementation details" of Server like sockets.
Different implementations of event loops (Yury gave the good example
of uvloop based on libuv) don't give (direct) access to sockets.

The problem is maybe that we try to document two different things at
the same place: asyncio "portable" API (the PEP 3156), and the
implementation of "default" event loops.

Even if it's not possible to directly use server sockets, it's kind of
useful to get them. What do you think?

Server.sockets is documented at:
https://docs.python.org/dev/library/asyncio-eventloop.html#asyncio.Server.sockets

Server.sockets is explicitly documented at:
https://docs.python.org/dev/library/asyncio-eventloop.html#asyncio.BaseEventLoop.create_server

I would prefer to keep the Server.sockets doc, but explain better that
it's an "implementation detail". We already have something similar for
"CPython" implementation details in the doc. Examples:
- https://docs.python.org/dev/library/sys.html#sys._debugmallocstats
- https://docs.python.org/dev/library/sys.html#sys._getframe
- https://docs.python.org/dev/library/dis.html#module-dis
- etc.

What do you think of reusing the ".. impl-detail:" markup? Or do you
prefer to write a different kind of box for asyncio? Or just not box
and a simple sentence?
msg255502 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2015-11-27 22:55
> Yeah, I recall this point. The doc points to Server, but Guido didn't
want to expose "implementation details" of Server like sockets.
Different implementations of event loops (Yury gave the good example
of uvloop based on libuv) don't give (direct) access to sockets.

I actually found a way to extract a fileno (there is an API for that).

But Ron suggested to actually export the Server class, so that it's available as 'asyncio.Server'. And I think that *that* is a bad idea, since the actual implementation of Server is tied to event loop and transports, which may be implemented differently (as in uvloop)
msg255505 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015-11-27 22:59
Yury Selivanov added the comment:
> But Ron suggested to actually export the Server class, so that it's available as 'asyncio.Server'. And I think that *that* is a bad idea, (...)

I know, but IMHO the issue is wider than just exporting or not the
symbol. Right now, create_server() doc explicitly says that the list
of sockets is available in server.sockets attribute. We should mention
that it's not always the case, it's only available on "some"
implementations of event loops.
msg308827 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2017-12-20 21:37
There's no point in exporting the Server class as "asyncio.Server".  It'd created and managed by the event loop, and there's no point to instantiate it in user code.

We can add an abstract base class AbstractServer though, similar to AbstractFuture and AbstractTask proposed in https://bugs.python.org/issue32364.

This is a low priority feature and can probably wait till 3.8 though.
msg308840 - (view) Author: Ron Frederick (Ron Frederick) * Date: 2017-12-21 02:54
In my original report, I suggested _either_ exporting asyncio.Server since that's what was documented elsewhere _OR_ adding AbstractServer to the documentation and changing existing references to asyncio.Server to point at asyncio.AbstractServer instead, as that symbol is already exported but is not currently documented. There appear to be good reasons for hiding the implementation details of Server, and I'm good with that. I just think the docs and the exports need to agree on one or the other.

I originally had references to AbstractServer in my project docs, but changed them to Server after seeing that only Server was currently in the public Python documentation.

As for references to the "sockets" member of Server, I'm not currently relying on that in my code. I have code that can handle getaddrinfo() returning more than one address to listen on, but I create separate Server instances for each individual address right now, as this gives me better control when a caller asks to listen on a dynamic port on multiple interfaces at once.
msg308841 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2017-12-21 03:08
Server.sockets property will be a public API and defined in the AbstractServer, so you can start using them. Just don't try to call socket.send() or socket.accept() as that will break things.
msg308843 - (view) Author: Ron Frederick (Ron Frederick) * Date: 2017-12-21 03:32
Thanks. Unfortunately, in the case of the way SSH listeners with dynamic ports, the protocol only allows a single port number to be returned. So, when binding on multiple interfaces there's a requirement that the SAME port be chosen for all of the socket bindings, which can't easily be done today with a single asyncio.Server object. That's a bit off-topic for this issue, though.

Regarding exposing the sockets, it would never really make sense to directly call send() on a listening socket, and I can see why it also wouldn't make sense to allow calling accept(). I'm really not sure there's ANY call other than getsockaddr() that really makes sense here, though, so perhaps it would be better to evolve the API in AbstractServer into returning a list of sockaddr tuples rather than sockets.
msg308845 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2017-12-21 03:45
>  I'm really not sure there's ANY call other than getsockaddr() that really makes sense here, though, so perhaps it would be better to evolve the API in AbstractServer into returning a list of sockaddr tuples rather than sockets.

Right, that's why uvloop exposes only SocketLike objects, that have getsockaddr() and getsockopt(), but raise when sock.send() is called.  Maybe we'll do the same thing in asyncio.
msg308848 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2017-12-21 03:54
>  _OR_ adding AbstractServer to the documentation and changing existing references to asyncio.Server to point at asyncio.AbstractServer instead, as that symbol is already exported but is not currently documented.

I completely missed the fact that we already export AbstractServer and that the documentation doesn't actually refer to it, and that it has less APIs than Server.  Thanks for reminding me of that, Ron.  I'll try to address the docs part before 3.7 comes out.

> So, when binding on multiple interfaces there's a requirement that the SAME port be chosen for all of the socket bindings, which can't easily be done today with a single asyncio.Server object. That's a bit off-topic for this issue, though.

And why do you need a single Server object?
msg308849 - (view) Author: Ron Frederick (Ron Frederick) * Date: 2017-12-21 04:09
That'd be great if you could add AbstractServer to the 3.7 docs - thanks!

Regarding the other issue, I'm already set up to use multiple asyncio.Server objects to deal with the shared port issue and it's working fine. It did mean duplicating a handful of lines of code from asyncio to iterate over the getaddrinfo() results (creating one asyncio.Server per sockaddr instead of letting asyncio create a single asyncio.Server with multiple listening sockets) and there are some other minor details like cleaning up if the kernel-selected dynamic port chosen for the first interface is not actually free on all other interfaces, but I'm not actually looking for any changes in how that's working right now.

Here's the code I'm referring to if you're curious:

https://github.com/ronf/asyncssh/blob/master/asyncssh/listener.py#L192
msg308850 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2017-12-21 04:19
> It did mean duplicating a handful of lines of code from asyncio to iterate over the getaddrinfo() results (creating one asyncio.Server per sockaddr instead of letting asyncio create a single asyncio.Server with multiple listening sockets) and there are some other minor details like cleaning up if the kernel-selected dynamic port chosen for the first interface is not actually free on all other interfaces, but I'm not actually looking for any changes in how that's working right now.

I think you're doing it the right way.  It's a rather niche requirement, so I don't think we should make create_server to somehow support this use case.

asyncssh looks absolutely amazing, btw.
msg308868 - (view) Author: Ron Frederick (Ron Frederick) * Date: 2017-12-21 12:38
> I think you're doing it the right way.  It's a rather niche requirement, so I don't think we should make create_server to somehow support this use case.

Agreed.


> asyncssh looks absolutely amazing, btw.

Thanks so much!
History
Date User Action Args
2022-04-11 14:58:24adminsetgithub: 69935
2017-12-21 12:38:39Ron Fredericksetmessages: + msg308868
2017-12-21 04:19:02yselivanovsetmessages: + msg308850
2017-12-21 04:09:50Ron Fredericksetmessages: + msg308849
2017-12-21 03:54:34yselivanovsetmessages: + msg308848
versions: + Python 3.7
2017-12-21 03:45:43yselivanovsetmessages: + msg308845
2017-12-21 03:32:58Ron Fredericksetmessages: + msg308843
2017-12-21 03:08:13yselivanovsetmessages: + msg308841
2017-12-21 02:54:20Ron Fredericksetmessages: + msg308840
2017-12-20 21:37:43yselivanovsetmessages: + msg308827
versions: + Python 3.8, - Python 3.6, Python 3.7
2017-12-20 20:57:36asvetlovsetversions: + Python 3.7, - Python 3.4, Python 3.5
2017-12-20 20:57:20asvetlovsetassignee: asvetlov

nosy: + asvetlov
2015-11-27 22:59:16vstinnersetmessages: + msg255505
2015-11-27 22:55:07yselivanovsetmessages: + msg255502
2015-11-27 22:18:54vstinnersetmessages: + msg255497
2015-11-27 19:32:26yselivanovsetmessages: + msg255477
2015-11-27 19:28:36Ron Frederickcreate