New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Running the Python test suite leaks .pem files in /tmp #93353
Comments
The problem comes from pip bootstrap when running the test_with_pip() test of test_venv. It seems like pip creates a .pem file in the temporary directory (like /tmp), but only when pip is imported from a ZIP file, which is the case when I run get-pip.py: https://bootstrap.pypa.io/get-pip.py |
I can reproduced the issue in the 3.9, 3.10, 3.11 and main branches (I didn't test older branches).
It seems to be a change between pip 20.2.3 and pip 21.1. |
In get-pip.py, |
Shell script to reproduce the issue: set -e -x
DIR=$PWD/TMP/
rm -rf $DIR
mkdir $DIR
rm -rf env
TMPDIR=$DIR TEMPDIR=$DIR ./python -m venv env --without-pip
TMPDIR=$DIR TEMPDIR=$DIR env/bin/python -m ensurepip -v
echo
echo
echo "=== TMPDIR ==="
find $DIR Output:
|
The PEM file is created indirectly by importlib.resources on It's created by this code: >>> import sys; sys.path.insert(0, 'pip.zip')
>>> import pip._vendor.certifi
>>> pip._vendor.certifi.where()
pip.zip/pip/_vendor/certifi/core.py:50: DeprecationWarning: path is deprecated. Use files() instead. Refer to https://importlib-resources.readthedocs.io/en/latest/using.html#migrating-from-legacy for migration advice.
'/home/vstinner/python/main/TMP/tmp9osgb6wrcacert.pem' Debugger:
If Python exits normally, the garbage collector deletes the temporary PEM file. When running ensurepip / get-pip.py, it's not deleted. I don't know why. importlib.resources only creates a temporary file if pip is imported from a ZIP file. |
Workaround to manually delete the temporaryfile: pip._vendor.certifi.core._CACERT_CTX = None |
OpenSSL requires a filename to a PEM file. certifi uses importlib.resources to create a temporary named file if pip._vendor.certifi is imported from a ZIP file. That's convenient. The problem is more that the |
Nice. How did you find this? |
I used a shell script similar to #93353 (comment) and I ran manually a bisection on the test list (./python -m test --list-tests) using a script taking a random.sample() of half of tests. I repeated the operation until I found a single test file, then I looked at the code. The leaked files was reported by more and more owners of buildbot workers. Once the PEM issue is solved, my plan is to enhance test.regrtest to do something similar than the shell script: define TMPFILE env var. Maybe in the main process spawning test processes. |
For pip/certifi, I hacked get-pip.py to add breakpoint() and I followed the code flow. It's not easy to identify which part of pip created the file. It's not just an import. It's an import + call to where() function, done in the module body that I failed to find which module. Maybe requests. It would be nice to be able to postpone when where() is called to workaround the issue: only call it when a filename to cacert.pem is needed. |
Reproducer without pip nor certifi, attached run.py and bug.py scripts:
The expected output is an empty list in run.py (parent process). bug.py never calls _tempfile() finally block. I'm still bisecting the issue. It smells like logging prevents deleting an object somehow. |
Ok, this problem is quite complicated:
Fixing importlib.resources is simple: keep a reference to os.remove() in importlib.resources implementation to make sure that we can remove the file very late during Python finalization. This problem is tricky because:
I'm working on a fix for importlib.resources. |
related: pypa/pip#10753 |
Add parameter I suppose that the finally code is executed after the module It is the answer of the puzzle. In general, you should not rely on the code executed at the interpreter shutdown stage (except registered with |
Fix the importlib.resources.as_file() context manager to remove the temporary file if destroyed late during Python finalization: keep a local reference to the os.remove() function. Patch by Victor Stinner.
I created the PR #93377 to fix importlib.resources. |
Running the Python test suite has two issues:
Moreover, test_tools enters an unlimited loop and fills the $TMPDIR directory if the $TMPDIR is a sub-directory of the Python source code directory. Example:
Running test_freeze_simple_script() of test_tools copies TMP/ into TMP/TMP/ and then into TMP/TMP/TMP/, etc. Quickly, it fills TMP/ with a "loop" of files :-)
The text was updated successfully, but these errors were encountered: