"__main__" --- Ambiente de código principal
*******************************************

======================================================================

Em Python, o nome especial "__main__" é usado em duas construções
importantes:

1. O nome do ambiente principal do programa, que pode ser verificado
   usando a expressão "__name__ == '__main__'"; e

2. o arquivo "__main__.py" em pacotes Python.

Ambas as formas estão relacionadas aos módulos Python; como os
usuários interagem com eles e como eles interagem entre si. Eles serão
explicados em detalhes abaixo. Se você ainda não conhece módulos
Python, veja a seção Módulos para uma introdução.


"__name__ == '__main__'"
========================

Quando um pacote ou módulo Python é importado, "__name__" é definido
como o nome do módulo. Normalmente, este é o nome do próprio arquivo
Python sem a extensão ".py":

   >>> import configparser
   >>> configparser.__name__
   'configparser'

Caso o arquivo seja parte de um pacote, "__name__" também incluirá o
nome da pasta raiz do pacote.

   >>> from concurrent.futures import process
   >>> process.__name__
   'concurrent.futures.process'

Porém, se o módulo for executado como o código principal, "__name__"
passa a ser definido como a string "'__main__'"


O que é o "ambiente de código principal"?
-----------------------------------------

"__main__" é o nome do ambiente principal no qual o código é
executado. "Ambiente de código principal" é o primeiro módulo Python
definido pelo usuário que começa a ser executado. É considerado
principal porque ele importa todos os outros módulos que o programa
precisa. As vezes, "ambiente principal" também pode ser chamado de
"ponto de entrada" da aplicação.

O ambiente de código principal pode ser:

* o escopo de um prompt de comando interativo:

     >>> __name__
     '__main__'

* o módulo Python passado ao interpretador do Python como um argumento
  correspondente ao nome do arquivo:

     $ python helloworld.py
     Hello, world!

* o módulo ou pacote Python passado ao interpretador do Python com o
  argumento "-m":

     $ python -m tarfile
     usage: tarfile.py [-h] [-v] (...)

* código Python lido pelo interpretador através da entrada padrão de
  linha de comando:

     $ echo "import this" | python
     The Zen of Python, by Tim Peters

     Beautiful is better than ugly.
     Explicit is better than implicit.
     ...

* código Python passado ao interpretador do Python com o argumento
  "-c":

     $ python -c "import this"
     The Zen of Python, by Tim Peters

     Beautiful is better than ugly.
     Explicit is better than implicit.
     ...

Em cada uma destas situações, a variável especial "__name__" passa a
ser definida como "'__main__'".

Como resultado, um módulo pode saber se está ou não sendo executado no
ambiente principal verificando seu próprio "__name__", que habilita um
termo comum para executar código condicionalmente quando o módulo não
é inicializado a partir de uma instrução de importação:

   if __name__ == '__main__':
       # Executa quando o módulo não é inicializado por uma instrução de importação.
       ...

Ver também:

  Para uma visão mais detalhada sobre como "__name__" é definido em
  todas as situações, veja a sessão Módulos.


Uso idiomático
--------------

Alguns módulos contém códigos que são específicos para serem usados
como scripts, como análise de argumentos através de linha de comando
ou leitura de dados da entrada padrão. Se um módulo como o citado for
importado em um outro módulo diferente, por exemplo em testes
unitários, o código do script também seria executado de forma
indesejada.

É aqui onde o uso do trecho de código "if __name__ == '__main__'"
revela-se útil. Os códigos dentro desta condicional não rodarão ao não
ser que o módulo seja executado através do ambiente principal.

Colocar o mínimo de instruções possível no bloco abaixo de "if
__name__ == '__main__'" pode melhorar a clareza e a precisão do
código. Na maioria das vezes, uma função chamada de "main" encapsula o
comportamento principal do programa:

   # echo.py

   import shlex
   import sys

   def echo(phrase: str) -> None:
      """Um invólucro fictício sobre print."""
      # com propósito de demonstração, imagine que existe
      # uma lógica reutilizável valiosa nesta função
      print(phrase)

   def main() -> int:
       """Ecoa os argumentos de entrada para a saída padrão."""
       phrase = shlex.join(sys.argv)
       echo(phrase)
       return 0

   if __name__ == '__main__':
       sys.exit(main())  # a próxima seção explica o uso de sys.exit

Repare quem se o módulo, ao invés de ter encapsulado o código dentro
da função "main", fosse colocado direto dentro do bloco "if __name__
== '__main__'", a variável "phrase" seria global para todo o módulo.
Isto é suscetível à erros pois outras funções dentro do módulo
poderiam inadvertidamente usar a variável global ao invés da local. A
função "main" resolve este problema.

O uso da função "main" tem o benefício adicional de a própria função
"echo" ser isolada e importável em outro lugar. Quando "echo.py" é
importado, as funções "echo" e "main" serão definidas, mas nenhuma
delas será chamada por conta do bloco "__name__ != '__main__'".


Considerações sobre pacotes
---------------------------

"main" são funções frequentemente usadas para criar ferramentas de
linha de comando especificando-as como pontos de entrada para scripts
de console. Quando isto é feito, pip insere a chamada da função em um
modelo de script, onde o valor de retorno de "main" é passado para
"sys.exit()". Por exemplo:

   sys.exit(main())

Uma vez que a chamada à "main" está embutida dentro de "sys.exit()", a
expectativa é que a sua função retorne somente valores aceitável para
"sys.exit()"; normalmente um inteiro ou "None" (retornado de forma
implícita, caso a sua função não tenha uma instrução de retorno).

Seguindo proativamente essa convenção, nosso módulo terá o mesmo
comportamento quando executado diretamente (ou seja, "python echo.py")
como terá também se posteriormente criarmos um pacote como um ponto de
entrada de script de console em um pacote instalável via pip.

Em particular, tenha cuidado ao retornar strings de sua função "main".
"sys.exit()" interpretará um argumento de string como uma mensagem de
falha, então seu programa terá um código de saída "1", indicando
falha, e a string será escrita em "sys.stderr". O exemplo anterior de
"echo.py" exemplifica o uso da convenção "sys.exit(main())".

Ver também:

  O Guia de Usuário para Empacotamento de Python contém uma coleção de
  tutoriais e referências sobre como distribuir e instalar pacotes
  Python com ferramentas modernas.


"__main__.py" em pacotes Python
===============================

Se você não estiver familiarizado com pacotes Python, veja a seção
Pacotes do tutorial. Mais comumente, o arquivo "__main__.py" é usado
para fornecer uma interface de linha de comando para um pacote.
Considere o seguinte pacote hipotético, "bandclass":

   bandclass
     ├── __init__.py
     ├── __main__.py
     └── student.py

"__main__.py" será executado quando o próprio pacote for invocado
diretamente da linha de comando usando o sinalizador "-m". Por
exemplo:

   $ python -m bandclass

Este comando fará com que "__main__.py" seja executado. Como você
utiliza esse mecanismo dependerá da natureza do pacote que você está
escrevendo, mas neste caso hipotético, pode fazer sentido permitir que
o professor procure alunos:

   # bandclass/__main__.py

   import sys
   from .student import search_students

   student_name = sys.argv[1] if len(sys.argv) >= 2 else ''
   print(f'Estudante encontrado(a): {search_students(student_name)}')

Observe que "from .student import search_students" é um exemplo de
importação relativa. Esse estilo de importação pode ser usado ao fazer
referência a módulos em um pacote. Para mais detalhes, veja
Referências em um mesmo pacote na seção Módulos do tutorial.


Uso idiomático
--------------

O conteúdo do "__main__.py" normalmente não é protegido por um bloco
"if __name__ == '__main__'" .  Em vez disso, esses arquivos são
mantidos curtos e importa funções para serem executados a partir de
outros módulos.  Esses outros módulos podem então ter suas unidades
facilmente testadas e são adequadamente reutilizáveis.

Se usado, o bloco condicional "if __name__ == '__main__'" ainda
funcionará como esperado para o arquivo "__main__.py" dentro de um
pacote, pois seu atributo "__name__" incluirá o caminho do pacote se
importado:

   >>> import asyncio.__main__
   >>> asyncio.__main__.__name__
   'asyncio.__main__'

Porém, isso não funcionará para arquivos "__main__.py" no diretório
raiz de um arquivo ".zip". Portanto, para consistência, é preferível
um "__main__.py" mínimo sem uma verificação de "__name__".

Ver também:

  Veja "venv", da biblioteca padrão, para um exemplo de pacote com um
  "__main__.py" minimalista. Ele não contém um bloco "if __name__ ==
  '__main__'". Você pode invocá-lo com "python -m venv [directory]".

  Veja "runpy" para mais detalhes sobre o sinalizador "-m" para o
  executável do interpretador.

  Veja "zipapp" para saber como executar aplicativos compactados como
  arquivos *.zip*. Nesse caso, o Python procura um arquivo
  "__main__.py" no diretório raiz do arquivo.


"import __main__"
=================

Independentemente do módulo com o qual um programa Python foi
iniciado, outros módulos executados no mesmo programa podem importar o
escopo do ambiente principal (*espaço de nomes*) importando o módulo
"__main__". Isso não faz a importação de um arquivo "__main__.py", mas
sim qualquer módulo que recebeu o nome especial "'__main__'".

Aqui está um módulo de exemplo que consome o espaço de nomes
"__main__":

   # namely.py

   import __main__

   def did_user_define_their_name():
       return 'my_name' in dir(__main__)

   def print_user_name():
       if not did_user_define_their_name():
           raise ValueError('Defina a variável `my_name`!')

       if '__file__' in dir(__main__):
           print(__main__.my_name, "encontrado no arquivo", __main__.__file__)
       else:
           print(__main__.my_name)

Exemplo de uso deste módulo pode ser como abaixo:

   # start.py

   import sys

   from namely import print_user_name

   # my_name = "Dinsdale"

   def main():
       try:
           print_user_name()
       except ValueError as ve:
           return str(ve)

   if __name__ == "__main__":
       sys.exit(main())

Agora, se iniciarmos nosso programa, o resultado seria assim:

   $ python start.py
   Defina a variável `my_name`!

O código de saída do programa seria 1, indicando um erro. Descomentar
a linha com "my_name = "Dinsdale"" corrige o programa e agora ele sai
com o código de status 0, indicando sucesso:

   $ python start.py
   Dinsdale encontrado no arquivo /path/to/start.py

Observe que a importação de "__main__" não causa nenhum problema com a
execução involuntária de código principal destinado ao uso de script
que é colocado no bloco "if __name__ == "__main__"" do módulo "start".
Por que isso funciona?

O Python insere um módulo "__main__" vazio em "sys.modules" na
inicialização do interpretador e o preenche executando o código
principal. Em nosso exemplo, este é o módulo "start" que executa linha
por linha e importa "namely". Por sua vez, "namely" importa "__main__"
(que é realmente "start"). Isso é um ciclo de importação! Felizmente,
como o módulo "__main__" parcialmente preenchido está presente em
"sys.modules", o Python o passa para "namely". Veja Considerações
especiais sobre __main__ na referência do sistema de importação para
detalhes sobre como isso funciona.

O REPL do Python é outro exemplo de um "ambiente principal", então
qualquer coisa definida no REPL se torna parte do escopo do
"__main__":

   >>> import namely
   >>> namely.did_user_define_their_name()
   False
   >>> namely.print_user_name()
   Traceback (most recent call last):
   ...
   ValueError: Defina a variável `my_name`!
   >>> my_name = 'Jabberwocky'
   >>> namely.did_user_define_their_name()
   True
   >>> namely.print_user_name()
   Jabberwocky

Note que neste caso o escopo "__main__" não contém um atributo
"__file__", pois é interativo.

O escopo "__main__" é usado na implementação de "pdb" e "rlcompleter".
