Управление ресурсами в приложениях pyz

У меня есть многомодульное приложение Python, которое я упаковал в pyz исполняемый файл с python3 -m zipapp.

Я хотел бы развернуть это приложение с некоторыми ресурсами, которые ему необходимы для запуска (некоторые текстовые файлы). Я попытался просто добавить эти текстовые файлы вproject_root/res а потом просто сделай что-нибудь вроде with open("./res/resource1.txt", "r") as f: do_something() но конечно файл не найден.

Как я могу прочитать текстовый файл, встроенный в zipapp применение?

1 ответ

Решение
import pkgutil
pkgutil.get_data(<module name>, 'resource1.txt')

вернет объект байтов.

<module name>должен быть строкой, содержащей путь к папке, в которой находится resource1.txt, как если бы вы пытались его импортировать; например, если хотите/res/some_subfolder/foo.txt тогда ты бы сделал pkgutil.get_data('yourmodule.res.some_subfolder', 'foo.txt').

Обратите внимание, что res а также some_subfolder в этом случае должно быть полно модулей Python, а не только каталогов (разница заключается в наличии или отсутствии пустого файла с именем __init__.py); в противном случае zipapp проигнорирует их. Таким образом, ваша структура папок в приведенном выше примере должна быть:

project root
 | main.py
 | <whatever other files you have>
 | res
 |  | __init__.py
 |  | some_subfolder
 |  |  | __init__.py
 |  |  | foo.txt

Опять же, эти файлы могут быть пустыми, если вы хотите, они просто должны существовать (хотя, если вы поместите в них код, вы можете сделать import res (или import res.some_subfolder) и он будет работать res/__init__.py, который может быть полезным способом организовать все ваши ресурсы из одного места, если вам так хочется). Между прочим, если кто-то, читающий это, как и большинство программистов Python, не знаком с принципами работы модулей, вот краткое руководство из документации Python.


Кроме того, если вы просто используете zipapp (в отличие от exe, созданного с помощью pyinstaller), вы можете просто:

 import sys
 import zipfile
 
 with zipfile.ZipFile(sys.argv[0]) as zf:
      with zf.open('res/resource1.txt') as f:
           print(f.read())
Другие вопросы по тегам