I've recenlty been trying to learn more about zipapps,
and so I bundled up one of my personal projects to test things out. My project uses
praw, and I kept running into configuration errors. I initially thought the problem
was that my praw.ini couldn't be found due to file indirection or security limits with
zipapp.
However, after setting some breakpoints and digging through the praw.config.Config
source, I realized that Config is looking for the default praw.ini on the filesystem
relative to the module source file. While this works on standard installs, in a zipapp,
the source files don't actually exist on disk. The default praw.ini isn't found, and
praw.config._config_boolean() errors out trying to call .lower() on _NotSet.
As of Python 3.7, the recommended way to handle "package data" like the default
praw.ini file is through the use of importlib.
This PR restructures praw.config.Config._load_config() a bit to use importlib and
still handle the rest of the config composition logic.
For an minimal reproducable example, the following bash script will create a minimal
zipapp, first installing praw from PyPi, and then installing from source (assuming
that this version of the code is checked out).
#!/usr/bin/bash
mkdir -p ./build
# Create basic app that uses praw
# Requires user to have a praw.ini that defines "testapp"
cat << EOF > __main__.py
import praw
rdt = praw.Reddit("testapp")
rdt.user.me()
print("Success!")
EOF
# Build zipapp with current version of praw
rm -rf ./build/*
cp __main__.py ./build/
python3 -m pip install --target build praw==7.8.1
python3 -m zipapp --output before.pyz build
# Build zipapp with patched version of praw
# This assumes you're running from a directory adjcent to praw source dir
rm -rf ./build/*
cp __main__.py ./build/
python3 -m pip install --target build ../praw
python3 -m zipapp --output after.pyz build
printf '\n\n\n\n'
echo "====================================================================================="
echo "Running zipapp with current version of praw - will fail ============================="
echo "====================================================================================="
python3 before.pyz
echo "====================================================================================="
echo "Running zipapp with patched version of praw - will succeed =========================="
echo "====================================================================================="
python3 after.pyz
Hello team,
I've recenlty been trying to learn more about zipapps, and so I bundled up one of my personal projects to test things out. My project uses
praw
, and I kept running into configuration errors. I initially thought the problem was that mypraw.ini
couldn't be found due to file indirection or security limits with zipapp.However, after setting some breakpoints and digging through the
praw.config.Config
source, I realized thatConfig
is looking for the defaultpraw.ini
on the filesystem relative to the module source file. While this works on standard installs, in a zipapp, the source files don't actually exist on disk. The defaultpraw.ini
isn't found, andpraw.config._config_boolean()
errors out trying to call.lower()
on_NotSet
.As of Python 3.7, the recommended way to handle "package data" like the default
praw.ini
file is through the use ofimportlib
. This PR restructurespraw.config.Config._load_config()
a bit to useimportlib
and still handle the rest of the config composition logic.For an minimal reproducable example, the following bash script will create a minimal zipapp, first installing
praw
from PyPi, and then installing from source (assuming that this version of the code is checked out).