0

I am trying to send arguments to a subprocess' stdin. In my case, it is an image downloaded with Requsts.

Here is my code:

from subprocess import Popen, PIPE, STDOUT
img = requests.get(url, stream=True)
i = img.raw.read()
proc = subprocess.Popen(['icat', '-'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)
proc.communicate(i)
#proc.stdin.write(i) # I tried this too

Unfortunately, the subprocess does nothing, and I get no errors. What is wrong with my code, and is there a cross-platform solution?

octosquidopus
  • 3,517
  • 8
  • 35
  • 53
  • You are not actually streaming anything here, so you can just use `img.content`. You are also ignoring the *output* from `proc`. – Martijn Pieters Nov 17 '14 at 19:36

1 Answers1

1

icat queries your terminal to see what dimensions to resize the image to, but a pipe is not suitable as a terminal and you end up with empty output. The help information from icat states:

Big images are automatically resized to your terminal width, unless with the -k option.

When you use the -k switch output is produced.

There is no need to stream here, you can just leave the loading to requests and pass in the response body, un-decoded:

img = requests.get(url)
proc = subprocess.Popen(['icat', '-k', '-'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)
stdout, stderr = proc.communicate(img.content)

The stderr value will be empty, but stdout should contain transformed image data (ANSI colour escapes):

>>> import requests
>>> import subprocess
>>> url = 'https://www.gravatar.com/avatar/24780fb6df85a943c7aea0402c843737'
>>> img = requests.get(url)
>>> from subprocess import PIPE, STDOUT
>>> proc = subprocess.Popen(['icat', '-k', '-'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)
>>> stdout, stderr = proc.communicate(img.content)
>>> len(stdout)
77239
>>> stdout[:20]
'\x1b[38;5;15m\x1b[48;5;15m'
octosquidopus
  • 3,517
  • 8
  • 35
  • 53
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • I'm using this [icat](https://github.com/atextor/icat), and the image still doesn't show up. – octosquidopus Nov 17 '14 at 19:43
  • @octosquidopus: that command requires a suitable terminal. A pipe is *not* a suitable terminal. – Martijn Pieters Nov 17 '14 at 19:44
  • @octosquidopus: hrm, I may be incorrect about `icat` here; I see that they also offer `> imagedata.txt` options. I have no problems using `cat -` to pipe an image through a subprocess however. – Martijn Pieters Nov 17 '14 at 19:52
  • Really? Bummer. In that case, I suppose you've answered my original question and I thank you for the clear example, but is there a way of using [icat](https://github.com/atextor/icat) with Python? – octosquidopus Nov 17 '14 at 19:52
  • @octosquidopus: BTW, I see that when you use `icat -`, the image data is [written to a temporary file](https://github.com/atextor/icat/blob/master/icat.c#L269-L280); IIRC you wanted to avoid that. – Martijn Pieters Nov 17 '14 at 19:55
  • @octosquidopus: can you try `icat -k -`? I see that the terminal is only queried to get the image size. Alternatively, set `-x` and `-y` to the output dimensions you want. – Martijn Pieters Nov 17 '14 at 19:57
  • @octosquidopus: I'll need to install XQuartz to test locally, I'll see about trying that later tonight. – Martijn Pieters Nov 17 '14 at 19:58
  • OK, I'll try `icat -k -` right away. Wow, you're very helpful! I wanted to avoid writin to a temporary file needlessly, but I didn't know that `icat` was doing it behind my back. It's fine, I suppose. – octosquidopus Nov 17 '14 at 19:59
  • Weird. My `len(stdout)` and `len(img.content)` don't match with `icat -k -`. – octosquidopus Nov 17 '14 at 20:03
  • @octosquidopus that's no surprise;,`icat` transforms the image data to an ANSI sequence. – Martijn Pieters Nov 17 '14 at 20:44
  • @octosquidopus: confirmed that `-k` fixes your issue. – Martijn Pieters Nov 17 '14 at 22:19
  • Notes to self: 1) add `print stdout` at the end. 2) buy Martijn Pieters a beer someday. – octosquidopus Nov 17 '14 at 23:22
  • I am struggling to make two pipes communicate. How can I resize `url` with `convert` before piping its output to `icat`? – octosquidopus Dec 08 '14 at 11:30
  • @octosquidopus: [How do I use subprocess.Popen to connect multiple processes by pipes?](http://stackoverflow.com/q/295459) and [link several Popen commands with pipes](http://stackoverflow.com/q/7389662) – Martijn Pieters Dec 08 '14 at 11:39
  • What I am trying to do seems considerably more complex than what I can glean from the examples given on these questions, and I cannot figure out the subprocess.Popen docs either. Should I edit my question here, or open a new question instead? I would really need some sort of in-depth explanation of the steps involved, as it currently feels mindbogglingly complex and I have tried everything. – octosquidopus Dec 09 '14 at 23:48
  • @octosquidopus you'd create a new question for that. – Martijn Pieters Dec 09 '14 at 23:49