I'm trying to implement a replacement for raw_input() that would use a configurable text editor like vim as the interface to to the user.
The ideal workflow would be like this:
- Your python script is running, and makes a call to my_raw_input().
- Vim (or emacs, or gedit, or any other text editor) opens w/ a blank document
- You type some text into the document, then save and exit
- The python script resumes running, with the contents of the file as the return value of my_raw_input().
If you're familiar with git, this is the experience when using git commit
, where the editor is configured via core.editor. Other utilities like crontab -e
also do this.
Ultimately, I would like this my_raw_input() function to also take an optional string w/ the default input contents, which the user could then edit.
Research so far
- os.exec replaces the current process with the editor command, but does not return. Ie, your python script exits when vim starts.
- popen does not start the child process interactively, there is no user interface displayed.
- vim has a
-
command-line parameter to read from stdin, but nothing to write to stdout with:w
. - I took a look at the code for git, which I can't follow at all.
Is this possible?
Edit
Good answers so far. I also found the mecurial code that's doing the same thing. I also came up with an example that works from looking at the crontab code, but it looks like it's needlessly complicated compared to some of the responses.
#!/usr/bin/pythonimport osimport tempfiledef raw_input_editor(default=None, editor=None):''' like the built-in raw_input(), except that it uses a visual text editor for ease of editing. Unline raw_input() it can also take a default value. ''' editor = editor or get_editor() with tempfile.NamedTemporaryFile(mode='r+') as tmpfile: if default: tmpfile.write(default) tmpfile.flush() child_pid = os.fork() is_child = child_pid == 0 if is_child: os.execvp(editor, [editor, tmpfile.name]) else: os.waitpid(child_pid, 0) tmpfile.seek(0) return tmpfile.read().strip()def get_editor(): return (os.environ.get('VISUAL') or os.environ.get('EDITOR') or 'vi')if __name__ == "__main__": print raw_input_editor('this is a test')