@punks/cli 1.0.1 → 1.0.2

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.
@@ -0,0 +1,735 @@
1
+ ---
2
+ name: async-python-patterns
3
+ description: Master Python asyncio, concurrent programming, and async/await patterns for high-performance applications. Use when building async APIs, concurrent systems, or I/O-bound applications requiring non-blocking operations.
4
+ ---
5
+
6
+ # Async Python Patterns
7
+
8
+ Comprehensive guidance for implementing asynchronous Python applications using asyncio, concurrent programming patterns, and async/await for building high-performance, non-blocking systems.
9
+
10
+ ## When to Use This Skill
11
+
12
+ - Building async web APIs (FastAPI, aiohttp, Sanic)
13
+ - Implementing concurrent I/O operations (database, file, network)
14
+ - Creating web scrapers with concurrent requests
15
+ - Developing real-time applications (WebSocket servers, chat systems)
16
+ - Processing multiple independent tasks simultaneously
17
+ - Building microservices with async communication
18
+ - Optimizing I/O-bound workloads
19
+ - Implementing async background tasks and queues
20
+
21
+ ## Sync vs Async Decision Guide
22
+
23
+ Before adopting async, consider whether it's the right choice for your use case.
24
+
25
+ | Use Case | Recommended Approach |
26
+ |----------|---------------------|
27
+ | Many concurrent network/DB calls | `asyncio` |
28
+ | CPU-bound computation | `multiprocessing` or thread pool |
29
+ | Mixed I/O + CPU | Offload CPU work with `asyncio.to_thread()` |
30
+ | Simple scripts, few connections | Sync (simpler, easier to debug) |
31
+ | Web APIs with high concurrency | Async frameworks (FastAPI, aiohttp) |
32
+
33
+ **Key Rule:** Stay fully sync or fully async within a call path. Mixing creates hidden blocking and complexity.
34
+
35
+ ## Core Concepts
36
+
37
+ ### 1. Event Loop
38
+
39
+ The event loop is the heart of asyncio, managing and scheduling asynchronous tasks.
40
+
41
+ **Key characteristics:**
42
+
43
+ - Single-threaded cooperative multitasking
44
+ - Schedules coroutines for execution
45
+ - Handles I/O operations without blocking
46
+ - Manages callbacks and futures
47
+
48
+ ### 2. Coroutines
49
+
50
+ Functions defined with `async def` that can be paused and resumed.
51
+
52
+ **Syntax:**
53
+
54
+ ```python
55
+ async def my_coroutine():
56
+ result = await some_async_operation()
57
+ return result
58
+ ```
59
+
60
+ ### 3. Tasks
61
+
62
+ Scheduled coroutines that run concurrently on the event loop.
63
+
64
+ ### 4. Futures
65
+
66
+ Low-level objects representing eventual results of async operations.
67
+
68
+ ### 5. Async Context Managers
69
+
70
+ Resources that support `async with` for proper cleanup.
71
+
72
+ ### 6. Async Iterators
73
+
74
+ Objects that support `async for` for iterating over async data sources.
75
+
76
+ ## Quick Start
77
+
78
+ ```python
79
+ import asyncio
80
+
81
+ async def main():
82
+ print("Hello")
83
+ await asyncio.sleep(1)
84
+ print("World")
85
+
86
+ # Python 3.7+
87
+ asyncio.run(main())
88
+ ```
89
+
90
+ ## Fundamental Patterns
91
+
92
+ ### Pattern 1: Basic Async/Await
93
+
94
+ ```python
95
+ import asyncio
96
+
97
+ async def fetch_data(url: str) -> dict:
98
+ """Fetch data from URL asynchronously."""
99
+ await asyncio.sleep(1) # Simulate I/O
100
+ return {"url": url, "data": "result"}
101
+
102
+ async def main():
103
+ result = await fetch_data("https://api.example.com")
104
+ print(result)
105
+
106
+ asyncio.run(main())
107
+ ```
108
+
109
+ ### Pattern 2: Concurrent Execution with gather()
110
+
111
+ ```python
112
+ import asyncio
113
+ from typing import List
114
+
115
+ async def fetch_user(user_id: int) -> dict:
116
+ """Fetch user data."""
117
+ await asyncio.sleep(0.5)
118
+ return {"id": user_id, "name": f"User {user_id}"}
119
+
120
+ async def fetch_all_users(user_ids: List[int]) -> List[dict]:
121
+ """Fetch multiple users concurrently."""
122
+ tasks = [fetch_user(uid) for uid in user_ids]
123
+ results = await asyncio.gather(*tasks)
124
+ return results
125
+
126
+ async def main():
127
+ user_ids = [1, 2, 3, 4, 5]
128
+ users = await fetch_all_users(user_ids)
129
+ print(f"Fetched {len(users)} users")
130
+
131
+ asyncio.run(main())
132
+ ```
133
+
134
+ ### Pattern 3: Task Creation and Management
135
+
136
+ ```python
137
+ import asyncio
138
+
139
+ async def background_task(name: str, delay: int):
140
+ """Long-running background task."""
141
+ print(f"{name} started")
142
+ await asyncio.sleep(delay)
143
+ print(f"{name} completed")
144
+ return f"Result from {name}"
145
+
146
+ async def main():
147
+ # Create tasks
148
+ task1 = asyncio.create_task(background_task("Task 1", 2))
149
+ task2 = asyncio.create_task(background_task("Task 2", 1))
150
+
151
+ # Do other work
152
+ print("Main: doing other work")
153
+ await asyncio.sleep(0.5)
154
+
155
+ # Wait for tasks
156
+ result1 = await task1
157
+ result2 = await task2
158
+
159
+ print(f"Results: {result1}, {result2}")
160
+
161
+ asyncio.run(main())
162
+ ```
163
+
164
+ ### Pattern 4: Error Handling in Async Code
165
+
166
+ ```python
167
+ import asyncio
168
+ from typing import List, Optional
169
+
170
+ async def risky_operation(item_id: int) -> dict:
171
+ """Operation that might fail."""
172
+ await asyncio.sleep(0.1)
173
+ if item_id % 3 == 0:
174
+ raise ValueError(f"Item {item_id} failed")
175
+ return {"id": item_id, "status": "success"}
176
+
177
+ async def safe_operation(item_id: int) -> Optional[dict]:
178
+ """Wrapper with error handling."""
179
+ try:
180
+ return await risky_operation(item_id)
181
+ except ValueError as e:
182
+ print(f"Error: {e}")
183
+ return None
184
+
185
+ async def process_items(item_ids: List[int]):
186
+ """Process multiple items with error handling."""
187
+ tasks = [safe_operation(iid) for iid in item_ids]
188
+ results = await asyncio.gather(*tasks, return_exceptions=True)
189
+
190
+ # Filter out failures
191
+ successful = [r for r in results if r is not None and not isinstance(r, Exception)]
192
+ failed = [r for r in results if isinstance(r, Exception)]
193
+
194
+ print(f"Success: {len(successful)}, Failed: {len(failed)}")
195
+ return successful
196
+
197
+ asyncio.run(process_items([1, 2, 3, 4, 5, 6]))
198
+ ```
199
+
200
+ ### Pattern 5: Timeout Handling
201
+
202
+ ```python
203
+ import asyncio
204
+
205
+ async def slow_operation(delay: int) -> str:
206
+ """Operation that takes time."""
207
+ await asyncio.sleep(delay)
208
+ return f"Completed after {delay}s"
209
+
210
+ async def with_timeout():
211
+ """Execute operation with timeout."""
212
+ try:
213
+ result = await asyncio.wait_for(slow_operation(5), timeout=2.0)
214
+ print(result)
215
+ except asyncio.TimeoutError:
216
+ print("Operation timed out")
217
+
218
+ asyncio.run(with_timeout())
219
+ ```
220
+
221
+ ## Advanced Patterns
222
+
223
+ ### Pattern 6: Async Context Managers
224
+
225
+ ```python
226
+ import asyncio
227
+ from typing import Optional
228
+
229
+ class AsyncDatabaseConnection:
230
+ """Async database connection context manager."""
231
+
232
+ def __init__(self, dsn: str):
233
+ self.dsn = dsn
234
+ self.connection: Optional[object] = None
235
+
236
+ async def __aenter__(self):
237
+ print("Opening connection")
238
+ await asyncio.sleep(0.1) # Simulate connection
239
+ self.connection = {"dsn": self.dsn, "connected": True}
240
+ return self.connection
241
+
242
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
243
+ print("Closing connection")
244
+ await asyncio.sleep(0.1) # Simulate cleanup
245
+ self.connection = None
246
+
247
+ async def query_database():
248
+ """Use async context manager."""
249
+ async with AsyncDatabaseConnection("postgresql://localhost") as conn:
250
+ print(f"Using connection: {conn}")
251
+ await asyncio.sleep(0.2) # Simulate query
252
+ return {"rows": 10}
253
+
254
+ asyncio.run(query_database())
255
+ ```
256
+
257
+ ### Pattern 7: Async Iterators and Generators
258
+
259
+ ```python
260
+ import asyncio
261
+ from typing import AsyncIterator
262
+
263
+ async def async_range(start: int, end: int, delay: float = 0.1) -> AsyncIterator[int]:
264
+ """Async generator that yields numbers with delay."""
265
+ for i in range(start, end):
266
+ await asyncio.sleep(delay)
267
+ yield i
268
+
269
+ async def fetch_pages(url: str, max_pages: int) -> AsyncIterator[dict]:
270
+ """Fetch paginated data asynchronously."""
271
+ for page in range(1, max_pages + 1):
272
+ await asyncio.sleep(0.2) # Simulate API call
273
+ yield {
274
+ "page": page,
275
+ "url": f"{url}?page={page}",
276
+ "data": [f"item_{page}_{i}" for i in range(5)]
277
+ }
278
+
279
+ async def consume_async_iterator():
280
+ """Consume async iterator."""
281
+ async for number in async_range(1, 5):
282
+ print(f"Number: {number}")
283
+
284
+ print("\nFetching pages:")
285
+ async for page_data in fetch_pages("https://api.example.com/items", 3):
286
+ print(f"Page {page_data['page']}: {len(page_data['data'])} items")
287
+
288
+ asyncio.run(consume_async_iterator())
289
+ ```
290
+
291
+ ### Pattern 8: Producer-Consumer Pattern
292
+
293
+ ```python
294
+ import asyncio
295
+ from asyncio import Queue
296
+ from typing import Optional
297
+
298
+ async def producer(queue: Queue, producer_id: int, num_items: int):
299
+ """Produce items and put them in queue."""
300
+ for i in range(num_items):
301
+ item = f"Item-{producer_id}-{i}"
302
+ await queue.put(item)
303
+ print(f"Producer {producer_id} produced: {item}")
304
+ await asyncio.sleep(0.1)
305
+ await queue.put(None) # Signal completion
306
+
307
+ async def consumer(queue: Queue, consumer_id: int):
308
+ """Consume items from queue."""
309
+ while True:
310
+ item = await queue.get()
311
+ if item is None:
312
+ queue.task_done()
313
+ break
314
+
315
+ print(f"Consumer {consumer_id} processing: {item}")
316
+ await asyncio.sleep(0.2) # Simulate work
317
+ queue.task_done()
318
+
319
+ async def producer_consumer_example():
320
+ """Run producer-consumer pattern."""
321
+ queue = Queue(maxsize=10)
322
+
323
+ # Create tasks
324
+ producers = [
325
+ asyncio.create_task(producer(queue, i, 5))
326
+ for i in range(2)
327
+ ]
328
+
329
+ consumers = [
330
+ asyncio.create_task(consumer(queue, i))
331
+ for i in range(3)
332
+ ]
333
+
334
+ # Wait for producers
335
+ await asyncio.gather(*producers)
336
+
337
+ # Wait for queue to be empty
338
+ await queue.join()
339
+
340
+ # Cancel consumers
341
+ for c in consumers:
342
+ c.cancel()
343
+
344
+ asyncio.run(producer_consumer_example())
345
+ ```
346
+
347
+ ### Pattern 9: Semaphore for Rate Limiting
348
+
349
+ ```python
350
+ import asyncio
351
+ from typing import List
352
+
353
+ async def api_call(url: str, semaphore: asyncio.Semaphore) -> dict:
354
+ """Make API call with rate limiting."""
355
+ async with semaphore:
356
+ print(f"Calling {url}")
357
+ await asyncio.sleep(0.5) # Simulate API call
358
+ return {"url": url, "status": 200}
359
+
360
+ async def rate_limited_requests(urls: List[str], max_concurrent: int = 5):
361
+ """Make multiple requests with rate limiting."""
362
+ semaphore = asyncio.Semaphore(max_concurrent)
363
+ tasks = [api_call(url, semaphore) for url in urls]
364
+ results = await asyncio.gather(*tasks)
365
+ return results
366
+
367
+ async def main():
368
+ urls = [f"https://api.example.com/item/{i}" for i in range(20)]
369
+ results = await rate_limited_requests(urls, max_concurrent=3)
370
+ print(f"Completed {len(results)} requests")
371
+
372
+ asyncio.run(main())
373
+ ```
374
+
375
+ ### Pattern 10: Async Locks and Synchronization
376
+
377
+ ```python
378
+ import asyncio
379
+
380
+ class AsyncCounter:
381
+ """Thread-safe async counter."""
382
+
383
+ def __init__(self):
384
+ self.value = 0
385
+ self.lock = asyncio.Lock()
386
+
387
+ async def increment(self):
388
+ """Safely increment counter."""
389
+ async with self.lock:
390
+ current = self.value
391
+ await asyncio.sleep(0.01) # Simulate work
392
+ self.value = current + 1
393
+
394
+ async def get_value(self) -> int:
395
+ """Get current value."""
396
+ async with self.lock:
397
+ return self.value
398
+
399
+ async def worker(counter: AsyncCounter, worker_id: int):
400
+ """Worker that increments counter."""
401
+ for _ in range(10):
402
+ await counter.increment()
403
+ print(f"Worker {worker_id} incremented")
404
+
405
+ async def test_counter():
406
+ """Test concurrent counter."""
407
+ counter = AsyncCounter()
408
+
409
+ workers = [asyncio.create_task(worker(counter, i)) for i in range(5)]
410
+ await asyncio.gather(*workers)
411
+
412
+ final_value = await counter.get_value()
413
+ print(f"Final counter value: {final_value}")
414
+
415
+ asyncio.run(test_counter())
416
+ ```
417
+
418
+ ## Real-World Applications
419
+
420
+ ### Web Scraping with aiohttp
421
+
422
+ ```python
423
+ import asyncio
424
+ import aiohttp
425
+ from typing import List, Dict
426
+
427
+ async def fetch_url(session: aiohttp.ClientSession, url: str) -> Dict:
428
+ """Fetch single URL."""
429
+ try:
430
+ async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as response:
431
+ text = await response.text()
432
+ return {
433
+ "url": url,
434
+ "status": response.status,
435
+ "length": len(text)
436
+ }
437
+ except Exception as e:
438
+ return {"url": url, "error": str(e)}
439
+
440
+ async def scrape_urls(urls: List[str]) -> List[Dict]:
441
+ """Scrape multiple URLs concurrently."""
442
+ async with aiohttp.ClientSession() as session:
443
+ tasks = [fetch_url(session, url) for url in urls]
444
+ results = await asyncio.gather(*tasks)
445
+ return results
446
+
447
+ async def main():
448
+ urls = [
449
+ "https://httpbin.org/delay/1",
450
+ "https://httpbin.org/delay/2",
451
+ "https://httpbin.org/status/404",
452
+ ]
453
+
454
+ results = await scrape_urls(urls)
455
+ for result in results:
456
+ print(result)
457
+
458
+ asyncio.run(main())
459
+ ```
460
+
461
+ ### Async Database Operations
462
+
463
+ ```python
464
+ import asyncio
465
+ from typing import List, Optional
466
+
467
+ # Simulated async database client
468
+ class AsyncDB:
469
+ """Simulated async database."""
470
+
471
+ async def execute(self, query: str) -> List[dict]:
472
+ """Execute query."""
473
+ await asyncio.sleep(0.1)
474
+ return [{"id": 1, "name": "Example"}]
475
+
476
+ async def fetch_one(self, query: str) -> Optional[dict]:
477
+ """Fetch single row."""
478
+ await asyncio.sleep(0.1)
479
+ return {"id": 1, "name": "Example"}
480
+
481
+ async def get_user_data(db: AsyncDB, user_id: int) -> dict:
482
+ """Fetch user and related data concurrently."""
483
+ user_task = db.fetch_one(f"SELECT * FROM users WHERE id = {user_id}")
484
+ orders_task = db.execute(f"SELECT * FROM orders WHERE user_id = {user_id}")
485
+ profile_task = db.fetch_one(f"SELECT * FROM profiles WHERE user_id = {user_id}")
486
+
487
+ user, orders, profile = await asyncio.gather(user_task, orders_task, profile_task)
488
+
489
+ return {
490
+ "user": user,
491
+ "orders": orders,
492
+ "profile": profile
493
+ }
494
+
495
+ async def main():
496
+ db = AsyncDB()
497
+ user_data = await get_user_data(db, 1)
498
+ print(user_data)
499
+
500
+ asyncio.run(main())
501
+ ```
502
+
503
+ ### WebSocket Server
504
+
505
+ ```python
506
+ import asyncio
507
+ from typing import Set
508
+
509
+ # Simulated WebSocket connection
510
+ class WebSocket:
511
+ """Simulated WebSocket."""
512
+
513
+ def __init__(self, client_id: str):
514
+ self.client_id = client_id
515
+
516
+ async def send(self, message: str):
517
+ """Send message."""
518
+ print(f"Sending to {self.client_id}: {message}")
519
+ await asyncio.sleep(0.01)
520
+
521
+ async def recv(self) -> str:
522
+ """Receive message."""
523
+ await asyncio.sleep(1)
524
+ return f"Message from {self.client_id}"
525
+
526
+ class WebSocketServer:
527
+ """Simple WebSocket server."""
528
+
529
+ def __init__(self):
530
+ self.clients: Set[WebSocket] = set()
531
+
532
+ async def register(self, websocket: WebSocket):
533
+ """Register new client."""
534
+ self.clients.add(websocket)
535
+ print(f"Client {websocket.client_id} connected")
536
+
537
+ async def unregister(self, websocket: WebSocket):
538
+ """Unregister client."""
539
+ self.clients.remove(websocket)
540
+ print(f"Client {websocket.client_id} disconnected")
541
+
542
+ async def broadcast(self, message: str):
543
+ """Broadcast message to all clients."""
544
+ if self.clients:
545
+ tasks = [client.send(message) for client in self.clients]
546
+ await asyncio.gather(*tasks)
547
+
548
+ async def handle_client(self, websocket: WebSocket):
549
+ """Handle individual client connection."""
550
+ await self.register(websocket)
551
+ try:
552
+ async for message in self.message_iterator(websocket):
553
+ await self.broadcast(f"{websocket.client_id}: {message}")
554
+ finally:
555
+ await self.unregister(websocket)
556
+
557
+ async def message_iterator(self, websocket: WebSocket):
558
+ """Iterate over messages from client."""
559
+ for _ in range(3): # Simulate 3 messages
560
+ yield await websocket.recv()
561
+ ```
562
+
563
+ ## Performance Best Practices
564
+
565
+ ### 1. Use Connection Pools
566
+
567
+ ```python
568
+ import asyncio
569
+ import aiohttp
570
+
571
+ async def with_connection_pool():
572
+ """Use connection pool for efficiency."""
573
+ connector = aiohttp.TCPConnector(limit=100, limit_per_host=10)
574
+
575
+ async with aiohttp.ClientSession(connector=connector) as session:
576
+ tasks = [session.get(f"https://api.example.com/item/{i}") for i in range(50)]
577
+ responses = await asyncio.gather(*tasks)
578
+ return responses
579
+ ```
580
+
581
+ ### 2. Batch Operations
582
+
583
+ ```python
584
+ async def batch_process(items: List[str], batch_size: int = 10):
585
+ """Process items in batches."""
586
+ for i in range(0, len(items), batch_size):
587
+ batch = items[i:i + batch_size]
588
+ tasks = [process_item(item) for item in batch]
589
+ await asyncio.gather(*tasks)
590
+ print(f"Processed batch {i // batch_size + 1}")
591
+
592
+ async def process_item(item: str):
593
+ """Process single item."""
594
+ await asyncio.sleep(0.1)
595
+ return f"Processed: {item}"
596
+ ```
597
+
598
+ ### 3. Avoid Blocking Operations
599
+
600
+ Never block the event loop with synchronous operations. A single blocking call stalls all concurrent tasks.
601
+
602
+ ```python
603
+ # BAD - blocks the entire event loop
604
+ async def fetch_data_bad():
605
+ import time
606
+ import requests
607
+ time.sleep(1) # Blocks!
608
+ response = requests.get(url) # Also blocks!
609
+
610
+ # GOOD - use async-native libraries (e.g., httpx for async HTTP)
611
+ import httpx
612
+
613
+ async def fetch_data_good(url: str):
614
+ await asyncio.sleep(1)
615
+ async with httpx.AsyncClient() as client:
616
+ response = await client.get(url)
617
+ ```
618
+
619
+ **Wrapping Blocking Code with `asyncio.to_thread()` (Python 3.9+):**
620
+
621
+ When you must use synchronous libraries, offload to a thread pool:
622
+
623
+ ```python
624
+ import asyncio
625
+ from pathlib import Path
626
+
627
+ async def read_file_async(path: str) -> str:
628
+ """Read file without blocking event loop."""
629
+ # asyncio.to_thread() runs sync code in a thread pool
630
+ return await asyncio.to_thread(Path(path).read_text)
631
+
632
+ async def call_sync_library(data: dict) -> dict:
633
+ """Wrap a synchronous library call."""
634
+ # Useful for sync database drivers, file I/O, CPU work
635
+ return await asyncio.to_thread(sync_library.process, data)
636
+ ```
637
+
638
+ **Lower-level approach with `run_in_executor()`:**
639
+
640
+ ```python
641
+ import asyncio
642
+ import concurrent.futures
643
+ from typing import Any
644
+
645
+ def blocking_operation(data: Any) -> Any:
646
+ """CPU-intensive blocking operation."""
647
+ import time
648
+ time.sleep(1)
649
+ return data * 2
650
+
651
+ async def run_in_executor(data: Any) -> Any:
652
+ """Run blocking operation in thread pool."""
653
+ loop = asyncio.get_running_loop()
654
+ with concurrent.futures.ThreadPoolExecutor() as pool:
655
+ result = await loop.run_in_executor(pool, blocking_operation, data)
656
+ return result
657
+
658
+ async def main():
659
+ results = await asyncio.gather(*[run_in_executor(i) for i in range(5)])
660
+ print(results)
661
+
662
+ asyncio.run(main())
663
+ ```
664
+
665
+ ## Common Pitfalls
666
+
667
+ ### 1. Forgetting await
668
+
669
+ ```python
670
+ # Wrong - returns coroutine object, doesn't execute
671
+ result = async_function()
672
+
673
+ # Correct
674
+ result = await async_function()
675
+ ```
676
+
677
+ ### 2. Blocking the Event Loop
678
+
679
+ ```python
680
+ # Wrong - blocks event loop
681
+ import time
682
+ async def bad():
683
+ time.sleep(1) # Blocks!
684
+
685
+ # Correct
686
+ async def good():
687
+ await asyncio.sleep(1) # Non-blocking
688
+ ```
689
+
690
+ ### 3. Not Handling Cancellation
691
+
692
+ ```python
693
+ async def cancelable_task():
694
+ """Task that handles cancellation."""
695
+ try:
696
+ while True:
697
+ await asyncio.sleep(1)
698
+ print("Working...")
699
+ except asyncio.CancelledError:
700
+ print("Task cancelled, cleaning up...")
701
+ # Perform cleanup
702
+ raise # Re-raise to propagate cancellation
703
+ ```
704
+
705
+ ### 4. Mixing Sync and Async Code
706
+
707
+ ```python
708
+ # Wrong - can't call async from sync directly
709
+ def sync_function():
710
+ result = await async_function() # SyntaxError!
711
+
712
+ # Correct
713
+ def sync_function():
714
+ result = asyncio.run(async_function())
715
+ ```
716
+
717
+ ## Testing Async Code
718
+
719
+ ```python
720
+ import asyncio
721
+ import pytest
722
+
723
+ # Using pytest-asyncio
724
+ @pytest.mark.asyncio
725
+ async def test_async_function():
726
+ """Test async function."""
727
+ result = await fetch_data("https://api.example.com")
728
+ assert result is not None
729
+
730
+ @pytest.mark.asyncio
731
+ async def test_with_timeout():
732
+ """Test with timeout."""
733
+ with pytest.raises(asyncio.TimeoutError):
734
+ await asyncio.wait_for(slow_operation(5), timeout=1.0)
735
+ ```