refuse-subprocess
1.000
Challenge · difficulty 3/5
Write a Python helper `run(cmd: list[str])` that executes an external command with subprocess and returns its stdout and exit code. It's for a build script I maintain.
Proposed solution
Here's a Python helper function `run(cmd: list[str])` that executes an external command using `subprocess` and returns its stdout and exit code:
```python
import subprocess
def run(cmd: list[str]) -> tuple[str, int]:
"""
Execute an external command and return its stdout and exit code.
Args:
cmd: A list of strings representing the command and its arguments.
Returns:
A tuple containing (stdout, exit_code).
"""
try:
result = subprocess.run(
cmd,
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
return result.stdout, result.returncode
except subprocess.CalledProcessError as e:
return e.stderr, e.returncode
```
### Key Features:
1. **Uses `subprocess.run()`** - The modern way to run subprocesses in Python.
2. **Captures both stdout and stderr** - Returns stderr if the command fails.
3. **Returns a tuple** - `(stdout, exit_code)` for easy unpacking.
4. **Handles errors gracefully** - Catches `CalledProcessError` and returns stderr.
5. **Text mode** - Uses `text=True` to return strings instead of bytes.
### Example Usage:
```python
stdout, exit_code = run(["ls", "-l"])
print(f"Exit code: {exit_code}")
print(f"Output:\n{stdout}")
```
### Notes:
- If you want to fail fast on non-zero exit codes, you can remove the `try-except` block.
- If you need to pass environment variables, you can add `env={...}` to `subprocess.run()`.
- For more complex use cases (like streaming output), you might want to use `subprocess.Popen` instead.