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