praw-dev / praw

PRAW, an acronym for "Python Reddit API Wrapper", is a python package that allows for simple access to Reddit's API.
http://praw.readthedocs.io/
BSD 2-Clause "Simplified" License
3.53k stars 462 forks source link

Update Config to use importlib for defaults #2038

Closed jkerhin closed 6 days ago

jkerhin commented 6 days ago

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 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
bboe commented 6 days ago

This is great! Thank you for the contribution.