@martinffx/atelier 1.4.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +95 -220
  3. package/agents/architect.md +6 -6
  4. package/agents/oracle.md +1 -1
  5. package/agents/{recon.md → sentinel.md} +3 -3
  6. package/dist/atelier.js +167 -26
  7. package/package.json +2 -1
  8. package/skills/atelier-orchestrator/SKILL.md +180 -0
  9. package/skills/atelier-setup/SKILL.md +80 -0
  10. package/skills/code-review/SKILL.md +2 -2
  11. package/skills/code-review/references/reviewers.md +3 -3
  12. package/skills/code-review/references/rq.md +9 -9
  13. package/skills/code-subagents/SKILL.md +44 -75
  14. package/skills/code-subagents/references/batch-reviewer-prompt.md +44 -0
  15. package/skills/code-subagents/references/implementor-prompt.md +10 -11
  16. package/skills/spec-brainstorm/SKILL.md +21 -16
  17. package/skills/spec-finish/SKILL.md +14 -14
  18. package/skills/spec-implement/SKILL.md +42 -42
  19. package/skills/spec-plan/SKILL.md +72 -26
  20. package/skills/spec-plan/references/plan_template.md +77 -0
  21. package/skills/code-subagents/references/code-quality-reviewer-prompt.md +0 -48
  22. package/skills/code-subagents/references/spec-reviewer-prompt.md +0 -62
  23. package/skills/python-architecture/SKILL.md +0 -148
  24. package/skills/python-architecture/references/data-modeling.md +0 -558
  25. package/skills/python-architecture/references/ddd.md +0 -569
  26. package/skills/python-architecture/references/functional-core.md +0 -337
  27. package/skills/python-build-tools/SKILL.md +0 -153
  28. package/skills/python-build-tools/references/basedpyright.md +0 -321
  29. package/skills/python-build-tools/references/ruff.md +0 -225
  30. package/skills/python-build-tools/references/uv.md +0 -257
  31. package/skills/python-fastapi/SKILL.md +0 -184
  32. package/skills/python-fastapi/references/api-design.md +0 -563
  33. package/skills/python-fastapi/references/dependencies.md +0 -479
  34. package/skills/python-fastapi/references/middleware.md +0 -475
  35. package/skills/python-fastapi/references/validation.md +0 -497
  36. package/skills/python-modern-python/SKILL.md +0 -201
  37. package/skills/python-modern-python/references/async.md +0 -76
  38. package/skills/python-modern-python/references/pattern-matching.md +0 -89
  39. package/skills/python-modern-python/references/typing.md +0 -96
  40. package/skills/python-monorepo/SKILL.md +0 -146
  41. package/skills/python-monorepo/references/docker.md +0 -515
  42. package/skills/python-monorepo/references/namespace-packages.md +0 -426
  43. package/skills/python-monorepo/references/workspace-config.md +0 -341
  44. package/skills/python-sqlalchemy/SKILL.md +0 -119
  45. package/skills/python-sqlalchemy/references/async.md +0 -51
  46. package/skills/python-sqlalchemy/references/models.md +0 -67
  47. package/skills/python-sqlalchemy/references/queries.md +0 -57
  48. package/skills/python-temporal/SKILL.md +0 -128
  49. package/skills/python-temporal/references/patterns.md +0 -87
  50. package/skills/python-temporal/references/testing.md +0 -54
  51. package/skills/python-testing/SKILL.md +0 -161
  52. package/skills/python-testing/references/boundaries.md +0 -449
  53. package/skills/python-testing/references/mocking.md +0 -449
  54. package/skills/python-testing/references/pytest.md +0 -524
  55. package/skills/spec-orchestrator/SKILL.md +0 -145
  56. package/skills/typescript-api-design/SKILL.md +0 -396
  57. package/skills/typescript-api-design/references/error-responses.md +0 -469
  58. package/skills/typescript-build-tools/SKILL.md +0 -447
  59. package/skills/typescript-build-tools/references/biome.md +0 -436
  60. package/skills/typescript-build-tools/references/bun.md +0 -432
  61. package/skills/typescript-build-tools/references/turborepo.md +0 -426
  62. package/skills/typescript-build-tools/references/vitest.md +0 -518
  63. package/skills/typescript-drizzle-orm/SKILL.md +0 -398
  64. package/skills/typescript-drizzle-orm/references/cloudflare.md +0 -392
  65. package/skills/typescript-drizzle-orm/references/entity-pattern.md +0 -412
  66. package/skills/typescript-drizzle-orm/references/postgresql.md +0 -191
  67. package/skills/typescript-drizzle-orm/references/repository-pattern.md +0 -479
  68. package/skills/typescript-drizzle-orm/references/sqlite.md +0 -291
  69. package/skills/typescript-dynamodb-toolbox/SKILL.md +0 -370
  70. package/skills/typescript-dynamodb-toolbox/references/entity-layer.md +0 -574
  71. package/skills/typescript-dynamodb-toolbox/references/error-handling.md +0 -495
  72. package/skills/typescript-dynamodb-toolbox/references/modeling.md +0 -332
  73. package/skills/typescript-dynamodb-toolbox/references/repository-layer.md +0 -631
  74. package/skills/typescript-dynamodb-toolbox/references/testing.md +0 -718
  75. package/skills/typescript-dynamodb-toolbox/references/transactions.md +0 -423
  76. package/skills/typescript-effect-ts/SKILL.md +0 -341
  77. package/skills/typescript-effect-ts/references/core.md +0 -412
  78. package/skills/typescript-effect-ts/references/error-handling.md +0 -468
  79. package/skills/typescript-effect-ts/references/resources.md +0 -476
  80. package/skills/typescript-effect-ts/references/schema.md +0 -536
  81. package/skills/typescript-effect-ts/references/services.md +0 -461
  82. package/skills/typescript-fastify/SKILL.md +0 -405
  83. package/skills/typescript-fastify/references/plugins.md +0 -329
  84. package/skills/typescript-fastify/references/typeid.md +0 -315
  85. package/skills/typescript-functional-patterns/SKILL.md +0 -402
  86. package/skills/typescript-functional-patterns/references/adts.md +0 -497
  87. package/skills/typescript-functional-patterns/references/branded-types.md +0 -542
  88. package/skills/typescript-functional-patterns/references/migration-guide.md +0 -695
  89. package/skills/typescript-functional-patterns/references/option-result.md +0 -585
  90. package/skills/typescript-testing/SKILL.md +0 -353
  91. package/skills/typescript-testing/references/mocking.md +0 -675
  92. package/skills/typescript-testing/references/msw.md +0 -852
  93. package/skills/typescript-testing/references/snapshot-testing.md +0 -774
@@ -1,497 +0,0 @@
1
- # Pydantic Validation in FastAPI
2
-
3
- FastAPI uses Pydantic for request/response validation. Comprehensive validation patterns.
4
-
5
- ## Field Constraints
6
-
7
- ### Numeric Constraints
8
-
9
- ```python
10
- from pydantic import BaseModel, Field
11
- from decimal import Decimal
12
-
13
- class Product(BaseModel):
14
- price: Decimal = Field(gt=0, decimal_places=2) # Greater than 0, 2 decimals
15
- discount: float = Field(ge=0, le=1) # Between 0 and 1
16
- stock: int = Field(ge=0) # Greater than or equal to 0
17
- rating: float = Field(ge=0, le=5) # Rating from 0 to 5
18
-
19
- # Field constraints:
20
- # gt: greater than
21
- # ge: greater than or equal
22
- # lt: less than
23
- # le: less than or equal
24
- # multiple_of: must be multiple of value
25
- # decimal_places: max decimal places
26
- ```
27
-
28
- ### String Constraints
29
-
30
- ```python
31
- class User(BaseModel):
32
- username: str = Field(min_length=3, max_length=50)
33
- email: str = Field(pattern=r"^[\w\.\+\-]+@[\w\.\-]+\.\w+$")
34
- password: str = Field(min_length=8, max_length=100)
35
- bio: str | None = Field(default=None, max_length=500)
36
-
37
- # String constraints:
38
- # min_length: minimum length
39
- # max_length: maximum length
40
- # pattern: regex pattern
41
- ```
42
-
43
- ### Collection Constraints
44
-
45
- ```python
46
- class Order(BaseModel):
47
- items: list[str] = Field(min_length=1, max_length=100) # 1-100 items
48
- tags: set[str] = Field(max_length=10) # Max 10 unique tags
49
-
50
- # Collection constraints:
51
- # min_length: minimum items
52
- # max_length: maximum items
53
- ```
54
-
55
- ## Field Types
56
-
57
- ### Special Pydantic Types
58
-
59
- ```python
60
- from pydantic import BaseModel, EmailStr, HttpUrl, UUID4, constr, conint
61
-
62
- class User(BaseModel):
63
- id: UUID4 # Valid UUID4
64
- email: EmailStr # Valid email
65
- website: HttpUrl # Valid HTTP URL
66
- username: constr(min_length=3, max_length=50) # Constrained string
67
- age: conint(ge=18, le=120) # Constrained int
68
-
69
- # Install email-validator for EmailStr:
70
- # pip install email-validator
71
- ```
72
-
73
- ### Datetime Types
74
-
75
- ```python
76
- from datetime import datetime, date, time
77
- from pydantic import BaseModel, Field
78
-
79
- class Event(BaseModel):
80
- name: str
81
- start_date: date # YYYY-MM-DD
82
- start_time: time # HH:MM:SS
83
- created_at: datetime # ISO 8601 datetime
84
-
85
- class Appointment(BaseModel):
86
- scheduled_at: datetime = Field(
87
- description="Appointment datetime in ISO 8601 format"
88
- )
89
- ```
90
-
91
- ## Custom Validators
92
-
93
- ### Field Validators
94
-
95
- ```python
96
- from pydantic import BaseModel, field_validator
97
-
98
- class User(BaseModel):
99
- email: str
100
- age: int
101
-
102
- @field_validator("email")
103
- @classmethod
104
- def validate_email(cls, v):
105
- """Validate email domain"""
106
- if not v.endswith("@example.com"):
107
- raise ValueError("Only @example.com emails allowed")
108
- return v
109
-
110
- @field_validator("age")
111
- @classmethod
112
- def validate_age(cls, v):
113
- """Validate age range"""
114
- if v < 18:
115
- raise ValueError("Must be at least 18 years old")
116
- if v > 120:
117
- raise ValueError("Age must be realistic")
118
- return v
119
- ```
120
-
121
- ### Model Validators
122
-
123
- Validate across multiple fields:
124
-
125
- ```python
126
- from pydantic import BaseModel, model_validator
127
-
128
- class DateRange(BaseModel):
129
- start_date: date
130
- end_date: date
131
-
132
- @model_validator(mode="after")
133
- def validate_date_range(self):
134
- """Validate start before end"""
135
- if self.start_date >= self.end_date:
136
- raise ValueError("start_date must be before end_date")
137
- return self
138
-
139
- class Order(BaseModel):
140
- items: list[str]
141
- discount: float
142
-
143
- @model_validator(mode="after")
144
- def validate_discount_eligibility(self):
145
- """Discount requires minimum items"""
146
- if self.discount > 0 and len(self.items) < 2:
147
- raise ValueError("Discount requires at least 2 items")
148
- return self
149
- ```
150
-
151
- ### Transform Values
152
-
153
- ```python
154
- from pydantic import field_validator
155
-
156
- class User(BaseModel):
157
- email: str
158
- username: str
159
-
160
- @field_validator("email")
161
- @classmethod
162
- def lowercase_email(cls, v):
163
- """Normalize email to lowercase"""
164
- return v.lower()
165
-
166
- @field_validator("username")
167
- @classmethod
168
- def strip_username(cls, v):
169
- """Remove whitespace from username"""
170
- return v.strip()
171
- ```
172
-
173
- ## Complex Validation
174
-
175
- ### Conditional Validation
176
-
177
- ```python
178
- from pydantic import BaseModel, model_validator
179
-
180
- class Payment(BaseModel):
181
- method: str # "card" or "bank"
182
- card_number: str | None = None
183
- bank_account: str | None = None
184
-
185
- @model_validator(mode="after")
186
- def validate_payment_method(self):
187
- """Validate required fields based on method"""
188
- if self.method == "card":
189
- if not self.card_number:
190
- raise ValueError("card_number required for card payment")
191
- elif self.method == "bank":
192
- if not self.bank_account:
193
- raise ValueError("bank_account required for bank payment")
194
- else:
195
- raise ValueError("Invalid payment method")
196
- return self
197
- ```
198
-
199
- ### List Item Validation
200
-
201
- ```python
202
- from pydantic import BaseModel, field_validator
203
-
204
- class Order(BaseModel):
205
- items: list[str]
206
-
207
- @field_validator("items")
208
- @classmethod
209
- def validate_items(cls, v):
210
- """Validate each item"""
211
- if not v:
212
- raise ValueError("Order must have at least one item")
213
-
214
- # Check for duplicates
215
- if len(v) != len(set(v)):
216
- raise ValueError("Duplicate items not allowed")
217
-
218
- # Validate each item
219
- for item in v:
220
- if len(item) < 3:
221
- raise ValueError(f"Item name too short: {item}")
222
-
223
- return v
224
- ```
225
-
226
- ## Nested Models
227
-
228
- ```python
229
- from pydantic import BaseModel, Field
230
-
231
- class Address(BaseModel):
232
- street: str
233
- city: str
234
- country: str = Field(pattern=r"^[A-Z]{2}$") # Country code
235
-
236
- class User(BaseModel):
237
- name: str
238
- email: EmailStr
239
- address: Address # Nested model
240
-
241
- # Request body:
242
- # {
243
- # "name": "Alice",
244
- # "email": "alice@example.com",
245
- # "address": {
246
- # "street": "123 Main St",
247
- # "city": "New York",
248
- # "country": "US"
249
- # }
250
- # }
251
- ```
252
-
253
- ## Optional Fields
254
-
255
- ```python
256
- from typing import Optional
257
-
258
- class User(BaseModel):
259
- name: str
260
- email: str
261
- bio: str | None = None # Optional, defaults to None
262
- age: int | None = None # Optional
263
- website: HttpUrl | None = None # Optional URL
264
-
265
- # Or use Optional (older syntax)
266
- class User(BaseModel):
267
- name: str
268
- bio: Optional[str] = None
269
- ```
270
-
271
- ## Default Values
272
-
273
- ```python
274
- from datetime import datetime
275
- from uuid import uuid4
276
-
277
- class User(BaseModel):
278
- id: UUID = Field(default_factory=uuid4) # Generate new UUID
279
- created_at: datetime = Field(default_factory=datetime.now)
280
- is_active: bool = True # Static default
281
- role: str = "user" # Static default
282
- ```
283
-
284
- ## Union Types
285
-
286
- ```python
287
- from typing import Union
288
- from pydantic import field_validator
289
-
290
- class FlexibleField(BaseModel):
291
- value: int | str # Can be int or string
292
-
293
- @field_validator("value")
294
- @classmethod
295
- def validate_value(cls, v):
296
- """Validate based on type"""
297
- if isinstance(v, int):
298
- if v < 0:
299
- raise ValueError("Int value must be positive")
300
- elif isinstance(v, str):
301
- if len(v) < 3:
302
- raise ValueError("String value too short")
303
- return v
304
- ```
305
-
306
- ## Discriminated Unions
307
-
308
- For handling multiple types with type field:
309
-
310
- ```python
311
- from typing import Literal, Union
312
- from pydantic import BaseModel, Field
313
-
314
- class CardPayment(BaseModel):
315
- type: Literal["card"]
316
- card_number: str
317
- cvv: str
318
-
319
- class BankPayment(BaseModel):
320
- type: Literal["bank"]
321
- account_number: str
322
- routing_number: str
323
-
324
- class Payment(BaseModel):
325
- payment: Union[CardPayment, BankPayment] = Field(discriminator="type")
326
-
327
- # FastAPI automatically validates based on "type" field
328
- ```
329
-
330
- ## Custom Error Messages
331
-
332
- ```python
333
- from pydantic import BaseModel, Field, field_validator, ValidationError
334
-
335
- class User(BaseModel):
336
- age: int = Field(
337
- ge=18,
338
- le=120,
339
- description="User age in years",
340
- )
341
-
342
- @field_validator("age")
343
- @classmethod
344
- def validate_age(cls, v):
345
- if v < 18:
346
- raise ValueError("You must be at least 18 years old to register")
347
- return v
348
-
349
- # Customize validation error response in FastAPI
350
- from fastapi import Request, status
351
- from fastapi.responses import JSONResponse
352
- from fastapi.exceptions import RequestValidationError
353
-
354
- @app.exception_handler(RequestValidationError)
355
- async def validation_exception_handler(request: Request, exc: RequestValidationError):
356
- """Custom validation error format"""
357
- errors = []
358
- for error in exc.errors():
359
- errors.append({
360
- "field": ".".join(str(loc) for loc in error["loc"]),
361
- "message": error["msg"],
362
- "type": error["type"],
363
- })
364
-
365
- return JSONResponse(
366
- status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
367
- content={"errors": errors},
368
- )
369
- ```
370
-
371
- ## Reusable Validation
372
-
373
- ```python
374
- from pydantic import field_validator
375
-
376
- def validate_positive(v: int) -> int:
377
- """Reusable validator"""
378
- if v <= 0:
379
- raise ValueError("Must be positive")
380
- return v
381
-
382
- class Product(BaseModel):
383
- price: int
384
- stock: int
385
-
386
- _validate_price = field_validator("price")(classmethod(validate_positive))
387
- _validate_stock = field_validator("stock")(classmethod(validate_positive))
388
- ```
389
-
390
- ## Config Options
391
-
392
- ```python
393
- from pydantic import BaseModel, ConfigDict
394
-
395
- class User(BaseModel):
396
- model_config = ConfigDict(
397
- str_strip_whitespace=True, # Strip whitespace from strings
398
- str_min_length=1, # Minimum string length
399
- validate_assignment=True, # Validate on assignment
400
- validate_default=True, # Validate default values
401
- from_attributes=True, # Enable ORM mode
402
- )
403
-
404
- name: str
405
- email: str
406
- ```
407
-
408
- ## Validation in Route
409
-
410
- ```python
411
- from fastapi import HTTPException
412
-
413
- @app.post("/users")
414
- def create_user(user: CreateUserRequest):
415
- """Pydantic validation happens automatically"""
416
- # If validation fails, FastAPI returns 422 with error details
417
- # If successful, user is validated CreateUserRequest instance
418
-
419
- # Additional business validation
420
- if user_exists(user.email):
421
- raise HTTPException(
422
- status_code=400,
423
- detail="User with this email already exists",
424
- )
425
-
426
- return create_user_in_db(user)
427
- ```
428
-
429
- ## Testing Validation
430
-
431
- ```python
432
- import pytest
433
- from pydantic import ValidationError
434
-
435
- def test_valid_user():
436
- """Test valid user creation"""
437
- user = CreateUserRequest(
438
- email="test@example.com",
439
- name="Test User",
440
- age=25,
441
- )
442
- assert user.email == "test@example.com"
443
-
444
- def test_invalid_email():
445
- """Test invalid email"""
446
- with pytest.raises(ValidationError) as exc_info:
447
- CreateUserRequest(
448
- email="invalid-email",
449
- name="Test User",
450
- age=25,
451
- )
452
- errors = exc_info.value.errors()
453
- assert any(error["loc"] == ("email",) for error in errors)
454
-
455
- def test_age_validation():
456
- """Test age must be >= 18"""
457
- with pytest.raises(ValidationError) as exc_info:
458
- CreateUserRequest(
459
- email="test@example.com",
460
- name="Test User",
461
- age=15,
462
- )
463
- errors = exc_info.value.errors()
464
- assert "age" in str(errors)
465
- ```
466
-
467
- ## Best Practices
468
-
469
- 1. **Use Field constraints**: Prefer `Field(gt=0)` over custom validators for simple cases
470
- 2. **Descriptive error messages**: Make validation errors helpful
471
- 3. **Validate early**: Pydantic validates at API boundary
472
- 4. **Normalize data**: Use validators to transform (lowercase email, strip whitespace)
473
- 5. **Test validators**: Write tests for complex validation logic
474
- 6. **Reuse validators**: Extract common validators to functions
475
- 7. **Model validators for cross-field**: Use `@model_validator` when validating multiple fields
476
-
477
- ## Summary
478
-
479
- **Field constraints:**
480
- - Numeric: `gt`, `ge`, `lt`, `le`, `multiple_of`
481
- - String: `min_length`, `max_length`, `pattern`
482
- - Collection: `min_length`, `max_length`
483
-
484
- **Validators:**
485
- - `@field_validator`: Validate single field
486
- - `@model_validator`: Validate across fields
487
- - Transform values in validators
488
-
489
- **Special types:**
490
- - `EmailStr`: Valid email
491
- - `HttpUrl`: Valid URL
492
- - `UUID4`: Valid UUID
493
-
494
- **Error handling:**
495
- - Automatic 422 responses
496
- - Custom error messages
497
- - Exception handlers
@@ -1,201 +0,0 @@
1
- ---
2
- name: python-modern-python
3
- description: Modern Python language features and typing patterns. Use when writing type hints, using generics, implementing pattern matching, working with async/await, or leveraging Python 3.10+ features.
4
- user-invocable: false
5
- ---
6
-
7
- # Modern Python Features
8
-
9
- Modern Python 3.10+ language features, type hints, and patterns.
10
-
11
- ## Type Hints
12
-
13
- ### Basic Types
14
-
15
- ```python
16
- def greet(name: str) -> str:
17
- return f"Hello {name}"
18
-
19
- age: int = 25
20
- prices: list[float] = [9.99, 19.99]
21
- mapping: dict[str, int] = {"a": 1}
22
- ```
23
-
24
- ### Union Types (Python 3.10+)
25
-
26
- ```python
27
- # Modern syntax
28
- def process(value: int | str) -> bool:
29
- ...
30
-
31
- # Optional
32
- def get_user(id: int) -> User | None:
33
- ...
34
-
35
- # Multiple types
36
- Result = int | str | bool
37
- ```
38
-
39
- ### Generic Types
40
-
41
- ```python
42
- from typing import TypeVar, Generic
43
-
44
- T = TypeVar("T")
45
-
46
- class Repository(Generic[T]):
47
- def get(self, id: int) -> T | None:
48
- ...
49
-
50
- def save(self, entity: T) -> T:
51
- ...
52
-
53
- user_repo = Repository[User]()
54
- ```
55
-
56
- ### Protocol (Structural Typing)
57
-
58
- ```python
59
- from typing import Protocol
60
-
61
- class Drawable(Protocol):
62
- """Structural type - any class with draw()"""
63
- def draw(self) -> None:
64
- ...
65
-
66
- def render(obj: Drawable) -> None:
67
- """Works with any object that has draw()"""
68
- obj.draw()
69
- ```
70
-
71
- ## Pattern Matching (Python 3.10+)
72
-
73
- ### Basic Matching
74
-
75
- ```python
76
- def handle_command(command: str):
77
- match command:
78
- case "start":
79
- start_process()
80
- case "stop":
81
- stop_process()
82
- case "status":
83
- return get_status()
84
- case _:
85
- raise ValueError("Unknown command")
86
- ```
87
-
88
- ### Matching with Values
89
-
90
- ```python
91
- def handle_http_status(status: int):
92
- match status:
93
- case 200:
94
- return "OK"
95
- case 404:
96
- return "Not Found"
97
- case 500 | 502 | 503:
98
- return "Server Error"
99
- case _:
100
- return "Unknown"
101
- ```
102
-
103
- ### Structural Pattern Matching
104
-
105
- ```python
106
- def process_point(point):
107
- match point:
108
- case (0, 0):
109
- return "Origin"
110
- case (x, 0):
111
- return f"On X-axis at {x}"
112
- case (0, y):
113
- return f"On Y-axis at {y}"
114
- case (x, y):
115
- return f"At ({x}, {y})"
116
- ```
117
-
118
- ## Async/Await
119
-
120
- ### Async Functions
121
-
122
- ```python
123
- async def fetch_data(url: str) -> dict:
124
- """Async function"""
125
- async with httpx.AsyncClient() as client:
126
- response = await client.get(url)
127
- return response.json()
128
-
129
- # Call async function
130
- data = await fetch_data("https://api.example.com")
131
- ```
132
-
133
- ### Concurrent Execution
134
-
135
- ```python
136
- import asyncio
137
-
138
- async def process_all(urls: list[str]):
139
- """Process multiple URLs concurrently"""
140
- tasks = [fetch_data(url) for url in urls]
141
- results = await asyncio.gather(*tasks)
142
- return results
143
- ```
144
-
145
- ### Async Context Managers
146
-
147
- ```python
148
- class AsyncDatabase:
149
- async def __aenter__(self):
150
- await self.connect()
151
- return self
152
-
153
- async def __aexit__(self, *args):
154
- await self.disconnect()
155
-
156
- async with AsyncDatabase() as db:
157
- await db.query(...)
158
- ```
159
-
160
- ## Dataclasses and attrs
161
-
162
- ```python
163
- from dataclasses import dataclass
164
-
165
- @dataclass(frozen=True)
166
- class Point:
167
- x: float
168
- y: float
169
-
170
- def distance_from_origin(self) -> float:
171
- return (self.x ** 2 + self.y ** 2) ** 0.5
172
- ```
173
-
174
- ## Walrus Operator
175
-
176
- ```python
177
- # Assign and use in one expression
178
- if (n := len(items)) > 10:
179
- print(f"Too many items: {n}")
180
-
181
- # In comprehensions
182
- results = [y for x in items if (y := process(x)) is not None]
183
- ```
184
-
185
- ## Modern String Formatting
186
-
187
- ```python
188
- name = "Alice"
189
- age = 30
190
-
191
- # f-strings
192
- message = f"Hello {name}, you are {age} years old"
193
-
194
- # f-strings with expressions
195
- message = f"In 5 years you'll be {age + 5}"
196
-
197
- # Debug f-strings (Python 3.8+)
198
- print(f"{name=}") # name='Alice'
199
- ```
200
-
201
- See references/ for advanced typing, async patterns, and pattern matching.