Skip to content
Published on

Python Ecosystem 2026 Deep-Dive — Python 3.13 · FastAPI · Django 5 · Litestar · SQLAlchemy 2 · Polars · Pydantic 2 · Ruff · uv · mypy

Authors

"By 2026, Python is no longer the 'slow language.' Pydantic is in Rust, Polars is in Rust, Ruff and uv are in Rust — every hot path of Python has moved into a systems language." — PyCon US 2026 keynote

Python lived through its biggest toolchain swap in 2025–2026. pip became uv (Astral, Rust-based). Black + isort + flake8 + pylint collapsed into a single Ruff binary. pandas gave way to PyArrow-backed pandas or, increasingly, to Polars. Four foundational tools changed at once — something Python has never seen in 30 years of history. And the engine behind all of them is Rust.

The language itself shifted too. Python 3.13 (2024-10) shipped an experimental JIT (PEP 744) and a free-threaded build (PEP 703). Python 3.14 (2025-10) stabilized both and refined the per-interpreter GIL. As of May 2026 Python is becoming "a language with types, native async, and the option to drop the GIL for multithreaded throughput." This post tours the language, web frameworks, ORM/data, validation, toolchain, testing, concurrency, ML adjacencies, CLIs, scraping, and real adoption stories in Korea and Japan.

1. The Python ecosystem in 2026 at a glance

The 2026 Python ecosystem can be summarized as five layers.

Layer2020 standard2026 standardNote
Package managerpip + virtualenvuv10–100x faster
Linter/formatterBlack + isort + flake8 + pylintRuffSingle binary
Type checkermypymypy or pyrightTwo strong options
Validationpydantic 1 (pure Python)Pydantic 2 (Rust core)5–50x faster
DataFramepandas 1.xPolars or pandas 2 (Arrow)New code goes Polars
Web APIFlask, Django RESTFastAPI, Litestar, Django 5 asyncASGI normalized
Asyncasyncioasyncio + anyioStructured concurrency

The takeaway: Python is converging on a single toolchain, but the internals of those tools are diverging into Rust. Your code is still Python; the code that processes your code is no longer.

2. Python 3.13 — JIT preview, free-threaded, better error messages

Python 3.13 (2024-10) introduced three changes that affect both backend and data work.

First, the experimental JIT compiler (PEP 744). A copy-and-patch JIT compiles hot loops into native code. As of 2026 a 5–15% improvement is typical; gains are limited where the workload is dominated by C extension calls such as NumPy. Second, the free-threaded build (PEP 703), shipped as python3.13t / python3.14t, completely removes the GIL. CPU-bound multithreaded code finally scales linearly with core count. Third, improved error messagesNameError suggests similar names, indentation errors render visually.

# Install free-threaded Python 3.13 with uv
uv python install 3.13t
uv venv --python 3.13t
source .venv/bin/activate

# Check GIL status
python -c "import sys; print('GIL enabled:', sys._is_gil_enabled())"
# False in a free-threaded build

# Enable the JIT and confirm
PYTHON_JIT=1 python -c "import sys; print('JIT:', sys._jit.is_enabled())"

Few teams run free-threaded in production yet — Granian and some data-processing workloads are early adopters. C extension compatibility is the dominant blocker.

3. Python 3.14 — Stabilized JIT and per-interpreter GIL

Python 3.14 (2025-10) is the polishing release for 3.13's experiments. The JIT is still "experimental" but regressions have shrunk, and the free-threaded build was promoted to a stable PEP. Per-interpreter GIL (PEP 684) lets a single process host multiple subinterpreters with their own GIL each, opening a path for WSGI/ASGI servers to exploit multiple cores without the cost of forking.

New syntax notably includes the stabilization of PEP 695 (generic syntax) and the arrival of PEP 750 (t-strings, template string literals). T-strings let you enforce auto-escaping in security-sensitive contexts like SQL or shell construction. PEP 695 generics let you write def first[T](items: list[T]) -> T: style declarations directly on classes and functions, and t-strings use t"SELECT * FROM users WHERE name = ..." syntax so the runtime can keep the values separate from the template.

4. uv — replacing pip and Poetry at once

Astral's uv has, since its 2024 debut, aggressively consumed the Python package-manager market through 2025–2026. It is a single Rust binary that subsumes pip / pip-tools / virtualenv / pyenv / pipx / Poetry / PDM. Benchmarks put it at 10–100x faster than pip + venv, and its cache-backed lockfiles deliver reproducible installs.

# pyproject.toml (uv standard)
[project]
name = "myapi"
version = "0.1.0"
requires-python = ">=3.13"
dependencies = [
    "fastapi[standard]>=0.115",
    "sqlalchemy[asyncio]>=2.0",
    "pydantic>=2.9",
    "uvicorn[standard]>=0.30",
    "httpx>=0.27",
]

[dependency-groups]
dev = [
    "pytest>=8.3",
    "pytest-asyncio>=0.24",
    "ruff>=0.7",
    "mypy>=1.13",
]

[tool.uv]
managed = true
default-groups = ["dev"]

The commands are intuitive: uv init, uv add fastapi, uv sync, uv run pytest, uv pip install -r requirements.txt. Existing Poetry projects migrate via uv migrate, and because uv follows the PEP 621 pyproject.toml standard, migration risk is low. As of May 2026, more than 70% of the top 100 PyPI packages ship uv-compatible metadata.

5. Ruff — one tool to replace four

Ruff is Astral's Rust-based linter and formatter. It unifies more than 700 rules from Black + isort + flake8 + pylint into one binary, and runs on average 10–100x faster. As of 2026 nearly every major OSS project — FastAPI, Pydantic, Polars, Hugging Face, pandas — has migrated to Ruff.

# Add to pyproject.toml
[tool.ruff]
line-length = 100
target-version = "py313"

[tool.ruff.lint]
select = [
    "E",   # pycodestyle errors
    "W",   # pycodestyle warnings
    "F",   # pyflakes
    "I",   # isort
    "B",   # flake8-bugbear
    "C4",  # flake8-comprehensions
    "UP",  # pyupgrade
    "N",   # pep8-naming
    "S",   # bandit (security)
    "RUF", # ruff-specific
]
ignore = ["E501"]  # line length is handled by the formatter

[tool.ruff.format]
quote-style = "double"
indent-style = "space"

ruff check . --fix && ruff format . finishes a full cleanup in a single line. Even on huge monorepos, a full check completes in under a second, which makes the pre-commit overhead essentially disappear.

6. mypy versus pyright — the type-checker duopoly

The type-checker space is dominated by mypy (written in Python, originally from Dropbox) and pyright (written in TypeScript, by Microsoft). mypy 1.13 (2025) supports PEP 695 generics, PEP 696 type defaults, and PEP 692 TypedDict for kwargs; pyright is the engine behind VS Code Pylance and offers the fastest incremental inference.

# pyproject.toml — mypy and pyright side by side
[tool.mypy]
python_version = "3.13"
strict = true
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
plugins = ["pydantic.mypy", "sqlalchemy.ext.mypy.plugin"]

[[tool.mypy.overrides]]
module = "third_party_without_stubs.*"
ignore_missing_imports = true

[tool.pyright]
include = ["src"]
exclude = ["**/__pycache__"]
pythonVersion = "3.13"
typeCheckingMode = "strict"
reportMissingImports = "error"
reportMissingTypeStubs = "warning"

The choice is clear. Pick mypy when you need CI/CD-grade type assurance (longer history, PEP authority). Pick pyright when you need VS Code integration and fast incremental analysis. More teams are running both — mypy in CI, pyright in the editor.

7. FastAPI 0.115+ — automatic OpenAPI · DI · Pydantic 2

FastAPI (2018–, Sebastián Ramírez) is still the de-facto standard for Python APIs in 2026. 80K+ GitHub stars, 200M+ monthly PyPI downloads. 0.115 bumps to Starlette 0.41 and Pydantic 2.9, pushing validation throughput even higher. Its core value is three-fold: automatic OpenAPI generation, type-driven dependency injection, and Pydantic 2 validation integration.

# Idiomatic FastAPI + Pydantic 2 + SQLAlchemy 2 async pattern
from fastapi import FastAPI, Depends, HTTPException
from pydantic import BaseModel, EmailStr, Field
from sqlalchemy.ext.asyncio import AsyncSession
from typing import Annotated

app = FastAPI(title="User API", version="1.0")

class UserCreate(BaseModel):
    email: EmailStr
    name: str = Field(min_length=1, max_length=50)
    age: int = Field(ge=0, le=150)

class UserOut(BaseModel):
    id: int
    email: EmailStr
    name: str

async def get_db() -> AsyncSession:
    async with AsyncSessionLocal() as session:
        yield session

DbSession = Annotated[AsyncSession, Depends(get_db)]

@app.post("/users", response_model=UserOut, status_code=201)
async def create_user(payload: UserCreate, db: DbSession) -> UserOut:
    user = User(**payload.model_dump())
    db.add(user)
    await db.commit()
    await db.refresh(user)
    return UserOut.model_validate(user, from_attributes=True)

The Annotated[Type, Depends(...)] pattern is recommended from 0.95 onward; 0.115 added small wins like sub-dependency caching options.

8. Django 5.1+ — async views finally mature

As of May 2026 Django 5.2 LTS is the current stable, and async views have finally reached real-world maturity. Django 5.1 stabilized async ORM methods (aget, acreate, afilter); 5.2 added async signals, async aggregation, and async middleware. The shift from WSGI + Gunicorn to ASGI + uvicorn/Daphne is now underway in earnest.

# Django 5 async view — real concurrent processing
from django.http import JsonResponse
from django.views.decorators.http import require_GET
from asgiref.sync import sync_to_async
import httpx, asyncio

@require_GET
async def aggregate(request):
    async with httpx.AsyncClient() as client:
        # Fetch two upstream APIs concurrently
        a, b = await asyncio.gather(
            client.get("https://api1.example.com/data"),
            client.get("https://api2.example.com/data"),
        )

    # async ORM
    user = await User.objects.aget(pk=request.user.id)

    return JsonResponse({
        "user": user.email,
        "data1": a.json(),
        "data2": b.json(),
    })

Django's strength remains its integrated package — Admin, Auth, Migrations, ORM all in one. Larger SaaS shops favor a hybrid where business logic lives in Django and the API gateway is FastAPI/Litestar.

9. Litestar 2.x — the controller-based next-gen ASGI

Litestar (formerly Starlite, 2021–) is the controller-based ASGI framework usually picked by teams unhappy with FastAPI's function-based API. 2.x adds DTO (Data Transfer Object) support, msgspec integration, first-class OpenTelemetry, and Channels (WebSocket pub/sub) — enterprise-shaped features.

# Litestar 2.x — controller-based
from litestar import Controller, Litestar, get, post
from msgspec import Struct

class User(Struct):
    id: int
    name: str

class UserController(Controller):
    path = "/users"

    @get("/")
    async def list_users(self) -> list[User]:
        return [User(id=1, name="Alice")]

    @post("/")
    async def create_user(self, data: User) -> User:
        return data

app = Litestar(route_handlers=[UserController])

Litestar recommends msgspec as the default validation engine. msgspec is 2–5x faster than Pydantic 2 at serialization/deserialization with lower memory use. But Pydantic 2 still dominates IDE integration and ecosystem reach, so msgspec alone is rarely a decisive enough reason to leave FastAPI.

10. Flask 3.x · Sanic · Quart · aiohttp 4 · Robyn — the rest of the field

Flask 3 (2023) survives as the classic WSGI microframework. Async views are supported but limited; the body of Flask is still synchronous. Quart is a near-identical-API fork that sits on ASGI — the async camp for Flask fans. Sanic bundles its own ASGI server in a single async package. aiohttp 4 is the classic async HTTP library covering both client and server.

Robyn is a newer attempt: Rust core with a Python interface. The router, middleware, and HTTP parser are all in Rust, invoking Python handlers. Simple routing benchmarks show 2–3x the throughput of uvicorn + FastAPI. Adoption is cautious because the ecosystem is narrow and Python-debugger integration is rough.

FrameworkASGI/WSGIValidation engine2026 recommended use
FastAPI 0.115+ASGIPydantic 2Default choice
Litestar 2.xASGImsgspecController / enterprise
Django 5.2WSGI/ASGIDRF · NinjaFull-stack
Flask 3WSGINoneExisting codebases
QuartASGINoneAsync for Flask fans
SanicASGIdataclassesSingle-package async
RobynASGI (Rust)Pydantic 2Extreme routing speed
aiohttp 4ASGINoneDual client/server

11. Starlette — common ancestor of FastAPI and Litestar

Starlette is the microframework that layers routing, middleware, and a test client on top of the ASGI standard. FastAPI takes Starlette as a direct dependency; Litestar has its own implementation but a very similar interface. Used standalone, from starlette.applications import Starlette is the one-line starting point — you compose routes and middleware by hand, which is also the best way to understand what FastAPI and Litestar are adding on top.

12. Granian — the Rust ASGI server taking on uvicorn

Granian is a Rust-based ASGI/WSGI/RSGI server from the author of the Emmett framework. It delivers 1.5–2x more throughput than uvicorn (uvloop + httptools) and naturally exploits multithreaded workers on free-threaded Python 3.13t.

Install with uv add granian and run via granian --interface asgi main:app as a uvicorn drop-in replacement. Tune workers and threads simultaneously with --workers 4 --threads 2. Granian's own RSGI mode (lighter than ASGI) activates with --interface rsgi.

As of May 2026, Granian sits as a "uvicorn-compatible drop-in," and both FastAPI and Litestar mention it alongside uvicorn in their docs. uvicorn is still the default, but performance-sensitive deployments are gradually moving over.

13. Pydantic 2 — Rust-core validation engine

Pydantic 2 (2023) is a validation engine rewritten with a Rust core. It runs 5–50x faster than v1, and JSON serialization approaches orjson speed. Nearly every API library on Python — FastAPI, Django Ninja, Litestar, LangChain, the OpenAI SDK — runs on top of Pydantic 2.

from pydantic import BaseModel, Field, EmailStr, computed_field, model_validator
from datetime import datetime
from typing import Self

class Order(BaseModel):
    id: int
    email: EmailStr
    items: list[str] = Field(min_length=1)
    quantity: int = Field(gt=0)
    price_per_item: float = Field(gt=0)
    created_at: datetime

    @computed_field
    @property
    def total(self) -> float:
        return self.quantity * self.price_per_item

    @model_validator(mode="after")
    def check_premium_quota(self) -> Self:
        if self.quantity > 1000 and "@example.com" not in self.email:
            raise ValueError("non-premium accounts limited to 1000")
        return self

# Serialization
order = Order(id=1, email="a@b.com", items=["sku-1"], quantity=10, price_per_item=99.0,
              created_at=datetime.now())
print(order.model_dump_json())

Pydantic 2's central trick is pydantic-core, which moves every validation step into Rust. The Python side is a thin wrapper.

14. msgspec · attrs · dataclasses · cattrs — validation alternatives

Pydantic 2 is not the only choice. msgspec is a faster serialization library that Litestar adopts by default. attrs is the class-definition library that influenced the stdlib. dataclasses is the stdlib's own answer, and cattrs layers structuring/unstructuring on top of attrs/dataclasses.

ToolValidationSerialization speedLearning curveEcosystem
Pydantic 2StrongFastModerateMassive
msgspecModerateVery fastLowMedium
attrsWeakFastLowLarge
dataclassesNoneFastVery lowstdlib
cattrsStrongFastModerateMedium
marshmallowStrongModerateHighLegacy

15. SQLAlchemy 2.x — unified Core + ORM async

SQLAlchemy 2.0 (2023) unified the Core and ORM APIs. The signature changes are select()-based unified queries, AsyncSession, type-hint-first mapping, and PEP 695 generic compatibility. As of May 2026 the latest is 2.0.36, and the asyncio dialects officially support PostgreSQL (asyncpg), MySQL (aiomysql), SQLite (aiosqlite), and MSSQL (aioodbc).

# SQLAlchemy 2 — async + typed mapping
from sqlalchemy import select, String
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from datetime import datetime

class Base(DeclarativeBase):
    pass

class User(Base):
    __tablename__ = "users"
    id: Mapped[int] = mapped_column(primary_key=True)
    email: Mapped[str] = mapped_column(String(255), unique=True)
    name: Mapped[str] = mapped_column(String(50))
    created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)

engine = create_async_engine("postgresql+asyncpg://user:pw@localhost/db", echo=True)
AsyncSessionLocal = async_sessionmaker(engine, expire_on_commit=False)

async def find_recent_users(session: AsyncSession) -> list[User]:
    stmt = select(User).where(User.created_at > datetime(2026, 1, 1)).order_by(User.id)
    result = await session.scalars(stmt)
    return list(result)

The Mapped[...] type annotation is the source of truth for column metadata, and integration with mypy/pyright is clean.

16. SQLModel · Tortoise · Peewee · Piccolo · Edgy — ORM choices

ORMFoundationAsyncMigrationsRecommended use
SQLAlchemy 2IndependentYesAlembicLarge-system standard
SQLModelSQLAlchemy + PydanticYesAlembicFastAPI default
Django ORMDjangoYes (5.1+)Built-inFull-stack
Tortoise ORMIndependentYesaerichDjango-style ASGI
PeeweeIndependentPartialExternalSmall projects
PiccoloIndependentYesBuilt-inAsync-first
EdgySQLAlchemy coreYesBuilt-inNewcomer
BeanieMongoDB + PydanticYesNoneNoSQL async

SQLModel layers Pydantic validation onto SQLAlchemy 2's column mapping, delivering the "one model = ORM + API DTO" pattern. With FastAPI's docs co-recommending it, that pattern has become the de-facto standard.

17. Polars — pandas successor candidate

Polars (Rust + Apache Arrow) rapidly took share from pandas through 2024–2025. It is a multithreaded query engine with lazy evaluation and the Arrow memory model. On large data (>1GB) it runs 5–30x faster than pandas, and data teams at NAVER (Korea) and ZOZO (Japan) have publicly stated that all new ETL is written in Polars.

import polars as pl

# eager API
df = pl.read_csv("orders.csv")
result = (
    df.filter(pl.col("country") == "KR")
      .group_by("user_id")
      .agg([
          pl.col("amount").sum().alias("total"),
          pl.col("order_id").count().alias("orders"),
      ])
      .sort("total", descending=True)
      .head(10)
)

# lazy API — query planner optimization
plan = (
    pl.scan_parquet("logs/*.parquet")
      .filter(pl.col("status") == 200)
      .group_by("path")
      .agg(pl.col("latency_ms").mean())
)
print(plan.explain())  # inspect the execution plan
top_paths = plan.collect()

The biggest hurdle for pandas migrants is Polars' lack of an index concept. Polars treats every column equally, so pandas' .set_index() / MultiIndex patterns have to be re-modeled.

18. pandas 2.x · PyArrow · DuckDB — coexistence inside the data stack

pandas 2.x (2023–) made PyArrow a first-class backend and improved memory use and string-dtype performance. From 2.2 onward pd.options.future.infer_string = True is the norm and copy-on-write is default. Even teams not migrating to Polars can pick up large performance gains by switching pandas 2 to the PyArrow backend.

DuckDB is an in-process OLAP database that runs SQL directly against pandas/Polars DataFrames. It has become the fastest analytics tool in Jupyter, and its httpfs extension lets you query S3/HTTP files directly.

import duckdb, polars as pl

# Run SQL directly against a Polars DataFrame
df = pl.read_csv("orders.csv")
result = duckdb.sql("""
    SELECT user_id, COUNT(*) AS orders, SUM(amount) AS total
    FROM df
    WHERE country = 'KR'
    GROUP BY user_id
    ORDER BY total DESC
    LIMIT 10
""").pl()  # back to Polars

# Query S3 Parquet directly
duckdb.sql("INSTALL httpfs; LOAD httpfs;")
top = duckdb.sql("""
    SELECT path, AVG(latency_ms) AS avg_lat
    FROM read_parquet('s3://bucket/logs/*.parquet')
    WHERE status = 200
    GROUP BY path
""").df()

19. pytest · pytest-asyncio · hypothesis · polyfactory — the testing standard

pytest is the single testing standard for Python. As of May 2026 pytest 8.3 and pytest-asyncio 0.24 are recommended. hypothesis is a property-based testing tool that explores input space automatically to find edge cases. polyfactory auto-generates fake data from Pydantic/dataclass/SQLAlchemy models.

# pytest + hypothesis
import pytest
from hypothesis import given, strategies as st

@given(st.lists(st.integers(min_value=0, max_value=1000), min_size=1))
def test_sum_is_nonneg(xs: list[int]) -> None:
    assert sum(xs) >= 0

# pytest-asyncio
@pytest.mark.asyncio
async def test_user_create(client):
    response = await client.post("/users", json={
        "email": "a@b.com", "name": "Alice", "age": 30
    })
    assert response.status_code == 201

# polyfactory
from polyfactory.factories.pydantic_factory import ModelFactory

class UserFactory(ModelFactory[UserCreate]):
    __model__ = UserCreate

def test_random_users():
    for _ in range(100):
        u = UserFactory.build()
        assert u.age >= 0

Parallel execution is pytest-xdist, coverage is coverage + pytest-cov, mutation testing is mutmut.

20. asyncio · anyio · trio — async concurrency

Python's concurrency story is built around stdlib asyncio. Its cancellation semantics and task-group model are tricky, which is why anyio emerged as an abstraction layer. anyio supports both asyncio and trio backends, and delivers a structured concurrency model. trio itself, by Nathaniel Smith, is a separate async library with strong academic/design coherence but a narrower ecosystem.

# anyio — structured concurrency
import anyio
import httpx

async def fetch(url: str) -> int:
    async with httpx.AsyncClient() as client:
        r = await client.get(url)
        return r.status_code

async def main():
    async with anyio.create_task_group() as tg:
        for url in ["https://a.example", "https://b.example", "https://c.example"]:
            tg.start_soon(fetch, url)
    # When the task group exits, all children are guaranteed cleaned up

anyio.run(main)

FastAPI 0.110+ / Starlette 0.36+ adopted anyio internally, and as of May 2026 new async code is recommended to follow anyio's patterns.

21. NumPy 2 · SciPy · scikit-learn · transformers · PyTorch — ML adjacencies

NumPy 2.0 (2024) brought ABI changes, a new string dtype, and a simplified API. SciPy 1.13+ is NumPy 2-compatible, and scikit-learn 1.5+ added NumPy 2 plus experimental free-threaded support. PyTorch 2.5+ stabilized torch.compile and began experimental free-threaded support. transformers 4.45+ and JAX 0.4+ both integrate parts of Pydantic 2 validation.

In ML workloads the data pipeline has consolidated on Polars + DuckDB + PyArrow, training on PyTorch + Lightning + datasets.

22. Typer · Click · argparse · fire — CLI camps

There are four CLI choices. Typer (FastAPI's sister project, type/Pydantic-based), Click (Flask side, decorator-based), argparse (stdlib), and fire (Google, exposes objects automatically). With Typer the function's type hints are the CLI options — write def ingest(path: Path, workers: int = 4, dry_run: bool = False) -> None: and add @app.command() to get a complete CLI.

uv + Typer is the cleanest way to combine "CLI script + automatic install": uv tool install mycli registers the binary on your system PATH.

23. httpx · playwright-python · Scrapy 2.12 — HTTP clients and scraping

httpx (Encode) is the async successor to requests. It keeps the requests API while adding async, HTTP/2, SOCKS, and OAuth, and async with httpx.AsyncClient(http2=True) as client: handles HTTP/2, connection pooling, and timeouts in a single line. playwright-python is the browser-automation standard, displacing Puppeteer/Selenium. Scrapy 2.12 is the heavyweight crawling framework; lightweight parsing uses parsel (split out of Scrapy).

24. Korean adoption — Toss · Naver · Coupang · Kakao

Toss put Python at the heart of its ML platform and data pipelines. Model serving runs on FastAPI + Pydantic 2 + Granian; feature engineering runs on Polars + DuckDB. A Q3 2025 engineering post documented "Polars yielding 8x faster ETL than pandas."

Naver uses Python for search-ranking feature engineering and Clova model serving, and in late 2025 announced it would standardize on Polars-first for internal data. Coupang serves ads/search ML on FastAPI + Pydantic 2 + SQLAlchemy 2, with Ruff + uv standardized across its monorepo. Kakao serves KoGPT-family inference on FastAPI + PyTorch + ONNX Runtime.

25. Japanese adoption — Mercari · ZOZO · LINE · SmartNews

Mercari wrote its entire ML-2 platform (introduced in 2024) on FastAPI + Pydantic 2 and partially adopted msgspec on the serialization hot path. ZOZO finished migrating its fashion-recommendation feature pipeline from pandas to Polars during 2025 and standardized internal linting on Ruff.

LINE uses FastAPI + SQLAlchemy 2 async for both its ad system and its recommendation system, and presented its Granian rollout at PyCon JP 2025 in Tokyo. SmartNews built its content-ranking training pipeline on PyTorch + Polars + DuckDB.

If you bootstrap a Python project to the 2026 standard, the safest combo is below. Don't migrate everything at once on existing projects — go gradually.

# 2026 recommended pyproject.toml setup
[project]
name = "myproject"
requires-python = ">=3.13"
dependencies = [
    "fastapi[standard]>=0.115",
    "pydantic>=2.9",
    "sqlalchemy[asyncio]>=2.0",
    "polars>=1.12",
    "httpx>=0.27",
]

[dependency-groups]
dev = [
    "pytest>=8.3",
    "pytest-asyncio>=0.24",
    "hypothesis>=6.115",
    "ruff>=0.7",
    "mypy>=1.13",
    "pyright>=1.1.385",
]

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

[tool.ruff.lint]
select = ["E", "W", "F", "I", "B", "C4", "UP", "N", "S", "RUF"]

[tool.mypy]
python_version = "3.13"
strict = true
plugins = ["pydantic.mypy", "sqlalchemy.ext.mypy.plugin"]

[tool.pytest.ini_options]
asyncio_mode = "auto"
addopts = "--strict-markers -ra"

Migration order: (1) Poetry/pip to uv, (2) Black + isort + flake8 to Ruff, (3) pandas to Polars (new code only), (4) pydantic 1 to pydantic 2 (careful — it is a breaking change). Don't pick between mypy and pyright; the most stable pattern is mypy in CI plus pyright in the IDE.

27. Conclusion — Python's hot path moved to Rust

If you compress 2026 Python into one sentence: "the language stayed, the tools went to Rust." Your code is still Python, but every essential tool that processes it — package manager (uv), linter/formatter (Ruff), validation engine (Pydantic 2 / pydantic-core), DataFrame (Polars), ASGI server (Granian) — is written in Rust.

The language itself is evolving via Python 3.13's free-threaded build and JIT, and 3.14's per-interpreter GIL and t-strings. By around 2030 a GIL-less Python is likely to be the default. Until then, the playbook is to learn the Pydantic 2 + SQLAlchemy 2 + Polars + FastAPI/Django/Litestar + Ruff + uv stack, apply it to new projects, and migrate older ones gradually.

References