[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Nicer export of an ipython-notebook style file

From: Dima Kogan
Subject: Nicer export of an ipython-notebook style file
Date: Tue, 10 Mar 2020 14:21:38 -0700
User-agent: mu4e 1.2.0; emacs 28.0.50


Recently I used org-babel to create documentation for a python plotting
library. The end-product of this documentation is a sequence of code
snippets and images (the result of evaluating the snippets).

A wrinkle is that I didn't want to use org to do the export. I'd use org
to generate all the images, and then push the .org to github, and use
its (half-assed) renderer to display the results.

This mostly worked out of the box, but some elisp was needed to get this
done fully. So this email is meant to

1. Ask the question "what is the right way to have done this?"

2. Talk about the elisp I wrote to get this done, and maybe get some of
   it merged upstream

The final product lives here:


The big "init" block at the end is the emacs lisp in question. I can't
get github to not display it, but that's a separate thing.

Now, each required piece, one at a time.

Each python block needs to know the file it should write the results to.
Org and python need to agree about this. It looks like some org-babel
backends do this (gnuplot for example), but in general there's no way. I
wrote a lisp thing to make ALL the org-babel headers for that snippet
available to the python block in a dict called "_org_babel_params". So
the python code writes its output to _org_babel_params['_file']. Clearly
this is specific to python, but any backend that has any sort of
associative array could maybe support something like this. The code:

  (defun dima-org-babel-python-var-to-python (var)
    "Convert an elisp value to a python variable.
    Like the original, but supports (a . b) cells and symbols
    (if (listp var)
        (if (listp (cdr var))
            (concat "[" (mapconcat #'org-babel-python-var-to-python var ", ") 
          (format "\"\"\"%s\"\"\"" var))
      (if (symbolp var)
          (format "\"\"\"%s\"\"\"" var)
        (if (eq var 'hline)
           (if (and (stringp var) (string-match "[\n\r]" var)) "\"\"%S\"\"" 
           (if (stringp var) (substring-no-properties var) var))))))
  (defun dima-alist-to-python-dict (alist)
    "Generates a string defining a python dict from the given alist"
    (let ((keyvalue-list
           (mapcar (lambda (x)
                     (format "%s = %s, "
                              "[^a-zA-Z0-9_]" "_"
                              (symbol-name (car x)))
                             (dima-org-babel-python-var-to-python (cdr x))))
       "dict( "
       (apply 'concat keyvalue-list)
  (defun dima-org-babel-python-pass-all-params (f params)
      "_org_babel_params = "
      (dima-alist-to-python-dict params))
     (funcall f params)))
   :around #'dima-org-babel-python-pass-all-params)

Now that org-babel can tell python where to write its output, we need to
tell org-babel this. I don't actually care what the output file is
called, as long as it's unique. So each of my org-babel snippets does
not specify the :file at all, instead I advice
org-babel-execute-src-block to set a :file with "guide-%d.svg" where the
%d is an integer that's incremented with each block:

  (defun dima-org-babel-python-unique-plot-filename
      (f &optional arg info params)
    (funcall f arg info
             (cons (cons ':file
                         (format "guide-%d.svg"
                                 (condition-case nil
                                     (setq dima-unique-plot-number (1+ 
                                   (error (setq dima-unique-plot-number 0)))))
   :around #'dima-org-babel-python-unique-plot-filename)

I'd like the count to start from 0 each time I (org-babel-execute-buffer):

  (defun dima-reset-unique-plot-number
      (&rest args)
      (setq dima-unique-plot-number 0))
   :after #'dima-reset-unique-plot-number)

Was there a better way to do this? Any of these advices upstreamable?


reply via email to

[Prev in Thread] Current Thread [Next in Thread]