8000 Set exception handler from within Application.run_async. · cool-RR/python-prompt-toolkit@8336f69 · GitHub
[go: up one dir, main page]

Skip to content

Commit 8336f69

Browse files
Set exception handler from within Application.run_async.
1 parent 62a065c commit 8336f69

File tree

1 file changed

+64
-53
lines changed

1 file changed

+64
-53
lines changed

prompt_toolkit/application/application.py

Lines changed: 64 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -571,12 +571,23 @@ def _pre_run(self, pre_run: Optional[Callable[[], None]] = None) -> None:
571571
c()
572572
del self.pre_run_callables[:]
573573

574-
async def run_async(self, pre_run: Optional[Callable[[], None]] = None) -> _AppResult:
574+
async def run_async(self, pre_run: Optional[Callable[[], None]] = None,
575+
set_exception_handler: bool = True) -> _AppResult:
575576
"""
576577
Run the prompt_toolkit :class:`~prompt_toolkit.application.Application`
577578
until :meth:`~prompt_toolkit.application.Application.exit` has been
578579
called. Return the value that was passed to
579580
:meth:`~prompt_toolkit.application.Application.exit`.
581+
582+
This is the main entry point for a prompt_toolkit
583+
:class:`~prompt_toolkit.application.Application` and usually the only
584+
place where the event loop is actually running.
585+
586+
:param pre_run: Optional callable, which is called right after the
587+
"reset" of the application.
588+
:param set_exception_handler: When set, in case of an exception, go out
589+
of the alternate screen and hide the application, display the
590+
exception, and wait for the user to press ENTER.
580591
"""
581592
assert not self._is_running, 'Application is already running.'
582593

@@ -709,22 +720,32 @@ def flush_input() -> None:
709720

710721
async def _run_async2() -> _AppResult:
711722
self._is_running = True
712-
with set_app(self):
713-
try:
714-
result = await _run_async()
715-
finally:
716-
# Wait for the background tasks to be done. This needs to
717-
# go in the finally! If `_run_async` raises
718-
# `KeyboardInterrupt`, we still want to wait for the
719-
# background tasks.
720-
await self.cancel_and_wait_for_background_tasks()
721-
722-
# Set the `_is_running` flag to `False`. Normally this
723-
# happened already in the finally block in `run_async`
724-
# above, but in case of exceptions, that's not always the
725-
# case.
726-
self._is_running = False
727-
return result
723+
724+
loop = get_event_loop()
725+
if set_exception_handler:
726+
previous_exc_handler = loop.get_exception_handler()
727+
loop.set_exception_handler(self._handle_exception)
728+
729+
try:
730+
with set_app(self):
731+
try:
732+
result = await _run_async()
733+
finally:
734+
# Wait for the background tasks to be done. This needs to
735+
# go in the finally! If `_run_async` raises
736+
# `KeyboardInterrupt`, we still want to wait for the
737+
# background tasks.
738+
await self.cancel_and_wait_for_background_tasks()
739+
740+
# Set the `_is_running` flag to `False`. Normally this
741+
# happened already in the finally block in `run_async`
742+
# above, but in case of exceptions, that's not always the
743+
# case.
744+
self._is_running = False
745+
return result
746+
finally:
747+
if set_exception_handler:
748+
loop.set_exception_handler(previous_exc_handler)
728749

729750
return await _run_async2()
730751

@@ -733,46 +754,36 @@ def run(self, pre_run: Optional[Callable[[], None]] = None,
733754
"""
734755
A blocking 'run' call that waits until the UI is finished.
735756
757+
:param pre_run: Optional callable, which is called right after the
758+
"reset" of the application.
736759
:param set_exception_handler: When set, in case of an exception, go out
737760
of the alternate screen and hide the application, display the
738761
exception, and wait for the user to press ENTER.
739762
"""
740-
loop = get_event_loop()
741-
742-
def run() -> _AppResult:
743-
coro = self.run_async(pre_run=pre_run)
744-
return get_event_loop().run_until_complete(coro)
745-
746-
def handle_exception(loop, context: Dict[str, Any]) -> None:
747-
" Print the exception, using run_in_terminal. "
748-
# For Python 2: we have to get traceback at this point, because
749-
# we're still in the 'except:' block of the event loop where the
750-
# traceback is still available. Moving this code in the
751-
# 'print_exception' coroutine will loose the exception.
752-
tb = get_traceback_from_context(context)
753-
formatted_tb = ''.join(format_tb(tb))
754-
755-
async def in_term() -> None:
756-
async with in_terminal():
757-
# Print output. Similar to 'loop.default_exception_handler',
758-
# but don't use logger. (This works better on Python 2.)
759-
print('\nUnhandled exception in event loop:')
760-
print(formatted_tb)
761-
print('Exception %s' % (context.get('exception'), ))
762-
763-
await _do_wait_for_enter('Press ENTER to continue...')
764-
ensure_future(in_term())
765-
766-
if set_exception_handler:
767-
# Run with patched exception handler.
768-
previous_exc_handler = loop.get_exception_handler()
769-
loop.set_exception_handler(handle_exception)
770-
try:
771-
return run()
772-
finally:
773-
loop.set_exception_handler(previous_exc_handler)
774-
else:
775-
return run()
763+
return get_event_loop().run_until_complete(self.run_async(pre_run=pre_run))
764+
765+
def _handle_exception(self, loop, context: Dict[str, Any]) -> None:
766+
"""
767+
Handler for event loop exceptions.
768+
This will print the exception, using run_in_terminal.
769+
"""
770+
# For Python 2: we have to get traceback at this point, because
771+
# we're still in the 'except:' block of the event loop where the
772+
# traceback is still available. Moving this code in the
773+
# 'print_exception' coroutine will loose the exception.
774+
tb = get_traceback_from_context(context)
775+
formatted_tb = ''.join(format_tb(tb))
776+
777+
async def in_term() -> None:
778+
async with in_terminal():
779+
# Print output. Similar to 'loop.default_exception_handler',
780+
# but don't use logger. (This works better on Python 2.)
781+
print('\nUnhandled exception in event loop:')
782+
print(formatted_tb)
783+
print('Exception %s' % (context.get('exception'), ))
784+
785+
await _do_wait_for_enter('Press ENTER to continue...')
786+
ensure_future(in_term())
776787

777788
def create_background_task(self, coroutine: Awaitable[None]) -> 'asyncio.Task[None]':
778789
"""

0 commit comments

Comments
 (0)
0