From mboxrd@z Thu Jan 1 00:00:00 1970 From: Eric Schulte Subject: Re: [RFC] [PATCH] ob-core.el: allow the auto-generation of output file names for src blocks. Date: Sun, 04 May 2014 07:55:57 -0600 Message-ID: <87ha55a5tb.fsf@gmail.com> References: <1398196476-4773-1-git-send-email-aaronecay@gmail.com> <87bnvt2h6r.fsf@bzg.ath.cx> <87d2g92au5.fsf@gmail.com> <8738h49u2g.fsf@gmail.com> <8761lugqyi.fsf@gmail.com> Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Return-path: Received: from eggs.gnu.org ([2001:4830:134:3::10]:54052) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WgyuV-0000VR-71 for emacs-orgmode@gnu.org; Sun, 04 May 2014 12:05:08 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1WgyuP-0007RL-PX for emacs-orgmode@gnu.org; Sun, 04 May 2014 12:05:03 -0400 Received: from mail-ig0-x231.google.com ([2607:f8b0:4001:c05::231]:34018) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WgyuP-0007R9-Hp for emacs-orgmode@gnu.org; Sun, 04 May 2014 12:04:57 -0400 Received: by mail-ig0-f177.google.com with SMTP id l13so1320782iga.4 for ; Sun, 04 May 2014 09:04:56 -0700 (PDT) List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org Sender: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org To: Aaron Ecay Cc: emacs-orgmode@gnu.org Hi Aaron, Thanks for this patch, and especially for including documentation. It looks good to me, and it (largely [1]) passes all tests. Aaron Ecay writes: > Hi Eric, Bastien, Achim, > > Thanks so much for the feedback. I=E2=80=99ve adopted the :file-ext appr= oach > 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 posi= tion >> (or (ignore-errors (nth 4 (org-heading-compon= ents))) >> "No heading") >> block-counter)))) >> ...)) > > I=E2=80=99m not sure I like this approach. It relies on counting source > blocks, so an addition/deletion of a block could change the index. > I=E2=80=99m 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. > Fair enough, especially given that this default will be applied to *all* code blocks, this seems like a reasonable approach. > >> >>> >>> 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=E2= =80=99s > suggestion. I can change this to your suggestion, if that is the > consensus. > Please do make this change, I'd then be happy to apply the resulting patch. Thanks again! Eric > > To address a comment from Bastien: :output-dir accepts absolute as well > as relative directory names. Referring to a =E2=80=9Csubdirectory=E2=80= =9D 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 > > From 4b428820432752117c60b79da0a79fd4e50e4ba1 Mon Sep 17 00:00:00 2001 > From: Aaron Ecay > Date: Tue, 22 Apr 2014 15:13:48 -0400 > Subject: [PATCH] ob-core.el: allow the auto-generation of output file nam= es > 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 hea= der 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 ar= e 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. >=20=20 > +@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 effe= ct > +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 direc= tory > +(beginning with @code{/}) or a relative directory (without @code{/}). I= t 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 effe= ct > +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))))) >=20=20 > (defvar org-babel-exp-reference-buffer nil > @@ -2890,6 +2892,38 @@ For the format of SAFE-LIST, see `org-babel-safe-h= eader-args'." > (member (cdr pair) (cdr entry))) > (t nil))))))) >=20=20 > +(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=3D 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))) >=20=20 > ;;; 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 > "0")))) > (format "elisp a:%d, b:%d, c:%d, d:%d, e:%d" a b c d e) > #+END_SRC > + > +* =3D:file-ext=3D and =3D:output-dir=3D header args > + :PROPERTIES: > + :ID: 93573e1d-6486-442e-b6d0-3fedbdc37c9b > + :END: > +#+name: file-ext-basic > +#+BEGIN_SRC emacs-lisp :file-ext txt > +nil > +#+END_SRC > + > +#+name: file-ext-dir-relative > +#+BEGIN_SRC emacs-lisp :file-ext txt :output-dir foo > +nil > +#+END_SRC > + > +#+name: file-ext-dir-relative-slash > +#+BEGIN_SRC emacs-lisp :file-ext txt :output-dir foo/ > +nil > +#+END_SRC > + > +#+name: file-ext-dir-absolute > +#+BEGIN_SRC emacs-lisp :file-ext txt :output-dir /tmp > +nil > +#+END_SRC > + > +#+name: file-ext-file-wins > +#+BEGIN_SRC emacs-lisp :file-ext txt :file foo.bar > +nil > +#+END_SRC > + > +#+name: file-ext-file-wins2 > +#+BEGIN_SRC emacs-lisp :output-dir xxx :file foo.bar > +nil > +#+END_SRC > 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\" > (org-babel-execute-src-block))) > (should (=3D noweb-expansions-in-cache-var 2))))) >=20=20 > +(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 (ass= q :file (nth 2 (org-babel-get-src-block-info t))))))) > + (at-next (should (equal "foo.bar" (cdr (assq :file (nth 2 (org-bab= el-get-src-block-info t))))))) > + (at-next (should (equal "foo.bar" (cdr (assq :file (nth 2 (org-bab= el-get-src-block-info t))))))) > + ))) > + > (provide 'test-ob) >=20=20 > ;;; test-ob ends here Footnotes:=20 [1] It took some extra work and manual refreshing of org-id's to get test-org-babel/file-ext-and-output-dir to pass. For some reason this tests still fails when I run "make test-dirty", but passes when run interactively. This could easily be particular to my system. --=20 Eric Schulte https://cs.unm.edu/~eschulte PGP: 0x614CA05D