Picture by Editor
# Introduction
Python mission setup used to imply making a dozen small selections earlier than you wrote your first helpful line of code. Which setting supervisor? Which dependency instrument? Which formatter? Which linter? Which sort checker? And in case your mission touched information, had been you supposed to start out with pandas, DuckDB, or one thing newer?
In 2026, that setup could be a lot less complicated.
For many new tasks, the cleanest default stack is:
- uv for Python set up, environments, dependency administration, locking, and command operating.
- Ruff for linting and formatting.
- Ty for sort checking.
- Polars for dataframe work.
This stack is quick, trendy, and notably coherent. Three of the 4 instruments (uv, Ruff, and Ty) really come from the identical firm, Astral, which implies they combine seamlessly with one another and along with your pyproject.toml.
# Understanding Why This Stack Works
Older setups usually seemed like this:
pyenv + pip + venv + pip-tools or Poetry + Black + isort + Flake8 + mypy + pandas
This labored, however it created important overlap, inconsistency, and upkeep overhead. You had separate instruments for setting setup, dependency locking, formatting, import sorting, linting, and typing. Each new mission began with a alternative explosion. The 2026 default stack collapses all of that. The top result’s fewer instruments, fewer configuration recordsdata, and fewer friction when onboarding contributors or wiring up steady integration (CI). Earlier than leaping into setup, let’s take a fast take a look at what every instrument within the 2026 stack is doing:
- uv: That is the bottom of your mission setup. It creates the mission, manages variations, handles dependencies, and runs your code. As an alternative of manually establishing digital environments and putting in packages, uv handles the heavy lifting. It retains your setting constant utilizing a lockfile and ensures all the things is appropriate earlier than operating any command.
- Ruff: That is your all-in-one instrument for code high quality. This can be very quick, checks for points, fixes a lot of them routinely, and likewise codecs your code. You should utilize it as an alternative of instruments like Black, isort, Flake8, and others.
- Ty: This can be a newer instrument for sort checking. It helps catch errors by checking sorts in your code and works with numerous editors. Whereas newer than instruments like mypy or Pyright, it’s optimized for contemporary workflows.
- Polars: This can be a trendy library for working with dataframes. It focuses on environment friendly information processing utilizing lazy execution, which implies it optimizes queries earlier than operating them. This makes it sooner and extra reminiscence environment friendly than pandas, particularly for giant information duties.
# Reviewing Conditions
The setup is sort of easy. Listed here are the few issues you’ll want to get began:
- Terminal: macOS Terminal, Home windows PowerShell, or any Linux shell.
- Web connection: Required for the one-time uv installer and bundle downloads.
- Code editor: VS Code is really helpful as a result of it really works effectively with Ruff and Ty, however any editor is okay.
- Git: Required for model management; observe that uv initializes a Git repository routinely.
That’s it. You do not want Python pre-installed. You do not want pip, venv, pyenv, or conda. uv handles set up and setting administration for you.
# Step 1: Putting in uv
uv offers a standalone installer that works on macOS, Linux, and Home windows with out requiring Python or Rust to be current in your machine.
macOS and Linux:
curl -LsSf | sh
Home windows PowerShell:
powershell -ExecutionPolicy ByPass -c "irm | iex"
After set up, restart your terminal and confirm:
Output:
uv 0.8.0 (Homebrew 2025-07-17)
This single binary now replaces pyenv, pip, venv, pip-tools, and the mission administration layer of Poetry.
# Step 2: Making a New Mission
Navigate to your mission listing and scaffold a brand new one:
uv init my-project
cd my-project
uv creates a clear beginning construction:
my-project/
├── .python-version
├── pyproject.toml
├── README.md
└── foremost.py
Reshape it right into a src/ format, which improves imports, packaging, take a look at isolation, and type-checker configuration:
mkdir -p src/my_project checks information/uncooked information/processed
mv foremost.py src/my_project/foremost.py
contact src/my_project/__init__.py checks/test_main.py
Your construction ought to now seem like this:
my-project/
├── .python-version
├── README.md
├── pyproject.toml
├── uv.lock
├── src/
│ └── my_project/
│ ├── __init__.py
│ └── foremost.py
├── checks/
│ └── test_main.py
└── information/
├── uncooked/
└── processed/
For those who want a particular model (e.g. 3.12), uv can set up and pin it:
uv python set up 3.12
uv python pin 3.12
The pin command writes the model to .python-version, making certain each crew member makes use of the identical interpreter.
# Step 3: Including Dependencies
Including dependencies is a single command that resolves, installs, and locks concurrently:
uv routinely creates a digital setting (.venv/) if one doesn’t exist, resolves the dependency tree, installs packages, and updates uv.lock with precise, pinned variations.
For instruments wanted solely throughout growth, use the --dev flag:
uv add --dev ruff ty pytest
This locations them in a separate [dependency-groups] part in pyproject.toml, maintaining manufacturing dependencies lean. You by no means have to run supply .venv/bin/activate; if you use uv run, it routinely prompts the right setting.
# Step 4: Configuring Ruff (Linting and Formatting)
Ruff is configured instantly inside your pyproject.toml. Add the next sections:
[tool.ruff]
line-length = 100
target-version = "py312"
[tool.ruff.lint]
choose = ["E4", "E7", "E9", "F", "B", "I", "UP"]
[tool.ruff.format]
docstring-code-format = true
quote-style = "double"
A 100-character line size is an effective compromise for contemporary screens. Rule teams flake8-bugbear (B), isort (I), and pyupgrade (UP) add actual worth with out overwhelming a brand new repository.
Working Ruff:
# Lint your code
uv run ruff test .
# Auto-fix points the place potential
uv run ruff test --fix .
# Format your code
uv run ruff format .
Discover the sample: uv run . You by no means set up instruments globally or activate environments manually.
# Step 5: Configuring Ty for Kind Checking
Ty can also be configured in pyproject.toml. Add these sections:
[tool.ty.environment]
root = ["./src"]
[tool.ty.rules]
all = "warn"
[[tool.ty.overrides]]
embody = ["src/**"]
[tool.ty.overrides.rules]
possibly-unresolved-reference = "error"
[tool.ty.terminal]
error-on-warning = false
output-format = "full"
This configuration begins Ty in warning mode, which is right for adoption. You repair apparent points first, then regularly promote guidelines to errors. Retaining information/** excluded prevents type-checker noise from non-code directories.
# Step 6: Configuring pytest
Add a piece for pytest:
[tool.pytest.ini_options]
testpaths = ["tests"]
Run your take a look at suite with:
# Step 7: Analyzing the Full pyproject.toml
Here’s what your last configuration seems like with all the things wired up — one file, each instrument configured, with no scattered config recordsdata:
[project]
title = "my-project"
model = "0.1.0"
description = "Modern Python project with uv, Ruff, Ty, and Polars"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"polars>=1.39.3",
]
[dependency-groups]
dev = [
"pytest>=9.0.2",
"ruff>=0.15.8",
"ty>=0.0.26",
]
[tool.ruff]
line-length = 100
target-version = "py312"
[tool.ruff.lint]
choose = ["E4", "E7", "E9", "F", "B", "I", "UP"]
[tool.ruff.format]
docstring-code-format = true
quote-style = "double"
[tool.ty.environment]
root = ["./src"]
[tool.ty.rules]
all = "warn"
[[tool.ty.overrides]]
embody = ["src/**"]
[tool.ty.overrides.rules]
possibly-unresolved-reference = "error"
[tool.ty.terminal]
error-on-warning = false
output-format = "full"
[tool.pytest.ini_options]
testpaths = ["tests"]
# Step 8: Writing Code with Polars
Exchange the contents of src/my_project/foremost.py with code that workouts the Polars aspect of the stack:
"""Sample data analysis with Polars."""
import polars as pl
def build_report(path: str) -> pl.DataFrame:
"""Build a revenue summary from raw data using the lazy API."""
q = (
pl.scan_csv(path)
.filter(pl.col("status") == "active")
.with_columns(
revenue_per_user=(pl.col("revenue") / pl.col("users")).alias("rpu")
)
.group_by("segment")
.agg(
pl.len().alias("rows"),
pl.col("revenue").sum().alias("revenue"),
pl.col("rpu").imply().alias("avg_rpu"),
)
.type("revenue", descending=True)
)
return q.acquire()
def foremost() -> None:
"""Entry point with sample in-memory data."""
df = pl.DataFrame(
{
"segment": ["Enterprise", "SMB", "Enterprise", "SMB", "Enterprise"],
"status": ["active", "active", "churned", "active", "active"],
"revenue": [12000, 3500, 8000, 4200, 15000],
"users": [120, 70, 80, 84, 150],
}
)
abstract = (
df.lazy()
.filter(pl.col("status") == "active")
.with_columns(
(pl.col("revenue") / pl.col("users")).spherical(2).alias("rpu")
)
.group_by("segment")
.agg(
pl.len().alias("rows"),
pl.col("revenue").sum().alias("total_revenue"),
pl.col("rpu").imply().spherical(2).alias("avg_rpu"),
)
.type("total_revenue", descending=True)
.acquire()
)
print("Revenue Summary:")
print(abstract)
if __name__ == "__main__":
foremost()
Earlier than operating, you want a construct system in pyproject.toml so uv installs your mission as a bundle. We’ll use Hatchling:
cat >> pyproject.toml << 'EOF'
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/my_project"]
EOF
Then sync and run:
uv sync
uv run python -m my_project.foremost
You must see a formatted Polars desk:
Income Abstract:
form: (2, 4)
┌────────────┬──────┬───────────────┬─────────┐
│ section ┆ rows ┆ total_revenue ┆ avg_rpu │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ u32 ┆ i64 ┆ f64 │
╞════════════╪══════╪═══════════════╪═════════╡
│ Enterprise ┆ 2 ┆ 27000 ┆ 100.0 │
│ SMB ┆ 2 ┆ 7700 ┆ 50.0 │
└────────────┴──────┴───────────────┴─────────┘
# Managing the Each day Workflow
As soon as the mission is about up, the day-to-day loop is simple:
# Pull newest, sync dependencies
git pull
uv sync
# Write code...
# Earlier than committing: lint, format, type-check, take a look at
uv run ruff test --fix .
uv run ruff format .
uv run ty test
uv run pytest
# Commit
git add .
git commit -m "feat: add revenue report module"
# Altering the Means You Write Python with Polars
The largest mindset shift on this stack is on the information aspect. With Polars, your defaults needs to be:
- Expressions over row-wise operations. Polars expressions let the engine vectorize and parallelize operations. Keep away from consumer outlined capabilities (UDFs) except there is no such thing as a native various, as UDFs are considerably slower.
- Lazy execution over keen loading. Use
scan_csv()as an alternative ofread_csv(). This creates aLazyFramethat builds a question plan, permitting the optimizer to push filters down and remove unused columns. - Parquet-first workflows over CSV-heavy pipelines. A superb sample for inner information preparation seems like this.
# Evaluating When This Setup Is Not the Greatest Match
You might have considered trying a distinct alternative if:
- Your crew has a mature Poetry or mypy workflow that’s working effectively.
- Your codebase relies upon closely on pandas-specific APIs or ecosystem libraries.
- Your group is standardized on Pyright.
- You might be working in a legacy repository the place altering instruments would create extra disruption than worth.
# Implementing Professional Ideas
- By no means activate digital environments manually. Use
uv runfor all the things to make sure you are utilizing the right setting. - All the time commit
uv.lockto model management. This ensures the mission runs identically on each machine. - Use
--frozenin CI. This installs dependencies from the lockfile for sooner, extra dependable builds. - Use
uvxfor one-off instruments. Run instruments with out putting in them in your mission. - Use Ruff’s
--fixflag liberally. It may auto-fix unused imports, outdated syntax, and extra. - Choose the lazy API by default. Use
scan_csv()and solely name.acquire()on the finish. - Centralize configuration. Use
pyproject.tomlas the one supply of reality for all instruments.
# Concluding Ideas
The 2026 Python default stack reduces setup effort and encourages higher practices: locked environments, a single configuration file, quick suggestions, and optimized information pipelines. Give it a attempt; when you expertise environment-agnostic execution, you’ll perceive why builders are switching.
Kanwal Mehreen is a machine studying engineer and a technical author with a profound ardour for information science and the intersection of AI with medication. She co-authored the e-book “Maximizing Productivity with ChatGPT”. As a Google Era Scholar 2022 for APAC, she champions range and tutorial excellence. She’s additionally acknowledged as a Teradata Range in Tech Scholar, Mitacs Globalink Analysis Scholar, and Harvard WeCode Scholar. Kanwal is an ardent advocate for change, having based FEMCodes to empower girls in STEM fields.



