Асинхронное исполнение в орг Бабель

14

Есть ли хорошая общая настройка org-babel для асинхронной работы? Недавно я планирую использовать MATLAB через org-babel, но я хотел бы использовать его асинхронно, поскольку некоторые вычисления требуют времени.

Я не хочу настраивать только ob-matlab. Это потому, что я думаю, что это должно быть сделано на уровне структуры вместо приложения. Другими словами, та же самая модификация должна включить функцию асинхронности для других языковых расширений, например, языка R.

У кого-нибудь есть хорошее решение? До сих пор я пытался, async.elа также deferred.elизменить org-babel-execute-safely-maybeто, что можно найти в ob-core.elданный момент.

diadochos
источник
Еще один совет - можно передать блок babel на экран или tmux.
stardiviner
Я никогда не реализовывал это, но это звучит возможно. Благодарю.
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.

Приведенная выше функция запускает emacs в --batchрежиме. При этом обычный init.el не будет загружен.

Вместо этого мы хотим создать более короткий файл конфигурации (для загрузки путей и т. Д.).

Путь к нашему новому файлу конфигурации хранится async-emacs-repl-org-babel-init-fileв приведенном выше фрагменте.

орг-столпотворение Асинхр-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 для оценки org-babel . Я хотел бы поблагодарить автора.

Комментарии для настройки

Идея здесь проста. Запустить новый Emacs обрабатывать как РЕПЛ для Elisp, сделать find-fileв тот же .org файл мы редактируемые goto-lineв ту же точку курсора, бегите org-babel-execute-src-block, save-buffer. Прекратите выход, пока пользователь не остановит процесс (в противном случае графики исчезнут сразу после их отображения). Естественно, можно подумать о расширении этого путем:

  • Использование режима org C-c C-cвместо запуска функций вручную / установки новой привязки клавиш (что может быть достигнуто советами).
  • Условное переключение имени процесса по: переменной сеанса и языку
  • Условное переключение файлов инициализации в зависимости от языка.

Фактически, успех этого подхода, как мне кажется, показывает общий способ разработки асинхронных функций в Emacs. Создание слоя «команд», добавление сценариев для выполнения задач и создание инфраструктуры для запуска и повторного использования процессов emacs. Точно так же, как PHP-фреймворк Symfony (в PHP нет потоков) имеет функции Command.

Редактировать историю

Рефакторинг кода (2016-04-02). Решение теперь повторно использует процесс Emacs (2016-04-02). Решение теперь упрощено и имеет только одну interactiveкоманду для запуска (2016-04-02. Добавлена ​​конфигурация (2016-04-12).

diadochos
источник
Ты видел async.el?
PythonNut
Да, у меня есть. По сути, он запускает новый процесс Emacs и запускает lambdaданную ему функцию. Я не использовал его для этого решения, потому что не смог найти способ отправить данные в новый процесс. Сообщение о процессе необходимо, если вы хотите использовать функцию: session org-babel.
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.извините, это должен быть комментарий, а не ответ, но мне просто не хватает очков.
Март
После выполнения этого вы видите буфер с именем " org-babel-async "? Если вы можете найти его, этот буфер, вероятно, содержит больше информации об ошибке. «аварийное завершение с кодом 255» обычно происходит, когда программа, которую вы хотели запустить в порожденном процессе emacs, не удалась. Возможные выходы: 1) Проверьте, есть ли у вас файл, указанный в моем / 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, иначе буфер «орг-столпотворение Асинхр» будет жаловаться: ...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?
Март