Archives | Categories

Babel Bug: execute scheme code blocks in batch mode

Table of Contents

<2014-04-03 Thu>

1 The problem

Few days ago I wrote a scheme code block in one of my org file, then I run a command(call emacs in batch mode) to export it into html, and I encounter an error.

You can reproduce the error (with org-mode version 8.2.5h ) by these steps:

  • install/setup org-mode and geiser.
  • write an org file (e.g. scheme-test.org) with below content:

    #+BEGIN_SRC scheme :exports results :results output raw
      (display "Hello Scheme in OrgMode")
    #+END_SRC
    
  • export the org file with command:

    emacs --batch --eval='(load "~/.emacs.d/init.el")' scheme-test.org -f org-html-export-to-html
    

    then you will see the error if you have set debug-on-error to non-nil:

    ...
    Guile REPL up and running!
    => "Hello Scheme in OrgMode"
    Debugger entered--Lisp error: (wrong-type-argument arrayp nil)
      org-babel-scheme-execute-with-geiser("(display \"Hello Scheme in OrgMode\")" t guile nil)
        org-babel-execute:scheme("(display \"Hello Scheme in OrgMode\")" ((:comments . "") (:shebang . "") (:cache . "no") (:padline . "") (:noweb . "no") (:tangle . "no") (:exports . "results") (:results . "raw output replace") (:colname-names) (:hlines . "no") (:result-params "replace" "output" "raw") (:result-type . output) (:rowname-names) (:session . "none")))
          org-babel-execute-src-block(nil ("scheme" "(display \"Hello Scheme in OrgMode\")" ((:colname-names) (:rowname-names) (:result-params "replace" "output" "raw" "replace" "output" "raw") (:result-type . output) (:comments . "") (:shebang . "") (:cache . "no") (:padline . "") (:noweb . "no") (:tangle . "no") (:exports . "results") (:results . "replace output raw") (:session . "none") (:rowname-names) (:result-type . output) (:result-params "replace" "output" "raw") (:hlines . "no") (:colname-names)) "" nil 0 2))
            org-babel-exp-results(("scheme" "(display \"Hello Scheme in OrgMode\")" ((:cache . "no") (:colname-names) (:comments . "") (:exports . "results") (:hlines . "no") (:noweb . "no") (:padline . "") (:result-params "replace" "output" "raw") (:result-type . output) (:results . "replace output raw") (:rowname-names) (:session . "none") (:shebang . "") (:tangle . "no")) "" nil 0 2) block nil "a01854650514fd2cec7ce6957a4622b52118fcd3")
    ...
    
  • But, if you export this org file in interactive mode (not batch mode), no error occurs.

2 The simple solution

After doing a little investigation of ob-scheme.el and geiser, I find the root cause:

function `current-message' always returns nil in batch mode

ob-scheme uses geiser repl to execute the scheme code block, geiser repl shows the results with function message, then ob-scheme gets the results by calling function current-message, and this exactly the problem is.

So I write a macro to capture the current message in both interactive mode and batch mode:

(defmacro org-babel-scheme-capture-current-message (&rest body)
  "Capture current message in both interactive and noninteractive mode"
  `(if noninteractive
       (let ((current-message nil))
         (flet ((message (fmt &rest args) (setq current-message (apply #'format fmt args))))
           ,@body
           current-message))
     (progn
       ,@body
       (current-message))))

Then wrap the geiser-eval-region call with the macro to get the results. Fortunately it works for me.

3 The patch

Here is the patch:

diff --git a/lisp/ob-scheme.el b/lisp/ob-scheme.el
index b7117e9..3b7ceb2 100644
--- a/lisp/ob-scheme.el
+++ b/lisp/ob-scheme.el
@@ -118,6 +118,17 @@ org-babel-scheme-execute-with-geiser will use a temporary session."
           (name))))
     result))

+(defmacro org-babel-scheme-capture-current-message (&rest body)
+  "Capture current message in both interactive and noninteractive mode"
+  `(if noninteractive
+       (let ((current-message nil))
+         (flet ((message (fmt &rest args) (setq current-message (apply #'format fmt args))))
+           ,@body
+           current-message))
+     (progn
+       ,@body
+       (current-message))))
+
 (defun org-babel-scheme-execute-with-geiser (code output impl repl)
   "Execute code in specified REPL. If the REPL doesn't exist, create it
 using the given scheme implementation.
@@ -142,10 +153,11 @@ is true; otherwise returns the last value."
                 (current-buffer)))))
    (setq geiser-repl--repl repl-buffer)
    (setq geiser-impl--implementation nil)
-   (geiser-eval-region (point-min) (point-max))
+   (setq result (org-babel-scheme-capture-current-message
+             (geiser-eval-region (point-min) (point-max))))
    (setq result
-         (if (equal (substring (current-message) 0 3) "=> ")
-         (replace-regexp-in-string "^=> " "" (current-message))
+         (if (and (stringp result) (equal (substring result 0 3) "=> "))
+         (replace-regexp-in-string "^=> " "" result)
        "\"An error occurred.\""))
    (when (not repl)
      (save-current-buffer (set-buffer repl-buffer)

4 Notice

flet (the dynamic flet) is obsolete since emacs 24.3, but this is a quick way I found to fix the problem without upstream package (geiser) changes.

5 Update 1

<2014-05-22 Thu>

I refactored a new patch without flet, and it has been merge into orgmode, see http://orgmode.org/cgit.cgi/org-mode.git/commit/?id=21d6d7c3a0b08382e4dfc5d7300f61ba5afe6f12 .

6 Discuss and Comment

comments powered by Disqus

Copyright © KDr2, SOME RIGHTS RESERVED UNDER CC BY-NC 4.0.

Built with Emacs 25.1.50.1 (Org mode 8.3.5).

Last updated: 2016-08-18 Thu 14:04.

Green Web Hosting! This site hosted by DreamHost.