1

-- UPDATE -- Declare what my code is trying to do:

I want to let org-babel insert an inline image link which is generated by Clojure code. Here the image is generated by Incanter's function save. Here is the problem, somehow in my following elisp advice code, the image file is not saved, it is empty (The file is generated).

The following org-mode babel block is what I wish to implement:

#+begin_src clojure :dir "data/images" :results file :file "clojure-babel-figure-result.png"
(use '(incanter core stats datasets charts io pdf))
(def my-plot (histogram (sample-normal 1000)))
(save my-plot output-file)
(.close output-file)
#+end_src

#+RESULTS:
[[file:data/images/ob-clojure-literate.png]]

Here is my ob-clojure-literate code: https://code.orgmode.org/bzg/org-mode/src/master/contrib/lisp/ob-clojure-literate.el (without the following advice code).

I try add advice as I posted at here How to write this advice for inject code into let-binding?

And here is my final code (some lines commented out for easy debug):

;;; Support header arguments  :results file :file "image.png"
(defun ob-clojure-literate-inject-code (args)
  "Inject Clojure code into `BODY' in `ARGS'.

This can be used as :filter-args advice of `org-babel-expand-body:clojure'.
It is used to change Clojure currently working directory and generate graphics image file result."
  (let* ((body (nth 0 args))
         (params (nth 1 args))
     (dir (cdr (assq :dir params)))
     (default-directory (and (buffer-file-name) (file-name-directory (buffer-file-name))))
         (directory (and dir (file-name-as-directory (expand-file-name dir))))
     (result-type (cdr (assq :results params)))
     (out-file (cdr (assq :file params)))
         (add-to-body (lambda (code)
                        (setq body (concat code "\n" body))))
     (append-to-body (lambda (code)
               (setq body (concat body "\n" code))))
         )
    (unless (file-directory-p (expand-file-name directory))
      (warn (format "ob-clojure-literate directory %s does not exist, please create it." dir)))
    ;; (when directory
    ;;   (funcall add-to-body (format "(System/setProperty \"user.dir\" \"%s\")" directory)))
    (when (string-match-p (regexp-opt '("graphics" "file")) result-type)
      ;; (member "graphics" (cdr (assq :result-params params)))
      ;; (funcall add-to-body "(import 'java.io.FileOutputStream)")
      ;; use static global variable `ob-clojure-literate-incanter-plot' as convention.
      ;; (funcall append-to-body
      ;;            (format "(def ob-clojure-literate-incanter-plot-file (FileOutputStream. \"%s\"))"
      ;;                (concat directory out-file)))
      ;; (funcall append-to-body
      ;;            "(save ob-clojure-literate-incanter-plot ob-clojure-literate-incanter-plot-file)")
      )
    (when out-file
      ;; (funcall append-to-body
      ;;            (format "(view ob-clojure-literate-incanter-plot)"))

      (funcall append-to-body
                 (format "(save ob-clojure-literate-incanter-plot \"%s\")"
                     (concat directory out-file)))
      )
    (list body params) ; return modified argument list
    ))

(advice-add 'org-babel-expand-body:clojure :filter-args #'ob-clojure-literate-inject-code)

When I add upper advice code. And execute the following org-babel src block:

#+begin_src clojure :cache no :dir "data/images" :results graphics :file "ob-clojure-literate.png"
(use '(incanter core stats datasets charts io pdf))
(def ob-clojure-literate-incanter-plot (histogram (sample-normal 1000)))

;; (save ob-clojure-literate-incanter-plot "/home/stardiviner/Org/Wiki/Computer Technology/Programming/Programming Languages/Clojure/Data/Clojure Packages/data/images/ob-clojure-literate.png")
#+end_src

Emacs reports error:

Not a PNG file: ‘/home/stardiviner/Org/Wiki/Computer Technology/Programming/Programming Languages/Clojure/Data/Clojure Packages/data/images/ob-clojure-literate.png’ [4 times]

But I execute the literate Clojure code:

(save ob-clojure-literate-incanter-plot "/home/stardiviner/Org/Wiki/Computer Technology/Programming/Programming Languages/Clojure/Data/Clojure Packages/data/images/ob-clojure-literate.png")

in CIDER REPL buffer is fine. The plot image is generated correctly.

I tried to Edebug the advice function. But can't find out where is wrong. Please someone can help me out. Thanks in advance.

-- UPDATE --

Here is the debug workflow steps:

  1. ob-clojure-literate create a project if not exist yet, then CIDER jack-in.

    [M-x ob-clojure-literate-auto-jackin]
    
  2. Now you have a session, then try the following org block:

    #+begin_src clojure :cache no :dir "data/images" :results graphics :file "ob-clojure-literate.png"
    (use '(incanter core stats datasets charts io pdf))
    (def ob-clojure-literate-incanter-plot (histogram (sample-normal 1000)))
    
    (save ob-clojure-literate-incanter-plot "<path-to>/data/images/ob-clojure-literate.png")
    #+end_src
    

    The upper path is relative to your test path.

    Because you already have a CIDER session, you can make debug simpler with just:

    #+begin_src clojure :cache no :dir "data/images" :results file :file "ob-clojure-literate.png"
    (save ob-clojure-literate-incanter-plot "<path-to>/data/images/ob-clojure-literate.png")
    #+end_src
    

    The upper path is relative to your test path.

I tried toggle Edebug on following functions:

  • org-babel-expand-body:clojure (expanding seems correct)
  • org-babel-execute:clojure (this seems correct too)
    • nrepl-sync-request:eval (correct)
    • nrepl-send-sync-request (correct)
      • nrepl-send-request (correct)
      • after (process-send-string nil message), the image is save (seems Incanter save works correct as @xuchunyang said.) I have not found this step works.

After @xuchunyang's answer hint, I try to edebug deeper, found indeed with :file header argument will cause image be empty. Then I found he is right. This part of code in org-babel-execute:clojure caused image file to be empty.

And my environment info:

  • Arch Linux
  • Java version: java-9-jdk
  • CIDER version: CIDER 0.17.0snapshot (package: 20180219.1846)
  • Emacs version: GNU Emacs 27.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.22.26) of 2018-02-10
  • Org-mode version: Org mode version 9.1.6 (9.1.6-48-gfe7619-elpaplus @ mixed installation! /home/stardiviner/.emacs.d/elpa/org-plus-contrib-20180212/ and /home/stardiviner/Code/Emacs/org-mode/lisp/)

-- UPDATE -- found why the image file is empty by org-babel now. The code is in org-babel-execute-src-block:

(let ((file (cdr (assq :file params))))
        ;; If non-empty result and :file then write to :file.
        (when file
          (when result
            (with-temp-file file
              (insert (org-babel-format-result
                   result (cdr (assq :sep params))))))
          (setq result file))

In my case the result is empty string "", it try to write this empty string result to file. So this is the problem.

7
  • After inspecting org-babel-exec:clojure I think your png file is overwritten with the return value of the save command. That would explain the error message. Could you open the png file with M-x find-file-literally and check? Thanks.
    – Tobias
    Commented Feb 20, 2018 at 22:56
  • For a first step we could replace the :file header argument with something other like :graphics-file and process that in the advice. Please avoid the :file header argument for now. Just visit the png file with C-x C-f for a test. We can later see whether we can adapt the :file header argument for our purposes when the result type graphics is given.
    – Tobias
    Commented Feb 20, 2018 at 23:03
  • Return the path "/home/stardiviner/Org/Projects/Programming Projects/data/images/ob-clojure-literate.png" directly does not work for :file header. Commented Feb 21, 2018 at 2:20
  • Aha, I see, I have not found :graphics-file in org-mode Info manual. And I tried some groups :results none :file (works, fixed upper declare result issue). And :results file :graphics-file and :results graphics :graphics-file all avoid org-babel to override the image file. But seems will not insert a full inline image link. It looks like this: [[file:/home/stardiviner/Org/Projects/Programming Projects/data/images/]] Commented Feb 28, 2018 at 17:31
  • Yes, I will try advice on the result. and use :graphics-file. Have to say, FINALLY. THANKS. Problem solved. You need to post answer. I will accept your answer. and clear monster question and some comments. Commented Feb 28, 2018 at 17:51

2 Answers 2

1
+400

Since you provide :file "ob-clojure-literate.png", when you execute the code block, Org will insert the result into the file, thus overwrite your image. I learnt about this by stepping through the function org-babel-execute-src-block.

BTW, your advice function is working as expected, you can confirm that via the command org-babel-expand-src-block.

8
  • That was also my first thought. It is not the problem. We already tried with :graphics-file instead of :file.
    – Tobias
    Commented Feb 28, 2018 at 12:48
  • @Tobias I'm sure the answer is the cause for the question. I can confirm other keywords, such as :graphics-file, work fine.
    – xuchunyang
    Commented Feb 28, 2018 at 13:17
  • Have you tested file names with spaces? File names without spaces are also working for the OP. See comment emacs.stackexchange.com/questions/38857/… for the already existing discussion of the :file header argument.
    – Tobias
    Commented Feb 28, 2018 at 13:56
  • @Tobias Yes, :graphics-file "foo bar.png" works fine. As I said before, I think I already answered the question. I don't think I need to follow the question's comments, if they are important, @stardiviner should update his/her question.
    – xuchunyang
    Commented Feb 28, 2018 at 14:22
  • What does "work" mean? Has clojure actually produced the image and have you referred to it in the org-file? It may be that it works for you. Maybe it is a version problem. Please give full version information in your answer: emacs-version, org-version, version of CIDER, and version of clojure.
    – Tobias
    Commented Feb 28, 2018 at 14:31
0

I create a new Org source block result type "link". Here is my patch https://code.orgmode.org/bzg/org-mode/commit/296b0de4e881b6bd8657dadf7e73fd323f961d8c

You can see the usage details in the file "org-manual.org" in the commit. Like this:

 #+begin_src shell :results link :file "download.tar.gz"
 wget -c "http://example.com/download.tar.gz"
 #+end_src

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.