zipimport - Carica Codice Python da Archivi ZIP
Scopo: Importa moduli Python salvati come membri di archivi ZIP.
Il modulo zipimport implementa la classe zipimporter
, che può essere usata per trovare e caricare moduli Python all'interno di archivi in formato .ZIP. zipimporter
supporta l'API di agganci di importazione specificati nel PEP 302; è il modo nel quale funzionano i Python Eggs. Un Python Egg è un formato di distribuzione di pacchetti python - n.d.t..
In genere non è necessario usare il modulo zipimport direttamente, visto che è possibile importare direttamente da un archivio ZIP fintanto che lo stesso sia nel percorso indirizzato da sys.path
. Tuttavia è istruttivo studiare come l'API di importazione possa essere usata, per imparare le funzionalità disponibili e comprendere come funziona l'importazione dei moduli. Conoscere come funziona l'importatore da ZIP aiuta anche a indirizzare problemi di debug che potrebbero sorgere quando si distribuiscono applicazioni basate su archivi ZIP creati con zipfile.PyZipFile
.
Esempio
Questi esempi riutilizzano parte del codice usato per l'articolo su zipfile per creare un archivio ZIP di esempio che contiene alcuni moduli Python.
# zipimport_make_example.py
import sys
import zipfile
if __name__ == '__main__':
zf = zipfile.PyZipFile('esempio_zipimport.zip', mode='w')
try:
zf.writepy('.')
zf.write('zipimport_get_source.py')
zf.write('pacchetto_esempio/README.txt')
finally:
zf.close()
for name in zf.namelist():
print(name)
Per eseguire tutti gli esempi in questo articolo occorre:
- scaricare gli esempi in una directory dedicata
- creare un file
__init__.py
- creare una sottodirectory
pacchetto_esempio
- creare in
pacchetto_esempio
un fileREADME.txt
- creare in
pacchetto_esempio
il file__init__.py
Si esegua poi zipimport_make_example.py
per creare un archivio ZIP che contiene tutti i moduli nella directory di esempio, assieme ad alcuni dati di test necessari per gli esempi di questa sezione.
$ python3 zipimport_make_example.py __init__.pyc zipimport_get_data_zip.pyc zipimport_get_data.pyc zipimport_load_module.pyc zipimport_is_package.pyc zipimport_find_module.pyc pacchetto_esempio/__init__.pyc zipimport_make_example.pyc zipimport_get_code.pyc zipimport_get_source.pyc zipimport_get_data_nozip.pyc zipimport_get_source.py pacchetto_esempio/README.txt
Trovare un Modulo
Dato il nome completo di un modulo, find_module()
cercherà di localizzarlo all'interno dell'archivio ZIP.
# zipimport_find_module.py
import zipimport
importer = zipimport.zipimporter('esempio_zipimport.zip')
for module_name in ['zipimport_find_module', 'non_qui']:
print(module_name, ':', importer.find_module(module_name))
Se il modulo viene trovato, viene ritornata l'istanza di zipimport
. Altrimenti viene restituito None
.
$ python3 zipimport_find_module.py zipimport_find_module : <zipimporter object "esempio_zipimport.zip"> non_qui : None
Accedere al Codice
Il metodo get_code()
carica l'oggetto codice per un modulo dall'archivio.
# zipimport_get_code.py
import zipimport
importer = zipimport.zipimporter('esempio_zipimport.zip')
code = importer.get_code('zipimport_get_code')
print(code)
L'oggetto codice non è lo stesso dell'oggetto module
, ma viene usato per crearne uno.
$ python3 zipimport_get_code.py <code object <module> at 0x7f1c238b31e0, file "./zipimport_get_code.py", line 3>
Per caricare il codice come modulo utilizzabile si usi viceversa load_module()
.
# zipimport_load_module.py
import zipimport
importer = zipimport.zipimporter('esempio_zipimport.zip')
module = importer.load_module('zipimport_get_code')
print('Nome :', module.__name__)
print('Caricatore :', module.__loader__)
print('Codice :', module.code)
Il risultato è un oggetto module
configurato come se il codice fosse stato caricato da una normale importazione.
$ python3 zipimport_load_module.py <code object <module> at 0x7f7c703bae40, file "./zipimport_get_code.py", line 3> Nome : zipimport_get_code Caricatore : <zipimporter object "esempio_zipimport.zip"> Codice : <code object <module> at 0x7f7c703bae40, file "./zipimport_get_code.py", line 3>
Sorgente
Così come per il modulo inspect, è possibile recuperare il codice sorgente per un modulo da un archivio ZIP, se l'archivio include il sorgente. Nel caso dell'esempio, solo zipimport_get_source.py
viene aggiunto ad esempio_zipimport.zip
(il resto dei moduli sono solo aggiunti come file .pyc
).
# zipimport_get_source.py
import zipimport
modules = [
'zipimport_get_code',
'zipimport_get_source',
]
importer = zipimport.zipimporter('esempio_zipimport.zip')
for module_name in modules:
source = importer.get_source(module_name)
print('=' * 80)
print(module_name)
print('=' * 80)
print(source)
print()
Se il sorgente per un modulo non è disponibile, get_source()
ritorna None
.
$ python3 zipimport_get_source.py ================================================================================ zipimport_get_code ================================================================================ None ================================================================================ zipimport_get_source ================================================================================ # zipimport_get_source.py import zipimport modules = [ 'zipimport_get_code', 'zipimport_get_source', ] importer = zipimport.zipimporter('esempio_zipimport.zip') for module_name in modules: source = importer.get_source(module_name) print('=' * 80) print(module_name) print('=' * 80) print(source) print()
Pacchetti
Per determinare se un nome si riferisce a un pacchetto invece che a un normale modulo, si usi is_package()
.
# zipimport_is_package.py
import zipimport
importer = zipimport.zipimporter('esempio_zipimport.zip')
for name in ['zipimport_is_package', 'pacchetto_esempio']:
print(name, importer.is_package(name))
In questo caso zipimport.is_package
proviene da un modulo ed esempio_pacchetto
è un pacchetto.
$ python3 zipimport_is_package.py zipimport_is_package False pacchetto_esempio True
Dati
Ci sono volte nelle quali moduli o pacchetti devono essere distribuiti con dati diversi da codice: immagini, file di configurazione, dati predefiniti, impianti di test sono solo alcuni esempi. Frequentemente gli attributi di modulo __path__
o __file__
sono usati per trovare questi file dati in relazione a dove il codice viene installato.
Ad esempio con un modulo "normale", il percorso di sistema del file può essere costruito con l'attributo __file__
del pacchetto importato così:
# zipimport_get_data_nozip.py
import os
import pacchetto_esempio
# Trova la directory contenente il pacchetto
# importato e da lì costruisce il nome per il file di dati
pkg_dir = os.path.dirname(pacchetto_esempio.__file__)
data_filename = os.path.join(pkg_dir, 'README.txt')
# Legge il file e mostra il suo contenuto.
print(data_filename, ':')
print(open(data_filename, 'r').read())
Il risultato dipenderà da dove si trova il codice di esempio sul file system.
$ python3 zipimport_get_data_nozip.py .../pacchetto_esempio/README.txt : Questo file rappresenta dati di esempio che potrebbero essere inseriti nell'archivio ZIP. Si potrebbe includere un file di configurazione, immagini o qualsiasi altro tipo di dato.
Se esempio_pacchetto
viene importato dall'archivio ZIP invece che dal file system, non funzionerà l'uso di __file__
.
# zipimport_get_data_zip.py
import sys
sys.path.insert(0, 'esempio_zipimport.zip')
import os
import pacchetto_esempio
print(pacchetto_esempio.__file__)
data_filename = os.path.join(
os.path.dirname(pacchetto_esempio.__file__),
'README.txt',
)
print(data_filename, ':')
print(open(data_filename, 'rt').read())
Il __file__
del pacchetto si riferisce all'archivio ZIP, e non a una directory, quindi la costruzione del percorso per README.txt
fornirà un valore errato.
$ python3 zipimport_get_data_zip.py esempio_zipimport.zip/pacchetto_esempio/__init__.pyc esempio_zipimport.zip/pacchetto_esempio/README.txt : Traceback (most recent call last): File "zipimport_get_data_zip.py", line 14, in <module> print(open(data_filename, 'rt').read()) NotADirectoryError: [Errno 20] Not a directory: 'esempio_zipimport.zip/pacchetto_esempio/README.txt'
Un modo più affidabile di recuperare il file è l'uso del metodo get_data()
. L'istanza di zipimporter
che ha caricato il modulo può essere indirizzata attraverso l'attributo __loader__
del modulo importato.
# zipimport_get_data.py
import sys
sys.path.insert(0, 'esempio_zipimport.zip')
import os
import pacchetto_esempio
print(pacchetto_esempio.__file__)
data = pacchetto_esempio.__loader__.get_data(
'pacchetto_esempio/README.txt')
print(data.decode('utf-8'))
pkgutil.get_data()
usa questa interfaccia per accedere a dati dall'interno di un pacchetto. Il valore restituito è una stringa di byte, che deve essere decodificata verso una stringa unicode prima della stampa.
$ python3 zipimport_get_data.py esempio_zipimport.zip/pacchetto_esempio/__init__.pyc Questo file rappresenta dati di esempio che potrebbero essere inseriti nell'archivio ZIP. Si potrebbe includere un file di configurazione, immagini o qualsiasi altro tipo di dato.
__loader__
non è impostato per moduli non importati via zipimport
.
Vedere anche:
- zipimport
- La documentazione della libreria standard per questo modulo.
- Note di portabilità da Python 2 a 3 per zipimport
- pkgutil
- Fornisce una interfaccia più generica a
get_data()
. - zipfile
- Legge e scrive file di archivio ZIP.
- PEP 302
- Nuovi agganci di importazione.