組織バベルでの非同期実行


14

非同期で実行するためのorg-babelの一般的なカスタマイズはありますか?最近、org-babel経由でMATLABを使用する予定ですが、一部の計算には時間がかかるため、非同期方式で使用したいと思います。

ob-matlabのみをカスタマイズしたくありません。これは、アプリケーションではなくフレームワークのレベルで行うべきだと思うからです。言い換えれば、同じ変更により、R言語などの他の言語拡張の非同期機能を有効にする必要があります。

誰かが良い解決策を持っていますか?これまでのところ、それを修正するasync.elだけでなく、現時点でそれを見つけることができます。deferred.elorg-babel-execute-safely-maybeob-core.el


別のヒントは、バベルブロックを画面またはtmuxに渡すことができることです。
-stardiviner

私はそれを実装したことはありませんが、可能だと思われます。ありがとう。
ディアドチョス

過去1か月間に他のソリューションが投稿されていないため、自分の回答を受け入れていると思います。
diadochos

回答:


6

これまでのところ、新しいEmacsプロセスの生成が解決策であることを発見しました。

これが私がやったことです。

1.外部emacsプロセスを開始する関数を追加します。

init.el

(defvar my/async-emacs-repl-org-babel-init-file "~/.emacs.d/org-babel-async-init" "File to load on executing async babel evaluation.")

(defun my/async-emacs-repl--start (process-name init-file)
  "Start a new Emacs process as a REPL server."
  (async-shell-command (concat
                        "TERM=vt200 emacs --batch -nw"
                        " --eval '(load \"" init-file "\")'"
                        " --eval '(while t (print (eval (read))))'"
                        )
                       process-name))

(defun my/async-emacs-repl--org-babel--start-server ()
  "Starts an Emacs process for async org-babel execution."
  (my/async-emacs-repl--start "*org-babel-async*" my/async-emacs-repl-org-babel-init-file))

(defun my/async-emacs-repl--org-babel--start-if-not-exists ()
  "Starts an Emacs process if the process does not exist."
  (if (not (get-buffer-process "*org-babel-async*")) (my/async-emacs-repl--org-babel--start-server)))

(defun my/async-emacs-repl--org-babel--execute--build-command (file-name line-number)
  "Build the command for executing `org-babel-execute-src-block'."
  (concat
   "(progn"
   " (find-file \"" file-name "\")"
   " (revert-buffer t t)"
   " (goto-line " (number-to-string line-number) ")"
   " (org-babel-execute-src-block t)"
   " (save-buffer)"
   ")"
   "\n"))

(defun my/async-emacs-repl--org-babel--execute (process-name file-name line-number)
  "Sends the command to the server to run the code-block the cursor is at."
  (process-send-string
   process-name
   (my/async-emacs-repl--org-babel--execute--build-command file-name line-number)))

(defun my/async-emacs-repl-org-babel-do-execute ()
  "Run org babel execution at point."
  (my/async-emacs-repl--org-babel--execute "*org-babel-async*" (buffer-file-name) (line-number-at-pos)))

(defun my/async-emacs-repl-org-babel-execute ()
  "Run by the user. Executes command. Starts buffer if not exists."
  (interactive)
  (save-buffer)
  (my/async-emacs-repl--org-babel--start-if-not-exists)
  (my/async-emacs-repl-org-babel-do-execute))

2.構成ファイルを追加して、新しいemacsプロセスにロードします。

上記の関数は、--batchモードでemacsを開始します。したがって、通常のinit.elはロードされません。

代わりに、(パスの読み込みなどのために)短い構成ファイルを作成します。

新しい設定ファイルへのパスasync-emacs-repl-org-babel-init-fileは、上記のスニペットに保存されています。

org-babel-async-init.el

;; 1
(package-initialize)

;; 2
(setq org-confirm-babel-evaluate nil)

;; 3
(let ((my/org-babel-evaluated-languages
       '(emacs-lisp
         ditaa
         python
         ruby
         C
         matlab
         clojure
         sh
         dot
         plantuml)))
  (org-babel-do-load-languages
   'org-babel-load-languages
   (mapcar (lambda (lang)
             (cons lang t))
           my/org-babel-evaluated-languages)))

ここで...

  1. パッケージパスを追加します。
  2. コードブロックを実行するかどうかを尋ねないようにorg-modeに指示します。
  3. org-babelに必要な言語を教えてください。

脚注1:この設定がないと、評価は失敗します "No org-babel-execute function for $lang!"

脚注2:もちろん、必要に応じて、新しい構成ファイルを作成する代わりに、通常のinit.elをロードできます。に追加(setq org-babel-async-init-file "~/.emacs.d/init")してくださいinit.el。しかし、このタスクの構成ファイルを作成する方が簡単だと思います。

3.さらに...

init.elに追加

;; This will stop the new process buffer from getting focus.
(setq display-buffer-alist (append display-buffer-alist '(("*org-babel-async*" display-buffer-no-window))))

;; This will automatically show the result section.
(global-auto-revert-mode 1)

org-babel-async-init.elに追加

;; This will skip the "Save anyway?" confirmation of automatically saving the file when you also edited the buffer from Emacs while an asynchronous process is running.
(defun advice:verify-visited-file-modtime (orig-func &rest args) t)
(advice-add 'verify-visited-file-modtime :around 'advice:verify-visited-file-modtime)

;; This will skip the "Select coding system" prompt that appears when the result is inserted. This may vary among environments.
(setq coding-system-for-write 'utf-8)

;; This will skip the "changed on disk; really edit the buffer?" checking.
(defun ask-user-about-supersession-threat (fn) "blatantly ignore files that changed on disk")

org-babel-async-init.elに追加します(これらは必要ない場合があります。これらはMATLAB用です)

;; This will set MATLAB cli path.
(setq-default matlab-shell-command "/Applications/MATLAB_R2016a.app/bin/matlab")
;; The MATLAB cli path can be obtained by running `fullfile(matlabroot, 'bin')` in your MATLAB.

;; This will stop MATLAB from showing the splash (the MATLAB logo) at the beginning.
(setq-default matlab-shell-command-switches '("-nodesktop" "-nosplash"))

org-babel-async-init.elに追加します(これらは必要ないかもしれません。これらはJulia、R、およびESSを使用する他の言語用です。)

;; This will enable :session header in Julia and other languages that use ESS (Emacs speaks statistics).
(load "/path/to/ess-site")
;; This will suppress ESS from prompting for session directory.
(setq ess-ask-for-ess-directory nil)

4.使い方

(上記のセットアップ後。)

  1. 実行するコードスニペットにカーソルを移動します。
  2. M-x my/async-emacs-repl-org-babel-execute(実行する代わりにC-c C-c)実行します。これにより、必要に応じて外部EmacsプロセスがREPLサーバーとして起動され、現在のソースブロックが実行されます。

謝辞

この投稿から、org-babel評価のためのemacsプロセスを開始するアイデアを学びました。著者に感謝したいと思います。

カスタマイズに関するコメント

ここでの考え方は簡単です。ElispのREPLとして新しいemacsプロセスを開始し、find-file編集しているのと同じ.orgファイルgoto-lineに対して、同じカーソルポイントに対して、を実行org-babel-execute-src-blocksave-bufferます。ユーザーがプロセスを停止するまで終了を停止します(そうしないと、グラフは表示された直後に消えます)。これを自然に拡張することについて考えることができます:

  • C-c C-c手動で機能を実行する代わりに組織モードを使用する/新しいキーバインドを設定する(アドバイスにより実現可能)。
  • :session変数と言語に応じて条件付きでプロセス名を切り替える
  • 言語に基づいて条件付きで初期化ファイルを切り替えます。

実際、このアプローチの成功は、Emacsで非同期機能を開発する一般的な方法を示しているように思われます。「コマンド」レイヤーを作成し、タスクを実行するスクリプトを追加し、emacsプロセスを開始および再利用するためのフレームワークを用意します。PHPのSymfonyフレームワーク(PHPにはスレッドがない)と同様に、コマンド機能があります。

履歴を編集

リファクタリングされたコード(2016-04-02)。ソリューションがEmacsプロセスを再利用するようになりました(2016-04-02)。ソリューションが簡素化され、interactive実行するコマンドが1つだけになりました(2016-04-02。構成が追加されました(2016-04-12)。


見たことあるasync.el
PythonNut

はい、あります。本質的にEmacsの新しいプロセスを開始し、それにlambda指定された機能を実行します。新しいプロセスにデータを送信する方法が見つからなかったため、このソリューションには使用しませんでした。org-babelの:session機能を使用する場合は、プロセスの通信が必要です。
-diadochos

このソリューションに取り組んでいただきありがとうございます。試してみましたが、このエラーメッセージが表示されます。TERM=vt200 emacs --batch -nw --eval '(load "~/.emacs.d/org-babel-async-init")' --eval '(while t (print (eval (read))))': exited abnormally with code 255.申し訳ありませんが、これはコメントではなく回答である必要がありますが、十分なポイントがありません。
mhartm

それを実行した後、「org-babel-async」という名前のバッファが表示されますか?見つかった場合、そのバッファにはおそらくエラーに関する詳細情報が含まれています。「コード255で異常終了しました」は、通常、生成されたemacsプロセスで実行したいプログラムが失敗したときに発生します。考えられる方法:1)my / async-emacs-repl-org-babel-init-fileでファイルが指定されているかどうかを確認します。そうでない場合は、上記の説明に従って作成してください。2)で使用する言語がリストされているかどうかを確認しますorg-babel-do-load-languages。3)#+SRC_BEGIN実行中のブロックにバグが含まれています。
-diadochos

問題は、私は実行する前に、自分の組織のファイルを保存する必要があるということだったのでわかりました、M-x my/async-emacs-repl-org-babel-executeそうでなければ「ORG-バベル-非同期」バッファは文句を言うでしょう:...t/Dropbox/org/work.org locked by maarhart@htkl... (pid 68694): (s, q, p, ?)? Please type q, s, or p; or ? for help。したがって、これを解決できれば素晴らしいことです。とにかくこれをありがとう、それは素晴らしいです!ところで、それをバインドすることは可能C-c C-cですか、それとも組織モードと競合しますか?
mhartm

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.