Python in 2025 feels as refreshed as Node.js: the language and its ecosystem have converged on a set of clear, “batteries-included” conventions that make everyday development faster, safer, and more maintainable. Below is a roadmap that mirrors the Node.js patterns you just explored, showing how contemporary Python tackles the same concerns.
pyproject.toml
EverywhereSince PEP 621–639 landed, one file describes metadata, build back-ends, and tooling:
[project]
name = "modern_py_app"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["httpx", "rich"]
[project.scripts]
modern-py-app = "modern_py_app.__main__:main"
[tool.pytest.ini_options]
addopts = "-q"
from .core import db
) are the norm. __init__.py
boilerplate. py.typed
) for automatic static analysis.urllib
gained async helpers (PEP 722), but most projects standardize on httpx for matching sync/async ergonomics:
import asyncio, httpx
async def fetch_json(url: str) -> dict:
async with httpx.AsyncClient(timeout=5) as client:
r = await client.get(url)
r.raise_for_status()
return r.json()
Cancellation uses asyncio.Timeout(5)
plus Task cancellation scopes, mirroring AbortController
in Node.js.
Python 3.12’s unittest
now includes:
-j auto
) --watch
) coverage
stdlib moduleGreen-field services can ship with zero external test dependencies.
asyncio.TaskGroup
(PEP 680) + ExceptionGroup
(PEP 654) give safe, linear async code:
import asyncio, json, pathlib, httpx
async def main() -> None:
task_data = pathlib.Path("config.json").read_text()
async with asyncio.TaskGroup() as tg:
cfg = tg.create_task(asyncio.to_thread(json.loads, task_data))
user = tg.create_task(fetch_json("https://api.example.com/user"))
config, user = cfg.result(), user.result()
print("Ready:", user["name"], "with", config["app_name"])
asyncio.run(main())
High-level helpers in asyncio.io
(PEP 688) streamline file processing:
async def uppercase_file(src: str, dst: str) -> None:
async for chunk in asyncio.io.read_text(src, chunk_size=16384):
await asyncio.io.append_text(dst, chunk.upper())
For back-pressure-aware pipelines, aiostream
integrates naturally with async for
.
Two options unlock multi-core performance:
concurrent.futures.ProcessPoolExecutor
(or asyncio.to_thread
) – drop-in CPU offloading.from concurrent.futures import ProcessPoolExecutor
import asyncio
def fib(n: int) -> int:
return n if n < 2 else fib(n-1) + fib(n-2)
async def async_fib(n: int) -> int:
loop = asyncio.get_running_loop()
with ProcessPoolExecutor() as pool:
return await loop.run_in_executor(pool, fib, n)
watchfiles
or uvicorn --reload
handle live-reload universally. python -m envdir .env.d
(stdlib) replaces python-dotenv
. pyproject.toml
; pipx
installs CLI tools into isolated envs by default.While PEP 702’s permission model is experimental, practical hardening relies on:
pydantic-v2
for runtime-typed settings --seeder=uv --clear
) systemd
Restrict=
flags or containers)logging.json
provides structured logs out of the box. sys.monitoring
hooks (PEP 669) offer lightweight APM-style metrics:import sys, time, sys.monitoring as mon
def slow_func():
time.sleep(0.2)
mon.add_hook(mon.Events.CALL, lambda f, *a: print("CALL", f.__qualname__))
slow_func()
--onefile --strip
). pyproject.toml
: one file for metadata, builds, and tooling. asyncio
+ TaskGroup: write cancel-safe, linear async code. Modern Python is still “simple and explicit,” but it now meets developers where they are in 2025: web-native, async-first, and production-ready on day one.