Асинхронне виконання в org babel


14

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

Я не хочу налаштовувати лише ob-matlab. Це тому, що я думаю, що це слід робити на рівні фреймворку, а не додатком. Іншими словами, та сама модифікація повинна включати функцію асинхронізації для інших розширень мови, наприклад, мови R.

Хтось має хороше рішення? Поки я намагався async.el, а також deferred.elзмінити org-babel-execute-safely-maybeте, що можна знайти ob-core.elна даний момент.


Ще одна підказка - це передати дівочий блок на екран або tmux.
stardiviner

Я ніколи цього не реалізовував, але це здається можливим. Спасибі.
діадохос

Я думаю, що я приймаю власну відповідь, оскільки жодного іншого рішення не було розміщено за останній місяць.
діадохос

Відповіді:


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фрагменті вище.

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 для оцінки org-babel з цієї посади . Хочеться подякувати автору.

Коментарі для налаштування

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

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

Насправді, успіх такого підходу, як мені здається, демонструє загальний спосіб розвитку функцій асинхронізації в Emacs. Створюючи шар "команди", додайте сценарії для виконання завдань і майте основу для запуску та повторного використання процесів emacs. Так само, як Symfony Framework PHP (PHP не має ниток) має функції Command.

Редагування історії

Відновлюваний код (2016-04-02). Тепер рішення повторно використовує процес Emacs (2016-04-02). Тепер рішення спрощено і має лише одну interactiveкоманду для запуску (2016-04-02. Додана конфігурація (2016-04-12).


Ви бачили async.el?
PythonNut

Так, у мене є. Він по суті запускає новий процес Emacs і виконує lambdaзадану йому функцію. Я не використовував це для цього рішення, оскільки не міг знайти спосіб надсилання даних у новий процес. Комунікація процесу необхідна, якщо ви хочете скористатися функцією: сеанс org-babel.
діадохос

Дякуємо, що працювали над цим рішенням. Я спробував це, але отримую це повідомлення про помилку: 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. Якщо цього немає, створіть його, як описано вище. 2) Перевірте, чи ви вказали мову, якою ви хочете користуватися org-babel-do-load-languages. 3) #+SRC_BEGINБлок, який ви виконуєте, містить помилку.
діадохос

Отже, питання в тому , що мені потрібно , щоб зберегти мій орг файл перед запуском 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?
mhartm

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.