Python subprocess on Windows, Mac and Linux
Paths to executables for Python subprocess on Windows and other operating systems should be handled robustly to avoid unexpected errors on end user systems that may not occur on the developer’s laptop or CI system. For best robustness, programs making important use of non-Python executables should use Python ≥ 3.7.
NOTE: relative paths (names with slashes and/or “..”) are not allowed–discussion. That means “build-on-run” or “build-at-setup/install” executables must live at the same directory level as the resource specified.
This example uses a black-box executable “amender.bin” that we assume has been already built and exists in the package directory.
#!/usr/bin/env python3
from pathlib import Path
import os
import subprocess
import importlib.resources
def my_func():
name = "amender.bin"
if os.name == "nt":
name += ".exe"
if not importlib.resources.is_resource(__package__, name:
raise ImportError(f"{name} not found, please see README")
with importlib.resources.path(__package__, name) as exe:
cmd = [str(exe), myparam1, myparam2]
subprocess.check_call(cmd)
Older Python versions < 3.7 have downsides for this application including:
- setuptools.pkg_resources is not always installed on user systems
__file__
is not always defined
Generally using __file__
is
not robust for packages.
Consider importlib.resources or using conftest.py to generate test files.