Manage resources in pyz applications

999 views Asked by At

I have a multi-module python application that I have packed in a pyz executable with python3 -m zipapp.

I would like to deploy this application with some resources it needs to run (some text files). I have tried to just add these text files to project_root/res and then just do something like with open("./res/resource1.txt", "r") as f: do_something() but of course the file is not found.

How can I read a text file that is embedded within the zipapp application?

1

There are 1 answers

2
wallefan On BEST ANSWER
import pkgutil
pkgutil.get_data(<module name>, 'resource1.txt')

will return a bytes object.

<module name> should be a string containing the path to the folder where resource1.txt is, as though you were trying to import it; e.g. if wanted /res/some_subfolder/foo.txt then you would do pkgutil.get_data('yourmodule.res.some_subfolder', 'foo.txt').

Note that res and some_subfolder in this instance must be full on Python modules, not just directories (the difference being the presence or absence of an empty file named __init__.py); otherwise zipapp will ignore them. Your folder structure in in the above example must therefore be:

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

Again, these files can be blank if you wish, they just have to exist (although if you do put code in them, you can then do import res (or import res.some_subfolder) and it will run res/__init__.py, which could be a useful way to organize all of your resoures from one place if you feel like doing it that way). By the way, if anyone reading this is, like the majority of Python programmers, unfamiliar with how modules work, here's a quick tutorial from the Python docs.


Additionally, if you are just using zipapp (as opposed to an exe created with pyinstaller) you can simply:

 import sys
 import zipfile
 
 with zipfile.ZipFile(sys.argv[0]) as zf:
      with zf.open('res/resource1.txt') as f:
           print(f.read())