Skip to content

Commit d11a2f7

Browse files
committed
Improve async_commands.py example by cleaning up uncancelled async tasks on interrupt
Also: - Added return param to a docstring
1 parent d6117d5 commit d11a2f7

File tree

2 files changed

+14
-3
lines changed

2 files changed

+14
-3
lines changed

cmd2/cmd2.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5690,6 +5690,7 @@ def cmdloop(self, intro: RenderableType = '') -> int:
56905690
- exit code
56915691
56925692
:param intro: if provided this overrides self.intro and serves as the intro banner printed once at start
5693+
:return: exit code
56935694
"""
56945695
# cmdloop() expects to be run in the main thread to support extensive use of KeyboardInterrupts throughout the
56955696
# other built-in functions. You are free to override cmdloop, but much of cmd2's features will be limited.

examples/async_commands.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,31 @@ def _get_event_loop() -> asyncio.AbstractEventLoop:
3333
return _event_loop
3434

3535

36-
def with_async_loop(func: Callable[..., Any]) -> Callable[..., Any]:
37-
"""Decorator to run a command method asynchronously in a background thread.
36+
def with_async_loop(func: Callable[..., Any], cancel_on_interrupt: bool = True) -> Callable[..., Any]:
37+
"""Decorate an async ``do_*`` command method to give it access to the event loop.
38+
3839
3940
This decorator wraps a do_* command method. When the command is executed,
4041
it submits the coroutine returned by the method to a background asyncio loop
4142
and waits for the result synchronously (blocking the cmd2 loop, as expected
4243
for a synchronous command).
44+
45+
:param func: do_* method to wrap
46+
:param cancel_on_interrupt: if True, cancel any running async task on an interrupt;
47+
if False, leave any async task running
4348
"""
4449

4550
@functools.wraps(func)
4651
def wrapper(self: cmd2.Cmd, *args: Any, **kwargs: Any) -> Any:
4752
loop = _get_event_loop()
4853
coro = func(self, *args, **kwargs)
4954
future = asyncio.run_coroutine_threadsafe(coro, loop)
50-
return future.result()
55+
try:
56+
return future.result()
57+
except KeyboardInterrupt:
58+
if cancel_on_interrupt:
59+
future.cancel()
60+
raise
5161

5262
return wrapper
5363

0 commit comments

Comments
 (0)