55Licensed to the PSF under a contributor agreement.
66"""
77
8+ import contextlib
89import ensurepip
910import os
1011import os .path
@@ -478,16 +479,10 @@ def do_test_with_pip(self, system_site_packages):
478479
479480# Actually run the create command with all that unhelpful
480481# config in place to ensure we ignore it
481- try :
482+ with self . nicer_error () :
482483self .run_with_capture (venv .create ,self .env_dir ,
483484system_site_packages = system_site_packages ,
484485with_pip = True )
485- except subprocess .CalledProcessError as exc :
486- # The output this produces can be a little hard to read,
487- # but at least it has all the details
488- details = exc .output .decode (errors = "replace" )
489- msg = "{}\n \n **Subprocess Output**\n {}"
490- self .fail (msg .format (exc ,details ))
491486# Ensure pip is available in the virtual environment
492487envpy = os .path .join (os .path .realpath (self .env_dir ),self .bindir ,self .exe )
493488# Ignore DeprecationWarning since pip code is not part of Python
@@ -508,13 +503,14 @@ def do_test_with_pip(self, system_site_packages):
508503# Check the private uninstall command provided for the Windows
509504# installers works (at least in a virtual environment)
510505with EnvironmentVarGuard ()as envvars :
511- # It seems ensurepip._uninstall calls subprocesses which do not
512- # inherit the interpreter settings.
513- envvars ["PYTHONWARNINGS" ]= "ignore"
514- out ,err = check_output ([envpy ,
515- '-W' ,'ignore::DeprecationWarning' ,
516- '-W' ,'ignore::ImportWarning' ,'-I' ,
517- '-m' ,'ensurepip._uninstall' ])
506+ with self .nicer_error ():
507+ # It seems ensurepip._uninstall calls subprocesses which do not
508+ # inherit the interpreter settings.
509+ envvars ["PYTHONWARNINGS" ]= "ignore"
510+ out ,err = check_output ([envpy ,
511+ '-W' ,'ignore::DeprecationWarning' ,
512+ '-W' ,'ignore::ImportWarning' ,'-I' ,
513+ '-m' ,'ensurepip._uninstall' ])
518514# We force everything to text, so unittest gives the detailed diff
519515# if we get unexpected results
520516err = err .decode ("latin-1" )# Force to text, prevent decoding errors
@@ -540,12 +536,32 @@ def do_test_with_pip(self, system_site_packages):
540536if not system_site_packages :
541537self .assert_pip_not_installed ()
542538
539+ @contextlib .contextmanager
540+ def nicer_error (self ):
541+ """
542+ Capture output from a failed subprocess for easier debugging.
543+
544+ The output this handler produces can be a little hard to read,
545+ but at least it has all the details.
546+ """
547+ try :
548+ yield
549+ except subprocess .CalledProcessError as exc :
550+ out = exc .output .decode (errors = "replace" )
551+ err = exc .stderr .decode (errors = "replace" )
552+ self .fail (
553+ f"{ exc } \n \n "
554+ f"**Subprocess Output**\n { out } \n \n "
555+ f"**Subprocess Error**\n { err } "
556+ )
557+
543558# Issue #26610: pip/pep425tags.py requires ctypes
544559@unittest .skipUnless (ctypes ,'pip requires ctypes' )
545560@requires_zlib ()
546561def test_with_pip (self ):
547562self .do_test_with_pip (False )
548563self .do_test_with_pip (True )
549564
565+
550566if __name__ == "__main__" :
551567unittest .main ()