@@ -571,12 +571,23 @@ def _pre_run(self, pre_run: Optional[Callable[[], None]] = None) -> None:
571
571
c ()
572
572
del self .pre_run_callables [:]
573
573
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 :
575
576
"""
576
577
Run the prompt_toolkit :class:`~prompt_toolkit.application.Application`
577
578
until :meth:`~prompt_toolkit.application.Application.exit` has been
578
579
called. Return the value that was passed to
579
580
: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.
580
591
"""
581
592
assert not self ._is_running , 'Application is already running.'
582
593
@@ -709,22 +720,32 @@ def flush_input() -> None:
709
720
710
721
async def _run_async2 () -> _AppResult :
711
722
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 )
728
749
729
750
return await _run_async2 ()
730
751
@@ -733,46 +754,36 @@ def run(self, pre_run: Optional[Callable[[], None]] = None,
733
754
"""
734
755
A blocking 'run' call that waits until the UI is finished.
735
756
757
+ :param pre_run: Optional callable, which is called right after the
758
+ "reset" of the application.
736
759
:param set_exception_handler: When set, in case of an exception, go out
737
760
of the alternate screen and hide the application, display the
738
761
exception, and wait for the user to press ENTER.
739
762
"""
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 ('\n Unhandled 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 ('\n Unhandled 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 ())
776
787
777
788
def create_background_task (self , coroutine : Awaitable [None ]) -> 'asyncio.Task[None]' :
778
789
"""
0 commit comments