Subprocessos

Código-fonte: Lib/asyncio/subprocess.py, Lib/asyncio/base_subprocess.py


Esta seção descreve APIs async/await asyncio de alto nível para criar e gerenciar subprocessos.

Aqui está um exemplo de como asyncio pode executar um comando shell e obter o seu resultado:

import asyncio

async def run(cmd):
    proc = await asyncio.create_subprocess_shell(
        cmd,
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.PIPE)

    stdout, stderr = await proc.communicate()

    print(f'[{cmd!r} exited with {proc.returncode}]')
    if stdout:
        print(f'[stdout]\n{stdout.decode()}')
    if stderr:
        print(f'[stderr]\n{stderr.decode()}')

asyncio.run(run('ls /zzz'))

irá exibir:

['ls /zzz' exited with 1]
[stderr]
ls: /zzz: No such file or directory

Devido ao fato que todas as funções de subprocessos asyncio são assíncronas e asyncio fornece muitas ferramentas para trabalhar com tais funções, é fácil executar e monitorar múltiplos subprocessos em paralelo. É na verdade trivial modificar o exemplo acima para executar diversos comandos simultaneamente:

async def main():
    await asyncio.gather(
        run('ls /zzz'),
        run('sleep 1; echo "hello"'))

asyncio.run(main())

Veja também a subseção Exemplos.

Criando subprocessos

coroutine asyncio.create_subprocess_exec(program, *args, stdin=None, stdout=None, stderr=None, limit=None, **kwds)

Cria um subprocesso.

O argumento limit define o limite do buffer para os wrappers StreamReader para Process.stdout e Process.stderr (se subprocess.PIPE for passado para os argumentos stdout e stderr).

Retorna uma instância de Process.

Veja a documentação de loop.subprocess_exec() para outros parâmetros.

Alterado na versão 3.10: Removido o parâmetro loop.

coroutine asyncio.create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, limit=None, **kwds)

Executa o comando cmd no shell.

O argumento limit define o limite do buffer para os wrappers StreamReader para Process.stdout e Process.stderr (se subprocess.PIPE for passado para os argumentos stdout e stderr).

Retorna uma instância de Process.

Veja a documentação de loop.subprocess_shell() para outros parâmetros.

Importante

É responsabilidade da aplicação garantir que todos os espaços em branco e caracteres especiais tenham aspas apropriadamente para evitar vulnerabilidades de injeção de shell. A função shlex.quote() pode ser usada para escapar espaços em branco e caracteres especiais de shell apropriadamente em strings que serão usadas para construir comandos shell.

Alterado na versão 3.10: Removido o parâmetro loop.

Nota

Subprocessos estão disponíveis para Windows se uma ProactorEventLoop for usada. Veja Suporte para subprocesso para Windows para detalhes.

Ver também

asyncio também tem as seguintes APIs de baixo nível para trabalhar com subprocessos: loop.subprocess_exec(), loop.subprocess_shell(), loop.connect_read_pipe(), loop.connect_write_pipe(), assim como os Transportes de Subprocesso e Protocolos de Subprocesso.

Constantes

asyncio.subprocess.PIPE

Pode ser passado para os parâmetros stdin, stdout ou stderr.

Se PIPE for passado para o argumento stdin, o atributo Process.stdin irá apontar para uma instância StreamWriter.

Se PIPE for passado para os argumentos stdout ou stderr, os atributos Process.stdout e Process.stderr irão apontar para instâncias StreamReader.

asyncio.subprocess.STDOUT

Valor especial que pode ser usado como o argumento stderr e indica que a saída de erro padrão deve ser redirecionada para a saída padrão.

asyncio.subprocess.DEVNULL

Valor especial que pode ser usado como argumento stdin, stdout ou stderr para funções de criação de processos. Ele indica que o arquivo especial os.devnull será usado para o fluxo de subprocesso correspondente.

Interagindo com subprocessos

Ambas as funções create_subprocess_exec() e create_subprocess_shell() retornam instâncias da classe Process. Process é um invólucro de alto nível que permite a comunicação com subprocessos e observar eles serem completados.

class asyncio.subprocess.Process

Um objeto que envolve processos do sistema operacional criados pelas funções create_subprocess_exec() e create_subprocess_shell().

Esta classe é projetada para ter uma API similar a classe subprocess.Popen, mas existem algumas diferenças notáveis:

  • ao contrário de Popen, instâncias de Process não têm um equivalente ao método poll();

  • os métodos communicate() e wait() não têm um parâmetro timeout: utilize a função wait_for();

  • o método Process.wait() é assíncrono, enquanto que o método subprocess.Popen.wait() é implementado como um laço bloqueante para indicar que está ocupado;

  • o parâmetro universal_newlines não é suportado.

Esta classe não é segura para thread.

Veja também a seção Subprocesso e Threads.

coroutine wait()

Aguarda o processo filho encerrar.

Define e retorna o atributo returncode.

Nota

Este método pode entrar em deadlock ao usar stdout=PIPE ou stderr=PIPE e o processo filho gera tantas saídas que ele bloqueia a espera pelo encadeamento de buffer do sistema operacional para aceitar mais dados. Use o método communicate() ao usar encadeamentos para evitar essa condição.

coroutine communicate(input=None)

Interage com processo:

  1. envia dados para stdin (se input for diferente de None);

  2. lê dados a partir de stdout e stderr, até que EOF (fim do arquivo) seja atingido;

  3. aguarda o processo encerrar.

O argumento opcional input é a informação (objeto bytes) que será enviada para o processo filho.

Retorna uma tupla (stdout_data, stderr_data).

Se qualquer exceção BrokenPipeError ou ConnectionResetError for levantada ao escrever input em stdin, a exceção é ignorada. Esta condição ocorre quando o processo encerra antes de todos os dados serem escritos em stdin.

Se for desejado enviar dados para o stdin do processo, o mesmo precisa ser criado com stdin=PIPE. De forma similar, para obter qualquer coisa além de None na tupla resultante, o processo precisa ser criado com os argumentos stdout=PIPE e/ou stderr=PIPE.

Perceba que, os dados lidos são armazenados em um buffer na memória, então não use este método se o tamanho dos dados é grande ou ilimitado.

send_signal(signal)

Envia o sinal signal para o processo filho.

Nota

No Windows, SIGTERM é um apelido para terminate(). CTRL_C_EVENT e CTRL_BREAK_EVENT podem ser enviados para processos iniciados com um parâmetro creationflags, o qual inclui CREATE_NEW_PROCESS_GROUP.

terminate()

Interrompe o processo filho.

Em sistemas POSIX este método envia signal.SIGTERM para o processo filho.

No Windows a função TerminateProcess() da API Win32 é chamada para interromper o processo filho.

kill()

Mata o processo filho.

Em sistemas POSIX este método envia SIGKILL para o processo filho.

No Windows, este método é um atalho para terminate().

stdin

Fluxo de entrada padrão (StreamWriter) ou None se o processo foi criado com stdin=None.

stdout

Fluxo de saída padrão (StreamReader) ou None se o processo foi criado com stdout=None.

stderr

Erro de fluxo padrão (StreamReader) ou None se o processo foi criado com stderr=None.

Aviso

Use o método communicate() ao invés de process.stdin.write(), await process.stdout.read() ou await process.stderr.read(). Isso evita deadlocks devido a fluxos pausando a leitura ou escrita, e bloqueando o processo filho.

pid

Número de identificação do processo (PID).

Perceba que para processos criados pela função create_subprocess_shell(), este atributo é o PID do console gerado.

returncode

Retorna o código do processo quando o mesmo terminar.

Um valor None indica que o processo ainda não terminou.

Um valor negativo -N indica que o filho foi terminado pelo sinal N (POSIX apenas).

Subprocesso e Threads

Laço de eventos padrão do asyncio suporta a execução de subprocessos a partir de diferentes threads por padrão.

No Windows, subprocessos são fornecidos pela classe ProactorEventLoop apenas (por padrão), a classe SelectorEventLoop não tem suporte a subprocesso.

Em sistemas UNIX, monitores de filhos são usados para aguardar o encerramento de subprocesso, veja Monitores de processos para mais informações.

Alterado na versão 3.8: UNIX mudou para usar ThreadedChildWatcher para gerar subprocessos a partir de diferentes threads sem qualquer limitação.

Gerar um subprocesso com um monitor inativo para o filho atual, levanta RuntimeError.

Perceba que implementações alternativas do laço de eventos podem ter limitações próprias; por favor, verifique a sua documentação.

Exemplos

Um exemplo de uso da classe Process para controlar um subprocesso e a classe StreamReader para ler a partir da sua saída padrão.

O subprocesso é criado pela função create_subprocess_exec():

import asyncio
import sys

async def get_date():
    code = 'import datetime; print(datetime.datetime.now())'

    # Create the subprocess; redirect the standard output
    # into a pipe.
    proc = await asyncio.create_subprocess_exec(
        sys.executable, '-c', code,
        stdout=asyncio.subprocess.PIPE)

    # Read one line of output.
    data = await proc.stdout.readline()
    line = data.decode('ascii').rstrip()

    # Wait for the subprocess exit.
    await proc.wait()
    return line

date = asyncio.run(get_date())
print(f"Current date: {date}")

Veja também o mesmo exemplo escrito usando APIs de baixo nível.