3

ユーザーへのインターフェースとしてvimのような構成可能なテキストエディターを使用するraw_input()の代わりを実装しようとしています。

理想的なワークフローは次のようになります。

  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のコードを見てみましたが、まったくフォローできません。

これは可能ですか?

編集

これまでのところ良い答え。同じことをしているmecurialコードも見つけました。また、 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')
4

2 に答える 2

9

データを一時ファイルに書き込み、エディターが戻ったときにそれを読み取ります。実行git commitすると、gitが同じことをしていることに気付くでしょう。

子プロセスが端末に接続されてstdinいてインタラクティブである限り、プログラムをインタラクティブに開始するための追加の手順はありません。stdout

編集者との連携には落とし穴があります。編集者の多くは、同じディレクトリに一時ファイルを書き込み、それを古いファイルに移動することでファイルを保存します。これにより、保存操作は完全にアトミックになります(電源が切れる可能性があることを無視します)が、古いファイルハンドルは、もはや一部ではないファイルを指しているため、エディターの実行後に一時ファイルを再度開く必要があることを意味します。ファイルシステム(ただし、まだディスク上にあります)。

この落とし穴は、ファイル記述子を閉じて、ファイルを削除せずに再度開くことができるように、TemporaryFileまたはを使用できないことを意味します。下位レベルの機能を使用する必要があります。NamedTemporaryFile

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)

subprocessGitサンプルコードは、Pythonのモジュールのような優れた高レベルのライブラリを使用していないため、非常に複雑です。モジュールのソースコードを読むと、そのsubprocess大きなチャンクはリンクされたGitソースコードのように見えます(CではなくPythonで記述されている場合を除く)。

于 2012-10-31T22:16:08.930 に答える
1

エディターがその内容を保存するために、一時ファイル名を作成する必要があります。これに使用できますtempfile.mkstemp()。そのファイルにコンテンツを入れたい場合は、それを行うことができます。

コマンドを実行するsubprocess.check_call()ために、Pythonはこのコマンドが戻るまで待機し、サブプロセスが失敗すると例外を発生させるため、ジョブの正しいツールのように見えます。だいたい:

import os
import tempfile
import subprocess

def my_raw_input(default=''):
    tf, tn = tempfile.mkstemp()
    os.close(tf)
    with open(tn) as tf:
        tf.write(default)
    rv = subprocess.check_call(['emacs', tn])
    with open(tn) as f:
        data = f.read()
    os.unlink(tn)
    return data

もちろん、使用するエディタなどをカスタマイズすることもできます。

于 2012-10-31T22:21:22.530 に答える