-
-
Notifications
You must be signed in to change notification settings - Fork 33.9k
bpo-32662: Implement Server.start_serving() and Server.serve_forever() #5312
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
asvetlov
left a comment
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.
Documentation is needed
Lib/asyncio/base_events.py
Outdated
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.
Backward incompatible change.
I pretty sure 99.9% of users newer modified the attribute but we should have a big warning about it.
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.
There was no point in modifying this attribute ever, but I agree, it needs to be documented. Will fix in the documentation.
Lib/asyncio/base_events.py
Outdated
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.
It is loop's private method call.
For me it's a bad smell.
Can we implement the feature with public API calls only?
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.
No, we can't, without re-architecturing half of create server machinery and exposing a new event loop public API. I have no interest in doing that.
And I don't see why this is a problem -- Loop and Server are designed to work together. Loop creates Server objects and knows everything about them. This is exactly the same relationship as between Transports and Loops.
Also you can't use Server object from uvloop and vice versa, so I don't see any problem here either.
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.
Ok, I don't insist. We have many tightly coupled classes in asyncio already.
Lib/asyncio/base_events.py
Outdated
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.
Parenthesis instead of backslash looks better.
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.
Fixed
Lib/asyncio/base_events.py
Outdated
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.
You forgot about self._serving_forever_fut.cancelled() check.
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.
Good catch!
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.
Wait... done() includes the cancelled() check, so adding it would have no effect... Cancelled futures are "done", so if a future is not "done" it means that it's not "cancelled".
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.
Sorry, you are right.
Lib/asyncio/base_events.py
Outdated
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.
Should check self._serving attribute too maybe.
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.
No, we document this method as idempotent as related to server already accepting connections. I.e. it's fine to do this:
await server.start_serving()
await server.serve_forever()The reason for this design is backwards-compatibility: loop.create_server by default returns a Server object that's already serving! So server.serve_forever() must be idempotent.
I found the utility in server.start_serving() when I was writing unittests. Sometimes we need to deterministically know when a server just started to listen, otherwise, for instance, it would be impossible for me to write a working loop.create_unix_server unittest.
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.
Thanks for explanation
Lib/asyncio/base_events.py
Outdated
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.
serve_forever() coroutine calls self.close() but doesn't wait for actual closing (await self.wait_closed()).
It is strange and complicated the usage: the proper code should always be:
try:
await server.serve_forever()
except Exception:
await server.wait_closed()
raise
Moreover server.sockets contains listening sockets only, long running accepted connections should be closed separately. I think it should be mentioned in documentation. Unfortunately I see no way how to fix the problem quickly (and the fix is out of scope of the PR, sure).
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.
serve_forever() coroutine calls self.close() but doesn't wait for actual closing (await self.wait_closed()).
OK, will do that.
Moreover server.sockets contains listening sockets only, long running accepted connections should be closed separately. I think it should be mentioned in documentation. Unfortunately I see no way how to fix the problem quickly (and the fix is out of scope of the PR, sure).
Yes, I agree :( Need to fix that in 3.8.
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.
Let's discuss it later. Actually at least in aiohttp we need stop listening first (server.close() does the job perfectly) and close all already accepted connections in controlled way (with signals for graceful shutdown and close timeout).
New methods: * Server.start_serving(), * Server.serve_forever(), and * Server.is_serving(). Add 'start_serving' keyword parameter to loop.create_server() and loop.create_unix_server().
asvetlov
left a comment
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.
Looks good.
Do you prefer to write documentation about new methods in this PR or want a separate one (documentation can be added in Python beta stage).
|
@asvetlov Andrew, I've pushed an update with docs and addressed review comments. Please take a look. |
asvetlov
left a comment
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.
LGTM
New methods:
Add 'start_serving' keyword parameter to loop.create_server() and
loop.create_unix_server().
https://bugs.python.org/issue32662