How to Activate Python Virtual Environment Programmatically
Three practical approaches to activating a Python virtual environment without user interaction: shebang lines, bash wrappers, and subprocess calls.
Normally you activate a virtual environment by running source venv/bin/activate before running your script. But in some situations — deployment scripts, scheduled tasks, or wrapper tools — you need to activate it programmatically without user intervention.
This guide covers three approaches, from the simplest to the most flexible.
Approach 1: Hardcode the Shebang Line
The simplest approach is to point the script's shebang directly at the Python interpreter inside your virtual environment:
#!/Users/foo/projects/myproject/venv/bin/python
import requests # site-packages from the venv are available
print("Running with venv Python")
When you run this script directly (./myscript.py), the OS uses the venv's Python interpreter, which automatically makes that environment's site-packages available.
Limitation: The path is hardcoded and won't work if the project moves or if other users have their venv in a different location.
Approach 2: Bash Wrapper Script
Create a small shell script that explicitly calls the venv's Python with your script:
run_script (no extension):
#!/bin/bash
/Users/foo/projects/myproject/venv/bin/python /Users/foo/projects/myproject/myscript.py "$@"
Make it executable:
chmod +x run_script
./run_script
This keeps your Python script portable (it can use a generic shebang like #!/usr/bin/env python) while the wrapper handles environment selection. The "$@" passes any arguments through to the Python script.
Approach 3: Subprocess with the venv Python
For automation scripts that need to launch another Python script in a specific virtual environment, use subprocess to call the venv's Python directly:
import subprocess
import sys
from pathlib import Path
venv_python = Path("/Users/foo/projects/myproject/venv/bin/python")
result = subprocess.run(
[str(venv_python), "myscript.py", "--arg1", "value"],
capture_output=True,
text=True,
)
print(result.stdout)
This is the most robust approach because it doesn't mutate the current process's environment — it simply spawns a new process using the correct interpreter.
What Doesn't Work: Sourcing the Activate Script
You might be tempted to run the activate script from Python:
# This does NOT work
import os
os.system("source venv/bin/activate")
source modifies the shell environment of the current shell session. When Python calls os.system(), it spawns a subprocess, sources the script in that subprocess's shell, and then the subprocess exits — the parent Python process is unchanged.
Choosing the Right Approach
| Scenario | Recommended approach | |---|---| | Single-user, fixed-path script | Hardcoded shebang | | Shared project, portable wrapper | Bash wrapper | | Automation launching other scripts | Subprocess with venv Python path |
Conclusion
For most automation use cases, the subprocess approach is the most reliable — it makes no assumptions about the caller's environment and works the same way across Linux, macOS, and Windows. Use the shebang approach for quick single-user scripts where portability isn't a concern.