55import sys
66import shutil
77import tempfile
8+ import pathlib
89
910from typing import Sequence , Optional , List , Any , Tuple
10- from subprocess import CalledProcessError , check_call
11+ import subprocess
1112
1213from ci_tools .parsing import ParsedSetup
1314from ci_tools .functions import discover_targeted_packages , get_venv_call , install_into_venv , get_venv_python
@@ -61,7 +62,7 @@ def create_venv(self, isolate: bool, venv_location: str) -> str:
6162 else :
6263 shutil .rmtree (venv_location , ignore_errors = True )
6364
64- check_call (venv_cmd + [venv_location ])
65+ subprocess . check_call (venv_cmd + [venv_location ])
6566
6667 # TODO: we should reuse part of build_whl_for_req to integrate with PREBUILT_WHL_DIR so that we don't have to fresh build for each
6768 # venv
@@ -84,6 +85,39 @@ def get_executable(self, isolate: bool, check_name: str, executable: str, packag
8485 os .makedirs (staging_directory , exist_ok = True )
8586 return executable , staging_directory
8687
88+ def run_venv_command (
89+ self , executable : str , command : Sequence [str ], cwd : str , check : bool = False
90+ ) -> subprocess .CompletedProcess [str ]:
91+ """Run a command in the given virtual environment.
92+ - Prepends the virtual environment's bin directory to the PATH environment variable (if one exists)
93+ - Uses the provided Python executable to run the command.
94+ - Collects the output.
95+ - If check is True, raise CalledProcessError on failure."""
96+
97+ if command [0 ].endswith ("python" ) or command [0 ].endswith ("python.exe" ):
98+ raise ValueError (
99+ "The command array should not include the python executable, it is provided by the 'executable' argument"
100+ )
101+
102+ env = os .environ .copy ()
103+
104+ python_exec = pathlib .Path (executable )
105+ if python_exec .exists ():
106+ venv_bin = str (python_exec .parent )
107+ venv_root = str (python_exec .parent .parent )
108+ env ["VIRTUAL_ENV" ] = venv_root
109+ env ["PATH" ] = venv_bin + os .pathsep + env .get ("PATH" , "" )
110+ env .pop ("PYTHONPATH" , None )
111+ env .pop ("PYTHONHOME" , None )
112+ else :
113+ raise RuntimeError (f"Unable to find parent venv for executable { executable } " )
114+
115+ result = subprocess .run (
116+ [executable , * command ], cwd = cwd , stdout = subprocess .PIPE , stderr = subprocess .PIPE , text = True , check = check
117+ )
118+
119+ return result
120+
87121 def get_targeted_directories (self , args : argparse .Namespace ) -> List [ParsedSetup ]:
88122 """
89123 Get the directories that are targeted for the check.
@@ -137,7 +171,7 @@ def install_dev_reqs(self, executable: str, args: argparse.Namespace, package_di
137171 try :
138172 logger .info (f"Installing dev requirements for { package_dir } " )
139173 install_into_venv (executable , requirements , package_dir )
140- except CalledProcessError as e :
174+ except subprocess . CalledProcessError as e :
141175 logger .error ("Failed to install dev requirements:" , e )
142176 raise e
143177 finally :
0 commit comments