環境
- Ubuntu 22.04
- Python 3.12.4
- Poetry 1.8.3
- pyinstaller 6.10.0
やりたいこと
以下のプロジェクトで、Pythonファイルからresources/foo.txt
を参照したいです。
$ tree
.
├── pyproject.toml
├── sample
│ ├── __init__.py
│ ├── __main__.py
│ ├── resources
│ │ └── foo.txt
│ └── utils.py
以下のようなコードで、resources/foo.txt
が存在するかどうかの結果を出力するようにしました。
from pathlib import Path
from sample.utils import foo_txt_exists as foo_txt_exists_in_utils
print(f"{__file__=} in '__main__.py")
def foo_txt_exists() -> bool:
path = Path(__file__).parent / "resources/foo.txt"
print(f"{path=}, {path.exists()=} in '__main__.py")
def main():
foo_txt_exists()
foo_txt_exists_in_utils()
if __name__ == "__main__":
main()
from pathlib import Path
print(f"{__file__=} in 'utils.py")
def foo_txt_exists() -> bool:
path = Path(__file__).parent / "resources/foo.txt"
print(f"{path=}, {path.exists()=} in 'utils.py")
[tool.poetry]
name = "sample"
version = "0.1.0"
description = ""
authors = ["yuji38kwmt"]
[tool.poetry.dependencies]
python = "^3.12"
[tool.poetry.group.publish.dependencies]
pyinstaller = { version = "^6.10", python = "=3.12" }
[tool.poetry.scripts]
sample = "sample.__main__:main"
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
pyproject.toml
のsample = "sample.__main__:main"
という設定により、sample
というコマンドで__main__.py
のmain
関数を実行できるようにしました。
sample
コマンドの実行結果を以下に記載します。__main__.py
とutils.py
のそれぞれで、path.exists()=True
と出力されたので、__main__.py
とutils.py
でresources/foo.txt
を参照することができました。
$ poetry run sample
__file__='/home/yuji/tmp/20240821/sample/sample/utils.py' in 'utils.py
__file__='/home/yuji/tmp/20240821/sample/sample/__main__.py' in '__main__.py
path=PosixPath('/home/yuji/tmp/20240821/sample/sample/resources/foo.txt'), path.exists()=True in '__main__.py
path=PosixPath('/home/yuji/tmp/20240821/sample/sample/resources/foo.txt'), path.exists()=True in 'utils.py
問題提起
Pyinstallerを使って、このツールを実行ファイルに変換しました。
$ poetry run pyinstaller sample/__main__.py --name sample --add-data "sample/resources:sample/resources"
$ tree dist/sample/
dist/sample/
├── _internal
│ ├── base_library.zip
│ ├── lib-dynload
│ │ ├── ...
│ ├── ...
│ └── sample
│ └── resources
│ └── foo.txt
└── sample
実行ファイルを実行した結果を以下に記載します。__main__.py
ではpath.exists()=False
と出力されました。utils.py
ではresources/foo.txt
を参照できましたが、__main__.py
では参照できませんでした。
$ dist/sample/sample
__file__='/home/yuji/tmp/20240821/sample/dist/sample/_internal/sample/utils.pyc' in 'utils.py
__file__='/home/yuji/tmp/20240821/sample/dist/sample/_internal/__main__.py' in '__main__.py
path=PosixPath('/home/yuji/tmp/20240821/sample/dist/sample/_internal/resources/foo.txt'), path.exists()=False in '__main__.py
path=PosixPath('/home/yuji/tmp/20240821/sample/dist/sample/_internal/sample/resources/foo.txt'), path.exists()=True in 'utils.py
原因
pyinstaller
コマンドに指定したスクリプトファイルsample/__main__.py
の__file__
は _internal/sample/__main__.py
ではなく_internal/__main__.py
で、パッケージ名sample
が含まれていませんでした。これが原因のようです。
解決方法は分かりませんでした。
ひとまず、__main__.py
ではresource
配下のファイルを参照しないようにします。