6. Exemplos de Distutils
************************

Nota:

  Este documento está sendo mantido apenas até que a documentação do
  "setuptools" em
  https://setuptools.readthedocs.io/en/latest/setuptools.html cubra
  independentemente todas as informações relevantes atualmente
  incluídas aqui.

Este capítulo fornece vários exemplos básicos para ajudar a começar
com distutils. Informações adicionais sobre o uso de distutils podem
ser encontradas no Distutils Cookbook.

Ver também:

  Distutils Cookbook
     Coleção de receitas mostrando como obter mais controle sobre
     distutils.


6.1. Distribuição Python pura (por módulo)
==========================================

Se você está apenas distribuindo alguns módulos, especialmente se eles
não residem em um pacote específico, você pode especificá-los
individualmente usando a opção "py_modules" no script de instalação.

No caso mais simples, você terá dois arquivos com os quais se
preocupar: um script de instalação e o único módulo que você está
distribuindo "foo.py" neste exemplo:

   <root>/
           setup.py
           foo.py

(Em todos os diagramas desta seção, *<root>* se refere ao diretório
raiz da distribuição.) Um script de instalação mínimo para descrever
essa situação seria:

   from distutils.core import setup
   setup(name='foo',
         version='1.0',
         py_modules=['foo'],
         )

Observe que o nome da distribuição é especificado independentemente
com a opção "name", e não existe uma regra que diga que deve ser igual
ao nome do único módulo na distribuição (embora seja provavelmente uma
boa convenção a seguir ) No entanto, o nome da distribuição é usado
para gerar nomes de arquivos, portanto, você deve usar letras,
dígitos, sublinhados e hífenes.

Como "py_modules" é uma lista, é claro que você pode especificar
vários módulos, por exemplo. se você estiver distribuindo os módulos
"foo" e "bar", sua configuração poderá ser assim:

   <root>/
           setup.py
           foo.py
           bar.py

e o script de configuração pode ser

   from distutils.core import setup
   setup(name='foobar',
         version='1.0',
         py_modules=['foo', 'bar'],
         )

Você pode colocar os arquivos fonte do módulo em outro diretório, mas
se você tiver módulos suficientes para fazer isso, provavelmente será
mais fácil especificar módulos por pacote do que listá-los
individualmente.


6.2. Distribuição Python pura (por pacote)
==========================================

Se você tiver mais do que alguns módulos para distribuir,
especialmente se estiverem em vários pacotes, provavelmente será mais
fácil especificar pacotes inteiros do que módulos individuais. Isso
funciona mesmo que seus módulos não estejam em um pacote; você pode
simplesmente dizer ao Distutils para processar os módulos do pacote
raiz, e isso funciona da mesma forma que em qualquer outro pacote
(exceto que você não precisa ter um arquivo "__init__.py").

O script de instalação do último exemplo também pode ser escrito como

   from distutils.core import setup
   setup(name='foobar',
         version='1.0',
         packages=[''],
         )

(A string vazia representa o pacote raiz.)

Se esses dois arquivos forem movidos para um subdiretório, mas
permanecerem no pacote raiz, por exemplo:

   <root>/
           setup.py
           src/      foo.py
                     bar.py

você ainda especificaria o pacote raiz, mas precisará informar ao
Distutils onde estão os arquivos fonte no pacote raiz:

   from distutils.core import setup
   setup(name='foobar',
         version='1.0',
         package_dir={'': 'src'},
         packages=[''],
         )

Mais tipicamente, porém, você deseja distribuir vários módulos no
mesmo pacote (ou em subpacotes). Por exemplo, se os módulos "foo" e
"bar" pertencem ao pacote "foobar", uma maneira de fazer o layout da
sua árvore de fontes é

   <root>/
           setup.py
           foobar/
                    __init__.py
                    foo.py
                    bar.py

Na verdade, esse é o layout padrão esperado pelo Distutils e o que
exige menos trabalho para descrever no seu script de instalação:

   from distutils.core import setup
   setup(name='foobar',
         version='1.0',
         packages=['foobar'],
         )

Se você quiser colocar módulos em diretórios não nomeados para o
pacote, precisará usar a opção "package_dir" novamente. Por exemplo,
se o diretório "src" contiver módulos no pacote "foobar":

   <root>/
           setup.py
           src/
                    __init__.py
                    foo.py
                    bar.py

um script de instalação apropriado seria

   from distutils.core import setup
   setup(name='foobar',
         version='1.0',
         package_dir={'foobar': 'src'},
         packages=['foobar'],
         )

Ou, você pode colocar módulos do seu pacote principal diretamente na
raiz da distribuição:

   <root>/
           setup.py
           __init__.py
           foo.py
           bar.py

nesse caso, seu script de instalação seria

   from distutils.core import setup
   setup(name='foobar',
         version='1.0',
         package_dir={'foobar': ''},
         packages=['foobar'],
         )

(A string vazia também representa o diretório atual.)

Se você possui subpacotes, eles devem ser listados explicitamente em
"packages", mas qualquer entrada em "package_dir" se estende
automaticamente aos subpacotes. (Em outras palavras, o Distutils *não*
varre sua árvore de fontes, tentando descobrir quais diretórios
correspondem aos pacotes Python procurando "__init__.py".) Portanto,
se o layout padrão aumentar um subpacote:

   <root>/
           setup.py
           foobar/
                    __init__.py
                    foo.py
                    bar.py
                    subfoo/
                              __init__.py
                              blah.py

então o script de instalação correspondente seria

   from distutils.core import setup
   setup(name='foobar',
         version='1.0',
         packages=['foobar', 'foobar.subfoo'],
         )


6.3. Módulo de extensão única
=============================

Os módulos de extensão são especificados usando a opção "ext_modules".
"package_dir" não afeta onde os arquivos de origem das extensões são
encontrados; isso afeta apenas a fonte dos módulos Python puros. O
caso mais simples, um único módulo de extensão em um único arquivo
fonte C, é:

   <root>/
           setup.py
           foo.c

Se a extensão "foo" pertencer ao pacote raiz, o script de instalação
para isso pode ser

   from distutils.core import setup
   from distutils.extension import Extension
   setup(name='foobar',
         version='1.0',
         ext_modules=[Extension('foo', ['foo.c'])],
         )

Se a extensão realmente pertence a um pacote, digamos "foopkg", então

Com exatamente o mesmo layout da árvore de fontes, esta extensão pode
ser colocada no pacote "foopkg" simplesmente alterando o nome da
extensão:

   from distutils.core import setup
   from distutils.extension import Extension
   setup(name='foobar',
         version='1.0',
         ext_modules=[Extension('foopkg.foo', ['foo.c'])],
         )


6.4. Verificando um pacote
==========================

O comando "check" permite verificar se os metadados do seu pacote
atendem aos requisitos mínimos para construir uma distribuição.

Para executá-lo, basta chamá-lo usando o script "setup.py". Se algo
estiver faltando, "check" exibirá um aviso.

Vamos dar um exemplo com um script simples:

   from distutils.core import setup

   setup(name='foobar')

A execução do comando "check" exibirá alguns avisos:

   $ python setup.py check
   running check
   warning: check: missing required meta-data: version, url
   warning: check: missing meta-data: either (author and author_email) or
            (maintainer and maintainer_email) must be supplied

Se você usar a sintaxe reStructuredText no campo "long_description" e
o docutils estiver instalado, poderá verificar se a sintaxe está
correta com o comando "check", usando a opção "restructuredtext".

Por exemplo, se o script "setup.py" for alterado assim:

   from distutils.core import setup

   desc = """\
   My description
   ==============

   This is the description of the ``foobar`` package.
   """

   setup(name='foobar', version='1', author='tarek',
       author_email='tarek@ziade.org',
       url='http://example.com', long_description=desc)

Onde a descrição longa está quebrada, "check" poderá detectá-la usando
o analisador "docutils":

   $ python setup.py check --restructuredtext
   running check
   warning: check: Title underline too short. (line 2)
   warning: check: Could not finish the parsing.


6.5. Lendo os metadados
=======================

A função "distutils.core.setup()" fornece uma interface de linha de
comando que permite consultar os campos de metadados de um projeto
através do script "setup.py" de um projeto fornecido:

   $ python setup.py --name
   distribute

Essa chamada lê os metadados "name" executando a função
"distutils.core.setup()". Embora, quando uma distribuição de origem ou
binária é criada com o Distutils, os campos de metadados sejam
gravados em um arquivo estático chamado "PKG-INFO". Quando um projeto
baseado no Distutils é instalado no Python, o arquivo "PKG-INFO" é
copiado juntamente com os módulos e pacotes da distribuição em "NOME-
VERSÃO-pyX.X.egg-info", em que "NOME" é o nome do projeto, "VERSÃO"
sua versão conforme definida nos metadados e "pyX.X" a versão
principal e secundária do Python, como "2.7" ou "3.2".

Você pode ler novamente esse arquivo estático usando a classe
"distutils.dist.DistributionMetadata" e seu método "read_pkg_file()":

   >>> from distutils.dist import DistributionMetadata
   >>> metadata = DistributionMetadata()
   >>> metadata.read_pkg_file(open('distribute-0.6.8-py2.7.egg-info'))
   >>> metadata.name
   'distribute'
   >>> metadata.version
   '0.6.8'
   >>> metadata.description
   'Easily download, build, install, upgrade, and uninstall Python packages'

Observe que a classe também pode ser instanciada com um caminho de
arquivo de metadados para carregar seus valores:

   >>> pkg_info_path = 'distribute-0.6.8-py2.7.egg-info'
   >>> DistributionMetadata(pkg_info_path).name
   'distribute'
