| name | python-modern |
| description | ALWAYS load before writing, editing, or creating any Python (.py) code — do not write Python without loading it first. Also use when explicitly reviewing Python language-version choices or modernization opportunities. Do not load for read-only file inspection, opening, navigation, or neutral summarization. Modern syntax preferences for 3.8-3.14 (walrus, pattern matching, exception groups, TaskGroup, generic syntax, type aliases, t-strings). |
Python Modern Features — Use These When Available
Assume Python 3.11+ unless a pyproject.toml or .python-version specifies otherwise.
Always prefer modern syntax over legacy equivalents.
🐍 3.8 — Walrus Operator := (always use when it reduces repetition)
if m := re.search(r"\d+", text):
print(m.group())
while chunk := f.read(8192):
process(chunk)
results = [y for x in data if (y := transform(x)) is not None]
🐍 3.10 — Structural Pattern Matching
match command.split():
case ["quit"]:
quit()
case ["go", direction]:
move(direction)
case ["get", obj] if obj in room.items:
pick(obj)
case _:
print("Unknown command")
match point:
case Point(x=0, y=0): print("origin")
case Point(x=0, y=y): print(f"y={y}")
case Point(x=x, y=y): print(f"({x}, {y})")
🐍 3.11 — Exception Groups + except* + TaskGroup
raise ExceptionGroup("validation", [
ValueError("bad email"),
TypeError("bad age"),
])
try:
raise ExceptionGroup("errs", [ValueError("a"), KeyError("b")])
except* ValueError as eg:
for e in eg.exceptions: print("val:", e)
except* KeyError as eg:
for e in eg.exceptions: print("key:", e)
async with asyncio.TaskGroup() as tg:
t1 = tg.create_task(fetch(url1))
t2 = tg.create_task(fetch(url2))
Also 3.11: Self type, tomllib, exception.add_note(), fine-grained traceback locations.
from typing import Self
class Builder:
def set_name(self, name: str) -> Self:
self.name = name
return self
🐍 3.12 — Generic Syntax + type Aliases + f-string Power-ups
def first[T](items: list[T]) -> T:
return items[0]
class Stack[T]:
def push(self, item: T) -> None: ...
def pop(self) -> T: ...
type Vector = list[float]
type Matrix[T] = list[list[T]]
name = "world"
msg = f"{'hello':>10}"
dbg = f"{2 + 2 = }"
multi = f"result: {
some_long_expression # inline comment ok
}"
🐍 3.13 — @deprecated, TypeVar Defaults, __static_attributes__
from warnings import deprecated
@deprecated("Use new_api() instead")
def old_api(): ...
from typing import TypeVar
T = TypeVar("T", default=str)
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
🐍 3.14 — t-strings, Deferred Annotations, except without parens
import templatelib
name = "Alice"; query = t"SELECT * FROM users WHERE name = {name}"
class Node:
def children(self) -> list[Node]: ...
next: Node | None = None
try:
connect()
except TimeoutError, ConnectionRefusedError:
retry()
import compression.zstd as zstd
data = zstd.compress(b"hello world")
🐍 3.14 — asyncio Introspection (ps / pstree)
No code changes needed — inspect live running processes by PID.
python -m asyncio ps <PID>
python -m asyncio pstree <PID>
Use cases: deadlock detection, hung task diagnosis, production debugging without restarting. Pairs with PEP 768 zero-overhead debugger attach.
💡 Agent tip: when debugging async code, suggest pstree before adding print/logging.
✅ Cheat Sheet: Prefer Modern Over Legacy
| Legacy | Modern (prefer this) |
|---|
Union[X, Y] | X | Y (3.10+) |
Optional[X] | X | None (3.10+) |
List[T], Dict[K,V] | list[T], dict[K,V] (3.9+) |
TypeVar("T") + Generic | def fn[T](...) (3.12+) |
TypeAlias | type Alias = ... (3.12+) |
asyncio.gather() | asyncio.TaskGroup (3.11+) |
from __future__ import annotations | Just write forward refs (3.14+) |
Bare except Exception: | except* for grouped errors (3.11+) |
Manual re.match then use | Walrus := (3.8+) |
-> 'MyClass' string annotation | -> Self (3.11+) |
🆕 New Capabilities (no legacy equivalent — just use them)
| Feature | Version | When to use |
|---|
f"{value=}" | 3.8 | Quick debug prints — prints value=42 |
dict_a | dict_b / d |= extra | 3.9 | Merge dicts without {**a, **b} |
exception.add_note("hint") | 3.11 | Enrich exceptions with runtime context |
tomllib.load(f) | 3.11 | Parse TOML (stdlib, no install needed) |
@override | 3.12 | Catch missed/mismatched method overrides at type-check time |
@deprecated("msg") | 3.13 | Mark APIs for removal; emits DeprecationWarning automatically |
t"Hello {name}" (t-strings) | 3.14 | Safe structured templates for SQL/HTML/i18n — NOT auto-interpolated |
python -m asyncio ps/pstree <PID> | 3.14 | Inspect live async tasks without modifying code |
import compression.zstd | 3.14 | Zstandard compression in stdlib |
Quick examples
x = [1, 2, 3]
print(f"{x=}")
print(f"{len(x)=}")
defaults = {"timeout": 30, "retries": 3}
overrides = {"retries": 5}
config = defaults | overrides
try:
load_config("prod.toml")
except FileNotFoundError as e:
e.add_note("Hint: run `make setup` to generate config files")
raise
import tomllib
with open("pyproject.toml", "rb") as f:
config = tomllib.load(f)
from typing import override
class Animal:
def speak(self) -> str: ...
class Dog(Animal):
@override
def speek(self) -> str: ...
from warnings import deprecated
@deprecated("Use connect_v2() instead")
def connect(): ...
name = "Alice'; DROP TABLE users;--"
query = t"SELECT * FROM users WHERE name = {name}"
🔧 Agent Rules
- Check
pyproject.toml requires-python before choosing syntax.
- Default to 3.11+ features if no constraint is set.
- Never use
typing.List, typing.Dict, typing.Optional — use builtin generics.
- Always use
:= walrus when it avoids re-computation or double lookup.
- Prefer
match over long if/elif chains on structured data.
- Use
type aliases and [T] generic syntax over TypeVar boilerplate in 3.12+.
- Annotate return types with
Self instead of repeating the class name.
- Use
TaskGroup over asyncio.gather() for structured concurrency.