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