Jump through the hoops needed to ensure error reporting is done in case of early termination of kittens that use overlay ready notifications

This commit is contained in:
Kovid Goyal 2022-03-24 08:49:36 +05:30
parent c41405fd57
commit bfb8532c52
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
5 changed files with 36 additions and 23 deletions

View File

@ -27,6 +27,7 @@ from kitty.utils import (
from ..tui.handler import Handler, result_handler from ..tui.handler import Handler, result_handler
from ..tui.loop import Loop from ..tui.loop import Loop
from ..tui.operations import faint, styled from ..tui.operations import faint, styled
from ..tui.utils import report_unhandled_error
@lru_cache() @lru_cache()
@ -117,6 +118,8 @@ def render(text: str, current_input: str, all_marks: Sequence[Mark], ignore_mark
class Hints(Handler): class Hints(Handler):
overlay_ready_report_needed = True
def __init__(self, text: str, all_marks: Sequence[Mark], index_map: Dict[int, Mark], args: HintsCLIOptions): def __init__(self, text: str, all_marks: Sequence[Mark], index_map: Dict[int, Mark], args: HintsCLIOptions):
self.text, self.index_map = text, index_map self.text, self.index_map = text, index_map
self.alphabet = args.alphabet or DEFAULT_HINT_ALPHABET self.alphabet = args.alphabet or DEFAULT_HINT_ALPHABET
@ -159,7 +162,6 @@ class Hints(Handler):
def initialize(self) -> None: def initialize(self) -> None:
self.init_terminal_state() self.init_terminal_state()
self.draw_screen() self.draw_screen()
self.cmd.overlay_ready()
def on_text(self, text: str, in_bracketed_paste: bool = False) -> None: def on_text(self, text: str, in_bracketed_paste: bool = False) -> None:
changed = False changed = False
@ -513,11 +515,7 @@ def run(args: HintsCLIOptions, text: str, extra_cli_args: Sequence[str] = ()) ->
m.index = largest_index - m.index + offset m.index = largest_index - m.index + offset
index_map = {m.index: m for m in all_marks} index_map = {m.index: m for m in all_marks}
except Exception: except Exception:
import traceback report_unhandled_error()
traceback.print_exc()
input('Press Enter to quit.')
raise SystemExit(1)
return run_loop(args, text, all_marks, index_map, extra_cli_args) return run_loop(args, text, all_marks, index_map, extra_cli_args)
@ -678,9 +676,7 @@ def main(args: List[str]) -> Optional[Dict[str, Any]]:
text = '' text = ''
if sys.stdin.isatty(): if sys.stdin.isatty():
if '--help' not in args and '-h' not in args: if '--help' not in args and '-h' not in args:
print('You must pass the text to be hinted on STDIN', file=sys.stderr) report_unhandled_error('You must pass the text to be hinted on STDIN')
input(_('Press Enter to quit'))
return None
else: else:
text = sys.stdin.buffer.read().decode('utf-8') text = sys.stdin.buffer.read().decode('utf-8')
sys.stdin = open(os.ctermid()) sys.stdin = open(os.ctermid())
@ -688,18 +684,14 @@ def main(args: List[str]) -> Optional[Dict[str, Any]]:
opts, items = parse_hints_args(args[1:]) opts, items = parse_hints_args(args[1:])
except SystemExit as e: except SystemExit as e:
if e.code != 0: if e.code != 0:
print(e.args[0], file=sys.stderr) report_unhandled_error(e.args[0])
input(_('Press Enter to quit'))
return None return None
if items and not (opts.customize_processing or opts.type == 'linenum'): if items and not (opts.customize_processing or opts.type == 'linenum'):
print('Extra command line arguments present: {}'.format(' '.join(items)), file=sys.stderr) report_unhandled_error('Extra command line arguments present: {}'.format(' '.join(items)))
input(_('Press Enter to quit'))
try: try:
return run(opts, text, items) return run(opts, text, items)
except Exception: except Exception:
import traceback report_unhandled_error()
traceback.print_exc()
input(_('Press Enter to quit'))
return None return None
@ -753,7 +745,7 @@ def linenum_handle_result(args: List[str], data: Dict[str, Any], target_window_i
}[action])(*cmd) }[action])(*cmd)
@result_handler(type_of_input='screen-ansi', has_ready_notification=True) @result_handler(type_of_input='screen-ansi', has_ready_notification=Handler.overlay_ready_report_needed)
def handle_result(args: List[str], data: Dict[str, Any], target_window_id: int, boss: BossType) -> None: def handle_result(args: List[str], data: Dict[str, Any], target_window_id: int, boss: BossType) -> None:
if data['customize_processing']: if data['customize_processing']:
m = load_custom_processor(data['customize_processing']) m = load_custom_processor(data['customize_processing'])

View File

@ -44,6 +44,7 @@ class Handler:
use_alternate_screen = True use_alternate_screen = True
mouse_tracking = MouseTracking.none mouse_tracking = MouseTracking.none
terminal_io_ended = False terminal_io_ended = False
overlay_ready_report_needed = False
def _initialize( def _initialize(
self, self,
@ -219,7 +220,7 @@ class HandleResult:
def result_handler( def result_handler(
type_of_input: Optional[str] = None, type_of_input: Optional[str] = None,
no_ui: bool = False, no_ui: bool = False,
has_ready_notification: bool = False has_ready_notification: bool = Handler.overlay_ready_report_needed
) -> Callable[[Callable[..., Any]], HandleResult]: ) -> Callable[[Callable[..., Any]], HandleResult]:
def wrapper(impl: Callable[..., Any]) -> HandleResult: def wrapper(impl: Callable[..., Any]) -> HandleResult:

View File

@ -236,6 +236,7 @@ class Loop:
else: else:
self.asyncio_loop = asyncio.get_event_loop() self.asyncio_loop = asyncio.get_event_loop()
self.return_code = 0 self.return_code = 0
self.overlay_ready_reported = False
self.optional_actions = optional_actions self.optional_actions = optional_actions
self.read_buf = '' self.read_buf = ''
self.decoder = codecs.getincrementaldecoder('utf-8')('ignore') self.decoder = codecs.getincrementaldecoder('utf-8')('ignore')
@ -435,6 +436,8 @@ class Loop:
self.asyncio_loop.set_exception_handler(handle_exception) self.asyncio_loop.set_exception_handler(handle_exception)
handler._initialize(self._get_screen_size(), term_manager, schedule_write, self, debug, image_manager) handler._initialize(self._get_screen_size(), term_manager, schedule_write, self, debug, image_manager)
with handler: with handler:
if handler.overlay_ready_report_needed:
handler.cmd.overlay_ready()
self.asyncio_loop.add_reader( self.asyncio_loop.add_reader(
tty_fd, self._read_ready, handler, tty_fd) tty_fd, self._read_ready, handler, tty_fd)
self.asyncio_loop.add_writer( self.asyncio_loop.add_writer(
@ -467,9 +470,12 @@ class Loop:
term_manager.extra_finalize = b''.join(self.write_buf).decode('utf-8') term_manager.extra_finalize = b''.join(self.write_buf).decode('utf-8')
if tb is not None: if tb is not None:
report_overlay_ready = handler.overlay_ready_report_needed and not self.overlay_ready_reported
self.return_code = 1 self.return_code = 1
if not handler.terminal_io_ended: if not handler.terminal_io_ended:
self._report_error_loop(tb, term_manager) self._report_error_loop(tb, term_manager, report_overlay_ready)
def _report_error_loop(self, tb: str, term_manager: TermManager) -> None: def _report_error_loop(self, tb: str, term_manager: TermManager, overlay_ready_report_needed: bool) -> None:
self.loop_impl(UnhandledException(tb), term_manager) handler = UnhandledException(tb)
handler.overlay_ready_report_needed = overlay_ready_report_needed
self.loop_impl(handler, term_manager)

View File

@ -48,3 +48,17 @@ def human_size(
from math import log from math import log
exponent = min(int(log(size, 1024)), len(unit_list) - 1) exponent = min(int(log(size, 1024)), len(unit_list) - 1)
return format_number(size / 1024**exponent, max_num_of_decimals) + sep + unit_list[exponent] return format_number(size / 1024**exponent, max_num_of_decimals) + sep + unit_list[exponent]
def report_unhandled_error(msg: str = '') -> None:
' Report an unhandled exception also sending the overlay ready message to ensure kitten is visible '
from .operations import overlay_ready
print(end=overlay_ready())
if msg:
print(msg, file=sys.stderr)
cls, e, tb = sys.exc_info()
if not isinstance(e, (SystemExit, KeyboardInterrupt)):
import traceback
traceback.print_exc()
input('Press Enter to quit.')
raise SystemExit(1)

View File

@ -28,6 +28,7 @@ from ..tui.operations import (
clear_screen, colored, cursor, faint, set_line_wrapping, set_window_title, clear_screen, colored, cursor, faint, set_line_wrapping, set_window_title,
sgr, styled sgr, styled
) )
from ..tui.utils import report_unhandled_error
HEX, NAME, EMOTICONS, FAVORITES = 'HEX', 'NAME', 'EMOTICONS', 'FAVORITES' HEX, NAME, EMOTICONS, FAVORITES = 'HEX', 'NAME', 'EMOTICONS', 'FAVORITES'
favorites_path = os.path.join(config_dir, 'unicode-input-favorites.conf') favorites_path = os.path.join(config_dir, 'unicode-input-favorites.conf')
@ -567,8 +568,7 @@ def main(args: List[str]) -> Optional[str]:
cli_opts, items = parse_unicode_input_args(args[1:]) cli_opts, items = parse_unicode_input_args(args[1:])
except SystemExit as e: except SystemExit as e:
if e.code != 0: if e.code != 0:
print(e.args[0], file=sys.stderr) report_unhandled_error(e.args[0])
input(_('Press Enter to quit'))
return None return None
loop = Loop() loop = Loop()