Рубрики
Без рубрики

zipimport – загрузка кода Python из ZIP-архивов

Автор оригинала: Doug Hellmann.

Цель:

Импортируйте модули Python, сохраненные как члены ZIP-архивов.

Модуль zipimport реализует класс zipimporter , который можно использовать для поиска и загрузки модулей Python внутри ZIP-архивов. zipimporter поддерживает API перехватчиков импорта, указанный в PEP 302; вот как работают Python Eggs.

Обычно нет необходимости использовать модуль zipimport напрямую, так как можно импортировать непосредственно из ZIP-архива, если этот архив находится в sys.path . Однако поучительно изучить, как можно использовать API импортера, изучить доступные функции и понять, как работает импорт модулей. Знание того, как работает импортер ZIP, также поможет отладить проблемы, которые могут возникнуть при распространении приложений в виде ZIP-архивов, созданных с помощью zipfile.PyZipFile .

Пример

В этих примерах повторно используется часть кода из обсуждения zipfile для создания примера ZIP-архива, содержащего несколько модулей Python.

zipimport_make_example.py

import sys
import zipfile

if __name__  '__main__':
    zf  zipfile.PyZipFile('zipimport_example.zip', mode'w')
    try:
        zf.writepy('.')
        zf.write('zipimport_get_source.py')
        zf.write('example_package/README.txt')
    finally:
        zf.close()
    for name in zf.namelist():
        print(name)

Запустите zipimport_make_example.py перед любым из остальных примеров, чтобы создать ZIP-архив, содержащий все модули в каталоге примеров, а также некоторые тестовые данные, необходимые для примеров в этом разделе.

$ python3 zipimport_make_example.py

__init__.pyc
example_package/__init__.pyc
zipimport_find_module.pyc
zipimport_get_code.pyc
zipimport_get_data.pyc
zipimport_get_data_nozip.pyc
zipimport_get_data_zip.pyc
zipimport_get_source.pyc
zipimport_is_package.pyc
zipimport_load_module.pyc
zipimport_make_example.pyc
zipimport_get_source.py
example_package/README.txt

Поиск модуля

Учитывая полное имя модуля, find_module () попытается найти этот модуль внутри ZIP-архива.

zipimport_find_module.py

import zipimport

importer  zipimport.zipimporter('zipimport_example.zip')

for module_name in ['zipimport_find_module', 'not_there']:
    print(module_name, ':', importer.find_module(module_name))

Если модуль найден, возвращается экземпляр zipimporter . В противном случае возвращается None .

$ python3 zipimport_find_module.py

zipimport_find_module : 
not_there : None

Код доступа

Метод get_code () загружает объект кода для модуля из архива.

zipimport_get_code.py

import zipimport

importer  zipimport.zipimporter('zipimport_example.zip')
code  importer.get_code('zipimport_get_code')
print(code)

Объект кода не такой же, как объект module , но используется для его создания.

$ python3 zipimport_get_code.py

 at 0x1012b4ae0, file
"./zipimport_get_code.py", line 6>

Чтобы загрузить код как используемый модуль, используйте вместо него load_module () .

zipimport_load_module.py

import zipimport

importer  zipimport.zipimporter('zipimport_example.zip')
module  importer.load_module('zipimport_get_code')
print('Name   :', module.__name__)
print('Loader :', module.__loader__)
print('Code   :', module.code)

Результатом является объект модуля, настроенный так, как если бы код был загружен из обычного импорта.

$ python3 zipimport_load_module.py

 at 0x1007b4c00, file
"./zipimport_get_code.py", line 6>
Name   : zipimport_get_code
Loader : 
Code   :  at 0x1007b4c00, file
"./zipimport_get_code.py", line 6>

Источник

Как и в случае с модулем inspect, можно получить исходный код модуля из архива ZIP, если архив включает исходный код. В случае примера только zipimport_get_source.py добавляется в zipimport_example.zip (остальные модули просто добавляются как .pyc файлы).

zipimport_get_source.py

import zipimport

modules  [
    'zipimport_get_code',
    'zipimport_get_source',
]

importer  zipimport.zipimporter('zipimport_example.zip')
for module_name in modules:
    source  importer.get_source(module_name)
    print( * 80)
    print(module_name)
    print( * 80)
    print(source)
    print()

Если источник для модуля недоступен, get_source () возвращает None .

$ python3 zipimport_get_source.py

zipimport_get_code
None

zipimport_get_source
#!/usr/bin/env python3
#
# Copyright 2007 Doug Hellmann.
#
"""Retrieving the source code for a module within a zip archive.
"""

#end_pymotw_header
import zipimport

modules = [
    'zipimport_get_code',
    'zipimport_get_source',
]

importer = zipimport.zipimporter('zipimport_example.zip')
for module_name in modules:
    source = importer.get_source(module_name)
   
    print(module_name)
   
    print(source)
    print()

Пакеты

Чтобы определить, относится ли имя к пакету, а не к обычному модулю, используйте is_package () .

zipimport_is_package.py

import zipimport

importer  zipimport.zipimporter('zipimport_example.zip')
for name in ['zipimport_is_package', 'example_package']:
    print(name, importer.is_package(name))

В этом случае zipimport_is_package был получен из модуля, а example_package – это пакет.

$ python3 zipimport_is_package.py

zipimport_is_package False
example_package True

Данные

Бывают случаи, когда исходные модули или пакеты необходимо распространять с данными, не содержащими кода. Образы, файлы конфигурации, данные по умолчанию и тестовые инструменты – лишь несколько примеров этого. Часто атрибуты модуля __path__ или __file__ используются для поиска этих файлов данных относительно места установки кода.

Например, для «обычного» модуля путь к файловой системе может быть построен из атрибута __file__ импортированного пакета следующим образом:

zipimport_get_data_nozip.py

import os
import example_package

# Find the directory containing the imported
# package and build the data filename from it.
pkg_dir  os.path.dirname(example_package.__file__)
data_filename  os.path.join(pkg_dir, 'README.txt')

# Read the file and show its contents.
print(data_filename, ':')
print(open(data_filename, 'r').read())

Результат будет зависеть от того, где находится образец кода в файловой системе.

$ python3 zipimport_get_data_nozip.py

.../example_package/README.txt :
This file represents sample data which could be embedded in the
ZIP archive.  You could include a configuration file, images, or
any other sort of noncode data.

Если example_package импортируется из ZIP-архива, а не из файловой системы, использование __file__ не работает.

zipimport_get_data_zip.py

import sys
sys.path.insert(0, 'zipimport_example.zip')

import os
import example_package
print(example_package.__file__)
data_filename  os.path.join(
    os.path.dirname(example_package.__file__),
    'README.txt',
)
print(data_filename, ':')
print(open(data_filename, 'rt').read())

__file__ пакета относится к ZIP-архиву, а не к каталогу, поэтому создание пути к файлу README.txt дает неверное значение.

$ python3 zipimport_get_data_zip.py

zipimport_example.zip/example_package/__init__.pyc
zipimport_example.zip/example_package/README.txt :
Traceback (most recent call last):
  File "zipimport_get_data_zip.py", line 20, in 
    print(open(data_filename, 'rt').read())
NotADirectoryError: [Errno 20] Not a directory:
'zipimport_example.zip/example_package/README.txt'

Более надежный способ получить файл – использовать метод get_data () . К экземпляру zipimporter , который загрузил модуль, можно получить доступ через атрибут __loader__ импортированного модуля:

zipimport_get_data.py

import sys
sys.path.insert(0, 'zipimport_example.zip')

import os
import example_package
print(example_package.__file__)
data  example_package.__loader__.get_data(
    'example_package/README.txt')
print(data.decode('utf-8'))

pkgutil.get_data () использует этот интерфейс для доступа к данным из пакета. Возвращаемое значение представляет собой байтовую строку, которую необходимо декодировать в строку Unicode перед печатью.

$ python3 zipimport_get_data.py

zipimport_example.zip/example_package/__init__.pyc
This file represents sample data which could be embedded in the
ZIP archive.  You could include a configuration file, images, or
any other sort of noncode data.

__loader__ не установлен для модулей, не импортированных через zipimport .

Смотрите также