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
====================

async 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 invólucros
   "StreamReader" para "stdout" e "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*.

async 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 invólucros
   "StreamReader" para "stdout" e "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.

   async 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.

   async communicate(input=None)

      Interage com processo:

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

      2. fecha *stdin*;

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

      4. 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.

      Alterado na versão 3.12: *stdin* é fechado quando "input=None" é
      também.

   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 "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.

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

Ver também: A seção Concorrência e multithreading em asyncio.


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())'

       # Cria um subprocesso; redireciona a saída padrão
       # para o encadeamento.
       proc = await asyncio.create_subprocess_exec(
           sys.executable, '-c', code,
           stdout=asyncio.subprocess.PIPE)

       # Lê uma linha da saída.
       data = await proc.stdout.readline()
       line = data.decode('ascii').rstrip()

       # Espera o subprocesso sair.
       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.
