views:

1750

answers:

4

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
+1  A: 

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.

namaste, Mark

Mark Hattarki
A: 

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
I didn't downvote your answer, but keep in mind that the subprocess module is meant to replace the popen* modules.
ΤΖΩΤΖΙΟΥ
+7  A: 

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
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
@gatoatigrado - at least on my systems, `/dev/fd` is a symlink to `/proc/self/fd`
Charles Duffy
+2  A: 

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.

pjz
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