
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 surroundings supervisor? Which dependency instrument? Which formatter? Which linter? Which sort checker? And in case your mission touched information, have been you supposed to start out with pandas, DuckDB, or one thing newer?
In 2026, that setup could be a lot easier.
For many new tasks, the cleanest default stack is:
- uv for Python set up, environments, dependency administration, locking, and command working.
- 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) truly come from the identical firm, Astral, which suggests they combine seamlessly with one another and together with your pyproject.toml.
# Understanding Why This Stack Works
Older setups typically seemed like this:
pyenv + pip + venv + pip-tools or Poetry + Black + isort + Flake8 + mypy + pandas
This labored, but it surely created important overlap, inconsistency, and upkeep overhead. You had separate instruments for surroundings setup, dependency locking, formatting, import sorting, linting, and typing. Each new mission began with a selection explosion. The 2026 default stack collapses all of that. The tip result’s fewer instruments, fewer configuration information, and fewer friction when onboarding contributors or wiring up steady integration (CI). Earlier than leaping into setup, let’s take a fast have 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 a substitute of manually organising digital environments and putting in packages, uv handles the heavy lifting. It retains your surroundings constant utilizing a lockfile and ensures all the pieces is appropriate earlier than working any command.
- Ruff: That is your all-in-one instrument for code high quality. This can be very quick, checks for points, fixes lots of them routinely, and likewise codecs your code. You should use it as a substitute of instruments like Black, isort, Flake8, and others.
- Ty: It is a newer instrument for sort checking. It helps catch errors by checking varieties in your code and works with numerous editors. Whereas newer than instruments like mypy or Pyright, it’s optimized for contemporary workflows.
- Polars: It is a trendy library for working with dataframes. It focuses on environment friendly information processing utilizing lazy execution, which suggests it optimizes queries earlier than working them. This makes it quicker and extra reminiscence environment friendly than pandas, particularly for big information duties.
# Reviewing Stipulations
The setup is sort of easy. Listed below are the few issues you might want to get began:
- Terminal: macOS Terminal, Home windows PowerShell, or any Linux shell.
- Web connection: Required for the one-time uv installer and package deal downloads.
- Code editor: VS Code is advisable as a result of it really works properly 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 surroundings 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 https://astral.sh/uv/set up.sh | sh
Home windows PowerShell:
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/set up.ps1 | 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 Venture
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
└── important.py
Reshape it right into a src/ structure, which improves imports, packaging, take a look at isolation, and type-checker configuration:
mkdir -p src/my_project exams information/uncooked information/processed
mv important.py src/my_project/important.py
contact src/my_project/__init__.py exams/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
│ └── important.py
├── exams/
│ └── test_main.py
└── information/
├── uncooked/
└── processed/
When you want a selected 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 group 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 surroundings (.venv/) if one doesn’t exist, resolves the dependency tree, installs packages, and updates uv.lock with actual, pinned variations.
For instruments wanted solely throughout improvement, use the --dev flag:
uv add --dev ruff ty pytest
This locations them in a separate [dependency-groups] part in pyproject.toml, protecting manufacturing dependencies lean. You by no means must run supply .venv/bin/activate; whenever you use uv run, it routinely prompts the right surroundings.
# Step 4: Configuring Ruff (Linting and Formatting)
Ruff is configured immediately 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.
Operating Ruff:
# Lint your code
uv run ruff test .
# Auto-fix points the place doable
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 good for adoption. You repair apparent points first, then steadily promote guidelines to errors. Conserving 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: Inspecting the Full pyproject.toml
Here’s what your ultimate configuration seems like with all the pieces wired up — one file, each instrument configured, with no scattered config information:
[project]
identify = "my-project"
model = "0.1.0"
description = "Fashionable Python mission 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/important.py with code that workout routines the Polars aspect of the stack:
"""Pattern information evaluation with Polars."""
import polars as pl
def build_report(path: str) -> pl.DataFrame:
"""Construct a income abstract from uncooked information utilizing the lazy API."""
q = (
pl.scan_csv(path)
.filter(pl.col("standing") == "lively")
.with_columns(
revenue_per_user=(pl.col("income") / pl.col("customers")).alias("rpu")
)
.group_by("phase")
.agg(
pl.len().alias("rows"),
pl.col("income").sum().alias("income"),
pl.col("rpu").imply().alias("avg_rpu"),
)
.type("income", descending=True)
)
return q.gather()
def important() -> None:
"""Entry level with pattern in-memory information."""
df = pl.DataFrame(
{
"phase": ["Enterprise", "SMB", "Enterprise", "SMB", "Enterprise"],
"standing": ["active", "active", "churned", "active", "active"],
"income": [12000, 3500, 8000, 4200, 15000],
"customers": [120, 70, 80, 84, 150],
}
)
abstract = (
df.lazy()
.filter(pl.col("standing") == "lively")
.with_columns(
(pl.col("income") / pl.col("customers")).spherical(2).alias("rpu")
)
.group_by("phase")
.agg(
pl.len().alias("rows"),
pl.col("income").sum().alias("total_revenue"),
pl.col("rpu").imply().spherical(2).alias("avg_rpu"),
)
.type("total_revenue", descending=True)
.gather()
)
print("Income Abstract:")
print(abstract)
if __name__ == "__main__":
important()
Earlier than working, you want a construct system in pyproject.toml so uv installs your mission as a package deal. We are going to use Hatchling:
cat >> pyproject.toml << 'EOF'
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.construct"
[tool.hatch.build.targets.wheel]
packages = ["src/my_project"]
EOF
Then sync and run:
uv sync
uv run python -m my_project.important
It’s best to see a formatted Polars desk:
Income Abstract:
form: (2, 4)
┌────────────┬──────┬───────────────┬─────────┐
│ phase ┆ 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 ready up, the day-to-day loop is easy:
# 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 income report module"
# Altering the Approach You Write Python with Polars
The most important mindset shift on this stack is on the info aspect. With Polars, your defaults ought to be:
- Expressions over row-wise operations. Polars expressions let the engine vectorize and parallelize operations. Keep away from person outlined capabilities (UDFs) except there is no such thing as a native different, as UDFs are considerably slower.
- Lazy execution over keen loading. Use
scan_csv()as a substitute ofread_csv(). This creates aLazyFramethat builds a question plan, permitting the optimizer to push filters down and eradicate unused columns. - Parquet-first workflows over CSV-heavy pipelines. An excellent sample for inside information preparation seems like this.
# Evaluating When This Setup Is Not the Finest Match
It’s your decision a special selection if:
- Your group has a mature Poetry or mypy workflow that’s working properly.
- Your codebase relies upon closely on pandas-specific APIs or ecosystem libraries.
- Your group is standardized on Pyright.
- You’re 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 pieces to make sure you are utilizing the right surroundings. - 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 quicker, 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 well auto-fix unused imports, outdated syntax, and extra. - Desire the lazy API by default. Use
scan_csv()and solely name.gather()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 Productiveness with ChatGPT”. As a Google Technology 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 ladies in STEM fields.















