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:file: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

When an extension has been successfully build, there are three ways to use it.

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.

If the source distribution has been build successfully, maintainers can also create binary distributions. Depending on the platform, one of the following commands can be used to do so.

python setup.py bdist_wininst
python setup.py bdist_rpm
python setup.py bdist_dumb