如何访问 zipapp 中的文件
How to access a file in zipapp
我使用以下命令构建了一个简单的 Python zipapp:
python -m pkg/ -c -o test -p '/usr/bin/python3' -m 'test:main' zipapp
我想从脚本访问二进制文件
$ cat pkg/test.py
def main():
with open('test.bin', 'rb') as f:
print(f.name)
目录结构
$ tree pkg/
pkg/
├── test.bin
└── test.py
0 directories, 2 files
但看起来脚本引用的是当前目录中的文件:
$ ./test
Traceback (most recent call last):
File "/usr/lib64/python3.7/runpy.py", line 193, in _run_module_as_main
"__main__", mod_spec)
File "/usr/lib64/python3.7/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "./test/__main__.py", line 3, in <module>
File "./test/test.py", line 2, in main
FileNotFoundError: [Errno 2] No such file or directory: 'test.bin'
这是一个相当大的二进制文件,所以我想避免创建变量。有没有办法从脚本本身访问这个文件?
好的,看来我可以在脚本本身中打开 zip 文件:
import zipfile
def main():
with zipfile.ZipFile(os.path.dirname(__file__)) as z:
print(z.namelist())
with z.open('test.bin') as f:
print(f.name)
虽然接受的答案有效,但它有一些缺陷。
主要是应用程序现在可以 仅 运行 作为 zipapp。
相反,可以使用 importlib.resources
模块同时支持两种模式:
import importlib.resources
def main():
# if you use multiple packages you can replace '__package__'
# with an explicit module specifier like 'foobar.templates'
print(importlib.resources.read_text(__package__, "data.txt"), end="")
if __name__ == "__main__":
# this is used to support calling with 'python3 -m ...'
main()
$ tree
.
└── myapp
└── mypackage
├── cli.py
├── data.txt (contains "Hello World")
└── __init__.py
2 directories, 3 files
$ (cd myapp && python3 -m mypackage.cli) # still callable in unzipped mode
Hello World
$ python3 -m zipapp --python "/usr/bin/env python3" --main "mypackage.cli:main" myapp
$ ./myapp.pyz
Hello World
您只需要注意将数据放入适当的包中即可。
那就是你的包目录中需要一个 __init__.py
文件
并且包本身必须是传递给 python3 -m zipapp
的目录的子目录。
否则 importlib.resources
将无法识别包本身并且无法导入任何数据。
$ unzip -l myapp.pyz
Archive: myapp.pyz
Length Date Time Name
--------- ---------- ----- ----
0 2021-05-11 21:26 mypackage/
282 2021-05-11 21:25 mypackage/cli.py
0 2021-05-11 19:39 mypackage/__init__.py
0 2021-05-11 21:26 mypackage/__pycache__/
12 2021-05-11 19:39 mypackage/data.txt
427 2021-05-11 21:26 mypackage/__pycache__/cli.cpython-38.pyc
168 2021-05-11 21:26 mypackage/__pycache__/__init__.cpython-38.pyc
66 2021-05-11 21:27 __main__.py
--------- -------
955 8 files
我使用以下命令构建了一个简单的 Python zipapp:
python -m pkg/ -c -o test -p '/usr/bin/python3' -m 'test:main' zipapp
我想从脚本访问二进制文件
$ cat pkg/test.py
def main():
with open('test.bin', 'rb') as f:
print(f.name)
目录结构
$ tree pkg/
pkg/
├── test.bin
└── test.py
0 directories, 2 files
但看起来脚本引用的是当前目录中的文件:
$ ./test
Traceback (most recent call last):
File "/usr/lib64/python3.7/runpy.py", line 193, in _run_module_as_main
"__main__", mod_spec)
File "/usr/lib64/python3.7/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "./test/__main__.py", line 3, in <module>
File "./test/test.py", line 2, in main
FileNotFoundError: [Errno 2] No such file or directory: 'test.bin'
这是一个相当大的二进制文件,所以我想避免创建变量。有没有办法从脚本本身访问这个文件?
好的,看来我可以在脚本本身中打开 zip 文件:
import zipfile
def main():
with zipfile.ZipFile(os.path.dirname(__file__)) as z:
print(z.namelist())
with z.open('test.bin') as f:
print(f.name)
虽然接受的答案有效,但它有一些缺陷。
主要是应用程序现在可以 仅 运行 作为 zipapp。
相反,可以使用 importlib.resources
模块同时支持两种模式:
import importlib.resources
def main():
# if you use multiple packages you can replace '__package__'
# with an explicit module specifier like 'foobar.templates'
print(importlib.resources.read_text(__package__, "data.txt"), end="")
if __name__ == "__main__":
# this is used to support calling with 'python3 -m ...'
main()
$ tree
.
└── myapp
└── mypackage
├── cli.py
├── data.txt (contains "Hello World")
└── __init__.py
2 directories, 3 files
$ (cd myapp && python3 -m mypackage.cli) # still callable in unzipped mode
Hello World
$ python3 -m zipapp --python "/usr/bin/env python3" --main "mypackage.cli:main" myapp
$ ./myapp.pyz
Hello World
您只需要注意将数据放入适当的包中即可。
那就是你的包目录中需要一个 __init__.py
文件
并且包本身必须是传递给 python3 -m zipapp
的目录的子目录。
否则 importlib.resources
将无法识别包本身并且无法导入任何数据。
$ unzip -l myapp.pyz
Archive: myapp.pyz
Length Date Time Name
--------- ---------- ----- ----
0 2021-05-11 21:26 mypackage/
282 2021-05-11 21:25 mypackage/cli.py
0 2021-05-11 19:39 mypackage/__init__.py
0 2021-05-11 21:26 mypackage/__pycache__/
12 2021-05-11 19:39 mypackage/data.txt
427 2021-05-11 21:26 mypackage/__pycache__/cli.cpython-38.pyc
168 2021-05-11 21:26 mypackage/__pycache__/__init__.cpython-38.pyc
66 2021-05-11 21:27 __main__.py
--------- -------
955 8 files