# 4. Construindo extensões C e C++

Uma extensão C para CPython é uma biblioteca compartilhada (por exemplo, um arquivo `.so` no Linux, `.pyd` no Windows), que exporta uma função de inicialização.

Para ser importável, a biblioteca compartilhada deve estar disponível em `PYTHONPATH`, e deve ser nomeada após o nome do módulo, com uma extensão apropriada. Ao usar distutils, o nome do arquivo correto é gerado automaticamente.

A função de inicialização tem a assinatura:

PyObject *PyInit_modulename(void)

Ela retorna um módulo totalmente inicializado ou uma instância de `PyModuleDef`. Veja Inicializando módulos C para detalhes.

Para módulos com nomes somente ASCII, a função deve ser nomeada `PyInit_<nomemódulo>`, com `<nomemódulo>` substituído pelo nome do módulo. Ao usar Inicialização multifásica, nomes de módulos não ASCII são permitidos. Neste caso, o nome da função de inicialização é `PyInitU_<nomemódulo>`, com `<nomemódulo>` codificado usando a codificação punycode do Python com hifenes substituídos por sublinhados. Em Python:

    def initfunc_name(name):
        try:
            suffix = b'_' + name.encode('ascii')
        except UnicodeEncodeError:
            suffix = b'U_' + name.encode('punycode').replace(b'-', b'_')
        return b'PyInit' + suffix

É possível exportar vários módulos de uma única biblioteca compartilhada, definindo várias funções de inicialização. No entanto, importá-los requer o uso de links simbólicos ou um importador personalizado, porque por padrão apenas a função correspondente ao nome do arquivo é encontrada. Veja a seção “Multiple modules in one library” na PEP 489 para detalhes.

## 4.1. Construindo extensões C e C ++ com distutils

Módulos de extensão podem ser construídos usando distutils, que está incluído no Python. Visto que distutils também suporta a criação de pacotes binários, os usuários não precisam necessariamente de um compilador e distutils para instalar a extensão.

Um pacote distutils contém um script guia `setup.py`. Este é um arquivo Python simples, que, no caso mais simples, pode ter a seguinte aparência:

    from distutils.core import setup, Extension

    module1 = Extension('demo',
                        sources = ['demo.c'])

    setup (name = 'PackageName',
           version = '1.0',
           description = 'This is a demo package',
           ext_modules = [module1])

Com este `setup.py` e um arquivo `demo.c`, executando

    python setup.py build

vai compilar `demo.c`, e produzir um módulo de extensão chamado `demo` no diretório `build`. Dependendo do sistema, o arquivo do módulo vai terminar em um subdiretório `build/lib.system`, e pode ter um nome como `demo.so` ou `demo.pyd`.

No `setup.py`, toda a execução é realizada chamando a função `setup`. Isso leva um número variável de argumentos nomeados, dos quais o exemplo acima usa apenas um subconjunto. Especificamente, o exemplo especifica meta-informação para construir pacotes e especifica o conteúdo do pacote. Normalmente, um pacote conterá módulos adicionais, como módulos-fonte Python, documentação, subpacotes etc. Consulte a documentação do distutils em Distribuindo Módulos Python (Versão legada) para aprender mais sobre os recursos do distutils; esta seção explica apenas a construção de módulos de extensão.

É comum pré-computar argumentos para `setup()`, para melhor estruturar o script guia. No exemplo acima, o argumento `ext_modules` para `setup()` é uma lista de módulos de extensão, cada um dos quais é uma instância de `Extension`. No exemplo, a instância define uma extensão chamada `demo` que é construída compilando um único arquivo fonte, `demo.c`.

Em muitos casos, construir uma extensão é mais complexo, uma vez que definições de pré-processador adicionais e bibliotecas podem ser necessárias. Isso é demonstrado no exemplo abaixo.

    from distutils.core import setup, Extension

    module1 = Extension('demo',
                        define_macros = [('MAJOR_VERSION', '1'),
                                         ('MINOR_VERSION', '0')],
                        include_dirs = ['/usr/local/include'],
                        libraries = ['tcl83'],
                        library_dirs = ['/usr/local/lib'],
                        sources = ['demo.c'])

    setup (name = 'PackageName',
           version = '1.0',
           description = 'This is a demo package',
           author = 'Martin v. Loewis',
           author_email = 'martin@v.loewis.de',
           url = 'https://docs.python.org/extending/building',
           long_description = '''
    This is really just a demo package.
    ''',
           ext_modules = [module1])

Neste exemplo, `setup()` é chamado com meta-informação adicional, o que é recomendado quando os pacotes de distribuição precisam ser construídos. Para a extensão em si, especifica definições de pré-processador, diretórios de inclusão, diretórios de biblioteca e bibliotecas. Dependendo do compilador, distutils passa essas informações de maneiras diferentes para o compilador. Por exemplo, no Unix, isso pode resultar nos comandos de compilação

    gcc -DNDEBUG -g -O3 -Wall -Wstrict-prototypes -fPIC -DMAJOR_VERSION=1 -DMINOR_VERSION=0 -I/usr/local/include -I/usr/local/include/python2.2 -c demo.c -o build/temp.linux-i686-2.2/demo.o

    gcc -shared build/temp.linux-i686-2.2/demo.o -L/usr/local/lib -ltcl83 -o build/lib.linux-i686-2.2/demo.so

Essas linhas são apenas para fins de demonstração; Os usuários de distutils devem confiar que distutils acertará as invocações.

## 4.2. Distribuindo seus módulos de extensão

Quando uma extensão é construída com sucesso, há três maneiras de usá-la.

Os usuários finais normalmente desejam instalar o módulo, eles fazem isso executando

    python setup.py install

Os mantenedores do módulo devem produzir pacotes fonte; para fazer isso, eles executam:

    python setup.py sdist

Em alguns casos, arquivos adicionais precisam ser incluídos em uma distribuição fonte; isso é feito através de um arquivo `MANIFEST.in`; veja Specifying the files to distribute para detalhes.

Se a distribuição fonte foi construída com sucesso, os mantenedores também podem criar distribuições binárias. Dependendo da plataforma, um dos seguintes comandos pode ser usado para fazer isso.

    python setup.py bdist_rpm
    python setup.py bdist_dumb