Skip to content
Published on

Modern Python in 2026 — Python 3.13 / 3.14 / free-threaded / uv / Ruff / Polars 1.0 / FastAPI / Litestar / Robyn deep-dive

Authors

Prologue — Two inflection points in one decade

Python was born in 1991. It turns 35 in 2026. Across 1.x to 2.x to 3.x it became the flagship dynamic language, the lingua franca of data and ML, and a major pillar of backend microservices. But it carried two chronic complaints.

  1. The GIL (Global Interpreter Lock) — CPython's single giant lock. CPU-bound multithreading was effectively impossible, and half of Python's "slow" reputation came from here.
  2. A fragmented packaging and tooling ecosystem — pip, easy_install, conda, poetry, pipenv, pdm, hatch, rye… there was no consensus. And all of them were slow.

2024 through 2026 is when these two chronic conditions both got major surgery, at the same time.

  • Python 3.13 (Oct 2024) — PEP 703 (free-threaded) and PEP 744 (JIT) builds landed as official previews. For the first time in 30 years, a CPython without the GIL existed as an ABI.
  • Python 3.14 (Oct 2025) — the free-threaded build was promoted to stable. Deferred imports (PEP 791), template strings, faster startup. A no-GIL Python is no longer "experimental."
  • uv (Feb 2024, Astral) — a single Rust binary. 10–100x faster installs than pip, a lockfile, venv management, and Python interpreter installation in one tool. It is becoming the de facto standard.
  • Ruff (Astral) — a single Rust linter and formatter. Absorbs flake8, isort, black, pyupgrade, and parts of bandit. Hundreds of times faster.
  • ty (Astral, alpha 2025) — a type checker rewritten in Rust, going after mypy and pyright.
  • Polars 1.0 (Aug 2024) — a Rust-core DataFrame. Targets the single-threaded ceiling and memory inefficiency of Pandas head-on.
  • Pandas 3.0 (May 2025) — Arrow backend, Copy-on-Write by default, PyArrow as a hard dependency.
  • Pydantic v2.10 — Rust-core validation, the foundation of FastAPI.
  • Web — FastAPI is the ASGI default; Litestar (formerly Starlite), Robyn (Rust core), Quart (async Flask), Sanic, and BlackSheep compete with distinct flavors. Django 5.x remains the rock-solid full-stack answer.

This article walks through all of it at once. Not "which tool should I pick" but "why did these changes happen, and what tradeoffs come with them." With code examples, commands, and an actual decision guide.


1. The 2026 modern Python landscape

As of May 2026, imagine starting a new Python project. Compare the procedure to five years ago.

2021:

pyenv install 3.10.0
pyenv local 3.10.0
python -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
pip-compile requirements.in
flake8 .
black .
isort .
mypy .
pytest

2026:

uv init                       # creates pyproject.toml + .python-version
uv python install 3.14        # uv manages the interpreter too
uv add fastapi polars         # add dep + lock + install
uv run ruff check --fix .     # lint
uv run ruff format .          # format
uv run ty check .             # type check (usable even at alpha)
uv run pytest                 # test

Summarized.

Area20212026
Python installpyenvuv
venvpython -m venvuv venv (auto)
Dep managementpip + requirements.txtuv + pyproject.toml + uv.lock
Lockpip-tools / poetryuv (built in)
Lintflake8 (~tens of seconds)ruff (~ms)
Formatblack (~seconds)ruff format (~ms)
Import sortisortruff (integrated)
Type checkmypy / pyrightty (alpha) / pyright / mypy
DataFramePandasPolars / Pandas 3.0
Validationdataclasses + manualPydantic v2
WebFlask/Django/FastAPIFastAPI/Litestar/Robyn/Django 5
Concurrencyasyncio / multiprocessingfree-threaded (3.13+) + asyncio

One-line summary: a wave of Rust-rewritten tools replaced half of Python's tool chain, and Python itself started shedding the GIL.


2. Python 3.13 (Oct 2024) — free-threaded preview, JIT preview

Python 3.13 shipped on October 7, 2024. On the surface a routine minor release, but two things were firsts.

PEP 703 — free-threaded (no-GIL) build

For a long time, the consensus was that GIL removal was "theoretically possible but breaks compatibility and single-thread performance." PEP 703 (Sam Gross, Meta) broke that consensus. Key ideas.

  • biased reference counting — the thread that owns an object mutates the refcount without a lock; other threads take a lock.
  • deferred reference counting — some immortal objects (None, small ints) skip refcounting entirely.
  • per-object locking — small locks attached to mutable containers like dict and list.
  • stop-the-world GC — all threads pause briefly during GC.

The build is opt-in. The same 3.13 source produces two builds.

# Standard build (with GIL)
./configure && make

# Free-threaded build (no GIL)
./configure --disable-gil && make
# Interpreter is python3.13t (t = threaded)

With uv:

uv python install 3.13         # standard
uv python install 3.13t        # free-threaded
uv python install 3.13+freethreaded   # explicit form

3.13's free-threaded build is preview. That means:

  • The ABI is separate. C extensions need a separate build (the Py_GIL_DISABLED macro).
  • Single-threaded performance is about 10–20% slower.
  • Some stdlib and many C extensions are not yet ready.

PEP 744 — Copy-and-Patch JIT (experimental)

Another big change in 3.13. A copy-and-patch JIT layered on the Tier 2 interpreter. At build time it uses LLVM to pre-generate "stencils" — small machine code fragments — then patches them at runtime to produce native code.

./configure --enable-experimental-jit && make

In 3.13 the speedup is small (0–5%). But it is the foundation for serious optimizations in 3.14 and 3.15.

Other notable 3.13 changes

  • New REPL — multi-line editing, color, exit on Ctrl+D.
  • Improved tracebacks — color, more precise location markers.
  • iOS Tier 3, Android Tier 3 — officially supported platforms, the foundation for BeeWare/Briefcase.
  • Redesigned locals API — accurate variable modification from debuggers.
  • typing.TypeIs — more precise narrowing than TypeGuard.
  • Deprecations — some argparse APIs, pkgutil.find_loader, etc.

3. Python 3.14 (Oct 2025) — free-threaded stable

Python 3.14 shipped October 7, 2025. The maturation of what 3.13 started.

Free-threaded promoted to stable

The "experimental" tag on the free-threaded build in 3.13 became an officially supported option in 3.14. The implications are large.

  • Core packages like numpy, scipy, pandas, polars, pillow, lxml, cryptography ship free-threaded wheels to PyPI.
  • pip install reads the interpreter type and pulls the right wheel.
  • The single-threaded performance penalty narrows to 3–5% (near zero when combined with the JIT).

Deferred imports (PEP 791)

# Through 3.13 — import is immediate
import heavy_module   # interpreter startup gets slow

# 3.14 — deferred imports
from __future__ import deferred_imports
import heavy_module   # import when actually used

It is a big deal for CLI tools and cold-start-sensitive environments (Lambda, serverless). Part of why uv and Ruff feel fast is that they emulate this pattern in Rust.

Template strings — t-strings

# f-strings evaluate immediately
name = "world"
greeting = f"Hello, {name}!"   # "Hello, world!" right away

# t-strings are lazy templates
template = t"Hello, {name}!"   # Template object, not evaluated
# Later you can render, escape, or run i18n on it
html = template.render(escape=html_escape)

Useful for SQL-injection / XSS protection, i18n, and structured logging. As of May 2026 adoption is still early.

Other notable 3.14 changes

  • JIT improvements — 10–15% on some benchmarks.
  • PEP 768 — external debuggers can attach safely.
  • PEP 765 — warns on return/break/continue inside finally.
  • Subinterpreter API — added to the stdlib via PEP 734.
  • typing.evaluate_forward_ref — an API for evaluating forward references.

4. What PEP 703 actually changes — life without the GIL

What does losing the GIL really change? In short.

1) CPU-bound multithreading actually works

# Pre-2024 — meaningless because of the GIL
from concurrent.futures import ThreadPoolExecutor

def cpu_heavy(n):
    s = 0
    for i in range(n):
        s += i * i
    return s

# More threads, still one core
with ThreadPoolExecutor(max_workers=8) as pool:
    list(pool.map(cpu_heavy, [10_000_000] * 8))

On the free-threaded build, eight threads actually spread across eight cores. Many workloads that needed multiprocessing can use a ThreadPool instead (with shared memory and no IPC overhead).

2) C extension authors take on more responsibility

The GIL enforced a single-threaded assumption. Now every C extension is responsible for its own thread safety.

  • If you have global mutable state, you need a lock.
  • PyObject manipulation is safe, but your own C data structures must be protected.
  • The Py_GIL_DISABLED macro lets you branch at build time.

Big packages like numpy and scipy reached free-threaded readiness over one to two years. Smaller C extensions still need time.

3) The relationship with asyncio

asyncio is a single-threaded event loop — a different axis of concurrency from GIL removal. The two compose more nicely now.

  • I/O via asyncio coroutines.
  • CPU-bound parts go to loop.run_in_executor(ThreadPoolExecutor(), ...) for actual parallelism.

4) The single-thread performance penalty

Mechanisms like biased refcounting add some unavoidable overhead. From ~15% in 3.13 down to ~3–5% in 3.14, but not zero. For workloads that don't scale N times with threading, the standard build can still win.

5) Risk of ecosystem split

Having two ABIs under one Python 3.14 doubles the burden for package maintainers. A small library may only support one side, and that confuses users. How this settles in 2027–2028 is the question.


5. uv (Astral) — one tool to replace pip / poetry / pipenv

uv is Astral's Rust-based, single-binary package manager, announced February 2024. Same company as Ruff. The launch slogan was "An extremely fast Python package installer and resolver." Accurate.

What it replaces

Old toolWhat uv replaces
pipinstall / uninstall
pip-toolsrequirements compile / lock
pipxglobal CLI install
poetryproject + dep management
pipenvdependencies + venv
pyenvPython interpreter install / management
virtualenvvenv creation
twinebuild/publish (uv build, uv publish)

Put differently: nearly every layer of Python packaging in one binary.

Install and basic use

# Install (macOS/Linux)
curl -LsSf https://astral.sh/uv/install.sh | sh

# New project
uv init my-app
cd my-app
# pyproject.toml, .python-version, README.md created

# Install a Python interpreter
uv python install 3.14

# Add packages
uv add fastapi pydantic 'polars[all]'
uv add --dev pytest ruff

# Sync (venv matches lock exactly)
uv sync

# Run
uv run python -m my_app
uv run pytest
uv run ruff check

Why it is fast

  • Rust + async I/O — dependency resolution in parallel.
  • Global cache + hard links — same wheel shared across projects, disk usage drops.
  • PubGrub solver — deterministic and fast.
  • Wheel-only preference — avoids sdist builds when possible.
  • HTTP/2 + zstd.

A measurement on my machine (late 2025, a typical FastAPI project, ~80 deps).

Operationpippoetryuv
Cold install48s62s2.1s
Warm install12s18s0.4s
Generate lockn/a24s0.9s

Once you try it, going back is hard.

pyproject.toml and uv.lock

# pyproject.toml
[project]
name = "my-app"
version = "0.1.0"
requires-python = ">=3.13"
dependencies = [
    "fastapi>=0.115",
    "pydantic>=2.10",
    "polars[all]>=1.0",
]

[tool.uv]
dev-dependencies = [
    "pytest>=8.0",
    "ruff>=0.7",
    "ty>=0.0.1a8",
]

[tool.ruff]
line-length = 100

uv.lock is a lockfile with exact hashes and markers. Commit it so the team shares it. uv sync matches the venv to the lock precisely.

Python interpreter management

uv python list                       # available versions
uv python install 3.13 3.14 3.14t   # install several
uv python pin 3.14                  # writes .python-version

Does what pyenv used to. Difference: uv downloads pre-built interpreters directly, so it is fast and needs no compile-time deps.

Global CLI tools

uv tool install ruff           # like pipx
uv tool install httpie
uv tool run pip-audit          # one-off run
uvx pip-audit                  # alias for uv tool run

Standardization in 2026

  • pip is still #1 by PyPI download count, but uv adoption in CI is exploding.
  • poetry still has a large user base, but new projects lean uv.
  • Astral is one of the PSF's corporate sponsors, with active collaboration with PSF / PyPA.

6. Ruff (Astral) — the Rust tool that absorbed flake8 / black / isort

Ruff is Astral's first product (first release 2022, explosive growth 2024–2025). In one sentence: "10–100x faster than flake8, with black/isort/pyupgrade folded in, as a single Rust binary."

What it replaces

Old toolRuff equivalent
flake8ruff check
pycodestyleruff check (E rules)
pyflakesruff check (F rules)
isortruff check --select I or ruff format
blackruff format
pyupgraderuff check --select UP
autoflakeruff check --fix --select F401
bandit (partial)ruff check --select S
flake8-bugbearruff check --select B
flake8-comprehensionsruff check --select C4
pydocstyleruff check --select D

Hundreds of rules live in one binary, opt-in via config.

Basic use

uv add --dev ruff

uv run ruff check .            # lint
uv run ruff check --fix .      # auto-fix
uv run ruff format .           # format (black compatible)
uv run ruff format --check .   # verify format in CI

pyproject.toml config

[tool.ruff]
line-length = 100
target-version = "py313"

[tool.ruff.lint]
select = [
    "E", "F", "W",   # pycodestyle, pyflakes
    "I",             # isort
    "UP",            # pyupgrade
    "B",             # bugbear
    "C4",            # comprehensions
    "SIM",           # simplify
    "RUF",           # ruff-specific
]
ignore = ["E501"]    # line too long (the formatter handles it)

[tool.ruff.lint.per-file-ignores]
"tests/*" = ["S101"]   # assert in tests

[tool.ruff.format]
quote-style = "single"

Why it is fast

  • One Rust AST pass — multiple tools no longer reparse files independently.
  • Parallel — per-file and per-rule parallelism.
  • Incremental — only re-checks changed files.
  • Memory efficient — much less likely to OOM than black on huge codebases.

A real benchmark on the Django codebase (~3500 files).

ToolTime
flake812.3s
black --check8.1s
isort --check4.5s
pyupgrade6.2s
Sum (serial)31.1s
ruff check + ruff format --check0.4s

Tens of times difference. That gap shows up as CI cost and developer feel.

Editor integration

  • VS Code — official Ruff extension (Astral maintained). LSP based.
  • PyCharm / IntelliJ — official Ruff plugin.
  • Neovim — ruff-lsp (or Ruff's built-in LSP).
  • Zed — integrated by default.

Formatter — black-compatible alpha

ruff format deliberately targets black's output (99%+ compatible). Differences are usually black bugs or intentional improvements (e.g. magic trailing comma handling). A black-using team can migrate losslessly.


7. ty (Astral) — a Rust-rewritten type checker (alpha)

ty is the type checker Astral unveiled in alpha in 2025. Aimed at mypy and pyright's territory. As of May 2026 it is still alpha, but some projects have started adopting it.

Why yet another type checker

The type checker market is already complex.

  • mypy — the original. Reference implementation of PEP 484. Written in Python. Slow.
  • pyright — Microsoft. TypeScript. Fast and accurate. Pylance's foundation in VS Code.
  • pyre — Meta. OCaml. Used at Instagram.
  • pytype — Google. Python. Strong on gradual typing.

ty's position.

  • Rust — aiming at pyright-level speed or better.
  • Incremental — like Ruff and uv, recheck only what changed.
  • MIT — consistent with Astral's other tools.
  • LSP first-class — editor integration is a priority.
  • Astral integration — slots cleanly with uv and ruff.

Basic use

uv add --dev ty

# Check a directory
uv run ty check src/

# A single file
uv run ty check src/main.py

# Watch mode
uv run ty check --watch src/

The reality in May 2026

ty is still alpha. Meaning:

  • Some typing features (especially generics, TypeVar bounds) are not yet implemented.
  • Error messages can be rougher than mypy or pyright.
  • The pattern in production is pyright or mypy as the main checker, with ty as a fast first gate.

The performance is striking. A large codebase where mypy takes 5 minutes runs in ~5 seconds under ty. If it settles, it could change the game.

  • Today (May 2026): main is pyright (strict) or mypy (gradual), with ty added to CI as a fast first gate.
  • 2027 outlook: when ty hits stable, it becomes plausible as the main.

8. Polars 1.0 (Aug 2024) — the alternative to Pandas

Polars is a Rust-based DataFrame library started by Ritchie Vink in 2020. 1.0 shipped in August 2024. It targets Pandas's single-threaded ceiling, column-wise inefficiencies, and memory blowups.

Core differences

  • Rust core — compiled native code.
  • Apache Arrow — column-oriented memory layout, zero-copy interop.
  • Lazy execution — the query planner optimizes before executing.
  • Multi-threaded by default — Rayon for automatic parallelism.
  • Predicate / projection pushdown — SQL-optimizer-grade optimizations.

eager vs lazy

import polars as pl

# eager — Pandas style
df = pl.read_csv("data.csv")
result = df.filter(pl.col("age") > 30).group_by("city").agg(pl.col("salary").mean())

# lazy — optimized, then run
lf = pl.scan_csv("data.csv")    # not read yet
result = (
    lf.filter(pl.col("age") > 30)
      .group_by("city")
      .agg(pl.col("salary").mean())
      .collect()                # executes here
)

In lazy mode the optimizer decides "how to apply the filter early" and "which columns to read," cutting disk I/O and memory significantly.

Performance vs Pandas

H2O.ai's DataFrame benchmark (group-by, join, 5 GB).

Librarygroup-byjoin
Pandas 2.x95sOOM
Pandas 3.0 (PyArrow)41s220s
Polars 1.0 (eager)12s38s
Polars 1.0 (lazy)6.8s22s

The gap is biggest near join and memory limits.

When Polars, when Pandas

Polars:

  • 100 MB or larger data.
  • New projects.
  • Multi-core utilization needed.
  • You want to express things like a SQL query optimizer.

Pandas:

  • Compatibility with existing notebooks and ecosystem (scikit-learn, statsmodels, some matplotlib paths).
  • A new data scientist who needs "ten years of Stack Overflow answers."
  • Small data (a few MB).
  • Exploration where .head() in Jupyter is enough.

Interop with Pandas

df_pl = pl.from_pandas(df_pd)
df_pd = df_pl.to_pandas()        # zero-copy with PyArrow
arr = df_pl.to_arrow()           # Arrow Table
df_pl2 = pl.from_arrow(arr)

Arrow-based zero-copy interop keeps standardizing, so mixing the two libraries is low-friction.


9. Pandas 3.0 (May 2025) — the comeback

Pandas did not die. The May 2025 release of 3.0 is its biggest major update.

Big changes in 3.0

  1. Apache Arrow becomes the default backend — the numpy backend is the deprecated path.
  2. Copy-on-Write by default — better memory and safety simultaneously.
  3. PyArrow is now a hard dependency — no longer optional.
  4. String dtype is default — pyarrow string instead of Python object.
  5. Many inplace methods removed — they conflict with CoW.
  6. Requires Python 3.10+.

What Copy-on-Write means

import pandas as pd
df1 = pd.DataFrame({"a": [1, 2, 3]})
df2 = df1   # alias

df2.loc[0, "a"] = 999

# 2.x: df1 also sees 999 (SettingWithCopyWarning)
# 3.0: df1 stays unchanged (CoW branches)

The end of the long-suffering SettingWithCopyWarning. But code that relied on inplace behavior breaks. Read the migration guide.

Performance

Thanks to the Arrow backend, string handling, group-by, and some joins run 2–5x faster than 2.x. Not Polars-fast, but the gap is narrower.


10. Pydantic v2.10 — the de facto data validation standard

Pydantic was pure Python through v1; v2 (2023) rewrote the core in Rust. v2.10 (late 2025) is the stabilized point in the v2 line.

What it does

  • Runtime data validation and serialization.
  • Foundation for FastAPI, Litestar, BlackSheep.
  • Default adapter for LangChain's BaseTool, OpenAI structured output, and other LLM tooling.

Basic example

from pydantic import BaseModel, Field, EmailStr
from datetime import datetime

class User(BaseModel):
    id: int
    name: str = Field(min_length=1, max_length=64)
    email: EmailStr
    created_at: datetime
    tags: list[str] = []

# Validate + coerce
u = User.model_validate({
    "id": "42",                       # str converts to int
    "name": "Alice",
    "email": "alice@example.com",
    "created_at": "2026-05-16T10:00:00Z",
})

print(u.model_dump_json())

v2 performance

Thanks to the Rust core, 5–50x faster than v1 (depending on schema complexity). In frameworks like FastAPI, P99 latency improves visibly.

Pydantic Settings

from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    database_url: str
    redis_url: str = "redis://localhost"
    log_level: str = "INFO"

    class Config:
        env_file = ".env"

settings = Settings()

Cleans up the 12-factor app config pattern.

Use in LLM tooling

OpenAI / Anthropic structured output and function calling take a Pydantic model as the schema and enforce it. LangChain, LlamaIndex, Pydantic AI, and friends use Pydantic as the default adapter.

from pydantic import BaseModel
from anthropic import Anthropic

class SearchQuery(BaseModel):
    query: str
    max_results: int = 10

# The Anthropic SDK extracts the schema as a tool definition
client = Anthropic()

11. Web frameworks — FastAPI / Litestar / Robyn / Quart / Sanic / BlackSheep

The Python web framework market in 2026 is more pluralistic than it has ever been. Each has a distinct edge.

FastAPI

  • Arrived 2018, ASGI-based.
  • Pydantic + Starlette + auto OpenAPI.
  • The de facto ASGI flagship.
  • Gentle learning curve, biggest community.
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    name: str
    price: float

@app.post("/items/")
async def create_item(item: Item) -> dict:
    return {"id": 1, **item.model_dump()}

Litestar (formerly Starlite)

  • Started in 2022 as Starlite, rebranded to Litestar in 2023.
  • Inspired by FastAPI but with bigger ambitions (controllers, layered DI, plugin system).
  • DTO system, supports msgspec / Pydantic / attrs.
  • Slightly faster than FastAPI under specific conditions.
from litestar import Litestar, post
from pydantic import BaseModel

class Item(BaseModel):
    name: str
    price: float

@post("/items/")
async def create_item(data: Item) -> dict:
    return {"id": 1, **data.model_dump()}

app = Litestar([create_item])

Robyn

  • Appeared 2022, Rust core + Python API.
  • Multi-threaded in a single process (Rust's tokio).
  • Very fast (2–5x FastAPI throughput).
  • Small ecosystem, chosen where performance is critical.
from robyn import Robyn

app = Robyn(__file__)

@app.get("/")
async def hello():
    return "hello, world"

app.start(port=8080)

Quart

  • Flask's async sibling.
  • Nearly identical API, some shared maintenance with Pallets.
  • Good when you want to reuse Flask code with async.

Sanic

  • A veteran since 2016.
  • Fast, blueprint pattern.
  • When you want "just ASGI."

BlackSheep

  • An ASGI framework influenced by .NET ASP.NET.
  • DI container, OpenAPI, strong typing.
  • A C core gives it good performance.

Selection guide

  • Starting / small team / ecosystem matters → FastAPI
  • Enterprise / structured large app → Litestar
  • Throughput is critical → Robyn or BlackSheep
  • Reusing Flask code → Quart
  • Simplicity → Sanic

12. Django 5.x — the classic full-stack workhorse

Django has been the grown-up since 2005. Still the Python full-stack default in 2026.

Major Django 5.x changes

  • 5.0 (Dec 2023) — DB-backed defaults, simplified form templates, GeneratedField.
  • 5.1 (Aug 2024) — CSRF middleware reorganization, more queryset exposure in the ORM.
  • 5.2 (Apr 2025) — partial async ORM queries, automatic model importing.

The core story is that async support is maturing in stages. Async in the ORM is partially stable in 5.2; the full picture is targeted for 6.0 (late 2026).

When Django

  • You need an admin (Django Admin remains best in class).
  • You need ORM, migrations, auth, forms, templates, and testing in one package.
  • A large monolith is the right answer.
  • You need a mature CMS like Wagtail or django-CMS.

When not

  • A single small API — FastAPI/Litestar are lighter.
  • Extreme concurrency / low latency — Django is still sync-first.
  • Microservice-sized units — Django's batteries are overhead.

Django Ninja — same ORM, FastAPI-style API

from ninja import NinjaAPI
from pydantic import BaseModel

api = NinjaAPI()

class ItemIn(BaseModel):
    name: str
    price: float

@api.post("/items/")
def create_item(request, item: ItemIn):
    return {"id": 1, **item.dict()}

A good answer when you want Django's ORM / auth / admin with a FastAPI-style API on top.


13. Packaging — uv / Briefcase / Nuitka / PyInstaller

Tools differ by distribution form.

Library packaging — uv build / hatchling

uv build               # create sdist + wheel
uv publish             # upload to PyPI (replaces twine)

Internally calls a PEP 517 build backend like hatchling. setup.py is effectively over.

Single executable — PyInstaller / Nuitka

PyInstaller:

uv tool install pyinstaller
pyinstaller --onefile my_app.py
# dist/my_app

Upside: works with almost any library. Downside: large binary, AV false positives, slow startup.

Nuitka:

uv add --dev nuitka
python -m nuitka --standalone --onefile my_app.py

Upside: compiles Python to actual C, so it is faster and smaller. Downside: slow build, weak on some dynamic imports.

Desktop / mobile — Briefcase (BeeWare)

Packages Python code as native iOS / Android / macOS / Windows apps.

uv tool install briefcase
briefcase new
briefcase create iOS
briefcase build iOS
briefcase run iOS

Combined with Python 3.13's iOS / Android Tier 3 support, running Python on mobile is a realistic scenario.

Others

  • shiv / zipapp — simple zip-based distribution.
  • conda-pack — package a whole conda environment.
  • Docker — the most common distribution unit. The python:3.14-slim + uv combo is standard.

14. Cloud — Modal / Replicate / Pyodide

  • Run a Python function on cloud GPU / CPU as-is.
  • Infrastructure defined via decorators.
  • The flagship "serverless GPU."
import modal

app = modal.App("ml-job")

@app.function(gpu="A100", timeout=600)
def train(epochs: int):
    import torch
    # training code
    return "done"

if __name__ == "__main__":
    with app.run():
        train.remote(epochs=10)

Replicate

  • ML model hosting + API.
  • Containerize models with Cog.
  • Common for inference of open-source models like Stable Diffusion or LLMs.

Pyodide

  • Compiles Python to WebAssembly to run in the browser.
  • Underpins JupyterLite, Streamlit-in-browser, marimo's browser mode, and more.
  • By 2026 nearly the entire scientific stack — numpy, pandas, scipy — works.

15. Python usage in Korea and Japan

Korea

  • Toss — backend leans Kotlin, but Python is used for ML / data engineering and internal tooling. Python is listed in job requirements.
  • Kakao — recommendation/search ML pipelines, ad tech data pipelines, KakaoTalk chatbots — broadly used.
  • Naver — training and serving infrastructure for Clova / HyperCLOVA, search data pipelines, LINE's ML backend.
  • Coupang — data science, recommender systems, price optimization with Python + PySpark.
  • Daangn (Karrot) — ML recommendations, data pipelines, A/B testing.

Startup trend: FastAPI + Polars + uv is becoming the de facto standard. Data roles are nearly Python-only.

Japan

  • Mercari — ML platform, search relevance, fraud detection in Python. Heavy investment in the internal ML platform Bakuraku and MLOps.
  • PFN (Preferred Networks) — the company behind Chainer. After moving to PyTorch, still Japan's flagship for Python ML R&D.
  • LINE Yahoo — ML / data pipelines for search, ads, messenger.
  • DeNA — Python ML in games, healthcare, MaaS.
  • SmartNews — recommendation systems.

Japan historically had heavy Ruby usage, but the move to Python in data / ML is strong.


16. Who picks what

Recommended stacks by situation.

Data analysis / science

  • Python 3.13 (free-threaded still secondary).
  • uv + Ruff.
  • Polars (big data) / Pandas 3.0 (ecosystem).
  • jupyter / marimo (covered separately).
  • scikit-learn, statsmodels, plotly / altair.

ML / AI training workloads

  • Python 3.13 (free-threaded or standard).
  • uv + Ruff.
  • PyTorch / JAX.
  • transformers / vLLM / sglang.
  • Modal (serverless GPU), or RunPod, or Replicate.
  • mlflow / wandb.

Web backend (microservice)

  • Python 3.13 or 3.14.
  • uv + Ruff + ty (secondary).
  • FastAPI or Litestar.
  • Pydantic v2.
  • SQLModel / SQLAlchemy 2.0 / Tortoise ORM.
  • Uvicorn / Granian.
  • Docker: python:3.14-slim.

Web backend (monolith + admin)

  • Django 5.x.
  • uv + Ruff.
  • Django Ninja (when REST is needed).
  • Celery / Django-Q.
  • PostgreSQL.

CLI tool

  • Python 3.14 (deferred imports for fast startup).
  • uv + Ruff.
  • Typer or Click.
  • Rich / Textual (TUI).
  • PyInstaller or Nuitka for distribution.

Desktop / mobile

  • Python 3.13 / 3.14.
  • Briefcase (mobile) / Toga (cross-platform UI).
  • Nuitka (single executable).

Embedded

  • MicroPython or CircuitPython (not CPython).
  • However, ARM Linux (Raspberry Pi, etc.) runs regular CPython.

17. Migration checklist — moving an existing project to 2026 style

If you already have a Python project, how do you move it.

Step 1 — adopt uv (low risk)

# If pyproject.toml already exists
uv lock              # generate uv.lock
uv sync              # create/sync venv

# If you are on requirements.txt
uv add -r requirements.txt

Coexists with the old workflow. Gradually flip CI to uv sync && uv run pytest.

Step 2 — adopt Ruff (low risk)

uv add --dev ruff
# Start with lint only; keep black for now and compare
uv run ruff check --select F,E .
# As you settle in, expand rules and move format to ruff too

Step 3 — Pydantic v1 to v2 (medium risk)

API changes substantially. Use the converter.

uv tool install bump-pydantic
bump-pydantic src/

Step 4 — Pandas to Polars (optional, big project)

Rather than full migration, pick hot paths. Interop is smooth so it can be gradual.

Step 5 — upgrade to Python 3.13 / 3.14

uv python install 3.14
uv python pin 3.14
uv sync
uv run pytest

The free-threaded build is a separate decision. Check C extension compatibility.

Step 6 — type checker

If you have mypy, keep it as main and add ty as a fast CI gate.


18. Common pitfalls and best practices

  • Do not just pip install -r requirements.txt in a Dockerfile — uv gives 10x faster builds.
  • Do not put import pandas as pd at module top in CLIs — cold start explodes. Lazy-import inside functions, or use 3.14 deferred imports.
  • Do not use the free-threaded build for all workloads — for I/O-heavy work, the standard build can be faster.
  • Lean on lazy mode when adopting Polars — eager-only captures only about half of the wins over Pandas.
  • Do not rebuild Pydantic models on every call — schema compilation has cost. Define at module level.
  • Do not call sync libraries from inside asyncio.run() — blocks the loop. Use run_in_executor.
  • Do not blindly apply Ruff's --fix — some rules change meaning. Review the diff.
  • Do not gitignore uv.lock — it is the heart of team reproducibility.
  • Do not run two type checkers as main at the same time — conflicting messages. One main + one CI gate.

19. The future — 2027–2028 outlook

  • Free-threaded as default — likely the standard build becomes free-threaded around 3.16 or 3.17, since maintaining two builds is expensive.
  • Real JIT wins — 10–30% in 3.15–3.16 is plausible.
  • ty hits stable — late 2026 to mid-2027.
  • Polars's chase accelerates — the gap with Pandas grows, or Pandas absorbs Polars-style patterns.
  • An Astral IDE or Python distribution — plausible. A unified ruff/uv/ty experience.
  • Pyodide goes mainstream — browser-based data analysis and visualization becomes the SaaS standard.
  • Mobile Python — Briefcase + Python 3.14 iOS/Android stable makes small native apps realistic.

20. Wrap-up — a new Python forged by two inflection points

Python in 2026 sits at the intersection of two big events.

  1. The language itself started shedding the GIL (PEP 703, 3.13 preview to 3.14 stable). The biggest runtime change in 30 years. CPU-bound threading actually works, and the combination with asyncio gets clean. But the C extension ecosystem will need another two to three years to fully adapt.

  2. Astral rewrote half of the tool chain in Rust (uv, Ruff, ty). pip / poetry / flake8 / black / isort / mypy are converging on two or three tools from a single company. With 10–100x speedups, CI cost and developer feel both improve simultaneously.

Add Pydantic v2's Rust core, Polars's Apache Arrow, the ASGI ecosystem of FastAPI / Litestar / Robyn pluralizing, Pandas 3.0 absorbing an Arrow backend, and Django 5.x's gradual async maturation — every layer is moving at once.

If you have seen Python code from five years ago, a modern 2026 Python project looks almost the same on the outside, but the tooling, runtime, and data-handling beneath it have nearly all changed. The good news about migration is you do not have to do it all at once. Adopt uv, then Ruff, then Pydantic v2, then Polars hot paths, then Python 3.14, then free-threaded (only when you need it). One step at a time.

Python is over 30 years old, and yet it is changing faster now than at almost any other point. A change worth keeping up with.


References