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_esempioun fileREADME.txt - creare in
pacchetto_esempioil 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.