@booklib/skills 1.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 (85) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +105 -0
  3. package/animation-at-work/SKILL.md +246 -0
  4. package/animation-at-work/assets/example_asset.txt +1 -0
  5. package/animation-at-work/references/api_reference.md +369 -0
  6. package/animation-at-work/references/review-checklist.md +79 -0
  7. package/animation-at-work/scripts/example.py +1 -0
  8. package/bin/skills.js +85 -0
  9. package/clean-code-reviewer/SKILL.md +292 -0
  10. package/clean-code-reviewer/evals/evals.json +67 -0
  11. package/data-intensive-patterns/SKILL.md +204 -0
  12. package/data-intensive-patterns/assets/example_asset.txt +1 -0
  13. package/data-intensive-patterns/references/api_reference.md +34 -0
  14. package/data-intensive-patterns/references/patterns-catalog.md +551 -0
  15. package/data-intensive-patterns/references/review-checklist.md +193 -0
  16. package/data-intensive-patterns/scripts/example.py +1 -0
  17. package/data-pipelines/SKILL.md +252 -0
  18. package/data-pipelines/assets/example_asset.txt +1 -0
  19. package/data-pipelines/references/api_reference.md +301 -0
  20. package/data-pipelines/references/review-checklist.md +181 -0
  21. package/data-pipelines/scripts/example.py +1 -0
  22. package/design-patterns/SKILL.md +245 -0
  23. package/design-patterns/assets/example_asset.txt +1 -0
  24. package/design-patterns/references/api_reference.md +1 -0
  25. package/design-patterns/references/patterns-catalog.md +726 -0
  26. package/design-patterns/references/review-checklist.md +173 -0
  27. package/design-patterns/scripts/example.py +1 -0
  28. package/domain-driven-design/SKILL.md +221 -0
  29. package/domain-driven-design/assets/example_asset.txt +1 -0
  30. package/domain-driven-design/references/api_reference.md +1 -0
  31. package/domain-driven-design/references/patterns-catalog.md +545 -0
  32. package/domain-driven-design/references/review-checklist.md +158 -0
  33. package/domain-driven-design/scripts/example.py +1 -0
  34. package/effective-java/SKILL.md +195 -0
  35. package/effective-java/assets/example_asset.txt +1 -0
  36. package/effective-java/references/api_reference.md +1 -0
  37. package/effective-java/references/items-catalog.md +955 -0
  38. package/effective-java/references/review-checklist.md +216 -0
  39. package/effective-java/scripts/example.py +1 -0
  40. package/effective-kotlin/SKILL.md +225 -0
  41. package/effective-kotlin/assets/example_asset.txt +1 -0
  42. package/effective-kotlin/references/api_reference.md +1 -0
  43. package/effective-kotlin/references/practices-catalog.md +1228 -0
  44. package/effective-kotlin/references/review-checklist.md +126 -0
  45. package/effective-kotlin/scripts/example.py +1 -0
  46. package/kotlin-in-action/SKILL.md +251 -0
  47. package/kotlin-in-action/assets/example_asset.txt +1 -0
  48. package/kotlin-in-action/references/api_reference.md +1 -0
  49. package/kotlin-in-action/references/practices-catalog.md +436 -0
  50. package/kotlin-in-action/references/review-checklist.md +204 -0
  51. package/kotlin-in-action/scripts/example.py +1 -0
  52. package/lean-startup/SKILL.md +250 -0
  53. package/lean-startup/assets/example_asset.txt +1 -0
  54. package/lean-startup/references/api_reference.md +319 -0
  55. package/lean-startup/references/review-checklist.md +137 -0
  56. package/lean-startup/scripts/example.py +1 -0
  57. package/microservices-patterns/SKILL.md +179 -0
  58. package/microservices-patterns/references/patterns-catalog.md +391 -0
  59. package/microservices-patterns/references/review-checklist.md +169 -0
  60. package/package.json +17 -0
  61. package/refactoring-ui/SKILL.md +236 -0
  62. package/refactoring-ui/assets/example_asset.txt +1 -0
  63. package/refactoring-ui/references/api_reference.md +355 -0
  64. package/refactoring-ui/references/review-checklist.md +114 -0
  65. package/refactoring-ui/scripts/example.py +1 -0
  66. package/storytelling-with-data/SKILL.md +238 -0
  67. package/storytelling-with-data/assets/example_asset.txt +1 -0
  68. package/storytelling-with-data/references/api_reference.md +379 -0
  69. package/storytelling-with-data/references/review-checklist.md +111 -0
  70. package/storytelling-with-data/scripts/example.py +1 -0
  71. package/system-design-interview/SKILL.md +213 -0
  72. package/system-design-interview/assets/example_asset.txt +1 -0
  73. package/system-design-interview/references/api_reference.md +582 -0
  74. package/system-design-interview/references/review-checklist.md +201 -0
  75. package/system-design-interview/scripts/example.py +1 -0
  76. package/using-asyncio-python/SKILL.md +242 -0
  77. package/using-asyncio-python/assets/example_asset.txt +1 -0
  78. package/using-asyncio-python/references/api_reference.md +267 -0
  79. package/using-asyncio-python/references/review-checklist.md +149 -0
  80. package/using-asyncio-python/scripts/example.py +1 -0
  81. package/web-scraping-python/SKILL.md +259 -0
  82. package/web-scraping-python/assets/example_asset.txt +1 -0
  83. package/web-scraping-python/references/api_reference.md +393 -0
  84. package/web-scraping-python/references/review-checklist.md +163 -0
  85. package/web-scraping-python/scripts/example.py +1 -0
@@ -0,0 +1,267 @@
1
+ # Using Asyncio in Python — Practices Catalog
2
+
3
+ Complete chapter-by-chapter catalog of practices from *Using Asyncio in Python*
4
+ by Caleb Hattingh.
5
+
6
+ ---
7
+
8
+ ## Chapter 1: Introducing Asyncio
9
+
10
+ ### What Asyncio Is
11
+ - **Single-threaded concurrency** — Asyncio uses a single thread with an event loop to handle many I/O operations concurrently
12
+ - **I/O-bound focus** — Designed for network programming, database access, file I/O — not CPU-bound computation
13
+ - **Cooperative multitasking** — Coroutines voluntarily yield control at await points; no preemptive switching
14
+ - **Event loop at the core** — The loop monitors I/O readiness and schedules coroutines to run when their I/O is ready
15
+
16
+ ### When to Use Asyncio
17
+ - **Network servers** — Handle thousands of concurrent connections without thousands of threads
18
+ - **API clients** — Fetch from multiple endpoints concurrently without threading overhead
19
+ - **Database access** — Run multiple queries concurrently with async database drivers
20
+ - **Microservices** — Async is natural for services that call other services over HTTP/gRPC
21
+ - **NOT for CPU-bound work** — Use multiprocessing or ProcessPoolExecutor for computation
22
+
23
+ ### Key Insight
24
+ - Asyncio makes concurrent I/O code look sequential — easier to read and reason about than callbacks or threads
25
+ - The main cost is that the entire ecosystem must be async-aware; mixing sync and async requires care
26
+
27
+ ---
28
+
29
+ ## Chapter 2: The Truth About Threads
30
+
31
+ ### Thread Drawbacks
32
+ - **Race conditions** — Shared mutable state plus preemptive scheduling creates hard-to-find bugs
33
+ - **Resource consumption** — Each thread costs ~8MB of stack memory; thousands of threads are impractical
34
+ - **Difficult debugging** — Thread bugs are non-deterministic and may not reproduce
35
+ - **GIL limitation** — Python's Global Interpreter Lock means threads don't provide true parallelism for CPU-bound code
36
+ - **Complexity** — Locks, semaphores, and condition variables add complexity and potential deadlocks
37
+
38
+ ### When Threads Are Still Useful
39
+ - **CPU-bound in executor** — ThreadPoolExecutor or ProcessPoolExecutor for blocking operations called from async code
40
+ - **Legacy library integration** — When async alternatives don't exist, wrap blocking calls in run_in_executor()
41
+ - **Simple scripts** — For quick scripts with few concurrent operations, threads may be simpler
42
+
43
+ ### ThreadPoolExecutor Pattern
44
+ - Use `loop.run_in_executor(executor, blocking_func, *args)` to offload blocking calls
45
+ - Set max_workers to bound resource usage: `ThreadPoolExecutor(max_workers=5)`
46
+ - For CPU-bound work, prefer ProcessPoolExecutor over ThreadPoolExecutor
47
+ - Always shut down the executor on application exit
48
+
49
+ ### The Case for Asyncio Over Threads
50
+ - **No race conditions by default** — Single-threaded means no shared state issues between await points
51
+ - **Lower resource usage** — Coroutines are lightweight; thousands cost almost nothing
52
+ - **Explicit yield points** — You know exactly where context switches happen (at every await)
53
+ - **Simpler reasoning** — Code between awaits runs atomically; no locks needed
54
+
55
+ ---
56
+
57
+ ## Chapter 3: Asyncio Walk-Through
58
+
59
+ ### Quickstart: asyncio.run()
60
+ - **Entry point** — `asyncio.run(main())` is the recommended way to start async code (Python 3.7+)
61
+ - **Creates and destroys loop** — It creates a new event loop, runs the coroutine, then closes the loop
62
+ - **Call once at top level** — Don't call asyncio.run() from within async code; use create_task() instead
63
+ - **Handles cleanup** — Cancels remaining tasks and shuts down async generators on exit
64
+
65
+ ### The Event Loop
66
+ - **Heart of asyncio** — Monitors I/O file descriptors and schedules callbacks/coroutines
67
+ - **loop.run_until_complete(coro)** — Runs a single coroutine to completion (low-level; prefer asyncio.run())
68
+ - **loop.run_forever()** — Runs the loop until stop() is called; useful for long-running servers
69
+ - **loop.stop()** — Stops the loop; typically called from a signal handler or callback
70
+ - **loop.close()** — Final cleanup; must be called after the loop is stopped
71
+ - **Debug mode** — `asyncio.run(main(), debug=True)` enables slow-callback warnings and extra checks
72
+
73
+ ### Coroutines (async def / await)
74
+ - **async def** — Defines a coroutine function; calling it returns a coroutine object (doesn't execute it)
75
+ - **await** — Suspends the current coroutine until the awaited coroutine/future completes
76
+ - **Coroutine vs function** — A coroutine runs to the first await, then yields control back to the loop
77
+ - **Must be awaited** — A coroutine that is called but never awaited will never execute (common bug)
78
+ - **Chaining** — Coroutines can await other coroutines for composition
79
+
80
+ ### Tasks
81
+ - **asyncio.create_task(coro)** — Wraps a coroutine in a Task and schedules it on the event loop (Python 3.7+)
82
+ - **Concurrent execution** — Tasks run concurrently; the event loop switches between them at await points
83
+ - **Task is a Future** — Tasks are a subclass of Future; you can await them and get results
84
+ - **Name tasks** — `create_task(coro, name="fetch-user")` for better debugging and logging
85
+ - **Keep references** — Store task references; orphaned tasks may silently swallow exceptions
86
+ - **task.cancel()** — Requests cancellation; raises CancelledError at the next await point in the task
87
+ - **task.result()** — Gets the result after the task completes; raises the exception if the task failed
88
+
89
+ ### asyncio.ensure_future() vs create_task()
90
+ - **create_task()** — Preferred for coroutines; explicitly creates a Task
91
+ - **ensure_future()** — Accepts both coroutines and futures; wraps coroutines in Tasks
92
+ - **Recommendation** — Use create_task() for coroutines; ensure_future() only when handling generic awaitables
93
+
94
+ ### Futures
95
+ - **Low-level construct** — Represents a result that will be available in the future
96
+ - **Rarely used directly** — Tasks and coroutines are higher-level and preferred
97
+ - **future.set_result(value)** — Sets the result; wakes up anyone awaiting the future
98
+ - **future.set_exception(exc)** — Sets an exception; awaiting the future will raise it
99
+ - **Callback based** — `future.add_done_callback(fn)` for callback-style programming
100
+
101
+ ### gather() and wait()
102
+ - **asyncio.gather(*coros)** — Run multiple coroutines concurrently and collect all results
103
+ - Returns results in the same order as the input coroutines
104
+ - `return_exceptions=True` — Returns exceptions as results instead of raising; prevents one failure from cancelling all
105
+ - Without return_exceptions, first exception cancels remaining tasks
106
+ - **asyncio.wait(tasks, return_when=...)** — More flexible; returns (done, pending) sets
107
+ - `FIRST_COMPLETED` — Returns when any task finishes
108
+ - `FIRST_EXCEPTION` — Returns when any task raises an exception
109
+ - `ALL_COMPLETED` — Returns when all tasks complete (default)
110
+ - **asyncio.as_completed(coros)** — Iterator yielding futures as they complete; process results as they arrive
111
+
112
+ ### Timeouts
113
+ - **asyncio.wait_for(coro, timeout=seconds)** — Cancels the coroutine if it exceeds the timeout
114
+ - **asyncio.timeout(seconds)** — Context manager for timeouts (Python 3.11+): `async with asyncio.timeout(5):`
115
+ - **Always set timeouts** — Network operations without timeouts can hang indefinitely
116
+
117
+ ### Async Context Managers (async with)
118
+ - **__aenter__ / __aexit__** — Async versions of context manager protocols
119
+ - **Resource cleanup** — Ensures connections, sessions, and files are properly closed
120
+ - **aiohttp example** — `async with aiohttp.ClientSession() as session:` ensures session cleanup
121
+ - **Database pools** — `async with pool.acquire() as conn:` borrows and returns connections
122
+ - **@asynccontextmanager** — Decorator from contextlib for creating async context managers with yield
123
+
124
+ ### Async Generators (async for)
125
+ - **async def with yield** — Async generator function; produces values asynchronously
126
+ - **async for** — Iterates over an async generator or any async iterable
127
+ - **Use cases** — Streaming data from network, paginated API results, database cursors
128
+ - **Cleanup** — Async generators are finalized when the loop shuts down (athrow GeneratorExit)
129
+
130
+ ### Async Comprehensions
131
+ - **List** — `[x async for x in aiter]` — Async list comprehension
132
+ - **Set** — `{x async for x in aiter}` — Async set comprehension
133
+ - **Dict** — `{k: v async for k, v in aiter}` — Async dict comprehension
134
+ - **Filtering** — `[x async for x in aiter if await predicate(x)]`
135
+ - **Concise** — Combines async iteration with comprehension syntax
136
+
137
+ ### Starting Up and Shutting Down
138
+
139
+ #### Startup Pattern
140
+ - Use asyncio.run() as the entry point
141
+ - Initialize resources (database pools, HTTP sessions) in the main coroutine
142
+ - Create long-running tasks with create_task()
143
+ - Use async context managers for resource lifecycle
144
+
145
+ #### Shutdown Pattern (Critical)
146
+ 1. **Signal handling** — Register signal handlers for SIGTERM and SIGINT:
147
+ ```
148
+ loop.add_signal_handler(signal.SIGTERM, handler)
149
+ loop.add_signal_handler(signal.SIGINT, handler)
150
+ ```
151
+ 2. **Cancel pending tasks** — Get all tasks and cancel them:
152
+ ```
153
+ tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()]
154
+ for task in tasks:
155
+ task.cancel()
156
+ await asyncio.gather(*tasks, return_exceptions=True)
157
+ ```
158
+ 3. **Shutdown async generators** — `await loop.shutdown_asyncgens()`
159
+ 4. **Shutdown default executor** — `await loop.shutdown_default_executor()` (Python 3.9+)
160
+ 5. **Close the loop** — `loop.close()`
161
+
162
+ #### Executor Integration
163
+ - **run_in_executor(executor, func, *args)** — Run blocking function in a thread/process pool
164
+ - **Default executor** — If executor is None, uses the default ThreadPoolExecutor
165
+ - **Custom executor** — Pass a custom ThreadPoolExecutor or ProcessPoolExecutor
166
+ - **Shutdown executor** — Call `executor.shutdown(wait=True)` or `loop.shutdown_default_executor()`
167
+
168
+ ---
169
+
170
+ ## Chapter 4: 20 Libraries You Aren't Using (But Oh, You Should)
171
+
172
+ ### aiohttp — Async HTTP Client and Server
173
+ - **ClientSession** — Always use session for connection pooling: `async with aiohttp.ClientSession() as session:`
174
+ - **GET/POST** — `async with session.get(url) as resp:` — async context manager for response
175
+ - **Response methods** — `await resp.json()`, `await resp.text()`, `await resp.read()` for different formats
176
+ - **Server** — `aiohttp.web.Application()` for building async web servers
177
+ - **WebSockets** — Built-in WebSocket support for both client and server
178
+ - **Middleware** — Add middleware for logging, auth, error handling
179
+ - **Connection limits** — `TCPConnector(limit=100)` to control connection pool size
180
+
181
+ ### aiofiles — Async File I/O
182
+ - **Non-blocking file ops** — `async with aiofiles.open('file.txt', 'r') as f:` — doesn't block the event loop
183
+ - **Same API as built-in open** — read(), write(), readline(), etc., all async
184
+ - **When to use** — When file I/O would block the event loop; especially in servers handling many requests
185
+ - **Under the hood** — Uses ThreadPoolExecutor internally; overhead is small but present
186
+
187
+ ### Sanic — Async Web Framework
188
+ - **Flask-like API** — Familiar decorator-based routing: `@app.route('/')`
189
+ - **Async handlers** — Route handlers are async def; can use await freely
190
+ - **High performance** — Built on uvloop for faster event loop; designed for speed
191
+ - **Middleware** — Request and response middleware for cross-cutting concerns
192
+ - **WebSockets** — Built-in WebSocket support
193
+
194
+ ### aioredis — Async Redis Client
195
+ - **Connection pooling** — `await aioredis.create_redis_pool('redis://localhost')`
196
+ - **Commands** — `await redis.get('key')`, `await redis.set('key', 'value')`
197
+ - **Pub/Sub** — Async pub/sub for real-time messaging: channel iteration with async for
198
+ - **Pipeline** — Batch multiple commands for efficiency
199
+
200
+ ### asyncpg — Async PostgreSQL Client
201
+ - **High performance** — Pure Python async PostgreSQL driver; significantly faster than psycopg2
202
+ - **Connection pool** — `pool = await asyncpg.create_pool(dsn=...)`
203
+ - **Prepared statements** — `stmt = await conn.prepare('SELECT ...')` for repeated queries
204
+ - **Transactions** — `async with conn.transaction():` for atomic operations
205
+ - **Binary protocol** — Uses PostgreSQL binary protocol for better performance
206
+
207
+ ### Other Notable Libraries
208
+ - **uvloop** — Drop-in replacement for asyncio event loop; 2-4x faster; `asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())`
209
+ - **aiodns** — Async DNS resolution
210
+ - **aiosmtplib** — Async SMTP client for sending emails
211
+ - **aiomysql** — Async MySQL client
212
+ - **motor** — Async MongoDB driver
213
+
214
+ ---
215
+
216
+ ## Chapter 5: Concluding Thoughts
217
+
218
+ ### Key Takeaways
219
+ - Asyncio is the future of Python I/O-bound concurrency
220
+ - The ecosystem is maturing; most major libraries have async versions
221
+ - Start simple and add complexity as needed
222
+ - Graceful shutdown is the hardest part to get right; invest time in it
223
+ - Testing async code requires async-aware test frameworks (pytest-asyncio)
224
+
225
+ ---
226
+
227
+ ## Appendix A: A Short History of Async Support in Python
228
+
229
+ ### Evolution Timeline
230
+ - **Generators (PEP 255)** — yield keyword enables lazy iteration
231
+ - **Generator-based coroutines (PEP 342)** — send(), throw(), close() turn generators into coroutines
232
+ - **yield from (PEP 380)** — Delegate to sub-generators; enables coroutine composition
233
+ - **asyncio module (PEP 3156)** — stdlib event loop and coroutine infrastructure (Python 3.4)
234
+ - **async/await syntax (PEP 492)** — Native coroutine syntax replaces @asyncio.coroutine and yield from (Python 3.5)
235
+ - **Async generators (PEP 525)** — async def with yield for async iteration (Python 3.6)
236
+ - **Async comprehensions (PEP 530)** — [x async for x in aiter] syntax (Python 3.6)
237
+ - **asyncio.run() (Python 3.7)** — Simplified entry point; no more manual loop management
238
+ - **TaskGroups (Python 3.11)** — Structured concurrency with automatic cancellation on failure
239
+
240
+ ### Why This History Matters
241
+ - Understanding the evolution helps read older codebases that use yield from or @asyncio.coroutine
242
+ - Modern code should always use async/await syntax (Python 3.5+)
243
+ - asyncio.run() should always be the entry point (Python 3.7+)
244
+ - TaskGroups provide the safest concurrency model (Python 3.11+)
245
+
246
+ ---
247
+
248
+ ## Appendix B: Supplementary Material
249
+
250
+ ### Debug Mode
251
+ - `asyncio.run(main(), debug=True)` — Enables extra checks
252
+ - Warns about coroutines that were never awaited
253
+ - Warns about callbacks that take too long (>100ms by default)
254
+ - Logs all exceptions in callbacks
255
+
256
+ ### Common Patterns Summary
257
+
258
+ | Pattern | Implementation |
259
+ |---------|---------------|
260
+ | Concurrent fan-out | `results = await asyncio.gather(*tasks, return_exceptions=True)` |
261
+ | Rate limiting | `sem = asyncio.Semaphore(10); async with sem: await work()` |
262
+ | Timeout | `await asyncio.wait_for(coro, timeout=5.0)` |
263
+ | Producer-consumer | `queue = asyncio.Queue(); create_task(producer); create_task(consumer)` |
264
+ | Blocking integration | `await loop.run_in_executor(executor, blocking_func)` |
265
+ | Graceful shutdown | Signal handler → cancel tasks → gather with return_exceptions → close |
266
+ | Connection pool | `async with aiohttp.ClientSession() as session:` |
267
+ | Retry with backoff | try/except in loop with asyncio.sleep(delay * 2**attempt) |
@@ -0,0 +1,149 @@
1
+ # Using Asyncio in Python — Async Code Review Checklist
2
+
3
+ Systematic checklist for reviewing async Python code against the chapters
4
+ from *Using Asyncio in Python* by Caleb Hattingh.
5
+
6
+ ---
7
+
8
+ ## 1. Concurrency Model (Chapters 1–2)
9
+
10
+ ### Workload Fit
11
+ - [ ] **Ch 1 — Asyncio appropriateness** — Is asyncio used for I/O-bound work? Is CPU-bound work offloaded to executors?
12
+ - [ ] **Ch 1 — Single-threaded awareness** — Does the code respect that asyncio runs on a single thread?
13
+ - [ ] **Ch 2 — Thread avoidance** — Are threads avoided where asyncio coroutines would suffice?
14
+ - [ ] **Ch 2 — Executor usage** — Is run_in_executor() used for blocking calls instead of calling them directly?
15
+
16
+ ### Thread Integration
17
+ - [ ] **Ch 2 — ThreadPoolExecutor bounds** — Is max_workers set to prevent unbounded thread creation?
18
+ - [ ] **Ch 2 — ProcessPoolExecutor for CPU** — Is ProcessPoolExecutor used instead of ThreadPoolExecutor for CPU-bound work?
19
+ - [ ] **Ch 2 — Executor shutdown** — Is the executor properly shut down on application exit?
20
+ - [ ] **Ch 2 — No shared mutable state** — Is shared state between threads protected or avoided?
21
+
22
+ ---
23
+
24
+ ## 2. Coroutines & Tasks (Chapter 3)
25
+
26
+ ### Coroutine Basics
27
+ - [ ] **Ch 3 — async def/await** — Are coroutines properly defined with async def and awaited?
28
+ - [ ] **Ch 3 — No unawaited coroutines** — Are all coroutine calls awaited or wrapped in create_task()?
29
+ - [ ] **Ch 3 — asyncio.run() entry** — Is asyncio.run() used as the entry point instead of manual loop management?
30
+ - [ ] **Ch 3 — No nested asyncio.run()** — Is asyncio.run() never called from within async code?
31
+
32
+ ### Task Management
33
+ - [ ] **Ch 3 — create_task() usage** — Is create_task() used instead of ensure_future() for coroutines?
34
+ - [ ] **Ch 3 — Task references kept** — Are created tasks stored in variables or collections to prevent GC and silent exceptions?
35
+ - [ ] **Ch 3 — Named tasks** — Are tasks named for debugging: `create_task(coro, name="descriptive-name")`?
36
+ - [ ] **Ch 3 — Concurrent when possible** — Are independent I/O operations run concurrently via gather() or create_task(), not sequentially?
37
+
38
+ ### gather() and wait()
39
+ - [ ] **Ch 3 — return_exceptions=True** — Is gather() called with return_exceptions=True to prevent one failure from cancelling all?
40
+ - [ ] **Ch 3 — Result checking** — Are gather() results checked for exceptions when return_exceptions=True?
41
+ - [ ] **Ch 3 — wait() for flexibility** — Is asyncio.wait() used when FIRST_COMPLETED or FIRST_EXCEPTION semantics are needed?
42
+ - [ ] **Ch 3 — as_completed() for streaming** — Is as_completed() used when results should be processed as they arrive?
43
+
44
+ ### Cancellation & Timeouts
45
+ - [ ] **Ch 3 — CancelledError handling** — Is CancelledError caught in coroutines for cleanup, then re-raised or allowed to propagate?
46
+ - [ ] **Ch 3 — Timeouts set** — Are asyncio.wait_for() or asyncio.timeout() used for operations that could hang?
47
+ - [ ] **Ch 3 — Cancellation propagation** — Does task.cancel() properly propagate through the task tree?
48
+ - [ ] **Ch 3 — No bare except catching CancelledError** — Does `except Exception` not accidentally catch CancelledError (Python <3.9)?
49
+
50
+ ---
51
+
52
+ ## 3. Async Patterns (Chapter 3)
53
+
54
+ ### Async Context Managers
55
+ - [ ] **Ch 3 — async with for resources** — Are connections, sessions, files, and pools managed with async with?
56
+ - [ ] **Ch 3 — __aenter__/__aexit__** — Do custom async context managers implement proper cleanup in __aexit__?
57
+ - [ ] **Ch 3 — @asynccontextmanager** — Is the contextlib decorator used for simple async context managers?
58
+
59
+ ### Async Iteration
60
+ - [ ] **Ch 3 — async for usage** — Is async for used for iterating over async generators and streams?
61
+ - [ ] **Ch 3 — Async generator cleanup** — Are async generators properly finalized on shutdown?
62
+ - [ ] **Ch 3 — Async comprehensions** — Are async comprehensions used for concise async collection building?
63
+
64
+ ---
65
+
66
+ ## 4. Startup & Shutdown (Chapter 3)
67
+
68
+ ### Startup
69
+ - [ ] **Ch 3 — Resource initialization** — Are database pools, HTTP sessions, and connections created in the main coroutine?
70
+ - [ ] **Ch 3 — Task creation** — Are long-running tasks created with create_task() after resource initialization?
71
+ - [ ] **Ch 3 — Context manager lifecycle** — Are resources wrapped in async with to ensure cleanup?
72
+
73
+ ### Shutdown (Critical)
74
+ - [ ] **Ch 3 — Signal handling** — Are SIGTERM and SIGINT handlers registered with loop.add_signal_handler()?
75
+ - [ ] **Ch 3 — Task cancellation** — Are all pending tasks cancelled on shutdown?
76
+ - [ ] **Ch 3 — Cancellation awaited** — Is gather(*tasks, return_exceptions=True) used to wait for cancelled tasks?
77
+ - [ ] **Ch 3 — Async generator shutdown** — Is loop.shutdown_asyncgens() called?
78
+ - [ ] **Ch 3 — Executor shutdown** — Is loop.shutdown_default_executor() called (Python 3.9+)?
79
+ - [ ] **Ch 3 — Loop closed** — Is loop.close() called as the final step?
80
+ - [ ] **Ch 3 — No resource leaks** — Are all connections, sessions, and file handles closed on shutdown?
81
+
82
+ ---
83
+
84
+ ## 5. Blocking Prevention (Chapters 2–3)
85
+
86
+ ### Event Loop Protection
87
+ - [ ] **Ch 2 — No time.sleep()** — Is asyncio.sleep() used instead of time.sleep()?
88
+ - [ ] **Ch 2 — No blocking HTTP** — Is aiohttp used instead of requests/urllib?
89
+ - [ ] **Ch 2 — No blocking file I/O** — Is aiofiles or run_in_executor() used instead of open()/read()/write()?
90
+ - [ ] **Ch 2 — No blocking database** — Are async database drivers (asyncpg, aiomysql) used instead of sync ones?
91
+ - [ ] **Ch 3 — Executor for legacy** — Is run_in_executor() used for any unavoidable blocking calls?
92
+ - [ ] **Ch 3 — Debug mode testing** — Has the code been tested with asyncio debug mode to detect slow callbacks?
93
+
94
+ ---
95
+
96
+ ## 6. Library Usage (Chapter 4)
97
+
98
+ ### aiohttp
99
+ - [ ] **Ch 4 — ClientSession reuse** — Is a single ClientSession reused across requests, not created per-request?
100
+ - [ ] **Ch 4 — Session as context manager** — Is ClientSession used with async with for proper cleanup?
101
+ - [ ] **Ch 4 — Connection limits** — Is TCPConnector configured with appropriate connection limits?
102
+ - [ ] **Ch 4 — Response consumed** — Are response bodies read (json/text/read) before the response context exits?
103
+
104
+ ### aiofiles
105
+ - [ ] **Ch 4 — Async file ops** — Is aiofiles used for file I/O in async contexts?
106
+ - [ ] **Ch 4 — Context manager** — Are file handles managed with async with?
107
+
108
+ ### Database Clients
109
+ - [ ] **Ch 4 — Connection pooling** — Are database connections pooled (asyncpg.create_pool, etc.)?
110
+ - [ ] **Ch 4 — Pool cleanup** — Is the connection pool closed on shutdown?
111
+ - [ ] **Ch 4 — Transaction management** — Are transactions used with async with for atomicity?
112
+ - [ ] **Ch 4 — Prepared statements** — Are prepared statements used for repeated queries?
113
+
114
+ ---
115
+
116
+ ## 7. Error Handling & Resilience
117
+
118
+ ### Exception Management
119
+ - [ ] **Ch 3 — Per-task error handling** — Does each task have its own try/except for error isolation?
120
+ - [ ] **Ch 3 — Exception logging** — Are task exceptions logged, not silently swallowed?
121
+ - [ ] **Ch 3 — Retry logic** — Are transient errors (network, timeout) retried with exponential backoff?
122
+ - [ ] **Ch 3 — Graceful degradation** — Does one task's failure not crash the entire application?
123
+
124
+ ### Concurrency Limits
125
+ - [ ] **Ch 3 — Semaphore usage** — Is asyncio.Semaphore used to limit concurrent operations?
126
+ - [ ] **Ch 3 — Queue backpressure** — Is asyncio.Queue with maxsize used for producer-consumer backpressure?
127
+ - [ ] **Ch 4 — Connection pool limits** — Are HTTP/database connection pools properly sized?
128
+
129
+ ---
130
+
131
+ ## Quick Review Workflow
132
+
133
+ 1. **Concurrency pass** — Verify asyncio is the right choice; check thread/executor integration
134
+ 2. **Coroutine pass** — Check async def/await correctness, task creation, gather/wait patterns
135
+ 3. **Resource pass** — Verify async context managers, session management, connection pooling
136
+ 4. **Shutdown pass** — Check signal handling, task cancellation, cleanup, executor shutdown
137
+ 5. **Blocking pass** — Scan for any blocking calls on the event loop; verify executor usage
138
+ 6. **Library pass** — Check correct usage of aiohttp, aiofiles, asyncpg, etc.
139
+ 7. **Error pass** — Verify cancellation handling, timeouts, retry logic, error isolation
140
+ 8. **Prioritize findings** — Rank by severity: blocking event loop > resource leaks > missing cancellation > missing timeouts > best practices
141
+
142
+ ## Severity Levels
143
+
144
+ | Severity | Description | Example |
145
+ |----------|-------------|---------|
146
+ | **Critical** | Blocks event loop or causes resource leaks | Calling requests.get() or time.sleep() directly, never closing sessions, no shutdown handling |
147
+ | **High** | Reliability or correctness issues | Missing CancelledError handling, no timeouts, unawaited coroutines, fire-and-forget tasks |
148
+ | **Medium** | Performance or maintainability gaps | Sequential awaits when concurrent possible, no connection pooling, no semaphore limiting, no logging |
149
+ | **Low** | Best practice improvements | Missing task names, no debug mode testing, ensure_future instead of create_task, no async comprehensions |