Skip to content

Commit c1c3718

Browse files
committed
docs(python): document guards, replace, contextvars, and ExitStack
- Add match/case guard examples - Add dataclasses.replace immutable update pattern - Add contextvars and ExitStack stdlib guidance
1 parent 2b17bc4 commit c1c3718

2 files changed

Lines changed: 61 additions & 2 deletions

File tree

skills/python-development/references/modern-python.md

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,23 @@ def handle_response(code: int) -> str:
2020
case _: return "Unknown"
2121
```
2222

23+
### match-case with guards (case ... if ...)
24+
25+
Use guards to express range checks or predicate logic without repeating the matched value:
26+
27+
```python
28+
def classify_amount(amount: float) -> str:
29+
match amount:
30+
case x if x < 0:
31+
return "invalid"
32+
case 0:
33+
return "zero"
34+
case x if x > 10_000:
35+
return "large"
36+
case _:
37+
return "normal"
38+
```
39+
2340
### Walrus Operator (:=)
2441

2542
```python
@@ -43,7 +60,7 @@ total = sum(x * 2 for x in numbers)
4360
## Dataclasses
4461

4562
```python
46-
from dataclasses import dataclass, field
63+
from dataclasses import dataclass, field, replace
4764

4865
@dataclass
4966
class Product:
@@ -55,6 +72,18 @@ class Product:
5572
class ImmutableConfig:
5673
host: str
5774
port: int = 8080
75+
76+
77+
@dataclass(slots=True, frozen=True)
78+
class Sale:
79+
amount: float
80+
currency: str
81+
converted_value: float | None = None
82+
83+
84+
def convert_sale(sale: Sale, rate: float) -> Sale:
85+
# Avoid mutation: return a new object with the updated field
86+
return replace(sale, converted_value=sale.amount * rate)
5887
```
5988

6089
## Functional Programming

skills/python-development/references/standard-library.md

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ short = textwrap.shorten("This is a very long string", width=20, placeholder="..
275275
**`contextlib` (Context managers):**
276276

277277
```python
278-
from contextlib import contextmanager, suppress, redirect_stdout
278+
from contextlib import ExitStack, contextmanager, suppress, redirect_stdout
279279
import io
280280

281281
# Custom context manager
@@ -297,6 +297,36 @@ f = io.StringIO()
297297
with redirect_stdout(f):
298298
print("This goes to StringIO")
299299
output = f.getvalue()
300+
301+
# ExitStack: manage a dynamic set of context managers
302+
def read_existing_files(paths: list[Path]) -> list[str]:
303+
with ExitStack() as stack:
304+
files = [stack.enter_context(p.open()) for p in paths if p.exists()]
305+
return [f.read() for f in files]
306+
```
307+
308+
**`contextvars` (Per-request context in async code):**
309+
310+
Use `contextvars` to carry request-scoped values (request_id, user_id, trace_id) through async call chains without threading them through every function parameter.
311+
Unlike globals, context variables are isolated per task.
312+
313+
```python
314+
from contextvars import ContextVar
315+
316+
request_id_var: ContextVar[str] = ContextVar("request_id", default="unknown")
317+
318+
319+
async def handle_request(request_id: str) -> None:
320+
token = request_id_var.set(request_id)
321+
try:
322+
await do_work()
323+
finally:
324+
request_id_var.reset(token)
325+
326+
327+
async def do_work() -> None:
328+
request_id = request_id_var.get()
329+
logger.info("Working", extra={"request_id": request_id})
300330
```
301331

302332
**`dataclasses` (Data containers):**

0 commit comments

Comments
 (0)