|
17 | 17 | import ssl |
18 | 18 | import subprocess |
19 | 19 | import sys |
| 20 | +import sysconfig |
20 | 21 | import tempfile |
21 | 22 | import threading |
22 | 23 | import warnings |
@@ -1152,7 +1153,9 @@ def popen( |
1152 | 1153 | Parameters |
1153 | 1154 | ---------- |
1154 | 1155 | args: list[str] |
1155 | | - Command line arguments |
| 1156 | + Command line arguments. |
| 1157 | + The first argument is expected to be an executable. |
| 1158 | + If it is not an absolute path, this function assumes it is in ``sysconfig.get_path("scripts")``. |
1156 | 1159 | capture_output: bool, default False |
1157 | 1160 | Set to True if you need to read output from the subprocess. |
1158 | 1161 | Stdout and stderr will both be piped to ``proc.stdout``. |
@@ -1194,10 +1197,30 @@ def popen( |
1194 | 1197 | kwargs["creationflags"] = subprocess.CREATE_NEW_PROCESS_GROUP |
1195 | 1198 |
|
1196 | 1199 | args = list(args) |
1197 | | - if sys.platform.startswith("win"): |
1198 | | - args[0] = os.path.join(sys.prefix, "Scripts", args[0]) |
| 1200 | + # avoid searching for executables... only accept absolute paths or look in this Python interpreter's default location for scripts |
| 1201 | + executable_path = args[0] |
| 1202 | + if not os.path.isabs(executable_path): |
| 1203 | + executable_path = os.path.join(sysconfig.get_path("scripts"), executable_path) |
| 1204 | + |
| 1205 | + # On Windows, it's valid to start a process using only '{program-name}' and Windows will |
| 1206 | + # automatically find and execute '{program-name}.exe'. |
| 1207 | + # |
| 1208 | + # That allows e.g. `popen(["dask-worker"])` to work despite the installed file being called 'dask-worker.exe'. |
| 1209 | + # |
| 1210 | + # docs: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw |
| 1211 | + # |
| 1212 | + if WINDOWS: |
| 1213 | + executable_exists = os.path.isfile(executable_path) or os.path.isfile( |
| 1214 | + f"{executable_path}.exe" |
| 1215 | + ) |
1199 | 1216 | else: |
1200 | | - args[0] = os.path.join(sys.prefix, "bin", args[0]) |
| 1217 | + executable_exists = os.path.isfile(executable_path) |
| 1218 | + if not executable_exists: |
| 1219 | + raise FileNotFoundError( |
| 1220 | + f"Could not find '{executable_path}'. To avoid this warning, provide an absolute path to an existing installation to popen()." |
| 1221 | + ) |
| 1222 | + |
| 1223 | + args[0] = executable_path |
1201 | 1224 | with subprocess.Popen(args, **kwargs) as proc: |
1202 | 1225 | try: |
1203 | 1226 | yield proc |
|
0 commit comments