-
Notifications
You must be signed in to change notification settings - Fork 39
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
Termination of AsynchronousCli
task with task groups
#104
Comments
Interesting, thanks for the report! First of all, note that
However there's still a problem with the code as it is: it does not discriminate between diff --git a/aioconsole/console.py b/aioconsole/console.py
index ae75917..b88d0ca 100644
--- a/aioconsole/console.py
+++ b/aioconsole/console.py
@@ -63,6 +63,8 @@ class AsynchronousConsole(code.InteractiveConsole):
self.locals["print"] = self.print
self.locals["help"] = self.help
self.locals["ainput"] = self.ainput
+ # Internals
+ self._sigint_received = False
@functools.wraps(print)
def print(self, *args, **kwargs):
@@ -120,6 +122,7 @@ class AsynchronousConsole(code.InteractiveConsole):
self.buffer = []
def handle_sigint(self, task):
+ self._sigint_received = True
task.cancel()
if task._fut_waiter._loop is not self.loop:
task._wakeup(task._fut_waiter)
@@ -200,6 +203,11 @@ class AsynchronousConsole(code.InteractiveConsole):
else:
more = await self.push(line)
except asyncio.CancelledError:
+ # Not our cancellation
+ if not self._sigint_received:
+ raise
+ # Manage cancellation
+ self._sigint_received = False
self.write("\nKeyboardInterrupt\n")
await self.flush()
self.resetbuffer() I'll add this patch to an upcoming PR. Does that help? |
Thanks for the thorough response, much appreciated. I have applied your patch and done some testing with various combinations of
So at least for my application, def doneCallback(_):
raise KeyboardInterrupt
taskGroup.create_task(
AsynchronousCli(commands).interact(
stop=False,
handle_sigint=True,
),
name="cli",
).add_done_callback(doneCallback) Possibly, that could be added into |
Nice report! That juste gave me an idea, what about applying this patch and using diff --git a/aioconsole/console.py b/aioconsole/console.py
index b88d0ca..199671f 100644
--- a/aioconsole/console.py
+++ b/aioconsole/console.py
@@ -158,6 +158,8 @@ class AsynchronousConsole(code.InteractiveConsole):
if handle_sigint:
self.add_sigint_handler()
await self._interact(banner)
+ if stop:
+ raise SystemExit
# Exit
except SystemExit:
if stop:
@@ -166,8 +168,6 @@ class AsynchronousConsole(code.InteractiveConsole):
finally:
if handle_sigint:
self.remove_sigint_handler()
- if stop:
- self.loop.stop()
async def _interact(self, banner=None):
# Get ps1 and ps2 Would this be the behavior you're looking for? I'd like to get rid of this |
With both patches applied and The "problem" is in the way So basically, if the CLI raises async def main():
try:
async with asyncio.TaskGroup() as tg:
tg.create_task(AsynchronousCli(commands).interact(), name="cli")
except SystemExit:
pass
if __name__ == "__main__":
asyncio.run(main()) In that case, I think I would prefer the CLI task just raising |
I investigated a bit more and I don't fully agree with what you described. From what I can tell I would argue that this is a bug in asyncio since the documentation clearly states that those exceptions should be handled properly:
I'll report it on the python tracker, since it can be reproduced very easily: async def raise_system_exit():
raise SystemExit
async def main():
async with asyncio.TaskGroup() as tg:
tg.create_task(raise_system_exit()) In the meantime, I think the workaround you found is fine: async def main():
try:
async with asyncio.TaskGroup() as tg:
tg.create_task(AsynchronousCli(commands).interact(), name="cli")
except SystemExit:
pass
if __name__ == "__main__":
asyncio.run(main())
I'm not sure we want to do that, since
class StopTaskGroup(Exception):
pass
async def cli():
await AsynchronousCli(commands).interact(stop=False)
# The CLI has exited, do something about it
...
# Maybe cancel the current task group
raise StopTaskGroup
async def main():
try:
async with asyncio.TaskGroup() as tg:
tg.create_task(cli(), name="cli")
while True:
await asyncio.sleep(1)
# The CLI has exited and cancelled the task group
except* StopTaskGroup:
# Maybe do something else here
... Hope that makes sense, thank you for your valuable feedback :) |
I agree, I was wondering why With the solutions and workarounds we have discussed, I am able to proceed so feel free to close this issue once you are ready - and thanks for the help! :) |
Great, I went ahead and merged #105. It'll be available in the next release :) Also I reported the asyncio issue here: python/cpython#101515 |
Version v0.6.0 is out 🎉 |
I'm trying to use aioconsole and AsynchronousCli in my asyncio based application. In my application, I use task groups for creating tasks, including my
AsynchronousCli
-based cli. Something like:Now, the command line part is working just fine, no problems. But I cannot figure out how to properly terminate the application. Without aioconsole,
asyncio.run(main())
install sigint handlers so thatctrl+c
terminates the program - as expected. Butaioconsole
capturesctrl+c
and doesn't terminate. If I use the built-inexit
command, the application terminates but with an exception regardingTask exception was never retrieved
.I'm not sure, but it seems to me like the problem is in the way the
_interact()
of theAsynchronousConsole
class is dealing withasyncio.CancelledError
, e.g. just ignoring it completely. For my needs, the following patch works:But the unit tests now fail so I'm not sure if that is the right approach.
The text was updated successfully, but these errors were encountered: