Skip to main content
Updated Feb 24, 2026

Installing uv and Creating SmartNotes

In Lesson 1, James learned why the discipline stack exists and which axiom each tool enforces. Now it is time to install the first tool and create the project that will evolve across every remaining chapter. The tool is uv. The project is SmartNotes.

James opens his terminal, ready to install Python. Every tutorial he has ever read starts the same way: go to python.org, download the installer, add Python to PATH, cross your fingers. He types the URL into his browser. Emma stops him before he hits Enter.

"There is a faster way," she says. "One tool handles Python installation, virtual environments, and packages. It is called uv."

James is skeptical. He has heard of pip, conda, poetry, pipenv. Each promised to simplify things. Each added complexity. "Why should I trust another tool?"

Emma does not argue. She opens her own terminal, types one command, and waits three seconds. "Done. Python installed, virtual environment ready, project scaffolded. One tool. One command." James stares at the screen. "That is... it?" Emma nods. "That is it."


The Problem Without uv

Before uv existed, setting up a Python project required coordinating multiple tools that knew nothing about each other. Each tool handled one piece of the puzzle, and the pieces did not fit together cleanly. This fragmentation matters even more when you work with AI assistants -- an AI can scaffold a project in seconds, but if the environment is not reproducible, the code it generates will only work on the machine that generated it.

TaskWithout uvWith uv
Install PythonDownload from python.org, manage PATH manuallyuv python install 3.12
Create virtual environmentpython -m venv .venv, then activate it per-platformAutomatic on first uv run
Install packagespip install requests, no lockfileuv add requests (updates pyproject.toml + lockfile + venv)
Pin Python versionCreate .python-version by hand, hope pyenv reads ituv init creates it automatically
Reproducible environmentspip freeze > requirements.txt, pray versions matchuv.lock generated automatically, cross-platform
Run project codeActivate venv first, then python main.pyuv run main.py (handles everything)

The old workflow required five separate tools: python.org installer, venv, pip, pip-tools (for lockfiles), and pyenv (for version management). Each maintained by different teams, with different release schedules, different configuration formats, and different assumptions about where files should live. When something broke, the debugging started with the question: "Which tool caused this?"

uv replaces all five. One binary, written in Rust, maintained by Astral (the same company that builds ruff). It installs Python, creates virtual environments, manages dependencies, generates lockfiles, and runs project code. All through a single command-line interface.

If you're new to programming

A virtual environment is like a private toolbox for one project. The tools in one toolbox do not interfere with tools in another. Without it, installing a library for one project can break a different project that needs a different version. You never need to manage this toolbox yourself -- uv creates it and keeps it organized automatically.


Installing uv

Installation takes one command. The command differs by platform. For the latest instructions, see the official uv installation guide.

Windows (PowerShell)
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
macOS / Linux (curl)
curl -LsSf https://astral.sh/uv/install.sh | sh
macOS (Homebrew alternative)
brew install uv
Windows (WinGet alternative)

Requires Windows App Installer (pre-installed on Windows 11, may need manual install on Windows 10).

winget install --id=astral-sh.uv -e
Any platform — pip fallback

If you already have Python installed, you can bootstrap uv with pip. You are using pip to install the tool that replaces pip — this is fine as a one-time bootstrap.

pip install uv

After installation, verify it worked:

uv --version

Output:

uv 0.10.4 (ce3ebd06b 2026-02-17)

Your version number will likely be different from the one shown here -- and that is expected. uv releases frequently, so a newer version is normal. The commands in this chapter work the same across versions. What matters is that you see a version number, not "command not found." If you see "command not found," close and reopen your terminal — the PATH update requires a new shell session.


Axiom I in Action

In Chapter 14, you learned Axiom I: Shell as Orchestrator. The shell coordinates programs; programs do computation. uv is this axiom made concrete for Python development.

Before uv, orchestrating Python required a chain of unrelated commands:

# OLD WAY: Five tools, five commands, five chances for something to break
pyenv install 3.12.0
pyenv local 3.12.0
python -m venv .venv
source .venv/bin/activate # Different on Windows: .venv\Scripts\activate
pip install -r requirements.txt

With uv, the shell orchestrates a single tool that handles everything:

# NEW WAY: One tool, one command
uv run main.py

That single command verifies the Python version, creates or syncs the virtual environment, installs any missing dependencies, and runs the file. Five responsibilities, one orchestrator. This is what Axiom I looks like in practice: the shell does not manage Python versions or parse dependency files — it calls uv, and uv handles the computation.


Practical Application

Creating the SmartNotes Project

SmartNotes is the project you will build across every chapter from here forward. It starts as a single file. By the end of the course, it will be a full application with types, tests, a database, an API, and deployment configuration. Right now, you create its foundation.

Open your terminal and run:

uv init smartnotes

Output:

Initialized project `smartnotes` at `/home/user/smartnotes`

Now look at what uv created:

cd smartnotes
ls -la

Output:

.gitignore
.python-version
README.md
main.py
pyproject.toml

Five files. Each has a specific purpose. No file is optional.

File-by-File Explanation

.python-version pins the Python version for this project. Open it:

3.12

One line. One number. Every developer who clones this project will use Python 3.12, regardless of what other versions they have installed. uv reads this file and automatically uses the correct Python version. No manual management required.

pyproject.toml is the project's identity document. It tells every tool — uv, pyright, ruff, pytest — what this project is and how it should be treated.

[project]
name = "smartnotes"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = []

The [project] section contains the project name, version, a description placeholder, and the minimum Python version required. The dependencies list is empty because SmartNotes has no external packages yet. You will add dependencies in Lesson 3.

main.py is the entry point — the file that runs when you execute the project.

Loading Python environment...

This file uses two Python features — def and the if __name__ guard — that you will learn about in later chapters. For now, the only line that matters is print("Hello from smartnotes!"). That is the line that produces the greeting you see in the terminal. When you run uv run main.py, Python executes this file and prints the message. You will understand every line of this file in later chapters.

.gitignore tells Git which files to exclude from version control. uv generates a default that excludes virtual environments and Python cache files — things that should never be committed to a repository.

README.md is a placeholder documentation file. Every professional project has one. You will update it as SmartNotes grows.

Running the Project

Now run SmartNotes for the first time:

uv run main.py

Output:

Hello from smartnotes!

Notice what happened behind the scenes. uv checked .python-version to confirm the correct Python version, created a .venv directory (the virtual environment), and ran main.py inside that environment. You did not activate anything. You did not install anything. You typed one command.

After this first run, two new items appear in the project:

smartnotes/
├── .gitignore
├── .python-version
├── .venv/ <-- Created automatically by uv run
├── README.md
├── main.py
├── pyproject.toml
└── uv.lock <-- Created automatically by uv run

.venv/ is the virtual environment — an isolated copy of Python and any packages your project needs. uv manages it entirely. You never need to activate it, deactivate it, or think about it. The .gitignore file already excludes it from version control.

uv.lock is the lockfile — a precise record of every dependency and its exact version. Right now it is nearly empty because SmartNotes has no dependencies. As you add packages in Lesson 3, this file will grow. It should be committed to Git so that every developer gets identical dependency versions.

Quick Check: After running uv run main.py, two new items appeared: .venv/ and uv.lock. One should be committed to Git and the other should not. Which is which, and why?

Checkpoint: Your SmartNotes project should now look like this
smartnotes/
├── .gitignore
├── .python-version
├── .venv/ ← created by uv run
├── README.md
├── main.py
├── pyproject.toml
└── uv.lock ← created by uv run

If you see all seven items, you are on track. If anything is missing, re-read the steps above before continuing.


Anti-Patterns

James almost fell into four traps on his first day. Each one is a habit from older tutorials that uv makes unnecessary.

Anti-PatternWhat It Looks LikeWhy It FailsThe Fix
Installing Python from python.orgDownloading the installer, running it, adding Python to PATH manuallyDifferent developers end up with different Python versions. PATH conflicts cause mysterious errors. No way to pin a version per project.Let uv manage Python. uv python install 3.12 if needed, but uv init and uv run handle versioning automatically.
Using pip directlypip install requests outside a project contextInstalls packages globally, polluting the system Python. No lockfile, no reproducibility. Different machines get different versions.Always use uv add inside a project. It updates pyproject.toml, generates uv.lock, and syncs the virtual environment in one step.
Activating virtual environments manuallysource .venv/bin/activate on Mac/Linux or .venv\Scripts\activate on WindowsPlatform-specific commands that students forget or get wrong. Leads to "it works on my machine" problems when someone forgets to activate.Use uv run instead. It runs commands inside the project environment automatically. No activation required, no platform differences.
Ignoring .python-versionNot creating the file, or creating it but not using a tool that reads itTeam members use whatever Python version they have installed. Code that works on 3.12 breaks silently on 3.10 because of syntax or library differences.uv init creates .python-version automatically. uv reads it on every command. The version is always consistent.

Try With AI

Prompt 1: Explain the Project Structure

I just ran `uv init smartnotes` and got these files:
- .gitignore
- .python-version (contains "3.12")
- README.md
- main.py
- pyproject.toml

Then I ran `uv run main.py` and two more items appeared:
- .venv/ directory
- uv.lock file

Explain what each file and directory does. For each one, tell me:
1. What it contains
2. Who reads it (humans, tools, or both)
3. Whether it should be committed to Git

What you're learning: How to use AI as an explainer for project structure. You ran the commands yourself and saw the output. Now you are asking AI to deepen your understanding of what each file does and why it exists. This builds the habit of running first, then asking AI to explain — not asking AI to do the work for you.

Prompt 2: Compare uv run to Direct Python Execution

What is the difference between running:
uv run main.py
and running:
python main.py

Explain what uv does behind the scenes before executing the file.
Include what happens with the virtual environment, dependency
checking, and Python version verification.

What you're learning: How to ask AI to reveal the mechanics behind a command you already use. You know uv run main.py works. Now you are learning what it does under the hood, which helps you understand why uv run is preferred over calling Python directly. This is the difference between using a tool and understanding a tool.

Prompt 3: Explore, Then Ask AI to Explain

Before prompting, look inside the .venv/ directory that uv created:

ls .venv/

You will see folders like bin/ (or Scripts/ on Windows), lib/, and a configuration file. You do not need to understand these yet. Now ask AI:

I ran `ls .venv/` inside my Python project and saw these folders:
[paste what you saw]

I'm new to Python. Explain:
1. What is this .venv/ directory and why does it exist?
2. What do the folders inside it contain?
3. Why does uv create and manage this directory automatically
instead of making me create it with python -m venv?

Use an analogy that does not involve programming.

What you're learning: The habit of observing before asking. You looked at real files on your machine first, then asked AI to explain what you saw. This is the opposite of asking AI to explain something abstract -- you have concrete evidence to anchor the explanation. The AI's response will connect the folders you saw to the concept of environment isolation, which is more memorable than a definition alone.


Key Takeaways

  1. uv replaces five tools. It handles Python version management, virtual environment creation, dependency installation, lockfile generation, and script execution — all through one interface.

  2. uv init creates a complete project structure. Five files, each with a clear purpose: .gitignore (what to exclude from Git), .python-version (which Python to use), README.md (documentation), main.py (entry point), and pyproject.toml (project identity).

  3. uv run is the only command you need to execute code. It verifies the Python version, syncs the virtual environment, installs missing dependencies, and runs your file. No activation step required.

  4. .python-version ensures consistency. Every developer working on SmartNotes uses the same Python version, enforced by uv automatically.

  5. This is Axiom I in practice. One shell command (uv run) orchestrates Python versioning, environment management, dependency resolution, and script execution. The shell coordinates; uv computes.


Looking Ahead

SmartNotes exists. It has a clean structure, a pinned Python version, and a working entry point. But pyproject.toml is still basic — it has no tool configurations, no development dependencies, and no way to run quality checks. In Lesson 3, you will install the rest of the discipline stack (pyright, ruff, and pytest) and configure pyproject.toml as the single source of truth for the entire project.