Skip to content
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

Client close function #112

Closed
wants to merge 2 commits into from

Conversation

majordoobie
Copy link
Collaborator

#111
[Added coc.Client.close_client coroutine] Added coroutine to allow us…ers to close the client connection from within a async function such as async main(). This is useful to allow users to use the asyncio.run() wrapper properly

…ers to close the client connection from within a async function such as async main(). This is useful to allow users to use the `asyncio.run()` wrapper properly
coc/client.py Show resolved Hide resolved
Comment on lines +281 to 287
def close_loops(self) -> None:
"""Closes the HTTP connection
"""
LOG.info("Clash of Clans client logging out...")
self.dispatch("on_client_close")
self.loop.run_until_complete(self.http.close())
self.loop.close()
Copy link
Contributor

@r-priyam r-priyam Aug 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def close_loops(self) -> None:
"""Closes the HTTP connection
"""
LOG.info("Clash of Clans client logging out...")
self.dispatch("on_client_close")
self.loop.run_until_complete(self.http.close())
self.loop.close()
def close_loops(self) -> None:
"""Closes the HTTP connection"""
self.loop.run_until_complete(self.http.close())
self.loop.close()

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you omitting the dispatch?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you omitting the dispatch?

because this is actually closing the asyncio loop and not doing what the dispatch name says. The thing that dispatch name says is actually being one by the close_client method

Copy link
Owner

@mathsman5133 mathsman5133 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I think you've found the proper way to fix all these loop cleanup issues :)

await client.close_client()

if __name__ == '__main__':
try:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line should probably be indented

@@ -278,14 +278,20 @@ def login_with_keys(self, *keys: str) -> None:

LOG.debug("HTTP connection created. Client is ready for use.")

def close(self) -> None:
def close_loops(self) -> None:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only little thing I'm worried about is making sure we don't break stuff in a non-major version, ie. I think (please correct me if I'm wrong) but client.close() now doesn't exist.

Long-term, should we always be creating and closing clients with await stuff and letting asyncio handle running async stuff in non-async places? I wonder whether that would solve all our issues, and whether I'm trying to do something that isn't really my job.

So change .close() to be an async function and either make coc.login() an async function or remove it completely.

Like you showed, it's easier (and better if it doesn't have all those loop cleanup issues) to leave it to asyncio.run() to run it all. Thoughts? I'm kinda just rambling. Either way, I think we can add this stuff with different names now, but need to keep the existing functions until v3 (maybe adding a depreciated warning)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

asyncio.run() requieres python 3.7 or newer, we would need to add that somewhere too.

The big downside with asyncio.run() is most likely the usage of a coc client with another aiohttp session, like a discord bot. We should definitely test that before we discussing this solution further.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely, and very true re. python 3.7

I guess the asyncio.run stuff would only be for people running it as a standalone script thing - else the current coc.login is fine (and doesn't have the loop cleanup issues / people don't care as much because there's a hundred other warnings when you close a bot). I think the real issue is client.close(), not login so much. .close() isn't really a thing for bots, and never works for non-bots as intended.

I don't think people generally have ever had many issues with login in it's current form, so maybe it's best to leave that

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you actually mentioned it I personally do not think it is on your library to handle the cleanup because it will create a race condition with other libraries that also depend on sessions to be cleaned out.

This is the best way I have found to handle cleanup of both coc.py sessions and asyncpg and that's to drop into "manual" loops instead of the abstracted run only to be able to properly clean up after myself. The asyncio.run does do the generator cleanup but it will not properly clean up sessions. Which may or may not be an issue.

https://github.com/majordoobie/PantherLily/blob/main/main.py#L117

@mathsman5133 mathsman5133 mentioned this pull request Aug 19, 2022
@doluk
Copy link
Collaborator

doluk commented Aug 19, 2022

the example code with the modifications above produces still an error.

Exception ignored in: <function _ProactorBasePipeTransport.__del__ at 0x0000018108223820>
Traceback (most recent call last):
  File "C:\Users\doluk\AppData\Local\Programs\Python\Python39\lib\asyncio\proactor_events.py", line 116, in __del__
    self.close()
  File "C:\Users\doluk\AppData\Local\Programs\Python\Python39\lib\asyncio\proactor_events.py", line 108, in close
    self._loop.call_soon(self._call_connection_lost, None)
  File "C:\Users\doluk\AppData\Local\Programs\Python\Python39\lib\asyncio\base_events.py", line 746, in call_soon
    self._check_closed()
  File "C:\Users\doluk\AppData\Local\Programs\Python\Python39\lib\asyncio\base_events.py", line 510, in _check_closed
    raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed

According to this aiohttp issue, this aiohttp issue and this httpx issue the problem is the underlaying SSL transport. Currently the only way to avoid this error message is to give the SSL transport time to shutdown by adding a time.sleep(0.25) or await asyncio.sleep(0.25) in coc.http.HTTPClient.close(). With that, no exception gets raised executing the sample code.
Another fix for that would be switch from aiohttp to trio as mentioned in the issues, but yeah...

The example bot is also affected by the same error but not as you may think. The discord bot itself has the same problem when using await bot.close(). Here also the "same" workaround time.sleep(0.25) solves the problem.
The added line in coc.http.HTTPClient.close() does not affect the bot (and to be honest most of us don't even logout the coc client). So we should be able to merge this fix with the described fixed without causing further issues.

doluk added a commit to doluk/coc.py that referenced this pull request Aug 19, 2022
changes described [here](mathsman5133#112 (comment))
@doluk
Copy link
Collaborator

doluk commented Aug 19, 2022

as soon as @majordoobie merges the pr we should be good to merge this one too

@majordoobie
Copy link
Collaborator Author

Moved to #116

@majordoobie majordoobie mentioned this pull request Aug 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants