emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
From: Aaron Ecay <aaronecay@gmail.com>
To: Eric Schulte <schulte.eric@gmail.com>, Bastien <bzg@gnu.org>
Cc: emacs-orgmode@gnu.org
Subject: Re: [RFC] [PATCH] ob-core.el: allow the auto-generation of output file names for src blocks.
Date: Sun, 27 Apr 2014 22:18:23 -0400	[thread overview]
Message-ID: <8761lugqyi.fsf@gmail.com> (raw)
In-Reply-To: <8738h49u2g.fsf@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 2452 bytes --]

Hi Eric, Bastien, Achim,

Thanks so much for the feedback.  I’ve adopted the :file-ext approach
suggested by Bastien, leaving the previous default behavior in place for
blocks with a :file argument.

2014ko apirilak 22an, Eric Schulte-ek idatzi zuen:


> One option might be to borrow naming behavior from the comment
> functionality in ob-tangle which looks like the following (from line 426
> in ob-tangle.el).
> (let (...
>       (source-name
>        (intern (or (nth 4 info)       ; explicit #+name:
>                    (format "%s:%d"    ; constructed from header and position
>                            (or (ignore-errors (nth 4 (org-heading-components)))
>                                "No heading")
>                            block-counter))))
>       ...))

I’m not sure I like this approach.  It relies on counting source
blocks, so an addition/deletion of a block could change the index.
I’m worried that this can lead to the accumulation of many output
files: heading:1.ext, heading:2.ext, ... all with no clear indication
of what block they were spawned by.  It would also be possible for
the result links in the buffer to become inconsistent with the actual
block:auto-generated name mapping.

I think I would prefer the code in this patch to do nothing in this case
(not create a :file value), but for language-specific code that needs a
:file to raise an error to prompt the user to add a name.

>> 2. should :output-dir apply to the :file case as well?
> If you mean "should :output-dir be used as the base when :file is a
> relative pathname" then I'd say "yes", and I think if this isn't the
> current behavior then the current behavior should be changed.

Achim raises a backwards compatibility concern.  I am not sure how
serious it is: the default settings (no :output-dir) are backwards
compatible, and if users set that arg we ought to just give them what
they ask for.

Nonetheless, the new version of the patch conservatively obeys Achim’s
suggestion.  I can change this to your suggestion, if that is the

To address a comment from Bastien: :output-dir accepts absolute as well
as relative directory names.  Referring to a “subdirectory” was a
mistake on my part; the docs in the new patch should be clearer.

The updated patch (now with docs and tests) is attached to this email.

Thanks again,

Aaron Ecay

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-ob-core.el-allow-the-auto-generation-of-output-file-.patch --]
[-- Type: text/x-diff, Size: 7429 bytes --]

From 4b428820432752117c60b79da0a79fd4e50e4ba1 Mon Sep 17 00:00:00 2001
From: Aaron Ecay <aaronecay@gmail.com>
Date: Tue, 22 Apr 2014 15:13:48 -0400
Subject: [PATCH] ob-core.el: allow the auto-generation of output file names
 for src blocks.

* lisp/ob-core.el (org-babel-generate-file-param): New function.
(org-babel-get-src-block-info): Use it.
* testing/lisp/test-ob.el (test-org-babel/file-ext-and-output-dir):
New test.
* doc/org.texi (Specific header arguments): Add doc for :file-ext and
:output-dir header args.
 doc/org.texi               | 27 +++++++++++++++++++++++++++
 lisp/ob-core.el            | 34 ++++++++++++++++++++++++++++++++++
 testing/examples/babel.org | 34 ++++++++++++++++++++++++++++++++++
 testing/lisp/test-ob.el    | 14 ++++++++++++++
 4 files changed, 109 insertions(+)

diff --git a/doc/org.texi b/doc/org.texi
index 2546be1..79cc044 100644
--- a/doc/org.texi
+++ b/doc/org.texi
@@ -14406,6 +14406,8 @@ argument in lowercase letters.  The following header arguments are defined:
                                 be collected and handled
 * file::                        Specify a path for file output
 * file-desc::                   Specify a description for file results
+* file-ext::                    Specify an extension for file output
+* output-dir::                  Specify a directory to write file output to
 * dir::                         Specify the default (possibly remote)
                                 directory for code block execution
 * exports::                     Export code and/or results
@@ -14840,6 +14842,31 @@ description for file code block results which are inserted as Org mode links
 with no value the link path will be placed in both the ``link'' and the
 ``description'' portion of the Org mode link.
+@node file-ext
+@subsubsection @code{:file-ext}
+@cindex @code{:file-ext}, src header argument
+The value of the @code{:file-ext} header argument is used to provide an
+extension to write the file output to.  It is combined with the
+@code{#+NAME:} of the source block and the value of the @ref{output-dir}
+header argument to generate a complete file name.
+This header arg will be overridden by @code{:file}, and thus has no effect
+when the latter is specified.
+@node output-dir
+@subsubsection @code{:output-dir}
+@cindex @code{:output-dir}, src header argument
+The value of the @code{:output-dir} header argument is used to provide a
+directory to write the file output to.  It may specify an absolute directory
+(beginning with @code{/}) or a relative directory (without @code{/}).  It is
+combined with the @code{#+NAME:} of the source block and the value of the
+@ref{output-dir} header argument to generate a complete file name.
+This header arg will be overridden by @code{:file}, and thus has no effect
+when the latter is specified.
 @node dir
 @subsubsection @code{:dir} and remote execution
 @cindex @code{:dir}, src header argument
diff --git a/lisp/ob-core.el b/lisp/ob-core.el
index 1348f04..4a3683d 100644
--- a/lisp/ob-core.el
+++ b/lisp/ob-core.el
@@ -283,6 +283,8 @@ Returns a list
     ;; resolve variable references and add summary parameters
     (when (and info (not light))
       (setf (nth 2 info) (org-babel-process-params (nth 2 info))))
+    (when info
+      (setf (nth 2 info) (org-babel-generate-file-param name (nth 2 info))))
     (when info (append info (list name indent head)))))
 (defvar org-babel-exp-reference-buffer nil
@@ -2890,6 +2892,38 @@ For the format of SAFE-LIST, see `org-babel-safe-header-args'."
 		      (member (cdr pair) (cdr entry)))
 		     (t nil)))))))
+(defun org-babel-generate-file-param (src-name params)
+  "Calculate the filename for source block results.
+The directory is calculated from the :output-dir property of the
+source block; if not specified, use the current directory.
+If the source block has a #+NAME and the :file parameter does not
+contain any period characters, then the :file parameter is
+treated as an extension, and the output file name is the
+concatenation of the directory (as calculated above), the block
+name, a period, and the parameter value as a file extension.
+Otherwise, the :file parameter is treated as a full file name,
+and the output file name is the directory (as calculated above)
+plus the parameter value."
+  (let* ((file-cons (assq :file params))
+	 (file-ext-cons (assq :file-ext params))
+	 (file-ext (cdr-safe file-ext-cons))
+	 (dir (or (cdr-safe (assq :output-dir params)) "")))
+    ;; Only operate if:
+    ;; 1. :file is not given
+    ;; 2. :file-ext is given
+    ;; 3. the source block has a #+name
+    (if (and (not file-cons) file-ext src-name)
+	(progn
+	  ;; Create the :output-dir if it does not exist
+	  (when (and file-ext (not (string= dir "")))
+	    (make-directory dir t)
+	    (unless (string-match "/\\'" dir)
+	      (setq dir (concat dir "/"))))
+	  (cons (cons :file (concat dir src-name "." file-ext)) params))
+      ;; Otherwise return params unmodified
+      params)))
 ;;; Used by backends: R, Maxima, Octave.
 (defun org-babel-graphical-output-file (params)
diff --git a/testing/examples/babel.org b/testing/examples/babel.org
index 449824f..68ba65a 100644
--- a/testing/examples/babel.org
+++ b/testing/examples/babel.org
@@ -458,3 +458,37 @@ function definition
   (format "elisp a:%d, b:%d, c:%d, d:%d, e:%d" a b c d e)
+* =:file-ext= and =:output-dir= header args
+  :ID:       93573e1d-6486-442e-b6d0-3fedbdc37c9b
+  :END:
+#+name: file-ext-basic
+#+BEGIN_SRC emacs-lisp :file-ext txt
+#+name: file-ext-dir-relative
+#+BEGIN_SRC emacs-lisp :file-ext txt :output-dir foo
+#+name: file-ext-dir-relative-slash
+#+BEGIN_SRC emacs-lisp :file-ext txt :output-dir foo/
+#+name: file-ext-dir-absolute
+#+BEGIN_SRC emacs-lisp :file-ext txt :output-dir /tmp
+#+name: file-ext-file-wins
+#+BEGIN_SRC emacs-lisp :file-ext txt :file foo.bar
+#+name: file-ext-file-wins2
+#+BEGIN_SRC emacs-lisp :output-dir xxx :file foo.bar
diff --git a/testing/lisp/test-ob.el b/testing/lisp/test-ob.el
index c0ca493..fb791d6 100644
--- a/testing/lisp/test-ob.el
+++ b/testing/lisp/test-ob.el
@@ -1237,6 +1237,20 @@ echo \"$data\"
       (should (= noweb-expansions-in-cache-var 2)))))
+(ert-deftest test-org-babel/file-ext-and-output-dir ()
+  (org-test-at-id "93573e1d-6486-442e-b6d0-3fedbdc37c9b"
+    (macrolet ((at-next (&rest body)
+			`(progn
+			   (org-babel-next-src-block)
+			   (save-match-data ,@body))))
+      (at-next (should (equal "file-ext-basic.txt" (cdr (assq :file (nth 2 (org-babel-get-src-block-info t)))))))
+      (at-next (should (equal "foo/file-ext-dir-relative.txt" (cdr (assq :file (nth 2 (org-babel-get-src-block-info t)))))))
+      (at-next (should (equal "foo/file-ext-dir-relative-slash.txt" (cdr (assq :file (nth 2 (org-babel-get-src-block-info t)))))))
+      (at-next (should (equal "/tmp/file-ext-dir-absolute.txt" (cdr (assq :file (nth 2 (org-babel-get-src-block-info t)))))))
+      (at-next (should (equal "foo.bar" (cdr (assq :file (nth 2 (org-babel-get-src-block-info t)))))))
+      (at-next (should (equal "foo.bar" (cdr (assq :file (nth 2 (org-babel-get-src-block-info t)))))))
+      )))
 (provide 'test-ob)
 ;;; test-ob ends here

  parent reply	other threads:[~2014-04-28  2:18 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-04-22 19:54 [RFC] [PATCH] ob-core.el: allow the auto-generation of output file names for src blocks Aaron Ecay
2014-04-22 21:22 ` Bastien
2014-04-23  0:04   ` Aaron Ecay
2014-04-23  1:35     ` Eric Schulte
2014-04-23 14:58       ` Bastien
2014-04-28  2:18       ` Aaron Ecay [this message]
2014-04-28  6:20         ` Achim Gratz
2014-04-29 13:25         ` Bastien
2014-05-04 13:55         ` Eric Schulte
2014-05-11 20:38           ` Aaron Ecay
2014-05-14 17:46             ` Achim Gratz
2014-05-15 10:05               ` Bastien
2014-05-16  3:28               ` Aaron Ecay
2014-05-17  6:20                 ` Achim Gratz
2014-04-23  6:27     ` Bastien
2014-04-23 11:07       ` Eric Schulte
2014-04-23 19:44     ` Achim Gratz

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:

  List information: https://www.orgmode.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=8761lugqyi.fsf@gmail.com \
    --to=aaronecay@gmail.com \
    --cc=bzg@gnu.org \
    --cc=emacs-orgmode@gnu.org \
    --cc=schulte.eric@gmail.com \


* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).