Skip to content

Instantly share code, notes, and snippets.

@agrif
Created April 5, 2013 22:26
Show Gist options
  • Select an option

  • Save agrif/5323185 to your computer and use it in GitHub Desktop.

Select an option

Save agrif/5323185 to your computer and use it in GitHub Desktop.
import ctypes
import ctypes.util
import asyncore
import sys
import atexit
import code
import readline as pyreadline
import rlcompleter
readline = ctypes.CDLL(ctypes.util.find_library("readline"))
rl_cb_type = ctypes.CFUNCTYPE(None, ctypes.c_char_p)
readline.rl_set_prompt.argtypes = [ctypes.c_char_p]
readline.rl_set_prompt.restype = ctypes.c_int
readline.rl_callback_handler_install.argtypes = [ctypes.c_char_p, rl_cb_type]
readline.rl_callback_handler_install.restype = None
readline.rl_callback_handler_remove.argtypes = []
readline.rl_callback_handler_remove.restype = None
readline.add_history.argtypes = [ctypes.c_char_p]
readline.add_history.restype = None
readline.rl_callback_read_char.argtypes = []
readline.rl_callback_read_char.restype = None
readline.rl_redisplay.argtypes = []
readline.rl_redisplay.restypes = None
atexit.register(readline.rl_callback_handler_remove)
class ReadlineDispatcher(asyncore.file_dispatcher):
def __init__(self):
asyncore.file_dispatcher.__init__(self, sys.stdin)
self._prompt = "async>> "
self.encoding = "ascii"
self._cb_wrap = lambda l: self._rl_cb(l)
readline.rl_callback_handler_install(self._prompt.encode(self.encoding), rl_cb_type(self._cb_wrap))
def get_prompt(self):
return self._prompt
def set_prompt(self, prompt):
self._prompt = prompt
readline.rl_set_prompt(self._prompt.encode(self.encoding))
readline.rl_redisplay()
prompt = property(get_prompt, set_prompt)
def _cleanup(self):
readline.rl_set_prompt(b"")
readline.rl_callback_handler_remove()
print("")
self.close()
def _rl_cb(self, ln):
if ln == None:
self._cleanup()
elif len(ln) > 0:
readline.add_history(ln)
self.handle(ln.decode(self.encoding))
readline.rl_callback_handler_install(self._prompt.encode(self.encoding), rl_cb_type(self._cb_wrap))
def handle(self, ln):
print(ln)
def handle_read(self):
readline.rl_callback_read_char()
class CommandDispatcher(ReadlineDispatcher):
def __init__(self, locals=None, filename="<console>"):
print("Python " + sys.version + " on " + sys.platform + " (CommandDispatcher)")
print("""Type "help", "copyright", "credits" or "license" for more information.""")
pyreadline.parse_and_bind("tab: complete")
ReadlineDispatcher.__init__(self)
self.console = code.InteractiveConsole(locals, filename)
self.prompt = getattr(sys, 'ps1', '>>> ')
def handle(self, ln):
more_needed = self.console.push(ln)
if more_needed:
self.prompt = getattr(sys, 'ps2', '... ')
else:
self.console.resetbuffer()
self.prompt = getattr(sys, 'ps1', '>>> ')
@mcba

mcba commented Aug 13, 2018

Copy link
Copy Markdown

Thanks for this snippet, very helpful.
One issue is that the pointer to the callback handler can be garbage collected, leading to a segmentation fault. To fix this, keep a reference to the pointer (I used self.ref = rl_cb_type(self._cb_wrap) in the init method, and then used self.ref for both occasions that the callback handler is installed).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment