subprocess.check_output () wydaje się nie istnieć (Python 2.6.5)

79

Czytałem dokumentację Pythona o module podprocesu (patrz tutaj ) i mówi o subprocess.check_output()poleceniu, które wydaje się być dokładnie tym, czego potrzebuję.

Jednak kiedy próbuję go użyć, pojawia się błąd, że nie istnieje, a kiedy dir(subprocess)go uruchamiam, nie ma go na liście.

Używam Pythona 2.6.5, a kod, którego użyłem, jest poniżej:

import subprocess
subprocess.check_output(["ls", "-l", "/dev/null"])

Czy ktoś ma pojęcie, dlaczego tak się dzieje?

robintw
źródło

Odpowiedzi:

123

Został wprowadzony w 2.7. Zobacz dokumentację .

Użyj subprocess.Popen, jeśli chcesz uzyskać wynik:

>>> import subprocess
>>> output = subprocess.Popen(['ls', '-l'], stdout=subprocess.PIPE).communicate()[0]
user225312
źródło
14
w przeciwieństwie do check_output, nie jest to zgłaszane, CalledProcessErrorgdy proces zwraca niezerowy kod powrotu.
Sridhar Ratnakumar
1
@SridharRatnakumar: oczywiście, ponieważ jest między nimi duża różnica, a mianowicie: blokowanie i nieblokowanie. Są do różnych zastosowań!
lpapp
I pchnął go w lambdaten sposób: check_output = lambda args: Popen(args, stdout = PIPE).communicate()[0]. Tylko dlatego, że jestem w interaktywnym tłumaczu i zapisywanie w nich definicji funkcji wielowierszowych jest czymś w rodzaju PITA. Użyłem from subprocess import Popen, PIPEwcześniej w sesji.
ArtOfWarfare
jak więc zrobić ping? czy nadal mogę używać Popen lub?
TheCrazyProfessor
56

JEŚLI jest intensywnie używany w kodzie, który chcesz uruchomić, ale ten kod nie musi być utrzymywany przez długi czas (lub potrzebujesz szybkiej naprawy niezależnie od potencjalnych problemów związanych z konserwacją w przyszłości), możesz uchylić się przed uderzeniem (inaczej łatka małpa) wszędzie tam, gdzie importowany jest podproces ...

Po prostu podnieś kod z 2.7 i wstaw go w ten sposób ...

import subprocess

if "check_output" not in dir( subprocess ): # duck punch it in!
    def f(*popenargs, **kwargs):
        if 'stdout' in kwargs:
            raise ValueError('stdout argument not allowed, it will be overridden.')
        process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs)
        output, unused_err = process.communicate()
        retcode = process.poll()
        if retcode:
            cmd = kwargs.get("args")
            if cmd is None:
                cmd = popenargs[0]
            raise subprocess.CalledProcessError(retcode, cmd)
        return output
    subprocess.check_output = f

Może być wymagane lekkie wiercenie się.

Pamiętaj, że to na tobie spoczywa obowiązek utrzymania takich brudnych, małych backportów. Jeśli błędy zostaną wykryte i poprawione w najnowszym Pythonie, to a) musisz to zauważyć i b) zaktualizować swoją wersję, jeśli chcesz zachować bezpieczeństwo. Ponadto, samodzielne nadpisywanie i definiowanie funkcji wewnętrznych jest najgorszym koszmarem następnego faceta, zwłaszcza gdy następny facet jest TY kilka lat później i zapomniałeś o wstrętnych hackach, które zrobiłeś ostatnim razem! Podsumowując: bardzo rzadko jest to dobry pomysł.

Roger Heathcote
źródło
2
Zgadzam się z tą metodą. Prawdopodobnie podałbym lokalizację źródła. Możesz go znaleźć na hg.python.org/cpython/file/d37f963394aa/Lib/subprocess.py#l544
Ehtesh Choudhury
1
Uwaga: CalledProcessError nie akceptuje danych wyjściowych w Pythonie 2.6. (Natychmiast ugryzłem mnie po użyciu tego hacka! :()
Andy Hayden,
cpython jest teraz na GitHubie - check_outputdla Pythona 2.7 jest obecnie tutaj: github.com/python/cpython/blob/2.7/Lib/subprocess.py#L194
jamesc
6

Dzięki sugestii małpy dotyczącej łatki (i moim próbom się nie powiodło - ale korzystaliśmy z wyjścia CalledProcessError, więc potrzebowaliśmy małpy to załatać)

znalazłem działającą łatkę 2.6 tutaj: http://pydoc.net/Python/pep8radius/0.9.0/pep8radius.shell/

"""Note: We also monkey-patch subprocess for python 2.6 to
give feature parity with later versions.
"""
try:
    from subprocess import STDOUT, check_output, CalledProcessError
except ImportError:  # pragma: no cover
    # python 2.6 doesn't include check_output
    # monkey patch it in!
    import subprocess
    STDOUT = subprocess.STDOUT

    def check_output(*popenargs, **kwargs):
        if 'stdout' in kwargs:  # pragma: no cover
            raise ValueError('stdout argument not allowed, '
                             'it will be overridden.')
        process = subprocess.Popen(stdout=subprocess.PIPE,
                                   *popenargs, **kwargs)
        output, _ = process.communicate()
        retcode = process.poll()
        if retcode:
            cmd = kwargs.get("args")
            if cmd is None:
                cmd = popenargs[0]
            raise subprocess.CalledProcessError(retcode, cmd,
                                                output=output)
        return output
    subprocess.check_output = check_output

    # overwrite CalledProcessError due to `output`
    # keyword not being available (in 2.6)
    class CalledProcessError(Exception):

        def __init__(self, returncode, cmd, output=None):
            self.returncode = returncode
            self.cmd = cmd
            self.output = output

        def __str__(self):
            return "Command '%s' returned non-zero exit status %d" % (
                self.cmd, self.returncode)
    subprocess.CalledProcessError = CalledProcessError
zapalony
źródło