By the end of this week, you will:
- Understand why proper tooling matters in professional development
- Have a fully configured Python development environment
- Know how to use
uvfor package and environment management - Have Git configured with proper SSH authentication
- Understand the difference between system Python and project Python
The setup you'll do this week, in order:
---
config:
look: handDrawn
theme: neutral
---
flowchart LR
Install["Install uv"]
Python["uv python install 3.12"]
Project["uv init"]
Deps["uv add --dev ruff pytest"]
GitCfg["git config + SSH key"]
Verify["verify_environment.py"]
Install --> Python --> Project --> Deps --> GitCfg --> Verify
Before starting exercises, read these documentation sections:
| Resource | Section | Time |
|---|---|---|
| uv Documentation | Getting Started, Concepts | 30 min |
| Git Handbook | Entire guide | 20 min |
| Python Virtual Environments | Full page | 15 min |
- A computer running macOS, Linux, or Windows (with WSL2)
- Basic terminal/command line familiarity
- A GitHub account
┌─────────────────────────────────────────────────────────────────-┐
│ Traditional Python Tooling │
├────────────────────────────────────────────────────────────────-─┤
│ │
│ pyenv virtualenv pip pip-tools │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ [Python] ──► [Virtual Env] ──► [Install] ──► [Lock deps] │
│ Versions Creation Packages Versions │
│ │
│ 4 separate tools, different maintainers, inconsistent APIs │
│ │
└─────────────────────────────────────────────────────────────────-┘
┌─────────────────────────────────────────────────────────────────-┐
│ Modern: uv │
├─────────────────────────────────────────────────────────────────-┤
│ │
│ uv │
│ │ │
│ ┌────────┬────────┼────────┬────────┐ │
│ ▼ ▼ ▼ ▼ ▼ │
│ [Python] [Venv] [Install] [Lock] [Run] │
│ │
│ Single tool, consistent API, 10-100x faster than pip │
│ │
└─────────────────────────────────────────────────────────────────-┘
Key Benefits:
- Speed: Installing packages is 10-100x faster than pip
- Reliability: Deterministic dependency resolution
- Simplicity: One tool instead of four
- Modern: Built in Rust, actively maintained by Astral
Every Django project we create will follow this structure:
my-project/
├── pyproject.toml # Project metadata & dependencies (replaces requirements.txt)
├── uv.lock # Locked dependency versions (auto-generated)
├── .python-version # Python version for this project
├── .venv/ # Virtual environment (auto-created by uv)
├── .gitignore # Files to exclude from git
├── .pre-commit-config.yaml # Code quality hooks
├── src/
│ └── my_project/ # Your Django project
└── tests/ # Test files
Task: Install uv on your system.
macOS/Linux:
curl -LsSf https://astral.sh/uv/install.sh | shWindows (PowerShell):
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"Verification:
# Close and reopen terminal, then:
uv --versionExpected Output:
uv 0.4.x (or higher)
📖 Documentation: uv Installation
Task: Use uv to install Python 3.12 (we'll use this version throughout the course).
# List available Python versions
uv python list
# Install Python 3.12
uv python install 3.12
# Verify installation
uv python list --only-installed
⚠️ Important: We useuv python installinstead of installing Python from python.org or homebrew. This ensures consistent versions across all your projects.
📖 Documentation: uv Python Management
Task: Set up Git with your identity and SSH key.
# Set your identity
git config --global user.name "Your Name"
git config --global user.email "your.email@example.com"
# Set default branch name
git config --global init.defaultBranch main
# Enable helpful colorization
git config --global color.ui auto
# Verify configuration
git config --listGenerate SSH Key (if you don't have one):
# Generate new SSH key
ssh-keygen -t ed25519 -C "your.email@example.com"
# Start SSH agent
eval "$(ssh-agent -s)"
# Add key to agent
ssh-add ~/.ssh/id_ed25519
# Display public key (copy this to GitHub)
cat ~/.ssh/id_ed25519.pubAdd to GitHub:
- Go to GitHub → Settings → SSH and GPG keys
- Click "New SSH key"
- Paste your public key
- Test connection:
ssh -T git@github.com
📖 Documentation: GitHub SSH Setup
Task: Create your first Python project using uv.
# Create a new directory for week 01 work
mkdir -p ~/django-learning/week-01
cd ~/django-learning/week-01
# Initialize a new Python project
uv init hello-python
# Enter the project
cd hello-python
# Examine what was created
ls -laExpected Structure:
hello-python/
├── .python-version # Contains "3.12"
├── pyproject.toml # Project configuration
├── README.md # Project readme
└── hello.py # Sample Python file
Examine pyproject.toml:
cat pyproject.tomlExpected Content:
[project]
name = "hello-python"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = []📖 Documentation: uv Projects
Task: Learn how uv manages dependencies.
# Add a dependency
uv add requests
# See what changed
cat pyproject.toml
# Notice the new lock file
ls -la
cat uv.lock | head -50Understanding the Lock File:
┌──────────────────────────────────────────────────────────────-────┐
│ Dependency Resolution │
├───────────────────────────────────────────────────────────────-───┤
│ │
│ pyproject.toml uv.lock │
│ ───────────── ─────── │
│ dependencies = [ Exact versions: │
│ "requests" ──────────────► requests==2.31.0 │
│ ] uv resolves urllib3==2.1.0 │
│ and locks certifi==2024.2.2 │
│ (flexible) charset-normalizer==3.3.2 │
│ idna==3.6 │
│ (precise & reproducible) │
│ │
└────────────────────────────────────────────────────────────────-──┘
Why Lock Files Matter:
- Reproducibility: Everyone gets exact same versions
- Security: Pinned versions, known checksums
- Collaboration: No "works on my machine" issues
# Add a development dependency (not needed in production)
uv add --dev pytest ruff
# View updated pyproject.toml
cat pyproject.toml📖 Documentation: uv Dependencies
Task: Execute Python code using uv.
Create a test script:
cat > hello.py << 'EOF'
import requests
def main():
response = requests.get("https://api.github.com")
print(f"GitHub API Status: {response.status_code}")
print(f"Rate Limit: {response.headers.get('X-RateLimit-Limit')}")
if __name__ == "__main__":
main()
EOFRun with uv:
# uv automatically uses the project's virtual environment
uv run python hello.pyExpected Output:
GitHub API Status: 200
Rate Limit: 60
⚠️ Key Insight:uv runensures your code runs in the correct virtual environment with all dependencies available. Never activate virtual environments manually - useuv runinstead.
Task: Set up ruff for linting and formatting.
# ruff should already be installed as dev dependency
# Create a ruff configuration in pyproject.toml
cat >> pyproject.toml << 'EOF'
[tool.ruff]
line-length = 88
target-version = "py312"
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"UP", # pyupgrade
]
[tool.ruff.format]
quote-style = "double"
EOFTest ruff:
# Create a file with style issues
cat > messy.py << 'EOF'
import os
import sys
import requests
x=1
y =2
def foo( ):
unused_var = "hello"
return x+y
EOF
# Check for issues
uv run ruff check messy.py
# Auto-fix issues
uv run ruff check --fix messy.py
# Format the code
uv run ruff format messy.py
# View the cleaned code
cat messy.py📖 Documentation: Ruff Configuration
Task: Initialize git and make your first commit.
# Create .gitignore
cat > .gitignore << 'EOF'
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
.venv/
venv/
ENV/
# uv
.uv/
# IDE
.idea/
.vscode/
*.swp
*.swo
# OS
.DS_Store
Thumbs.db
# Testing
.pytest_cache/
.coverage
htmlcov/
# Distribution
dist/
build/
*.egg-info/
EOF
# Initialize repository
git init
# Stage all files
git add .
# Check status
git status
# Make first commit
git commit -m "Initial commit: Hello Python project with uv"
# View commit history
git log --onelineTask: Create a script that verifies your entire development environment is correctly configured.
Create verify_environment.py:
#!/usr/bin/env python3
"""
Environment Verification Script
Checks that all required tools are properly installed and configured.
"""
import shutil
import subprocess
import sys
def check_command(name: str, command: list[str], expected_in_output: str = "") -> bool:
"""Check if a command exists and runs successfully."""
print(f"Checking {name}...", end=" ")
path = shutil.which(command[0])
if not path:
print(f"❌ {command[0]} not found in PATH")
return False
try:
result = subprocess.run(
command,
capture_output=True,
text=True,
timeout=10,
)
if expected_in_output and expected_in_output not in result.stdout + result.stderr:
print(f"❌ Unexpected output")
return False
print(f"✅ Found at {path}")
return True
except subprocess.TimeoutExpired:
print("❌ Command timed out")
return False
except Exception as e:
print(f"❌ Error: {e}")
return False
def check_python_version() -> bool:
"""Verify Python version is 3.12+."""
print("Checking Python version...", end=" ")
version = sys.version_info
# Tuple comparison - `major >= 3 and minor >= 12` would reject Python
# 4.0 (minor 0 < 12) even though it satisfies "3.12+".
if version >= (3, 12):
print(f"✅ Python {version.major}.{version.minor}.{version.micro}")
return True
print(f"❌ Python {version.major}.{version.minor} (need 3.12+)")
return False
def check_git_config() -> bool:
"""Verify Git is configured with name and email."""
print("Checking Git configuration...", end=" ")
try:
name = subprocess.run(
["git", "config", "user.name"],
capture_output=True,
text=True,
)
email = subprocess.run(
["git", "config", "user.email"],
capture_output=True,
text=True,
)
if name.stdout.strip() and email.stdout.strip():
print(f"✅ {name.stdout.strip()} <{email.stdout.strip()}>")
return True
print("❌ Name or email not configured")
return False
except Exception as e:
print(f"❌ Error: {e}")
return False
def main() -> int:
"""Run all environment checks."""
print("=" * 60)
print("Django Mentorship - Environment Verification")
print("=" * 60)
print()
checks = [
("Python Version", check_python_version),
("uv", lambda: check_command("uv", ["uv", "--version"])),
("Git", lambda: check_command("git", ["git", "--version"])),
("Git Config", check_git_config),
("ruff", lambda: check_command("ruff", ["uv", "run", "ruff", "--version"])),
("pytest", lambda: check_command("pytest", ["uv", "run", "pytest", "--version"])),
]
results = []
for name, check_func in checks:
results.append(check_func())
print()
print("=" * 60)
passed = sum(results)
total = len(results)
if passed == total:
print(f"✅ All checks passed! ({passed}/{total})")
print("Your environment is ready for Django development.")
return 0
else:
print(f"⚠️ {passed}/{total} checks passed")
print("Please fix the issues above before proceeding.")
return 1
if __name__ == "__main__":
sys.exit(main())Run verification:
uv run python verify_environment.pyExpected Output (all green):
============================================================
Django Mentorship - Environment Verification
============================================================
Checking Python version... ✅ Python 3.12.x
Checking uv... ✅ Found at /path/to/uv
Checking Git... ✅ Found at /usr/bin/git
Checking Git configuration... ✅ Your Name <your@email.com>
Checking ruff... ✅ Found at /path/to/ruff
Checking pytest... ✅ Found at /path/to/pytest
============================================================
✅ All checks passed! (6/6)
Your environment is ready for Django development.
Before moving to Week 02, ensure you can check ALL boxes:
-
uv --versionshows version 0.4.0 or higher -
uv python list --only-installedshows Python 3.12 -
git config user.nameshows your name -
git config user.emailshows your email -
ssh -T git@github.comauthenticates successfully - Created
hello-pythonproject with uv -
uv run python verify_environment.pypasses all checks - Project is committed to git with proper .gitignore
- uv GitHub Repository
- Why Astral is Building uv
- Git Immersion Tutorial
- Oh Shit, Git! - Common Git mistakes and fixes
Restart your terminal after installation, or add uv to your PATH manually.
On Linux/Mac, you may need to use sudo for system-wide installation, or install to user directory.
Ensure the SSH agent is running: eval "$(ssh-agent -s)"