7

I would like to replicate this in python:

gvimdiff <(hg cat file.txt) file.txt

(hg cat file.txt outputs the most recently committed version of file.txt)

I know how to pipe the file to gvimdiff, but it won't accept another file:

$ hg cat file.txt | gvimdiff file.txt -
Too many edit arguments: "-"

Getting to the python part...

# hgdiff.py
import subprocess
import sys
file = sys.argv[1]
subprocess.call(["gvimdiff", "<(hg cat %s)" % file, file])

When subprocess is called it merely passes <(hg cat file) onto gvimdiff as a filename.

So, is there any way to redirect a command as bash does? For simplicity's sake just cat a file and redirect it to diff:

diff <(cat file.txt) file.txt
alif
  • 619
  • 1
  • 6
  • 10

4 Answers4

10

It can be done. As of Python 2.5, however, this mechanism is Linux-specific and not portable:

import subprocess
import sys

file = sys.argv[1]
p1 = subprocess.Popen(['hg', 'cat', file], stdout=subprocess.PIPE)
p2 = subprocess.Popen([
    'gvimdiff',
    '/proc/self/fd/%s' % p1.stdout.fileno(),
    file])
p2.wait()

That said, in the specific case of diff, you can simply take one of the files from stdin, and remove the need to use the bash-alike functionality in question:

file = sys.argv[1]
p1 = subprocess.Popen(['hg', 'cat', file], stdout=subprocess.PIPE)
p2 = subprocess.Popen(['diff', '-', file], stdin=p1.stdout)
diff_text = p2.communicate()[0]
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • Nice solution. Also of interest is what bash does: > python -c "import sys; print(sys.argv)" <(echo hello) function_proj.cc ['-c', '/dev/fd/63', 'function_proj.cc'] – gatoatigrado Apr 09 '09 at 04:09
  • @gatoatigrado - at least on my systems, `/dev/fd` is a symlink to `/proc/self/fd` – Charles Duffy Jun 06 '10 at 18:27
2

There is also the commands module:

import commands

status, output = commands.getstatusoutput("gvimdiff <(hg cat file.txt) file.txt")

There is also the popen set of functions, if you want to actually grok the data from a command as it is running.

Mark Hattarki
  • 123
  • 2
  • 8
2

This is actually an example in the docs:

p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
output = p2.communicate()[0]

which means for you:

import subprocess
import sys

file = sys.argv[1]
p1 = Popen(["hg", "cat", file], stdout=PIPE)
p2 = Popen(["gvimdiff", "file.txt"], stdin=p1.stdout, stdout=PIPE)
output = p2.communicate()[0]

This removes the use of the linux-specific /proc/self/fd bits, making it probably work on other unices like Solaris and the BSDs (including MacOS) and maybe even work on Windows.

twasbrillig
  • 17,084
  • 9
  • 43
  • 67
pjz
  • 41,842
  • 6
  • 48
  • 60
  • This answers the question the poster should have asked, and not the one he did -- but it's a good answer nonetheless, so I'm nabbing it; hope you don't mind. :) (btw, gvimdiff's docs don't indicate that it can read from stdin; did you try that?) – Charles Duffy Sep 17 '08 at 03:06
-1

It just dawned on me that you are probably looking for one of the popen functions.

from: http://docs.python.org/lib/module-popen2.html

popen3(cmd[, bufsize[, mode]]) Executes cmd as a sub-process. Returns the file objects (child_stdout, child_stdin, child_stderr).

namaste, Mark

Mark Hattarki
  • 123
  • 2
  • 8
  • I didn't downvote your answer, but keep in mind that the subprocess module is meant to replace the popen* modules. – tzot Apr 24 '09 at 22:23