Setting up readline

Readline Summary

Readline is implemented in the repository for a few reasons.

  1. It’s ubiquity in open source programming

  2. In addition, it’s utilized here to compensate for the weaknesses of other implementations.

    1. IPython’s auto-completion is entirely non-configurable and exceedingly slow.

      1. Frequently auto-completion will time out rather than serving a response

    2. prompt_toolkit has a rather rigid interface for it’s API. There are assert's all over the code base ensuring that the proper type is passed to a class constructor, in spite of the Python’s dynamically typed foundation.

Setting up autocompletion

IPython provides it’s users the ability to add in custom completers.

To take advantage of this ability one can call get_ipython and add their custom Completers via the method set_custom_completer().

However, all that the function does is a simple call to types.MethodTypes and then tacks it on to the list bound at get_ipython().Completer.matchers.

set_custom_completer(completer, pos=0)[source]

Adds a new custom completer function. The position argument (defaults to 0) is the index in the completers list where you want the completer to be inserted.

Source:

def set_custom_completer(self, completer, pos=0):
    """Adds a new custom completer function.

    The position argument (defaults to 0) is the index in the completers
    list where you want the completer to be inserted."""

    newcomp = types.MethodType(completer,self.Completer)
    self.Completer.matchers.insert(pos,newcomp)
In [1]: _ip = get_ipython()

In [2]: _ip.Completer.matchers
Out[2]: 
[<bound method IPCompleter.file_matches of <IPython.core.completer.IPCompleter object at 0x0000020407BB18C0>>,
 <bound method IPCompleter.magic_matches of <IPython.core.completer.IPCompleter object at 0x0000020407BB18C0>>,
 <bound method IPCompleter.dict_key_matches of <IPython.core.completer.IPCompleter object at 0x0000020407BB18C0>>]

Useful to see the magic_matches, dict_key_matches, file_matches and I think there’s supposed to be a python_matches or a keyword specific one.

Interestingly, there’s also a method on the completer that returns a IPython.utils.strdispatchers.StrDispatch class.

If so, then that would possibly make it the only use I’ve seen in the repo of this. Still unsure of what this class is though.

In [3]: from IPython.utils.strdispatch import StrDispatch

In [4]: what = StrDispatch()
>>> dis = StrDispatch()
>>> dis.add_s('hei',34, priority = 4)
>>> dis.add_s('hei',123, priority = 2)
>>> dis.add_re('h.i', 686)
>>> print(list(dis.flat_matches('hei')))
[123, 34, 686]
In [45]: _ip.Completer.custom_completers
Out[45]: <Strdispatch {
      'import': <IPython.core.hooks.CommandChainDispatcher object at 0x7f0acc3defa0>,
      'from': <IPython.core.hooks.CommandChainDispatcher object at 0x7f0acc3dee50>,
      '%aimport': <IPython.core.hooks.CommandChainDispatcher object at 0x7f0acc3de370>,
      '%run': <IPython.core.hooks.CommandChainDispatcher object at 0x7f0acc3def40>,
      '%cd': <IPython.core.hooks.CommandChainDispatcher object at 0x7f0acc3def10>,
      '%reset': <IPython.core.hooks.CommandChainDispatcher object at 0x7f0acc3ded00>},
      }

In [46]: dir(_ip.Completer.custom_completers)
Out[46]: ['...',
         'add_re',
         'add_s',
         'dispatch',
         'flat_matches',
         'regexs',
         's_matches']

So hooks are implemented in the completion mechanism too? Dude I’m counting 3 nested classes and counting.

Autogenerated Readline Docs

In tandem with ../pyreadlineconfig.ini, keybindings are set up to attempt adding the standard readline bindings to Vim insert mode.

Extended Summary

The keybindings that prompt_toolkit provides are more powerful; however they’re substantially more complicated and for simple modifications of how to work with a line-editor buffer, this proves needlessly complex in addition to periodically leaving the entire terminal in an unusuable state.

class ViExternalEditor(line: AnyStr = None)[source][source]

Bases: object

If I’m not mistaken, there are typos that are causing Vi mode to fail.

03/22/2020: After pressing Esc to go into Normal mode, I tried invoking the external editor with k.

>>> def cd(arg):
>>>     pass

Let’s see what happens if we get rid of the space between file and (

Alright well in my defense this class was created quitely poorly; however, it’s a little too easy to initialize this sucker with no state.

__init__(line: AnyStr = None)[source][source]

Instantiate the editor vi.

Parameters

line (str) – Line that the user is typing.

run_editor(filename: Optional[Union[AnyStr, os.PathLike]] = None)[source][source]

It’s the goddamn ViExternalEditor default to Vim!

run_command(command: AnyStr)[source][source]
async async_run_command(command: AnyStr)[source][source]
class SimpleCompleter(options: Iterable)[source][source]

Bases: object

Completion mechanism that tracks candidates for different subcommands.

URL

https://pymotw.com/3/readline/

The SimpleCompleter class keeps a list of options that are candidates for auto-completion. The complete method for an instance is designed to be registered with readline as the source of completions.

__init__(options: Iterable)[source][source]
complete(text, state)[source][source]

Complete a user’s input.

The arguments are a text string to complete and a state value, indicating how many times the function has been called with the same text.

The function is called repeatedly with the state incremented each time. It should return a string if there is a candidate for that state value or None if there are no more candidates.

The implementation of complete here looks for a set of matches when state is 0, and then returns all of the candidate matches one at a time on subsequent calls.

complete(text, state)[source][source]

If no text in front of the cursor, return 4 spaces. Otherwise use the standard completer.

input_loop()[source][source]

Simulate a runnnig loop with a call to input.

Todo

Register the completer function.:

OPTIONS = ['start', 'stop', 'list', 'print']
readline.set_completer(SimpleCompleter(OPTIONS).complete)

Check what the API is to add a completer to ipython. I think it’s InteractiveShell`.set_custom_completer`.

gnu_readline_config()[source][source]

Readline configuration that’s incompatible with GNU readline.

Pyreadline doesn’t recognize a lot of the standard Readline functions, so to avoid emitting warnings upon startup, a handful of bindings were separated from readline_config.

readline_config()[source][source]

The main point of execution for the readline configs.

Should be cross-platform and independent of GNU readline or py readline.

This sets up history where the history file is saved to and calls atexit with the setup_historyfile and teardown_historyfile functions as parameters.

pyreadline_specific(rl=None)[source][source]

Utilize the pyreadline API.

:param pyreadline.rlmain.Readline:

Returns

  • Currently None. Seemingly works by modifying attributes on the instance

  • and relying on side effects.

setup_historyfile(filename: Optional[Union[AnyStr, os.PathLike]] = None)[source][source]

Add a history file to readline.

Parameters

filename (str, path, or os.Pathlike) – path to the history file. Internally converted to a pathlib.Path.

last_input() → AnyStr[source][source]

Returns the user’s last input.

Utilizes the raw_cell attribute found on an IPython.core.interactiveshell.ExecutionInfo instance.

add_last_input()[source][source]

Calls readline’s add_history function with last_input.