Esecuzione asincrona in org babel


14

Esiste una buona personalizzazione generale di org-babel per l'esecuzione in modo asincrono? Recentemente ho intenzione di usare MATLAB tramite org-babel, ma mi piacerebbe farlo in modo asincrono, poiché alcuni calcoli richiedono tempo.

Non desidero personalizzare solo ob-matlab. Questo perché penso che dovrebbe essere fatto a livello di framework anziché in un'applicazione. In altre parole, una stessa modifica dovrebbe abilitare la funzione asincrona per altre estensioni di lingua, ad esempio la lingua R.

Qualcuno ha una buona soluzione? Finora ho provato async.eloltre deferred.ela modificare org-babel-execute-safely-maybeciò che può essere trovato in ob-core.elquesto momento.


Un altro suggerimento è che può passare il blocco babel allo schermo o al tmux.
stardiviner il

Non l'ho mai implementato, ma sembra possibile. Grazie.
diadochos,

Immagino di accettare la mia risposta poiché non è stata pubblicata alcuna altra soluzione nell'ultimo mese.
diadochos,

Risposte:


6

Finora ho scoperto che la generazione di un nuovo processo Emacs è una soluzione.

Ecco cosa ho fatto.

1. Aggiungi una funzione per avviare un processo emacs esterno.

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. Aggiungi un file di configurazione da caricare nel nuovo processo di emacs.

La funzione sopra avvia emacs nella --batchmodalità. Pertanto il normale init.el non verrà caricato.

Invece, vogliamo creare un file di configurazione più breve (per caricare percorsi e così via).

Il percorso del nostro nuovo file di configurazione è archiviato nel async-emacs-repl-org-babel-init-fileframmento sopra.

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)))

Qui noi ...

  1. Aggiungi percorsi del pacchetto.
  2. Di 'alla modalità org di non chiedere se eseguire il blocco di codice.
  3. Di 'a org-babel quali lingue sono necessarie.

Nota 1: senza questa impostazione, la valutazione fallirà "No org-babel-execute function for $lang!"

Nota 2: Ovviamente puoi caricare il normale init.el invece di creare un nuovo file di configurazione, se lo desideri. Fallo aggiungendo (setq org-babel-async-init-file "~/.emacs.d/init")al tuo init.el. Ma penso che la creazione di un file di configurazione per questa attività sia più semplice.

3. Inoltre ...

Aggiungi a 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)

Aggiungi a 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")

Aggiungi a org-babel-async-init.el (potresti non averne bisogno. Questi sono per 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"))

Aggiungi a org-babel-async-init.el (potresti non averne bisogno. Questi sono per Julia, R e altre lingue che usano 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. Utilizzo

(Dopo l'installazione sopra.)

  1. Sposta il cursore sullo snippet di codice che desideri eseguire.
  2. Esegui M-x my/async-emacs-repl-org-babel-execute(invece di farlo C-c C-c). Questo avvierà un processo Emacs esterno come server REPL, se necessario, e quindi eseguirà il blocco sorgente in cui ti trovi.

Ringraziamenti

Da questo post ho imparato l'idea di avviare un processo emacs per la valutazione org-babel . Vorrei ringraziare l'autore.

Commenti per la personalizzazione

L'idea qui è semplice. Avviare una nuova emacs processo come un REPL per Elisp, fare find-fileallo stesso file .org si sta modificando, goto-lineallo stesso punto del cursore, eseguire org-babel-execute-src-block, save-buffer. Interrompere l'uscita fino a quando l'utente non interrompe il processo (in caso contrario, i grafici scomparirebbero immediatamente dopo essere stati visualizzati). Si può naturalmente pensare di estenderlo:

  • Usare la modalità org C-c C-cinvece di eseguire manualmente le funzioni / impostare un nuovo keybind (che può essere ottenuto con consigli).
  • Commutazione condizionale del nome del processo in base a: variabile di sessione e lingua
  • Cambio condizionale dei file init in base alla lingua.

In effetti, il successo di questo approccio mi sembra mostrare un modo generale di sviluppare funzionalità asincrone in Emacs. Creare un livello "comandi", aggiungere script per eseguire attività e disporre di un framework per l'avvio e il riutilizzo dei processi di emacs. Proprio come il framework Symfony di PHP (PHP non ha thread) ha funzioni di comando.

Modifica cronologia

Codice refactored (02-04-2016). La soluzione ora riutilizza un processo Emacs (02-04-2016). La soluzione ora è semplificata e ha solo un interactivecomando da eseguire (02-04-2016. Aggiunta configurazione (12-04-2016).


Hai visto async.el?
PythonNut,

Sì. In sostanza avvia un nuovo processo di Emacs ed esegue la lambdafunzione assegnatagli. Non l'ho usato per questa soluzione perché non riuscivo a trovare un modo per inviare dati al nuovo processo. La comunicazione del processo è necessaria se si desidera utilizzare la funzione: session di org-babel.
diadochos,

Grazie per aver lavorato su questa soluzione. L'ho provato ma ricevo questo messaggio di errore: mi TERM=vt200 emacs --batch -nw --eval '(load "~/.emacs.d/org-babel-async-init")' --eval '(while t (print (eval (read))))': exited abnormally with code 255.dispiace, questo dovrebbe essere un commento e non una risposta, ma non ho abbastanza punti.
mhartm,

Dopo averlo eseguito, vedi un buffer chiamato " org-babel-async "? Se riesci a trovarne uno, quel buffer probabilmente contiene più informazioni sull'errore. "uscito in modo anomalo con il codice 255" si verifica generalmente quando il programma che si desidera eseguire sul processo emacs generato non è riuscito. Possibili vie d'uscita: 1) Controlla se hai il file specificato nel mio / async-emacs-repl-org-babel-init-file. In caso contrario, creane uno come descritto sopra. 2) Controlla se hai elencato la lingua in cui desideri utilizzare org-babel-do-load-languages. 3) Il #+SRC_BEGINblocco che stai eseguendo contiene un bug.
diadochos,

Okay, quindi il problema è che ho bisogno di salvare il mio file org prima di eseguire M-x my/async-emacs-repl-org-babel-execute, in caso contrario il buffer "org-babel-Async" si lamentano: ...t/Dropbox/org/work.org locked by maarhart@htkl... (pid 68694): (s, q, p, ?)? Please type q, s, or p; or ? for help. Quindi, se questo può essere risolto, sarebbe fantastico. Grazie comunque per questo, è fantastico! A proposito, è possibile associarlo C-c C-co sarà in conflitto con la modalità org?
mhartm,

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.