Vibe Coding Python Virtual Environment
Vibe Coding with Python
Setting up Your Virtual Environment.
When I first started vibe coding, I asked the agent to make a program for me using Python. And away it went, and we wasted hours because I didn’t have the necessary packages installed on my machine. We spent a ton of time struggling to install the Python packages on my machine. I did not have this problem on Windows. Why was my Mac giving me so much grief? Because it turns out, Apple wanted to keep me safe. I don’t know when, but Apple moved away from allowing users to install Python packages systemwide, and they locked down the install that comes with the system. Now, the recommended way to install community plugins and packages is to set up a virtual environment.
What are packages? They are libraries of prebuilt code that solve common problems. For example, if you want to write CSV files (comma-separated value files which can be imported into MS Excel or Google Sheets), there’s a Python package for that. There is even a package to let you write real XLSX files! Yes, agents can create spreadsheets and even formulas, if you have the right library of packages.
But a Mac doesn’t come with those packages installed. And you cannot simply install them by running pip install package anymore. You get an annoying error. So, I want to save you some time and show you how I set up my virtual environment. A virtual environment creates a kind of sandbox where you can install almost anything you want, including even newer or updated versions of Python.
Setting up a virtual environment is simple, but it does add one more step to your daily vibe coding environment. You need to activate the virtual environment during each work session. I show you how to automate that as well.
So, the key steps to setting up a virtual environment that allows you to install all Python packages on macOS look like this:
1. Check if Python is Installed
python3 --version
A word about Python 3: Macs used to ship with Python 2, and for a while supported both Python 2 and 3 (they are not compatible). If you are curious about a bit of tech history and how nerds (coders) work through hard decisions, the story is at the end of this article.
2. Navigate to Your Project Folder
cd your-project-folder
3. Create a Virtual Environment
python3 -m venv venv
(The last “venv” is the name - you can call it anything, such as .venv). Note that when you run this command it will create a new folder at the root of your project with that name. So you will have your-project-folder/venv. This lets you set up different environments for different projects. For me, I set this up in my agentwork project folder so it persists across all my Vibe projects.
4. Activate the Virtual Environment
source venv/bin/activate
You’ll see (venv) appear at the start of your terminal prompt.
5. Install Packages as Needed
pip install package-name
6. Deactivate when Done
deactivate
The virtual environment keeps your project’s dependencies isolated from other Python projects on your system.
[!Note] You Can Always Ask AI This article in some ways is superfluous, because you can always ask AI how to do everything I tell you to do. What’s more, the advice you get is likely to be tailored to your specific machine and environment. However, I share this here because what the AI won’t tell you are my stories, perspective, or experience.
Now the only downside to this is that you likely need to fire up your virtual environment every time you want to work with Python, and you need to do this before you launch your CLI agent.
Consequently, I created a shell script that does all of this for me, and I assigned an alias to run that script. So when I start a terminal, I type:
goagents
And it sets up my development environment for me. This is how I did it.
Setting up Goagents
When working with Vibe Coding, I now have several things that need to be initialized every time I start a terminal window, so I asked GenAI to help me create a startup script. The prompt was something like:
can you create a shell script that I can run when I start a terminal to make sure my virtual python environment is running, and all my API keys are loaded from .envrc? Also, it would be great to know the status of the repository as well.
Claude produced a file called start-llm-session.sh:
#!/usr/bin/env sh
# Start an LLM dev session in this repo
#
# Usage (recommended):
# source scripts/start-llm-session.sh
# or
# . scripts/start-llm-session.sh
#
# Sourcing ensures env changes (direnv + .venv) persist in your shell.
set -e
set -u
set -o pipefail
TARGET_DIR="$HOME/agentwork"
echo "→ Changing directory to: $TARGET_DIR"
if ! cd "$TARGET_DIR" 2>/dev/null; then
echo "! Error: directory not found: $TARGET_DIR" >&2
# Return if sourced; exit if executed
(return 1) 2>/dev/null || exit 1
fi
# Load direnv if available to bring in API keys from .envrc
if command -v direnv >/dev/null 2>&1; then
if [ -f .envrc ]; then
# Allow current dir (no-op if already allowed)
direnv allow . >/dev/null 2>&1 || true
# Detect shell flavor for correct export
if [ -n "${ZSH_VERSION:-}" ]; then SHELL_FLAVOR="zsh";
elif [ -n "${BASH_VERSION:-}" ]; then SHELL_FLAVOR="bash";
else SHELL_FLAVOR="bash"; fi
# Apply env to current shell
# shellcheck disable=SC2046
eval "$(direnv export "$SHELL_FLAVOR")"
echo "✓ direnv loaded (.envrc)"
else
echo "! Note: .envrc not found; skipping direnv load"
fi
else
echo "! direnv not installed; skipping. Install: https://direnv.net/"
fi
# Activate Python virtual environment if present and not already active
if [ -z "${VIRTUAL_ENV:-}" ]; then
if [ -f .venv/bin/activate ]; then
# shellcheck disable=SC1091
. .venv/bin/activate
echo "✓ Python venv activated (.venv)"
elif [ -f venv/bin/activate ]; then
# shellcheck disable=SC1091
. venv/bin/activate
echo "✓ Python venv activated (venv)"
else
echo "! No .venv or venv found; skipping Python venv activation"
fi
elif [ -n "${VIRTUAL_ENV:-}" ]; then
echo "✓ Python venv already active: $VIRTUAL_ENV"
fi
# Quick sanity check for common API keys (do not print values)
check_var() {
var_name="$1"
if [ -n "${!var_name-}" ]; then
printf " • %s: set\n" "$var_name"
else
printf " • %s: MISSING\n" "$var_name"
fi
}
echo "→ Checking common API keys"
for v in OPENAI_API_KEY ANTHROPIC_API_KEY GOOGLE_API_KEY GEMINI_API_KEY OPENROUTER_API_KEY AZURE_OPENAI_API_KEY HUGGINGFACE_API_KEY; do
# Indirect expansion compatible with sh via eval
eval "__tmp=\"\${$v-}\""
if [ -n "$__tmp" ]; then
printf " • %s: set\n" "$v"
else
printf " • %s: MISSING\n" "$v"
fi
done
# Show concise Git status
if command -v git >/dev/null 2>&1; then
echo "→ Git status"
git status -sb || true
else
echo "! git not installed; skipping git status"
fi
echo "✓ Environment ready. Happy hacking!"
# Restore safe interactive shell settings
# (The strict settings above are useful during script execution,
# but would cause the shell to exit on any command error if left on)
set +e
set +u
set +o pipefail
Warning: Directories When I started my agent project, I put it in
~/mcp/agentworkbut I have been telling you to put it in~/agentwork, so if you do use this script, make sure theTARGET_DIRon line 111 is set to the correct path. But if you get it wrong, this script will warn you.
In order to execute this script like a command, you do need to mark it as executable with the following command:
cd ~/agentwork
chmod +x ./scripts/start-llm-session.sh
Where does this file belong? I created a scripts folder under my agentwork folder. Therefore to run the script, when I open a terminal I type:
# Assume you are still in the agentwork directory
./scripts/start-llm-session.sh
Being a lazy developer, that’s too many characters, so I added the following line to the end of my .zshrc file. To be honest, you can ask your agent to do this for you. So I have three shortcuts: llmstart, agentstart, or goagent.
# === LLM session helper ===
goagent() { source ~/agentwork/scripts/start-llm-session.sh; }
alias llmstart='goagent'
alias agentstart='goagent'
# === End LLM helper ===
Again, make sure the source path points to the right file where you put your script.
Summary
You should now be able to fire up your development environment and launch the virtual environment for Python so you can install any packages or plugins you might need to start coding. This final bit of setup should now leave you in a position to begin solving all manner of cool problems. From now on my posts will focus more on the process of vibe coding and less on the technical details of what you should have installed. The truth is, even as I got to this point, most of the technical stuff you see in these last few posts was the result - not of my brilliance - but of me negotiating with my active agent. I told it what I wanted. It proposed a solution. I approved it to make those changes. It did, and then I tested it. We iterated until together we had a solution that worked for me. I feel confident that process will also work for you.
[!tip] The power of copy pasting errors When the AI does something for you that then causes an error, my fast fix is to copy the text out of the terminal window with the error message and paste it into the agent window with the prefix: “Hey I got this error” (paste in text from the other terminal window).
Note, after you go through this process, my best practice is to direct the agent to update its memory/context artifact, check in all changes, and push to origin. This takes a snapshot in time for all the changes, saves that state to our remote repository, creating a backup in the cloud. It also gives us a marker so we can return to this point if we need to.
The Strange yet Interesting History of Python
I am always asking, “how did it get to be like this?” (Etymology - the origin of word definitions is a favorite pastime of mine.) Consequently, in fact-checking this post, I unearthed the curious history of Python and the drama surrounding the switch from Python 2 to Python 3. I have been around tech long enough to remember when this was first announced and the decade-plus of drama that ensued. If you are interested in such things, please enjoy. If you are not, feel free to skip this section. You do not need to know any of this to vibe code. But I think it’s cool, so here is what I learned.
Python was created by a single guy in the late 1980s. His name is Guido van Rossum. He literally invented the language while working at a Dutch research institute. The first release was in 1991.
Python was extremely popular, but by the time Python 2 became the standard, certain kinds of bugs started popping up across thousands of applications and websites. The same kinds of bugs. Here is a brief list of the problems and how Python 3 moved to fix them:
1. Unicode/String Handling (the Big one)
- Python 2: Strings were bytes by default, Unicode was a mess (
u"string"vs"string") - Python 3: All strings are Unicode by default, bytes are explicit
- This fixed a huge source of bugs in international applications and web development
2. Integer Division
- Python 2:
5 / 2 = 2(truncated) - Python 3:
5 / 2 = 2.5(true division), use//for integer division - Python 2’s behavior was a common source of subtle bugs
3. Iterators Everywhere
range(),zip(),map(),dict.keys()etc. return iterators instead of lists- More memory efficient, but broke a lot of code
4. Stricter Error Handling
- Removed implicit type coercion that caused bugs
- Better exception chaining
But the real shock was how the creator, Guido van Rossum, decided to implement these changes. In 2008, they launched Python 3. Rossum had decided to break backward compatibility intentionally to fix fundamental design flaws. No other major web programming language had taken that approach. JavaScript, PHP, and others maintained what is called backward compatibility. The real surprise was that most developers ignored Python 3. For years. It was not until 2014, when Rossum and his team announced the end of life for Python 2 in 2020 (giving everyone a 6-year heads up), that people actually started to switch. The lack of backward compatibility was hugely controversial.
How did Rossum have the power to make such a decision? That, in my mind, is the real story.
Rossum’s Authority: The “BDFL” Model
Guido held the title “Benevolent Dictator For Life” (BDFL) - a half-joking, half-serious title in the Python community. This meant:
- He had final say on all major Python decisions
- The community would debate, propose, vote on PEPs (Python Enhancement Proposals)
- But ultimately, Guido could approve or reject anything
- His vision shaped the language philosophy (“There should be one obvious way to do it”)
Why People Accepted This
- He created it - hard to argue with the inventor
- Good track record - Python became hugely successful under his leadership
- Open but decisive - He listened to community input but could break deadlocks
- No corporate ownership - Python was open-source, not controlled by a company
The Twist: He Stepped Down
In 2018, after a particularly contentious debate (about the “walrus operator” :=), Guido stepped down as BDFL. He said he was tired of the pressure and conflict.
Now Python is governed by a Steering Council elected by core developers - a more democratic model.
The Irony
The Python 2→3 decision was possible because of the BDFL model. A committee might never have had the guts to break backward compatibility that drastically. For better or worse, that benevolent dictatorship enabled bold moves.
But what is a Walrus Operator?
It is a part of Python syntax but it is also a fascinating piece of Python community drama!
The := operator (officially “assignment expression”) lets you assign and use a value in one line:
# Without walrus
n = len(items)
if n > 10:
print(f"Too many items: {n}")
# With walrus
if (n := len(items)) > 10:
print(f"Too many items: {n}")
The Conflict (PEP 572, 2018)
The Python community was deeply divided:
Pro-walrus camp:
- More concise, especially in loops and comprehensions
- Avoids redundant function calls
- Common pattern in other languages (C, Go)
Anti-walrus camp:
- Violates “There should be one obvious way to do it”
- Hurts readability - assignment hidden inside expressions
- Encourages overly clever one-liners
- “Too much like C” (Python had avoided this for 27 years)
The debate got ugly: Personal attacks, heated mailing list threads, accusations of elitism vs. populism.
How It Was Decided
July 2018: Guido approved PEP 572 despite significant opposition.
Days later: Guido announced he was stepping down as BDFL, saying:
“I’m tired of the weight of being the final decision-maker… I’m basically giving myself a permanent vacation from being BDFL.”
The Aftermath
- The walrus operator was added to Python 3.8 (2019)
- Python transitioned to the Steering Council model
- The controversy showed even Guido couldn’t make everyone happy
The irony: The walrus operator works fine, many people use it, but it literally cost Python its creator as leader. That’s how contentious language design can get!