Как проверить, выдает ли Popen из подпроцесса ошибку - PullRequest
0 голосов
/ 16 января 2019

Я пишу конвейер, который использует несколько вызовов bash в определенном порядке.

Как я могу определить, что моя команда вызвала ошибку?

Например, я запускаю программу Java с подпроцессом, и он не предупреждает меня и не завершает работу, когда этот процесс вызывает ошибки.

Есть ли что-то в subprocess или в моем process объекте, который имеет эту утилиту?

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

Когда я пробую предложение от Python: проверка "subprocess.Popen" на наличие ошибок и ошибок Я получаю следующие ошибки:

In [6]: subprocess.check_call(process)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-6-f8f8752a245f> in <module>
----> 1 subprocess.check_call(process)

~/anaconda/envs/µ_env/lib/python3.6/subprocess.py in check_call(*popenargs, **kwargs)
    284     check_call(["ls", "-l"])
    285     """
--> 286     retcode = call(*popenargs, **kwargs)
    287     if retcode:
    288         cmd = kwargs.get("args")

~/anaconda/envs/µ_env/lib/python3.6/subprocess.py in call(timeout, *popenargs, **kwargs)
    265     retcode = call(["ls", "-l"])
    266     """
--> 267     with Popen(*popenargs, **kwargs) as p:
    268         try:
    269             return p.wait(timeout=timeout)

~/anaconda/envs/µ_env/lib/python3.6/subprocess.py in __init__(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags, restore_signals, start_new_session, pass_fds, encoding, errors)
    707                                 c2pread, c2pwrite,
    708                                 errread, errwrite,
--> 709                                 restore_signals, start_new_session)
    710         except:
    711             # Cleanup if the child failed starting.

~/anaconda/envs/µ_env/lib/python3.6/subprocess.py in _execute_child(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, restore_signals, start_new_session)
   1218                 args = [args]
   1219             else:
-> 1220                 args = list(args)
   1221
   1222             if shell:

TypeError: 'Popen' object is not iterable

In [7]: subprocess.check_output(process)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-7-0ec9e7eac1c2> in <module>
----> 1 subprocess.check_output(process)

~/anaconda/envs/µ_env/lib/python3.6/subprocess.py in check_output(timeout, *popenargs, **kwargs)
    334
    335     return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,
--> 336                **kwargs).stdout
    337
    338

~/anaconda/envs/µ_env/lib/python3.6/subprocess.py in run(input, timeout, check, *popenargs, **kwargs)
    401         kwargs['stdin'] = PIPE
    402
--> 403     with Popen(*popenargs, **kwargs) as process:
    404         try:
    405             stdout, stderr = process.communicate(input, timeout=timeout)

~/anaconda/envs/µ_env/lib/python3.6/subprocess.py in __init__(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags, restore_signals, start_new_session, pass_fds, encoding, errors)
    707                                 c2pread, c2pwrite,
    708                                 errread, errwrite,
--> 709                                 restore_signals, start_new_session)
    710         except:
    711             # Cleanup if the child failed starting.

~/anaconda/envs/µ_env/lib/python3.6/subprocess.py in _execute_child(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, restore_signals, start_new_session)
   1218                 args = [args]
   1219             else:
-> 1220                 args = list(args)
   1221
   1222             if shell:

TypeError: 'Popen' object is not iterable

1 Ответ

0 голосов
/ 16 января 2019

И check_call, и check_output должны быть переданы список команд, которые должны быть запущены системой команд (тот же список команд, который будет отправлен на Popen). Оба они являются блокирующими вызовами, это означает, что Python будет ждать, пока они не закончат выполнять больше кода.

Использование Popen отправляет список команд в систему команд, но не блокирует дальнейшее выполнение кода Python. Вы можете зарегистрироваться в процессе, используя метод .poll объекта Popen, или вы можете сделать блокирующий вызов, используя .communicate, который вернет кортеж из стандартных выходных и стандартных потоков ошибок.

Предполагая, что вы хотите получить результаты выполненной команды, и команда будет правильно сообщать об ошибке в поток ошибок, вы можете использовать:

process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = process.communicate()

if err:
     print('The process raised an error:', err.decode())

Вот несколько примеров:

Использование Popen:

import subprocess

# first a command that works correctly
proc = subprocess.Popen(['ls', '-a'], , stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = proc.communicate()
if not err:
    print('--No errors--\n', out.decode())
else:
    print('--Error--\n', err.decode())

# prints:
--No errors--
anaconda3
Desktop
Documents
Downloads

# next a command generates and error, the `-w` switch is invalid
proc = subprocess.Popen(['ls', '-w'], , stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = proc.communicate()
if not err:
    print('--No errors--\n', out.decode())
else:
    print('--Error--\n', err.decode())

# prints:
--Error--
 ls: option requires an argument -- 'w'
Try 'ls --help' for more information.

Использование check_call

check_call вызовет исключение Python, если код возврата из системы команд не равен 0.

# first with a working command:
ret_code = subprocess.check_call(['ls', '-a'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
ret_code
# returns:
0

# and now with the command that generates an error:
ret_code = subprocess.check_call(['ls', '-w'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)

# raises an exception:
---------------------------------------------------------------------------
CalledProcessError                        Traceback (most recent call last)
<ipython-input-25-3cd5107991a2> in <module>()
----> 1 ret_code = subprocess.check_call(['ls', '-w'], stdout=subprocess.PIPE, 
        stderr=subprocess.PIPE)

~/anaconda3/lib/python3.6/subprocess.py in check_call(*popenargs, **kwargs)
    289         if cmd is None:
    290             cmd = popenargs[0]
--> 291         raise CalledProcessError(retcode, cmd)
    292     return 0
    293

CalledProcessError: Command '['ls', '-w']' returned non-zero exit status 2.

Для обработки исключения используйте блок try/except.

try:
    ret_code = subprocess.check_call(['ls', '-w'], stdout=subprocess.PIPE, 
        stderr=subprocess.PIPE)
except subprocess.CalledProcessError as e:
    ret_code = e.returncode
    print('An error occurred.  Error code:', ret_code)

# prints:
An error occurred.  Error code: 2

Использование check_output

check_output очень похож на check_call в том, что он вызовет исключение Python, если код возврата из системы команд не равен 0. Однако, если код возврата равен 0, он вернет вывод в стандарте выходной поток.

# just with the error process this time
try:
    ret_code = subprocess.check_output(['ls', '-w'], stdout=subprocess.PIPE, 
        stderr=subprocess.PIPE)
except subprocess.CalledProcessError as e:
    ret_code = e.returncode
    print('An error occurred.  Error code:', ret_code)

# prints:
An error occurred.  Error code: 2
...