Exécution asynchrone dans org babel

14

Existe-t-il une bonne personnalisation générale d'org-babel pour une exécution asynchrone? Récemment, je prévois d'utiliser MATLAB via org-babel, mais j'aimerais qu'il soit asynchrone, car certains calculs prennent du temps.

Je ne souhaite pas personnaliser uniquement ob-matlab. C'est parce que je pense que cela devrait être fait au niveau du framework au lieu d'une application. En d'autres termes, une même modification devrait activer la fonction asynchrone pour d'autres extensions de langue, par exemple la langue R.

Quelqu'un at-il une bonne solution? Jusqu'à présent , je l' ai essayé async.el, ainsi que deferred.elde modifier org-babel-execute-safely-maybequi se trouvent dans ob-core.elle moment.

diadochos
la source
Un autre indice est que vous pouvez passer le bloc babel à l'écran ou au tmux.
stardiviner
Je n'ai jamais implémenté cela, mais cela semble possible. Merci.
diadochos
Je suppose que j'accepte ma propre réponse car aucune autre solution n'a été publiée au cours du dernier mois.
diadochos

Réponses:

6

Jusqu'à présent, j'ai découvert que la création d'un nouveau processus Emacs est une solution.

Voici ce que j'ai fait.

1. Ajoutez une fonction pour démarrer un processus emacs externe.

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. Ajoutez un fichier de configuration à charger dans le nouveau processus emacs.

La fonction ci-dessus démarre emacs dans le --batchmode. Ainsi, l'init.el normal ne sera pas chargé.

Au lieu de cela, nous voulons créer un fichier de configuration plus court (pour charger les chemins et ainsi de suite).

Le chemin d'accès à notre nouveau fichier de configuration est stocké async-emacs-repl-org-babel-init-filedans l'extrait ci-dessus.

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

Ici nous ...

  1. Ajoutez des chemins d'accès aux packages.
  2. Dites à org-mode de ne pas demander s'il faut exécuter le bloc de code.
  3. Dites à org-babel quelles langues sont nécessaires.

Note de bas de page 1: Sans ce paramètre, l'évaluation échouera avec "No org-babel-execute function for $lang!"

Note de bas de page 2: Bien sûr, vous pouvez charger le fichier init.el normal au lieu de créer un nouveau fichier de configuration, si vous le souhaitez. Pour ce faire, en ajoutant (setq org-babel-async-init-file "~/.emacs.d/init")à votre init.el. Mais je pense que la création d'un fichier de configuration pour cette tâche est plus simple.

3. De plus ...

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

Ajouter à 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")

Ajouter à org-babel-async-init.el (vous n'en aurez peut-être pas besoin. Ce sont pour 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"))

Ajoutez à org-babel-async-init.el (vous n'en aurez peut-être pas besoin. Ce sont pour Julia, R et d'autres langues qui utilisent 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. Utilisation

(Après la configuration ci-dessus.)

  1. Déplacez le curseur sur l'extrait de code que vous souhaitez exécuter.
  2. Courez M-x my/async-emacs-repl-org-babel-execute(au lieu de faire C-c C-c). Cela lancera un processus Emacs externe en tant que serveur REPL si nécessaire, puis exécutera le bloc source où vous vous trouvez.

Remerciements

J'ai appris l'idée de démarrer un processus emacs pour l'évaluation org-babel de ce post . Je remercie l'auteur.

Commentaires pour la personnalisation

L'idée ici est simple. Organiser un nouveau processus emacs comme REPL pour Elisp, faire find-filele même fichier .org en cours d' édition, goto-lineau même point du curseur, exécutez org-babel-execute-src-block, save-buffer. Arrêtez de quitter jusqu'à ce que l'utilisateur arrête le processus (sinon, les graphiques disparaîtraient immédiatement après leur affichage). On peut naturellement penser à étendre cela en:

  • Utiliser le mode org au C-c C-clieu d'exécuter des fonctions à la main / définir un nouveau raccourci clavier (qui peut être réalisé par des conseils).
  • Changement de nom de processus conditionnel selon: la variable de session et la langue
  • Changement conditionnel des fichiers init en fonction de la langue.

En fait, le succès de cette approche me semble montrer une manière générale de développer des fonctionnalités asynchrones dans Emacs. Créer une couche "commandes", ajouter des scripts pour effectuer des tâches et disposer d'un cadre pour démarrer et réutiliser les processus emacs. Tout comme le framework Symfony de PHP (PHP n'a pas de threads) a des fonctionnalités de commande.

Modifier l'historique

Code refactorisé (2016-04-02). La solution réutilise maintenant un processus Emacs (2016-04-02). La solution est désormais simplifiée et n'a qu'une seule interactivecommande à exécuter (2016-04-02. Ajout de la configuration (2016-04-12).

diadochos
la source
Vous avez vu async.el?
PythonNut
Oui j'ai. Il démarre essentiellement un nouveau processus d'Emacs et exécute la lambdafonction qui lui est attribuée. Je ne l'ai pas utilisé pour cette solution car je n'ai pas trouvé de moyen d'envoyer des données au nouveau processus. La communication du processus est nécessaire si vous souhaitez utiliser la fonction: session d'org-babel.
diadochos
Merci d'avoir travaillé sur cette solution. Je l'ai essayé mais je reçois ce message d'erreur: TERM=vt200 emacs --batch -nw --eval '(load "~/.emacs.d/org-babel-async-init")' --eval '(while t (print (eval (read))))': exited abnormally with code 255.Désolé, cela devrait être un commentaire et non une réponse, mais je n'ai tout simplement pas assez de points.
mhartm
Après avoir exécuté cela, voyez-vous un tampon nommé " org-babel-async "? Si vous en trouvez un, ce tampon contient probablement plus d'informations sur l'erreur. «sortie anormale avec le code 255» se produit généralement lorsque le programme que vous souhaitez exécuter sur le processus emacs généré a échoué. Voies de sortie possibles: 1) Vérifiez si vous avez le fichier spécifié dans mon / async-emacs-repl-org-babel-init-file. Si vous ne le faites pas, créez-en un comme décrit ci-dessus. 2) Vérifiez si vous avez répertorié la langue dans laquelle vous souhaitez utiliser org-babel-do-load-languages. 3) Le #+SRC_BEGINbloc que vous exécutez contient un bug.
diadochos
D' accord, donc la question est que je dois enregistrer mon fichier org avant d' exécuter M-x my/async-emacs-repl-org-babel-execute, sinon le tampon « org-babel-async » se plaindra: ...t/Dropbox/org/work.org locked by maarhart@htkl... (pid 68694): (s, q, p, ?)? Please type q, s, or p; or ? for help. Donc, si cela peut être résolu, ce serait fantastique. Merci quand même, c'est incroyable! Au fait, est-il possible de le lier à C-c C-cou va-t-il entrer en conflit avec le mode org?
mhartm