Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why don't I see errors from readline.set_completion_display_matches_hook?

Consider this code:

#!/usr/bin/env python3

from cmd import Cmd
import readline

class mycmd(Cmd):
    def match_display_hook(self, substitution, matches, longest_match_length):
        someNonexistentMethod()
        print()
        for match in matches:
            print(match)
        print(self.prompt, readline.get_line_buffer(), sep='', end='', flush=True)

    def do_crash(self, s):
        someNonexistentMethod()

    def do_quit(self, s):
        return True

if __name__ == '__main__':
    obj = mycmd()
    readline.set_completion_display_matches_hook(obj.match_display_hook)
    obj.cmdloop()

I expect to see NameError: name 'someNonexistentMethod' is not defined when I run that and hit TabTab. However, nothing actually seems to happen at all (the error does occur, so the other functions that would print the completion don't run; I just don't see the error). I do see the expected error when I run crash, so I know error handling works fine in the program overall, but is just broken inside of the set_completion_display_matches_hook callback. Why is this, and can I do something about it?

like image 436
Joseph Sible-Reinstate Monica Avatar asked Oct 20 '25 21:10

Joseph Sible-Reinstate Monica


1 Answers

Why?

I would guess that this is by design. According to rlcompleter docs:

Any exception raised during the evaluation of the expression is caught, silenced and None is returned.

See the rlcompleter source code for the rationale:

  • Exceptions raised by the completer function are ignored (and generally cause the completion to fail). This is a feature -- since readline sets the tty device in raw (or cbreak) mode, printing a traceback wouldn't work well without some complicated hoopla to save, reset and restore the tty state.

Workaround

As a workaround, for debugging, wrap your hook in a function that catches all exceptions (or write a function decorator), and use the logging module to log your stack traces to a file:

import logging
logging.basicConfig(filename="example.log", format='%(asctime)s %(message)s')

def broken_function():
    raise NameError("Hi, my name is Name Error")

def logging_wrapper(*args, **kwargs):
    result = None
    try:
        result = broken_function(*args, **kwargs)
    except Exception as ex:
        logging.exception(ex)
    return result

logging_wrapper()

This script runs successfully, and example.log contains both the log message and the stack trace:

2020-11-17 13:55:51,714 Hi, my name is Name Error
Traceback (most recent call last):
  File "/Users/traal/python/./stacktrace.py", line 12, in logging_wrapper
    result = function_to_run()
  File "/Users/traal/python/./stacktrace.py", line 7, in broken_function
    raise NameError("Hi, my name is Name Error")
NameError: Hi, my name is Name Error
like image 172
traal Avatar answered Oct 23 '25 12:10

traal