All articles
2 min read

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.