Subsections


2.3 Descrivere i moduli di estensione

Come la scrittura di moduli di estensione risulta un po' più complicata della scrittura di moduli in puro Python, anche la loro descrizione alle Distutils è lievemente più complicata. Diversamente dai moduli puri, non è sufficiente una semplice lista di moduli o package ed aspettarsi che le Distutils trovino autonomamente i giusti file; si dovranno specificare i nomi delle estensioni, i file sorgenti ed ogni richiesta di compilazione/link (incluse directory, librerie da linkare, etc. etc.).

Tutto questo viene fatto attraverso un altro argomento chiave di setup(), l'opzione extension. extension è semplicemente una lista di istanze Extension, ognuna delle quali descrive un singolo modulo di estensione. Supponiamo che la distribuzione includa una singola estensione, chiamata foo ed implementata da foo.c. Se non sono necessarie ulteriori istruzioni per il compilatore/linker, descrivere quest'estensione è molto semplice:

Extension('foo', ['foo.c'])

La classe Extension può essere importata da distutils.core attraverso setup(). Pertanto, lo script di setup per la distribuzione di un modulo che contenga solamente un'estensione e niente altro, potrebbe essere:

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

La classe Extension (attualmente, quell'importante meccanismo di estensione/compilazione implementato dal comando build_ext) si comporta in maniera molto flessibile nei riguardi della descrizione delle estensioni Python e viene spiegata nella sezione seguente.

2.3.1 Nomi di estensioni e package

Il primo argomento del costruttore Extension è sempre il nome dell'estensione, incluso ogni nome di package. Per esempio:

Extension('foo', ['src/foo1.c', 'src/foo2.c'])

descrive un'estensione che si trova nel package principale, mentre:

Extension('pkg.foo', ['src/foo1.c', 'src/foo2.c'])

descrive la stessa estensione nel package pkg. Il file sorgente ed il risultante codice oggetto sono identici in entrambi i casi; l'unica differenza è dove nel filesystem (e pertanto all'interno della gerarchia dello spazio dei nomi di Python) le risultanti estensioni risiedono.

Se si hanno a disposizione un gran numero di estensioni, tutte all'interno dello stesso package (o tutte sotto lo stesso package base), si dovrà usare l'argomento chiave ext_package della funzione setup(). Per esempio:

setup(...
      ext_package='pkg',
      ext_modules=[Extension('foo', ['foo.c']),
                   Extension('subpkg.bar', ['bar.c'])],
     )

compilerà foo.c nell'estensione pkg.foo, e bar.c in pkg.subpkg.bar.

2.3.2 Estensioni di file sorgenti

Il secondo argomento del costruttore Extension è una lista di file sorgenti. Siccome le Distutils supportano correntemente solo estensioni C, C++, e Objective-C, sono presenti normalmente solo file sorgenti C/C++/Objective-C. Si deve essere sicuri di usare le estensioni appropriate per distinguere il file sorgente C++: .cc e .cpp sembrano essere riconosciuti sia dai compilatori Unix che Windows.

Comunque, potete anche includere nell'elenco file di interfaccia SWIG (.i); il comando build_ext sa come comportarsi con le estensioni SWIG: esegue SWIG sul file di interfaccia e compila il file risultante C/C++ nell'estensione scelta.

** Il supporto SWIG è ancora da rifinire e largamente non testato; specialmente il supporto SWIG per l'estensione C++! Vi saranno in questo documento spiegazioni più dettagliate quando l'interfaccia sarà completa. **

Su alcune piattaforme, si possono includere file non sorgenti che verranno elaborati dal compilatore ed inclusi nell'estensione. Attualmente, questo è valido solo per file di messaggi di testo Windows (.mc) e file di definizione di risorse per Visual C++ (.res), che saranno linkati all'interno dell'eseguibile.

2.3.3 Opzioni del preprocessore

Tre argomenti facoltativi di Extension saranno di aiuto se si ha bisogno di directory include da cercare o macro per preprocessori per definire/non-definire: include_dirs, define_macros e undef_macros.

Per esempio, se l'estensione richiede file di intestazione nella directory include, sotto la directory principale della distribuzione, si usa l'opzione include_dirs:

Extension('foo', ['foo.c'], include_dirs=['include'])

Qui si può specificare la directory assoluta; se siamo a conoscenza che l'estensione verrà compilata solo su sistemi Unix con X11R6 installato in /usr, si può procedere con

Extension('foo', ['foo.c'], include_dirs=['/usr/include/X11'])

Si dovrebbe evitare questa specie di uso non portabile se si stabilisce di distribuire il proprio codice: è sicuramente meglio scrivere codice C come:

#include <X11/Xlib.h>

Se si ha la necessità di includere file di intestazione di qualche altra estensione Python, ci si può avvantaggiare dal fatto che i file di intestazione vengono installati in modo coerente dal comando Distutils install_header. Per esempio, i file di intestazione Python per la rappresentazione numerica vengono installati (nelle installazioni standard Unix) in /usr/local/include/python1.5/Numerical. La posizione esatta può differire rispetto alla propria piattaforma ed al tipo di installazione Python. Finché la directory di inclusione di Python--in questo caso /usr/local/include/python1.5 -- viene sempre inclusa nel percorso di ricerca, quando si compilano le estensioni Python, il migliore approccio è scrivere codice C come:

#include <Numerical/arrayobject.h>

Se si deve inserire la directory degli include Numerical direttamente nel proprio percorso di ricerca delle intestazioni, comunque si può cercare questa directory usando il modulo Distutils distutils.sysconfig:

from distutils.sysconfig import get_python_inc
incdir = os.path.join(get_python_inc(plat_specific=1), 'Numerical')
setup(...,
      Extension(..., include_dirs=[incdir]),
      )

Si può considerare questo metodo portabile--lavorerà su ogni installazione Python, indipendentemente dalla piattaforma, è probabilmente più facile da scrivere così che direttamente in codice C.

Si possono definire o non-definire macro per il preprocessore con le opzioni define_macros e undef_macros. define_macros prende una lista di tuple (name, value), dove name è il nome della macro da definire (una stringa) e value è il suo valore: può essere una stringa o None. (Definire una macro #define F00 con none è l'equivalente di un semplce #define F00 all'interno di codice sorgente C: con la maggior parte dei compilatori, questo imposta F00 nella stringa 1). undef_macros è semplicemente una lista di macro che non necessitano di essere definite.

Per esempio:

Extension(...,
          define_macros=[('NDEBUG', '1'),
                         ('HAVE_STRFTIME', None)],
          undef_macros=['HAVE_FOO', 'HAVE_BAR'])

è equivalente ad avere questo codice al principio del proprio codice sorgente in C:

#define NDEBUG 1
#define HAVE_STRFTIME
#undef HAVE_FOO
#undef HAVE_BAR

2.3.4 Opzioni per le librerie

Si possono anche specificare le librerie da linkare quando si compila la propria estensione e le directory dove cercare queste librerie. L'opzione libraries è una lista di librerie da linkare insieme, library_dirs è una lista di directory dove cercare le librerie al momento del linking e runtime_library_dirs è una lista di directory dove cercare per librerie condivise (caricate dinamicamente) durante l'esecuzione.

Per esempio, se si ha la necessità di linkare librerie che si trovano all'interno del percorso di ricerca della libreria standard sul sistema di destinazione:

Extension(...,
          libraries=['gdbm', 'readline'])

Se si necessita di eseguire il link con la libreria, in posizioni non standard, se ne deve includere la posizione in library_dirs

Extension(...,
          library_dirs=['/usr/X11R6/lib'],
          libraries=['X11', 'Xt'])

Nuovamente, questa sorta di costrutto non portabile dovrebbe essere evitato se si desidera distribuire il proprio codice.

** Si dovrebbe menzionare la libreria clib qui o da qualche altra parte! **

2.3.5 Altre opzioni

Ci sono alcune altre opzioni che possono essere utilizzate per gestire casi particolari.

L'opzione extra_objects è una lista di oggetti file da passare al linker. Questi file non devono avere estensione, oppure dovranno avere l'estensione predefinita utilizzata dal compilatore.

Le opzioni extra_compile_args ed extra_link_args possono essere usate per specificare opzioni da riga di comando facoltative per le rispettive righe di comando del compilatore e del linker.

export_symbols è utile solo in ambiente Windows. Può contenere una lista di simboli (funzioni o variabili) che devono essere esportati. Questa opzione non è necessaria quando si preparano estensioni compilate: Distutils aggiunge automaticamente initmodule alla lista dei simboli esportati.

Vedete Circa questo documento... per informazioni su modifiche e suggerimenti.