Subprocesso
***********

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, loop=None, limit=None, **kwds)

   Cria um subprocesso.

   O argumento *limit* define o buffer limite 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.

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

   Executa o comando *cmd* no shell.

   O argumento *limit* define o buffer limite 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.

Nota:

  The default asyncio event loop implementation on **Windows** does
  not support subprocesses. Subprocesses are available for Windows if
  a "ProactorEventLoop" is used. See Subprocess Support on Windows for
  details.

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

Ambas as funções "create_subprocess_exec()" e
"create_subprocess_shell()" retornam instâncias da classe *Process*.
*Process* é um wrapper 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 tem um
     equivalente ao método "poll()";

   * os métodos "communicate()" e "wait()" não tem 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 é seguro 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 partid 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()" function, 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
---------------------

Standard asyncio event loop supports running subprocesses from
different threads, but there are limitations:

* An event loop must run in the main thread.

* The child watcher must be instantiated in the main thread before
  executing subprocesses from other threads. Call the
  "get_child_watcher()" function in the main thread to instantiate the
  child watcher.

Note that alternative event loop implementations might not share the
above limitations; please refer to their documentation.

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

       # 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

   if sys.platform == "win32":
       asyncio.set_event_loop_policy(
           asyncio.WindowsProactorEventLoopPolicy())

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

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