Asynchrone Ausführung in org babel

14

Gibt es eine gute allgemeine Anpassung von org-babel, um asynchron zu laufen? Vor kurzem habe ich vor, MATLAB über org-babel zu verwenden, aber ich würde es gerne asynchron verwenden, da einige Berechnungen einige Zeit in Anspruch nehmen.

Ich möchte nicht nur ob-matlab anpassen. Dies liegt daran, dass ich denke, dass dies auf der Ebene des Frameworks und nicht auf der Ebene einer Anwendung erfolgen sollte. Mit anderen Worten, dieselbe Änderung sollte die Asynchronitätsfunktion für andere Spracherweiterungen aktivieren, z. B. R-Sprache.

Hat jemand eine gute Lösung? Bisher habe ich versucht, async.eldas auch deferred.elzu modifizieren org-babel-execute-safely-maybe, was im ob-core.elMoment zu finden ist.

Diadochos
quelle
Ein weiterer Hinweis ist, dass babel block an screen oder tmux übergeben werden kann.
Stardiviner
Ich habe das noch nie implementiert, aber es klingt möglich. Vielen Dank.
Diadochos
Ich nehme an, dass ich meine eigene Antwort akzeptiere, da seit einem Monat keine andere Lösung veröffentlicht wurde.
Diadochos

Antworten:

6

Ich habe bisher herausgefunden, dass das Laichen eines neuen Emacs-Prozesses eine Lösung ist.

Folgendes habe ich getan.

1. Fügen Sie eine Funktion hinzu, um einen externen Emacs-Prozess zu starten.

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. Fügen Sie eine Konfigurationsdatei hinzu, die in den neuen Emacs-Prozess geladen werden soll.

Die obige Funktion startet den Emacs im --batchModus. Somit wird das normale Init.el nicht geladen.

Stattdessen möchten wir eine kürzere Konfigurationsdatei erstellen (um Pfade usw. zu laden).

Der Pfad zu unserer neuen Konfigurationsdatei ist im async-emacs-repl-org-babel-init-fileobigen Snippet gespeichert .

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

Hier wir ...

  1. Paketpfade hinzufügen.
  2. Weisen Sie org-mode an, nicht zu fragen, ob der Codeblock ausgeführt werden soll.
  3. Sagen Sie org-babel, welche Sprachen erforderlich sind.

Fußnote 1: Ohne diese Einstellung schlägt die Auswertung mit fehl "No org-babel-execute function for $lang!"

Fußnote 2: Natürlich können Sie die normale init.el-Datei laden, anstatt eine neue Konfigurationsdatei zu erstellen. Tun Sie das, indem Sie (setq org-babel-async-init-file "~/.emacs.d/init")zu Ihrem hinzufügen init.el. Ich halte es jedoch für einfacher, eine Konfigurationsdatei für diese Aufgabe zu erstellen.

3. Zusätzlich ...

Zu init.el hinzufügen

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

Fügen Sie zu org-babel-async-init.el hinzu

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

Fügen Sie org-babel-async-init.el hinzu (möglicherweise benötigen Sie diese nicht. Diese sind für 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"))

Fügen Sie org-babel-async-init.el hinzu (möglicherweise benötigen Sie diese nicht. Dies gilt für Julia, R und andere Sprachen, die ESS verwenden.)

;; 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. Verwendung

(Nach dem obigen Setup.)

  1. Bewegen Sie den Cursor auf das Codefragment, das Sie ausführen möchten.
  2. Führen Sie aus M-x my/async-emacs-repl-org-babel-execute(anstatt zu tun C-c C-c). Dadurch wird bei Bedarf ein externer Emacs-Prozess als REPL-Server gestartet und anschließend der Quellblock ausgeführt, an dem Sie sich gerade befinden.

Danksagung

Aus diesem Beitrag habe ich die Idee gelernt, einen Emacs-Prozess für die Org-Babel-Evaluierung zu starten . Ich möchte dem Autor danken.

Kommentare zur Anpassung

Die Idee hier ist einfach. Starten Sie eine neue Emacs als REPL für Elisp verarbeiten, zu tun , find-fileum die gleiche .org Datei , die wir bearbeiten, goto-lineauf den gleichen Cursor Punkt laufen org-babel-execute-src-block, save-buffer. Beenden Sie das Programm, bis der Benutzer den Vorgang beendet hat (andernfalls verschwinden die Grafiken sofort, nachdem sie angezeigt wurden). Man kann natürlich darüber nachdenken, dies zu erweitern durch:

  • Verwenden von Org-Modi C-c C-canstelle des manuellen Ausführens von Funktionen / Festlegen einer neuen Tastenkombination (was durch Hinweise erreicht werden kann).
  • Bedingtes Umschalten des Prozessnamens gemäß: Sitzungsvariable und Sprache
  • Bedingtes Umschalten von Init-Dateien basierend auf der Sprache.

Tatsächlich scheint mir der Erfolg dieses Ansatzes ein allgemeiner Weg zu sein, asynchrone Funktionen in Emacs zu entwickeln. Erstellen Sie eine "Befehls" -Ebene, fügen Sie Skripts hinzu, um Aufgaben auszuführen, und verfügen Sie über ein Framework zum Starten und Wiederverwenden von Emacs-Prozessen. Genau wie das Symfony-Framework von PHP (PHP hat keine Threads) über Befehlsfunktionen verfügt.

Verlauf bearbeiten

Überarbeiteter Code (2016-04-02). Die Lösung verwendet jetzt einen Emacs-Prozess (02.04.2016). Die Lösung wurde jetzt vereinfacht und es muss nur noch ein interactiveBefehl ausgeführt werden (2016-04-02. Konfiguration hinzugefügt (2016-04-12).

Diadochos
quelle
Hast du gesehen async.el?
PythonNut
Ja, habe ich. Es startet im Wesentlichen einen neuen Prozess von Emacs und führt die lambdaihm zugewiesene Funktion aus. Ich habe es für diese Lösung nicht verwendet, weil ich keine Möglichkeit gefunden habe, Daten an den neuen Prozess zu senden. Die Kommunikation des Prozesses ist erforderlich, wenn Sie die Sitzungsfunktion von org-babel verwenden möchten.
Diadochos
Vielen Dank, dass Sie an dieser Lösung gearbeitet haben. Ich habe es versucht, aber ich bekomme folgende Fehlermeldung: TERM=vt200 emacs --batch -nw --eval '(load "~/.emacs.d/org-babel-async-init")' --eval '(while t (print (eval (read))))': exited abnormally with code 255.Entschuldigung, dies sollte ein Kommentar und keine Antwort sein, aber ich habe einfach nicht genug Punkte.
mhartm
Sehen Sie nach der Ausführung einen Puffer mit dem Namen " org-babel-async "? Wenn Sie einen finden können, enthält dieser Puffer wahrscheinlich weitere Informationen zu dem Fehler. "Abnormal mit Code 255 beendet" tritt im Allgemeinen auf, wenn das Programm, das Sie auf dem gespawnten Emacs-Prozess ausführen wollten, fehlschlug. Mögliche Auswege: 1) Überprüfen Sie, ob Sie die Datei in meiner / async-emacs-repl-org-babel-init-Datei angegeben haben. Wenn nicht, erstellen Sie eine wie oben beschrieben. 2) Überprüfen Sie, ob Sie die Sprache angegeben haben, in der Sie arbeiten möchten org-babel-do-load-languages. 3) Der #+SRC_BEGINBlock, den Sie ausführen, enthält einen Fehler.
Diadochos
Okay, also das Problem war , dass ich meine org - Datei zu speichern , bevor ausgeführt wird M-x my/async-emacs-repl-org-babel-execute, da sonst die „org-babel-async“ Puffer wird sich beschweren: ...t/Dropbox/org/work.org locked by maarhart@htkl... (pid 68694): (s, q, p, ?)? Please type q, s, or p; or ? for help. Wenn dies also gelöst werden kann, wäre es fantastisch. Trotzdem vielen Dank dafür, es ist unglaublich! Ist es übrigens möglich, es an den C-c C-corg-mode zu binden oder widerspricht es ihm?
Mhartm