STARTED Working directories and remote execution

  • State "STARTED" from "TODO" 2010-02-22 Mon 14:41
  • State "TODO" from "DONE" 2010-02-21 Sun 01:16
  • State "DONE" from "STARTED" 2010-02-21 Sun 00:50
  • State "STARTED" from "PROPOSED" 2010-02-18 Thu 17:54
  • State "PROPOSED" from "TODO" 2010-02-14 Sun 14:00
  • State "TODO" from "TODO" 2010-02-14 Sun 10:41

Working directory is specified using :dir. If this is remote, then processes run remotely.

There is a working implementation for R, ruby, python and shell (branch ded-babel-remote).

One issue discussed below is that, as things stand in emacs, shell-command-on-region does not use tramp to handle the case of a remote default-directory (unlike shell-command).

The underlying reason is that call-process-region does not use tramp. The current working solution is, instead of using call-process-region, to use an org-babel version of the tramp handler for call-process-region (this handler is present but unused in tramp).

The current implementation does that only when default-directory is remote; otherwise we call the emacs version of call-process-region.

DONE Improve the way call-process-region is handled

  • State "DONE" from "TODO" 2010-03-02 Tue 14:53
  • State "TODO" from "" 2010-02-21 Sun 16:53

At line 217 of org-babel.el, we rebind call-process-region so that a handler from tramp is used:

(let (
      ...
      (call-process-region-original (symbol-function 'call-process-region))
      result)
  ;; (message "params=%S" params) ;; debugging
  (flet ((call-process-region (&rest args)
                              (apply 'org-babel-tramp-handle-call-process-region args)))
    ;; ...
    ))

Currently, there is a bug in that if we try to run the tests, a number of them fail. Furthermore, afterwards, it seems that

(symbol-function 'call-process-region))

returns the rebound definition made in the flet, rather than the original definition, despite the fact that the flet has terminated.

This all needs to be fixed.

TODO Improve temp file creation and remote reading

  • State "TODO" from "" 2010-02-21 Sun 17:02 See reply from Michael Albinus

As an example from several such instances, consider this in ob-ruby.el:

(case result-type
  (output (org-babel-eval org-babel-ruby-command body))
  (value (let ((tmp-file (make-temp-file "org-babel-ruby-results-")))
           (org-babel-eval org-babel-ruby-command
                           (format (if (member "pp" result-params)
                                       org-babel-ruby-pp-wrapper-method
                                     org-babel-ruby-wrapper-method)
                                   body tmp-file))
           ((lambda (raw)
              (if (or (member "code" result-params)
                      (member "pp" result-params))
                  raw
                (org-babel-ruby-table-or-string raw)))
            (org-babel-eval-read-file tmp-file)))))

The prefix passed to make-temp-file is relative; if default-directory happens to be remote, then make-temp-file guarantees that the file name returned does not already exists on the local host, whereas what is wanted is a guarantee that it does not exist on thr remote host. It seems from Michael Albinu's reply below that we should in that case be passing a prefix using remote syntax, expanded against the value of temporary-file-directory appropriate for the remote host.

Email from Dan

From: Dan Davison <davison@stats.ox.ac.uk> Subject: retrieving output from temp file User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/23.1 (gnu/linux) Date: Sun, 21 Feb 2010 11:39:33 -0500 To: tramp-devel@gnu.org

I want to retrieve the contents of a file created by a shell process, which might be running remotely. My code (below) works, but I am trying to learn how to use tramp, and I think that this is not how it would be done by someone who knew what they were doing.

(defun retrieve-output ()
  (let ((default-directory "/user@host:dirpath")
        (output-file (make-temp-file "zzz-")))
    (shell-command (format "hostname > %s" output-file))
    (insert-file-contents
     (if (file-remote-p default-directory) (make-remote-file-name output-file) output-file))))

(defun make-remote-file-name (file)
  (let* ((vec (tramp-dissect-file-name default-directory))
         (user (tramp-file-name-user vec))
         (host (tramp-file-name-host vec)))
    (concat "/" user (when user "@") host ":" file)))

(retrieve-output)

If default-directory is not remote, then I want this to work for someone who does not have tramp installed (because aIui an XEmacs user might not have tramp?)

In my case I do need to store the output in a file. I.e. although in the example above the output is created by redirecting stdout to file, in general the output of the remote process will not be on stdout (the output file will be created in some other way by the shell process).

One thing that feels like a hack is the way that, when the process runs remotely, I manually convert the temp file path into a remote file path.

Another problem is that with my code there is no guarantee that the temp file name doesn't already exist on the remote host.

Thanks a lot,

Dan

reply from Michael Albinus

From: Michael Albinus <michael.albinus@gmx.de> Subject: Re: retrieving output from temp file User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/23.1.92 (gnu/linux) Date: Sun, 21 Feb 2010 18:57:51 +0100 To: Dan Davison <davison@stats.ox.ac.uk> Cc: tramp-devel@gnu.org

Dan Davison <davison@stats.ox.ac.uk> writes:

> I want to retrieve the contents of a file created by a shell process, > which might be running remotely. My code (below) works, but I am trying > to learn how to use tramp, and I think that this is not how it would be > done by someone who knew what they were doing.

What about

(process-file "process" nil t)

> If default-directory is not remote, then I want this to work for someone > who does not have tramp installed (because aIui an XEmacs user might not > have tramp?)

It works also for a local `default-directory'. XEmacs comes with Tramp 2.0, but it doesn't know `process-file' (yet).

> In my case I do need to store the output in a file. I.e. although in > the example above the output is created by redirecting stdout to file, > in general the output of the remote process will not be on stdout (the > output file will be created in some other way by the shell process).

This case, I would do

(defun retrieve-output ()
  (let ((tmpfile
         (make-temp-file
          (concat (file-remote-p default-directory) "/tmp/zzz-"))))
    (unwind-protect
        (progn
          (process-file
           "process" nil nil nil
           (or (file-remote-p tmpfile 'localname) tmpfile))
          (insert-file-contents tmpfile))
      (delete-file tmpfile))))

(retrieve-output)

I have added the local file name part of tmpfile to the `process-file' call; it depends on the "process" command, where it does expect the output file.

> One thing that feels like a hack is the way that, when the process runs > remotely, I manually convert the temp file path into a remote file path.

`make-temp-file' works also wit a remote prefix, as you see.

> Another problem is that with my code there is no guarantee that the temp > file name doesn't already exist on the remote host.

With this approach, `make-temp-file' does it for you.

> Thanks a lot, > > Dan

Best regards, Michael.

TODO Extend to other languages

  • State "TODO" from "" 2010-02-21 Sun 16:52

We need to extend some of the changes to some of the other languages, e.g. make sure that they construct remote file names when attemtping to read remote data, as in 010cd73feb4a1dcb2da6f9a7352a35cfb4dac00e.

TODO Make gnuplot respond to default-directory

  • State "TODO" from "" 2010-02-28 Sun 15:34

gnuplot currently uses shell-command-to-string. I think this may be the reason why it does not respond to a change in default-directory. We may want to use shell-command-on-region (like many other languages) or shell-command instead.

STARTED Make sure file links are pointing into dir

  • State "STARTED" from "TODO" 2010-03-03 Wed 14:37
  • State "TODO" from "" 2010-02-25 Thu 18:16

E.g. if I use :file pca.png, but the working directory of the R session is "/tmp", then we need to ensure that the org file link points to the location of the file created by R.

TODO relation of :dir and :exports

  • State "TODO" from "" 2010-03-03 Wed 14:48

> on export the following generates a > broken link (link is relative but file is created in ~/images) > > #+beginsrc ditaa :file images/ditaa.png :dir ~ :export results > > Shall we just leave that problem as is for now, or would you prefer it > to be solved before committing? Err, and do you happen to have a good > solution? :) >

Hmm,

I would be inclined to say we've given people alot of rope, and if they're digging this deep into complex combinations of header arguments then we can't stop themselves from hanging themselves. If that sounds good to you then I'd say we're set and go ahead and commit.

Since remote directories are working, the user could always put the path to the directory on their webserver in the :dir option, in which case the links may actually resolve.

PROPOSED should we allow :results file without explicitly giving path?

I.e. should we create a file in /tmp or in the current directory?

DONE Improve support for :session t :results output

  • State "DONE" from "TODO" 2010-02-21 Sun 00:48
  • State "TODO" from "" 2010-02-16 Tue 13:59

DEFERRED :dir has no effect on existing session

  • State "DEFERRED" from "TODO" 2010-02-21 Sun 00:48
  • State "TODO" from "" 2010-02-16 Tue 15:53

Should we warn user in this case? (In general o-b tends to silently ignore some nonsensical header args at the moment I think)

DONE :session t :results value

  • State "DONE" from "TODO" 2010-02-21 Sun 00:49
  • State "TODO" from "PROPOSED" 2010-02-11 Thu 14:40

A remote session can be started using tramp (the way I do it at the moment is to visit a remote file and then issue e.g. M-x R. It will run on the remote machine) or M-x ssh. This will work with :results output. However :results value currently (in the target language) writes the data to a file on the remote machine and then (in elisp) attempts to retrieve it from the local machine. Thus the necessary network data transmission is not currently attempted. Perhaps tramp can be used to achieve this?

Hmm, If there is some programmatic way to detect that the current buffer is visiting a remote machine, then it should be easy to change the code which is writing and reading to temporary files to explicitly do all such operations on the local host. [Eric]

I'm not quite clear on this. With ":results value" in (say) R, the R process has to write the return data to file. If the R process is running remotely then someone (R?, emacs?, shell?) has to transfer the data between machines. If this is to be robust across languages, then we probably want to allow the langauge processes to continue to write locally as they do currently (because the ability of languages to deal with the network transfer will vary), and therefore we would require a new data transfer stage (implemented perhaps in emacs or by standard shell utilities) in order for the data to be read into org-babel [Dan]

I currently don't ever really run anything on a remote machine, but if someone else got this going I'd be happy to help iron out the details. [Eric]

For the record, my situation is the opposite – all the data is stored remotely (and the remote machines have more appropriate computing power than my netbook), but I don't really want to run emacs over ssh, for a variety of reasons (performance, code libraries) – so having org-babel execute remotely is very desirable for me. Having said that, ':session :results output' is working quite adequately; I've only occasionally felt the need for ':results value'. [Dan]

DONE external process evaluation on remote machines

  • State "DONE" from "TODO" 2010-02-21 Sun 00:50
  • State "TODO" from "" 2010-02-11 Thu 14:49

We need:

  • A way to specify the machine on which a remote process should be run (and to retrieve stdout/stderr)
  • A way of retrieving file contents in the case of :results value.

The second of the above should be handled by the remote-results-value case above. As for telling org-babel to start the process on a remote machine, it's not immediately clear to me how that would work.

The first thing that occurs to me is that maybe if we accept a path so that a buffer on the remote machine can be opened using tramp then we can switch to that buffer and remote execution will happen naturally in a language-agnostic way. This is a slight automation of the process described above. So something like

#+begin_src clojure :host my-other-machine:~/some-file
  (println (System/getProperties))
#+end_src

would result in Org-babel first opening a buffer with the value of :host and then running through the same execution pattern as above.