小编典典

使用可配置文本编辑器的Python raw_input()替换

python

我正在尝试实现raw_input()的替换,它将使用可配置的文本编辑器(如vim)作为与用户的接口。

理想的工作流程如下:

  1. 您的python脚本正在运行,并调用my_raw_input()。
  2. Vim(或emacs,gedit或任何其他文本编辑器)使用空白文档打开
  3. 您在文档中键入一些文本,然后保存并退出
  4. python脚本恢复运行,并将文件内容作为my_raw_input()的返回值。

如果您熟悉git,则可以使用git commit,这是通过core.editor配置编辑器的经验。其他实用程序crontab -e也可以这样做。

最终,我希望my_raw_input()函数也可以使用带有默认输入内容的可选字符串,然后用户可以对其进行编辑。

到目前为止的研究

  • os.exec用editor命令替换当前进程,但不返回。即,当vim启动时,您的python脚本退出。
  • popen不会以交互方式启动子进程,没有显示用户界面。
  • vim有一个-命令行参数可以从stdin读取,但是没有任何内容可以使用写入stdout :w
  • 我看了一下git代码,我根本无法理解。

这可能吗?

编辑

到目前为止,很好的答案。我还发现了做相同事情的商业代码。我还想出了一个可以通过查看crontab代码来工作的示例,但是与某些响应相比,它看起来像是不必要的复杂。

#!/usr/bin/python
import os
import tempfile


def 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')

阅读 209

收藏
2021-01-20

共1个答案

小编典典

您将数据写入一个临时文件,然后在编辑器返回时读取它。如果运行,git commit您会注意到git在做同样的事情。

只要子进程具有stdinstdout连接到终端的交互过程,就没有多余的步骤来交互式地启动程序。

与编辑器打交道有一个陷阱-
他们中的许多人将通过在同一目录中写入临时文件并将其移至旧文件上来保存文件。这使保存操作完全原子化(忽略电源可能会耗尽),但意味着我们必须在编辑器运行后重新打开临时文件,因为旧文件句柄将指向不再属于该文件的文件。文件系统(但仍在磁盘上)。

这个陷阱意味着我们不能使用TemporaryFileNamedTemporaryFile,我们必须使用较低级别的工具,以便我们可以关闭文件描述符并重新打开文件而不删除它。

import tempfile
import subprocess
import os

def edit(data):
    fdes = -1
    path = None
    fp = None
    try:
        fdes, path = tempfile.mkstemp(suffix='.txt', text=True)
        fp = os.fdopen(fdes, 'w+')
        fdes = -1
        fp.write(data)
        fp.close()
        fp = None

        editor = (os.environ.get('VISUAL') or
                  os.environ.get('EDITOR') or
                  'nano')
        subprocess.check_call([editor, path])

        fp = open(path, 'r')
        return fp.read()
    finally:
        if fp is not None:
            fp.close()
        elif fdes >= 0:
            os.close(fdes)
        if path is not None:
            try:
                os.unlink(path)
            except OSError:
                pass

text = edit('Hello, World!')
print(text)

Git示例代码非常复杂,因为它没有使用像Python的subprocess模块这样的高级库。如果您阅读了subprocess模块源代码,则其中的很大一部分将看起来像链接的Git源代码(用Python代替C编写的除外)。

2021-01-20