Python subprocess communicate freezes when reading output

♀尐吖头ヾ 提交于 2019-12-08 10:03:07

问题


I'm using Gphoto2 to take pictures on a DSLR. As its based on bash commands I tried to use subprocess.communicate but it freezes after the camera takes a picture.

If I try the gphoto2 --capture-image-and-download in the terminal it takes less than 2 seconds. I'm working on a Raspberry Pi.

Code:

import subprocess

class Wrapper(object):

    def __init__(self, subprocess):
        self._subprocess = subprocess

    def call(self,cmd):
        p = self._subprocess.Popen(cmd, shell=True, stdout=self._subprocess.PIPE, stderr=self._subprocess.PIPE)
        out, err = p.communicate()
        return p.returncode, out.rstrip(), err.rstrip()


class Gphoto(Wrapper):
    def __init__(self, subprocess):
        Wrapper.__init__(self,subprocess)
        self._CMD = 'gphoto2'

    def captureImageAndDownload(self):
        code, out, err = self.call(self._CMD + " --capture-image-and-download")
        if code != 0:
            raise Exception(err)
        filename = None
        for line in out.split('\n'):
            if line.startswith('Saving file as '):
                filename = line.split('Saving file as ')[1]
        return filename


def main():
    camera = Gphoto(subprocess)

    filename = camera.captureImageAndDownload()
    print(filname)

if __name__ == "__main__":
    main()

If I exit I get this:

Traceback (most recent call last):
  File "test.py", line 39, in <module>
   main()
  File "test.py", line 35, in main
    filename = camera.captureImageAndDownload()
  File "test.py", line 22, in captureImageAndDownload
    code, out, err = self.call(self._CMD + " --capture-image-and-download")
  File "test.py", line 11, in call
    out, err = p.communicate()
  File "/usr/lib/python2.7/subprocess.py", line 799, in communicate
    return self._communicate(input)
  File "/usr/lib/python2.7/subprocess.py", line 1409, in _communicate
    stdout, stderr = self._communicate_with_poll(input)
  File "/usr/lib/python2.7/subprocess.py", line 1463, in _communicate_with_poll
    ready = poller.poll()
KeyboardInterrupt

Any ideas?


回答1:


Based on the comments above, here's what we came up with. The .communicate() call hung the program, and much to my suspicion it was because the executed command didn't exit properly.

One thing you can use to get around this is by manually polling the process if it's finished and print the output as you go along.
Now the gist above was written on the phone so it didn't utelize this properly, but here's an example code you can use to catch the output as you go and manually poll the command.

import subprocess
from time import time
class Wrapper():
    def call(self, cmd):
        p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        O = ''
        E = ''
        last = time()
        while p.poll() is None:
            if time() - last > 5:
                print('Process is still running')
                last = time()
            tmp = p.stdout.read(1)
            if tmp:
                O += tmp
            tmp = p.stderr.read(1)
            if tmp:
                E += tmp
        ret = p.poll(), O+p.stdout.read(), E+p.stderr.read() # Catch remaining output
        p.stdout.close() # Always close your file handles, or your OS might be pissed
        p.stderr.close()
        return ret

Three important things to notice, using shell=True might be bad, unsafe and tricky.
I personally favor it because I rarely handle user input or "unknown variables" when I execute stuff. But a few words of caution - Never use it!

Second being, if you don't need to separate error and regular output, you can also do:

Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

It will give you one less file handle to worry about.

Finally thing, ALWAYS empty the stdout/stderr buffers, and always close them. These two things are important.
If you don't empty them, they might themselves hang the application because they're full and Popen can't put more data in them, so it will wait for you (in best case scenario) to empty them.
Second being not closing those file handles, that might render your OS to run out of possible file handles to open (there's only a certain ammount of collective file handles a OS can have open at any given time, so not closing them could render your OS useless for a bit).

(note: Depending on if you're using Python2 or 3, p.stdout.read() might give you bytes data back, meaning O = '' should be O = b'' instead etc)



来源:https://stackoverflow.com/questions/41462957/python-subprocess-communicate-freezes-when-reading-output

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!