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

- Name
- Youngju Kim
- @fjvbn20031
"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.
| Layer | 2020 standard | 2026 standard | Note |
|---|---|---|---|
| Package manager | pip + virtualenv | uv | 10–100x faster |
| Linter/formatter | Black + isort + flake8 + pylint | Ruff | Single binary |
| Type checker | mypy | mypy or pyright | Two strong options |
| Validation | pydantic 1 (pure Python) | Pydantic 2 (Rust core) | 5–50x faster |
| DataFrame | pandas 1.x | Polars or pandas 2 (Arrow) | New code goes Polars |
| Web API | Flask, Django REST | FastAPI, Litestar, Django 5 async | ASGI normalized |
| Async | asyncio | asyncio + anyio | Structured 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 messages — NameError 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.
| Framework | ASGI/WSGI | Validation engine | 2026 recommended use |
|---|---|---|---|
| FastAPI 0.115+ | ASGI | Pydantic 2 | Default choice |
| Litestar 2.x | ASGI | msgspec | Controller / enterprise |
| Django 5.2 | WSGI/ASGI | DRF · Ninja | Full-stack |
| Flask 3 | WSGI | None | Existing codebases |
| Quart | ASGI | None | Async for Flask fans |
| Sanic | ASGI | dataclasses | Single-package async |
| Robyn | ASGI (Rust) | Pydantic 2 | Extreme routing speed |
| aiohttp 4 | ASGI | None | Dual 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.
| Tool | Validation | Serialization speed | Learning curve | Ecosystem |
|---|---|---|---|---|
| Pydantic 2 | Strong | Fast | Moderate | Massive |
| msgspec | Moderate | Very fast | Low | Medium |
| attrs | Weak | Fast | Low | Large |
| dataclasses | None | Fast | Very low | stdlib |
| cattrs | Strong | Fast | Moderate | Medium |
| marshmallow | Strong | Moderate | High | Legacy |
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
| ORM | Foundation | Async | Migrations | Recommended use |
|---|---|---|---|---|
| SQLAlchemy 2 | Independent | Yes | Alembic | Large-system standard |
| SQLModel | SQLAlchemy + Pydantic | Yes | Alembic | FastAPI default |
| Django ORM | Django | Yes (5.1+) | Built-in | Full-stack |
| Tortoise ORM | Independent | Yes | aerich | Django-style ASGI |
| Peewee | Independent | Partial | External | Small projects |
| Piccolo | Independent | Yes | Built-in | Async-first |
| Edgy | SQLAlchemy core | Yes | Built-in | Newcomer |
| Beanie | MongoDB + Pydantic | Yes | None | NoSQL 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.
26. Toolchain migration — recommended 2026 setup
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
- Python official docs: https://docs.python.org/3/
- FastAPI: https://fastapi.tiangolo.com/
- Django: https://docs.djangoproject.com/en/5.2/
- Litestar: https://litestar.dev/
- Starlette: https://www.starlette.io/
- SQLAlchemy: https://docs.sqlalchemy.org/en/20/
- Polars: https://pola.rs/
- Pydantic: https://docs.pydantic.dev/latest/
- Ruff: https://docs.astral.sh/ruff/
- uv: https://docs.astral.sh/uv/
- mypy: https://mypy.readthedocs.io/
- pyright: https://github.com/microsoft/pyright
- pytest: https://docs.pytest.org/
- DuckDB: https://github.com/duckdb/duckdb
- msgspec: https://jcristharif.com/msgspec/
- Granian: https://github.com/emmett-framework/granian