I've set up a bunch of greenfield Python 3.12 projects lately. Here's what actually works when you're starting fresh, not what those tutorial blogs suggest.
Environment Setup That Won't Break
Use pyenv, not system Python: System Python installations are for system tools, not your projects. pyenv lets you switch between Python versions without breaking your OS. Follow the pyenv installation guide for your platform:
## Install the latest Python 3.12 patch release
pyenv install 3.12.11 # Takes forever on M1 Macs, grab coffee
pyenv global 3.12.11 # Set as default
python --version # Should show 3.12.11
Pro tip: On Mac, pyenv with Apple Silicon is a pain in the ass. If it fails with "BUILD FAILED", you probably need to install Xcode command line tools first. Took me like 3 tries to figure this out. And even then, sometimes it just randomly fails for no apparent reason and you have to try again.
Virtual environments are mandatory: Never install packages globally. I learned this the hard way when a Django update broke my system Python and killed three other projects. Python 3.12's venv module works fine, but I prefer Poetry for dependency management. See the Poetry installation guide and dependency management documentation:
## Poetry handles venv creation and dependency locking
poetry init
poetry add fastapi uvicorn
poetry install
Poetry's lockfile mechanism prevents the "works on my machine" bullshit that ruins team projects.
Modern Project Structure for Python 3.12
Follow the src layout pattern: Don't put your code in the project root. The src layout prevents import issues and makes testing cleaner:
my-project/
├── pyproject.toml # Modern Python packaging
├── README.md
├── src/
│ └── myproject/ # Your actual code
│ ├── __init__.py
│ ├── main.py
│ └── models.py
├── tests/
│ ├── __init__.py
│ └── test_main.py
└── docs/
Use pyproject.toml, not setup.py: Python 3.12 fully supports PEP 518 standardized builds. setup.py is legacy cruft. See the PyPA packaging guide and pyproject.toml specification:
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
[tool.poetry]
name = "myproject"
version = "0.1.0"
description = "A modern Python 3.12 project"
python = "^3.12"
[tool.poetry.dependencies]
fastapi = "^0.104.0"
pydantic = "^2.5.0"
Framework Selection for New Python 3.12 Projects
Web APIs: FastAPI is pretty popular now: FastAPI usage has been growing a lot according to JetBrains surveys. It's designed for Python 3.12's async performance and modern typing. Check out the FastAPI benchmarks and async tutorial:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
## Python 3.12's new generic syntax
class UserResponse[T](BaseModel):
data: T
message: str
@app.get("/users/{user_id}")
async def get_user(user_id: int) -> UserResponse[dict]:
# Leverages Python 3.12's 75% asyncio improvement
user_data = await fetch_user_from_db(user_id)
return UserResponse(data=user_data, message="Success")
Full-stack web apps: Still Django or Flask: Django 5.0+ fully supports Python 3.12. Check the Django compatibility chart and async views documentation. Flask 3.0 works but doesn't leverage 3.12's performance improvements as well as async frameworks.
Data science: Stick with proven tools: NumPy 1.26+, pandas 2.1+, and matplotlib 3.8+ all support Python 3.12. Check the scientific Python roadmap and conda-forge package status for compatibility updates.
Modern Python 3.12 Typing and Data Models
Use Pydantic v2 for data validation: Pydantic v2.11 has official Python 3.12 generic syntax support. It's faster than dataclasses for validation-heavy applications:
from pydantic import BaseModel, Field
from typing import Annotated
## Python 3.12 generic syntax with Pydantic
class User[T](BaseModel):
id: int
name: str
metadata: T
email: Annotated[str, Field(pattern=r'^[^@]+@[^@]+\.[^@]+$')]
## Type aliases are cleaner in 3.12
type UserID = int
type APIKey = str
Dataclasses for simple data containers: Python 3.12's dataclasses work great when you don't need validation:
from dataclasses import dataclass
from datetime import datetime
@dataclass
class LogEntry:
timestamp: datetime
level: str
message: str
def __str__(self) -> str:
# f-strings finally work properly in 3.12
return f\"{self.timestamp}: [{self.level}] {self.message}\"
Database Integration for Python 3.12 Projects
Async databases are first-class citizens: Python 3.12's asyncio improvements make async database drivers practical:
PostgreSQL: Use asyncpg or SQLAlchemy 2.0+ with async support:
import asyncpg
async def get_user(user_id: int) -> dict:
conn = await asyncpg.connect('postgresql://user:pass@localhost/db')
result = await conn.fetchrow('SELECT * FROM users WHERE id = $1', user_id)
await conn.close()
return dict(result)
MongoDB: Motor (async MongoDB driver) performs significantly better with Python 3.12's asyncio improvements.
Redis: redis-py has full async support and benefits from Python 3.12's I/O optimizations.
Testing Setup for Modern Python Projects
pytest is the standard: pytest 7.4+ supports Python 3.12 fully. Read the pytest configuration guide and configure it in pyproject.toml:
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
async testing works properly: Python 3.12's asyncio improvements make async testing less painful:
import pytest
import asyncio
@pytest.mark.asyncio
async def test_async_function():
result = await some_async_operation()
assert result == expected_value
Type checking with mypy: mypy 1.7+ supports Python 3.12's new generic syntax:
poetry add --group dev mypy
poetry run mypy src/
Container Strategy for Python 3.12
Use official Python 3.12 images: python:3.12-slim is production-ready and gets security updates:
FROM python:3.12-slim
## Install Poetry
RUN pip install poetry
## Copy dependency files
COPY pyproject.toml poetry.lock ./
## Install dependencies
RUN poetry config virtualenvs.create false \
&& poetry install --no-dev
## Copy application
COPY src/ ./src/
CMD ["python", "-m", "src.myproject.main"]
Multi-stage builds for optimization: Keep production images small:
## Build stage
FROM python:3.12 AS builder
RUN pip install poetry
COPY pyproject.toml poetry.lock ./
RUN poetry export -f requirements.txt --output requirements.txt
## Production stage
FROM python:3.12-slim
COPY --from=builder requirements.txt .
RUN pip install -r requirements.txt
COPY src/ ./src/
CMD ["python", "-m", "src.myproject.main"]
Development Tools That Actually Help
Code formatting with Ruff: Ruff is faster than Black and Flake8 combined:
[tool.ruff]
target-version = "py312"
line-length = 88
select = ["E", "F", "UP", "B", "SIM", "I"]
Pre-commit hooks prevent shit commits: pre-commit runs checks automatically:
## .pre-commit-config.yaml
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.6
hooks:
- id: ruff
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.7.1
hooks:
- id: mypy
Starting fresh with Python 3.12 lets you use modern tools and patterns from day one. Check out the Python Developer's Guide and best practices documentation to build on solid foundations. No legacy workarounds, no migration pain—just modern Python development the way it should work.