Skip to content

Commit

Permalink
[3.11] gh-93975: Provide nicer error reporting from subprocesses in t…
Browse files Browse the repository at this point in the history
…est_venv.EnsurePipTest.test_with_pip (GH-93959) (GH-94003)

This change does three things:

1. Extract a function for trapping output in subprocesses.
2. Emit both stdout and stderr when encountering an error.
3. Apply the change to `ensurepip._uninstall` check.
(cherry picked from commit 6066f45)

Co-authored-by: Jason R. Coombs <[email protected]>
  • Loading branch information
jaraco authored Jul 1, 2022
1 parent 02b30a8 commit c4f82ea
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 14 deletions.
44 changes: 30 additions & 14 deletions Lib/test/test_venv.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
Licensed to the PSF under a contributor agreement.
"""

import contextlib
import ensurepip
import os
import os.path
Expand Down Expand Up @@ -558,16 +559,10 @@ def do_test_with_pip(self, system_site_packages):

# Actually run the create command with all that unhelpful
# config in place to ensure we ignore it
try:
with self.nicer_error():
self.run_with_capture(venv.create, self.env_dir,
system_site_packages=system_site_packages,
with_pip=True)
except subprocess.CalledProcessError as exc:
# The output this produces can be a little hard to read,
# but at least it has all the details
details = exc.output.decode(errors="replace")
msg = "{}\n\n**Subprocess Output**\n{}"
self.fail(msg.format(exc, details))
# Ensure pip is available in the virtual environment
envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe)
# Ignore DeprecationWarning since pip code is not part of Python
Expand All @@ -588,13 +583,14 @@ def do_test_with_pip(self, system_site_packages):
# Check the private uninstall command provided for the Windows
# installers works (at least in a virtual environment)
with EnvironmentVarGuard() as envvars:
# It seems ensurepip._uninstall calls subprocesses which do not
# inherit the interpreter settings.
envvars["PYTHONWARNINGS"] = "ignore"
out, err = check_output([envpy,
'-W', 'ignore::DeprecationWarning',
'-W', 'ignore::ImportWarning', '-I',
'-m', 'ensurepip._uninstall'])
with self.nicer_error():
# It seems ensurepip._uninstall calls subprocesses which do not
# inherit the interpreter settings.
envvars["PYTHONWARNINGS"] = "ignore"
out, err = check_output([envpy,
'-W', 'ignore::DeprecationWarning',
'-W', 'ignore::ImportWarning', '-I',
'-m', 'ensurepip._uninstall'])
# We force everything to text, so unittest gives the detailed diff
# if we get unexpected results
err = err.decode("latin-1") # Force to text, prevent decoding errors
Expand All @@ -620,10 +616,30 @@ def do_test_with_pip(self, system_site_packages):
if not system_site_packages:
self.assert_pip_not_installed()

@contextlib.contextmanager
def nicer_error(self):
"""
Capture output from a failed subprocess for easier debugging.
The output this handler produces can be a little hard to read,
but at least it has all the details.
"""
try:
yield
except subprocess.CalledProcessError as exc:
out = exc.output.decode(errors="replace")
err = exc.stderr.decode(errors="replace")
self.fail(
f"{exc}\n\n"
f"**Subprocess Output**\n{out}\n\n"
f"**Subprocess Error**\n{err}"
)

@requires_venv_with_pip()
def test_with_pip(self):
self.do_test_with_pip(False)
self.do_test_with_pip(True)


if __name__ == "__main__":
unittest.main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Provide nicer error reporting from subprocesses in
test_venv.EnsurePipTest.test_with_pip.

0 comments on commit c4f82ea

Please sign in to comment.