组织Babel中的异步执行


14

是否有一个良好的org-babel通用定制可以异步运行?最近,我计划通过org-babel使用MATLAB,但由于某些计算确实需要时间,因此我希望以异步方式使用它。

我不想仅自定义ob-matlab。这是因为我认为应该在框架级别而不是在应用程序级别完成。换句话说,相同的修改应启用其他语言扩展(例如R语言)的异步功能。

有没有人有一个好的解决方案?到目前为止,我已经尝试async.el过以及deferred.el修改org-babel-execute-safely-maybe目前可以找到ob-core.el的内容。


另一个提示是可以将babel块传递到屏幕或tmux。
stardiviner '16

我从未实现过,但这听起来可能。谢谢。
diadochos's

我想我已经接受了自己的答案,因为最近一个月没有发布任何其他解决方案。
diadochos

Answers:


6

到目前为止,我发现产生新的Emacs进程是一种解决方案。

这是我所做的。

1.添加一个功能以启动外部emacs进程。

初始化

(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服务器,然后执行您所在的源代码块。

致谢

我从这篇文章中学到了启动emacs流程以进行组织-babel评估的想法。我要感谢作者。

定制注释

这里的想法很简单。以Elisp的REPL身份启动一个新的emacs流程,find-file对我们正在编辑的相同.org文件,goto-line对相同的光标点运行org-babel-execute-src-blocksave-buffer。停止退出,直到用户停止该过程为止(否则,图形将在显示后立即消失)。人们自然可以考虑通过以下方式扩展它:

  • 使用组织模式C-c C-c而不是手动运行功能/设置新的键绑定(可以通过建议来实现)。
  • 根据:session变量和语言有条件地切换进程名称
  • 根据语言有条件地切换初始化文件。

实际上,在我看来,这种方法的成功似乎表明了在Emacs中开发异步功能的一般方法。创建一个“命令”层,添加执行任务的脚本,并具有用于启动和重用emacs进程的框架。就像PHP的Symfony框架(PHP没有线程)一样,它具有Command功能。

编辑历史

重构代码(2016-04-02)解决方案现在重用了Emacs流程(2016-04-02)。解决方案现已简化,仅interactive运行一个命令(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 '16

执行之后,您是否看到一个名为“ org-babel-async ” 的缓冲区?如果找到一个,则该缓冲区可能包含有关该错误的更多信息。当您要在生成的emacs进程上运行的程序失败时,通常会发生“代码255异常退出”。可能的解决方法:1)检查是否在my / async-emacs-repl-org-babel-init-file中指定了文件。如果不这样做,请如上所述创建一个。2)检查是否列出了要使用的语言org-babel-do-load-languages。3)#+SRC_BEGIN您正在执行的块包含一个错误。
diadochos 16-4-27的

好的,所以问题是我需要在运行之前保存org文件M-x my/async-emacs-repl-org-babel-execute,否则“ org-babel-async”缓冲区将抱怨:...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或与org-mode冲突?
mhartm '16

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.