@booklib/core 2.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 (374) hide show
  1. package/.cursor/rules/booklib-standards.mdc +40 -0
  2. package/.gemini/context.md +372 -0
  3. package/AGENTS.md +166 -0
  4. package/CHANGELOG.md +226 -0
  5. package/CLAUDE.md +81 -0
  6. package/CODE_OF_CONDUCT.md +31 -0
  7. package/CONTRIBUTING.md +304 -0
  8. package/LICENSE +21 -0
  9. package/PLAN.md +28 -0
  10. package/README.ja.md +198 -0
  11. package/README.ko.md +198 -0
  12. package/README.md +503 -0
  13. package/README.pt-BR.md +198 -0
  14. package/README.uk.md +241 -0
  15. package/README.zh-CN.md +198 -0
  16. package/SECURITY.md +9 -0
  17. package/agents/architecture-reviewer.md +136 -0
  18. package/agents/booklib-reviewer.md +90 -0
  19. package/agents/data-reviewer.md +107 -0
  20. package/agents/jvm-reviewer.md +146 -0
  21. package/agents/python-reviewer.md +128 -0
  22. package/agents/rust-reviewer.md +115 -0
  23. package/agents/ts-reviewer.md +110 -0
  24. package/agents/ui-reviewer.md +117 -0
  25. package/assets/logo.svg +36 -0
  26. package/bin/booklib-mcp.js +304 -0
  27. package/bin/booklib.js +1705 -0
  28. package/bin/skills.cjs +1292 -0
  29. package/booklib-router.mdc +36 -0
  30. package/booklib.config.json +19 -0
  31. package/commands/animation-at-work.md +10 -0
  32. package/commands/clean-code-reviewer.md +10 -0
  33. package/commands/data-intensive-patterns.md +10 -0
  34. package/commands/data-pipelines.md +10 -0
  35. package/commands/design-patterns.md +10 -0
  36. package/commands/domain-driven-design.md +10 -0
  37. package/commands/effective-java.md +10 -0
  38. package/commands/effective-kotlin.md +10 -0
  39. package/commands/effective-python.md +10 -0
  40. package/commands/effective-typescript.md +10 -0
  41. package/commands/kotlin-in-action.md +10 -0
  42. package/commands/lean-startup.md +10 -0
  43. package/commands/microservices-patterns.md +10 -0
  44. package/commands/programming-with-rust.md +10 -0
  45. package/commands/refactoring-ui.md +10 -0
  46. package/commands/rust-in-action.md +10 -0
  47. package/commands/skill-router.md +10 -0
  48. package/commands/spring-boot-in-action.md +10 -0
  49. package/commands/storytelling-with-data.md +10 -0
  50. package/commands/system-design-interview.md +10 -0
  51. package/commands/using-asyncio-python.md +10 -0
  52. package/commands/web-scraping-python.md +10 -0
  53. package/community/registry.json +1616 -0
  54. package/hooks/hooks.json +23 -0
  55. package/hooks/posttooluse-capture.mjs +67 -0
  56. package/hooks/suggest.js +153 -0
  57. package/lib/agent-behaviors.js +40 -0
  58. package/lib/agent-detector.js +96 -0
  59. package/lib/config-loader.js +39 -0
  60. package/lib/conflict-resolver.js +148 -0
  61. package/lib/context-builder.js +574 -0
  62. package/lib/discovery-engine.js +298 -0
  63. package/lib/doctor/hook-installer.js +83 -0
  64. package/lib/doctor/usage-tracker.js +87 -0
  65. package/lib/engine/ai-features.js +253 -0
  66. package/lib/engine/auditor.js +103 -0
  67. package/lib/engine/bm25-index.js +178 -0
  68. package/lib/engine/capture.js +120 -0
  69. package/lib/engine/corrections.js +198 -0
  70. package/lib/engine/doctor.js +195 -0
  71. package/lib/engine/graph-injector.js +137 -0
  72. package/lib/engine/graph.js +161 -0
  73. package/lib/engine/handoff.js +405 -0
  74. package/lib/engine/indexer.js +242 -0
  75. package/lib/engine/parser.js +53 -0
  76. package/lib/engine/query-expander.js +42 -0
  77. package/lib/engine/reranker.js +40 -0
  78. package/lib/engine/rrf.js +59 -0
  79. package/lib/engine/scanner.js +151 -0
  80. package/lib/engine/searcher.js +139 -0
  81. package/lib/engine/session-coordinator.js +306 -0
  82. package/lib/engine/session-manager.js +429 -0
  83. package/lib/engine/synthesizer.js +70 -0
  84. package/lib/installer.js +70 -0
  85. package/lib/instinct-block.js +33 -0
  86. package/lib/mcp-config-writer.js +88 -0
  87. package/lib/paths.js +57 -0
  88. package/lib/profiles/design.md +19 -0
  89. package/lib/profiles/general.md +16 -0
  90. package/lib/profiles/research-analysis.md +22 -0
  91. package/lib/profiles/software-development.md +23 -0
  92. package/lib/profiles/writing-content.md +19 -0
  93. package/lib/project-initializer.js +916 -0
  94. package/lib/registry/skills.js +102 -0
  95. package/lib/registry-searcher.js +99 -0
  96. package/lib/rules/rules-manager.js +169 -0
  97. package/lib/skill-fetcher.js +333 -0
  98. package/lib/well-known-builder.js +70 -0
  99. package/lib/wizard/index.js +404 -0
  100. package/lib/wizard/integration-detector.js +41 -0
  101. package/lib/wizard/project-detector.js +100 -0
  102. package/lib/wizard/prompt.js +156 -0
  103. package/lib/wizard/registry-embeddings.js +107 -0
  104. package/lib/wizard/skill-recommender.js +69 -0
  105. package/llms-full.txt +254 -0
  106. package/llms.txt +70 -0
  107. package/package.json +45 -0
  108. package/research-reports/2026-04-01-current-architecture.md +160 -0
  109. package/research-reports/IDEAS.md +93 -0
  110. package/rules/common/clean-code.md +42 -0
  111. package/rules/java/effective-java.md +42 -0
  112. package/rules/kotlin/effective-kotlin.md +37 -0
  113. package/rules/python/effective-python.md +38 -0
  114. package/rules/rust/rust.md +37 -0
  115. package/rules/typescript/effective-typescript.md +42 -0
  116. package/scripts/gen-llms-full.mjs +36 -0
  117. package/scripts/gen-og.mjs +142 -0
  118. package/scripts/validate-frontmatter.js +25 -0
  119. package/skills/animation-at-work/SKILL.md +270 -0
  120. package/skills/animation-at-work/assets/example_asset.txt +1 -0
  121. package/skills/animation-at-work/evals/evals.json +44 -0
  122. package/skills/animation-at-work/evals/results.json +13 -0
  123. package/skills/animation-at-work/examples/after.md +64 -0
  124. package/skills/animation-at-work/examples/before.md +35 -0
  125. package/skills/animation-at-work/references/api_reference.md +369 -0
  126. package/skills/animation-at-work/references/review-checklist.md +79 -0
  127. package/skills/animation-at-work/scripts/audit_animations.py +295 -0
  128. package/skills/animation-at-work/scripts/example.py +1 -0
  129. package/skills/clean-code-reviewer/SKILL.md +444 -0
  130. package/skills/clean-code-reviewer/audit.json +35 -0
  131. package/skills/clean-code-reviewer/evals/evals.json +185 -0
  132. package/skills/clean-code-reviewer/evals/results.json +13 -0
  133. package/skills/clean-code-reviewer/examples/after.md +48 -0
  134. package/skills/clean-code-reviewer/examples/before.md +33 -0
  135. package/skills/clean-code-reviewer/references/api_reference.md +158 -0
  136. package/skills/clean-code-reviewer/references/practices-catalog.md +282 -0
  137. package/skills/clean-code-reviewer/references/review-checklist.md +254 -0
  138. package/skills/clean-code-reviewer/scripts/pre-review.py +206 -0
  139. package/skills/data-intensive-patterns/SKILL.md +267 -0
  140. package/skills/data-intensive-patterns/assets/example_asset.txt +1 -0
  141. package/skills/data-intensive-patterns/evals/evals.json +54 -0
  142. package/skills/data-intensive-patterns/evals/results.json +13 -0
  143. package/skills/data-intensive-patterns/examples/after.md +61 -0
  144. package/skills/data-intensive-patterns/examples/before.md +38 -0
  145. package/skills/data-intensive-patterns/references/api_reference.md +34 -0
  146. package/skills/data-intensive-patterns/references/patterns-catalog.md +551 -0
  147. package/skills/data-intensive-patterns/references/review-checklist.md +193 -0
  148. package/skills/data-intensive-patterns/scripts/adr.py +213 -0
  149. package/skills/data-intensive-patterns/scripts/example.py +1 -0
  150. package/skills/data-pipelines/SKILL.md +259 -0
  151. package/skills/data-pipelines/assets/example_asset.txt +1 -0
  152. package/skills/data-pipelines/evals/evals.json +45 -0
  153. package/skills/data-pipelines/evals/results.json +13 -0
  154. package/skills/data-pipelines/examples/after.md +97 -0
  155. package/skills/data-pipelines/examples/before.md +37 -0
  156. package/skills/data-pipelines/references/api_reference.md +301 -0
  157. package/skills/data-pipelines/references/review-checklist.md +181 -0
  158. package/skills/data-pipelines/scripts/example.py +1 -0
  159. package/skills/data-pipelines/scripts/new_pipeline.py +444 -0
  160. package/skills/design-patterns/SKILL.md +271 -0
  161. package/skills/design-patterns/assets/example_asset.txt +1 -0
  162. package/skills/design-patterns/evals/evals.json +46 -0
  163. package/skills/design-patterns/evals/results.json +13 -0
  164. package/skills/design-patterns/examples/after.md +52 -0
  165. package/skills/design-patterns/examples/before.md +29 -0
  166. package/skills/design-patterns/references/api_reference.md +1 -0
  167. package/skills/design-patterns/references/patterns-catalog.md +726 -0
  168. package/skills/design-patterns/references/review-checklist.md +173 -0
  169. package/skills/design-patterns/scripts/example.py +1 -0
  170. package/skills/design-patterns/scripts/scaffold.py +807 -0
  171. package/skills/domain-driven-design/SKILL.md +142 -0
  172. package/skills/domain-driven-design/assets/example_asset.txt +1 -0
  173. package/skills/domain-driven-design/evals/evals.json +48 -0
  174. package/skills/domain-driven-design/evals/results.json +13 -0
  175. package/skills/domain-driven-design/examples/after.md +80 -0
  176. package/skills/domain-driven-design/examples/before.md +43 -0
  177. package/skills/domain-driven-design/references/api_reference.md +1 -0
  178. package/skills/domain-driven-design/references/patterns-catalog.md +545 -0
  179. package/skills/domain-driven-design/references/review-checklist.md +158 -0
  180. package/skills/domain-driven-design/scripts/example.py +1 -0
  181. package/skills/domain-driven-design/scripts/scaffold.py +421 -0
  182. package/skills/effective-java/SKILL.md +227 -0
  183. package/skills/effective-java/assets/example_asset.txt +1 -0
  184. package/skills/effective-java/evals/evals.json +46 -0
  185. package/skills/effective-java/evals/results.json +13 -0
  186. package/skills/effective-java/examples/after.md +83 -0
  187. package/skills/effective-java/examples/before.md +37 -0
  188. package/skills/effective-java/references/api_reference.md +1 -0
  189. package/skills/effective-java/references/items-catalog.md +955 -0
  190. package/skills/effective-java/references/review-checklist.md +216 -0
  191. package/skills/effective-java/scripts/checkstyle_setup.py +211 -0
  192. package/skills/effective-java/scripts/example.py +1 -0
  193. package/skills/effective-kotlin/SKILL.md +271 -0
  194. package/skills/effective-kotlin/assets/example_asset.txt +1 -0
  195. package/skills/effective-kotlin/audit.json +29 -0
  196. package/skills/effective-kotlin/evals/evals.json +45 -0
  197. package/skills/effective-kotlin/evals/results.json +13 -0
  198. package/skills/effective-kotlin/examples/after.md +36 -0
  199. package/skills/effective-kotlin/examples/before.md +38 -0
  200. package/skills/effective-kotlin/references/api_reference.md +1 -0
  201. package/skills/effective-kotlin/references/practices-catalog.md +1228 -0
  202. package/skills/effective-kotlin/references/review-checklist.md +126 -0
  203. package/skills/effective-kotlin/scripts/example.py +1 -0
  204. package/skills/effective-python/SKILL.md +441 -0
  205. package/skills/effective-python/evals/evals.json +44 -0
  206. package/skills/effective-python/evals/results.json +13 -0
  207. package/skills/effective-python/examples/after.md +56 -0
  208. package/skills/effective-python/examples/before.md +40 -0
  209. package/skills/effective-python/ref-01-pythonic-thinking.md +202 -0
  210. package/skills/effective-python/ref-02-lists-and-dicts.md +146 -0
  211. package/skills/effective-python/ref-03-functions.md +186 -0
  212. package/skills/effective-python/ref-04-comprehensions-generators.md +211 -0
  213. package/skills/effective-python/ref-05-classes-interfaces.md +188 -0
  214. package/skills/effective-python/ref-06-metaclasses-attributes.md +209 -0
  215. package/skills/effective-python/ref-07-concurrency.md +213 -0
  216. package/skills/effective-python/ref-08-robustness-performance.md +248 -0
  217. package/skills/effective-python/ref-09-testing-debugging.md +253 -0
  218. package/skills/effective-python/ref-10-collaboration.md +175 -0
  219. package/skills/effective-python/references/api_reference.md +218 -0
  220. package/skills/effective-python/references/practices-catalog.md +483 -0
  221. package/skills/effective-python/references/review-checklist.md +190 -0
  222. package/skills/effective-python/scripts/lint.py +173 -0
  223. package/skills/effective-typescript/SKILL.md +262 -0
  224. package/skills/effective-typescript/audit.json +29 -0
  225. package/skills/effective-typescript/evals/evals.json +37 -0
  226. package/skills/effective-typescript/evals/results.json +13 -0
  227. package/skills/effective-typescript/examples/after.md +70 -0
  228. package/skills/effective-typescript/examples/before.md +47 -0
  229. package/skills/effective-typescript/references/api_reference.md +118 -0
  230. package/skills/effective-typescript/references/practices-catalog.md +371 -0
  231. package/skills/effective-typescript/scripts/review.py +169 -0
  232. package/skills/kotlin-in-action/SKILL.md +261 -0
  233. package/skills/kotlin-in-action/assets/example_asset.txt +1 -0
  234. package/skills/kotlin-in-action/evals/evals.json +43 -0
  235. package/skills/kotlin-in-action/evals/results.json +13 -0
  236. package/skills/kotlin-in-action/examples/after.md +53 -0
  237. package/skills/kotlin-in-action/examples/before.md +39 -0
  238. package/skills/kotlin-in-action/references/api_reference.md +1 -0
  239. package/skills/kotlin-in-action/references/practices-catalog.md +436 -0
  240. package/skills/kotlin-in-action/references/review-checklist.md +204 -0
  241. package/skills/kotlin-in-action/scripts/example.py +1 -0
  242. package/skills/kotlin-in-action/scripts/setup_detekt.py +224 -0
  243. package/skills/lean-startup/SKILL.md +160 -0
  244. package/skills/lean-startup/assets/example_asset.txt +1 -0
  245. package/skills/lean-startup/evals/evals.json +43 -0
  246. package/skills/lean-startup/evals/results.json +13 -0
  247. package/skills/lean-startup/examples/after.md +80 -0
  248. package/skills/lean-startup/examples/before.md +34 -0
  249. package/skills/lean-startup/references/api_reference.md +319 -0
  250. package/skills/lean-startup/references/review-checklist.md +137 -0
  251. package/skills/lean-startup/scripts/example.py +1 -0
  252. package/skills/lean-startup/scripts/new_experiment.py +286 -0
  253. package/skills/microservices-patterns/SKILL.md +384 -0
  254. package/skills/microservices-patterns/evals/evals.json +45 -0
  255. package/skills/microservices-patterns/evals/results.json +13 -0
  256. package/skills/microservices-patterns/examples/after.md +69 -0
  257. package/skills/microservices-patterns/examples/before.md +40 -0
  258. package/skills/microservices-patterns/references/patterns-catalog.md +391 -0
  259. package/skills/microservices-patterns/references/review-checklist.md +169 -0
  260. package/skills/microservices-patterns/scripts/new_service.py +583 -0
  261. package/skills/programming-with-rust/SKILL.md +209 -0
  262. package/skills/programming-with-rust/evals/evals.json +37 -0
  263. package/skills/programming-with-rust/evals/results.json +13 -0
  264. package/skills/programming-with-rust/examples/after.md +107 -0
  265. package/skills/programming-with-rust/examples/before.md +59 -0
  266. package/skills/programming-with-rust/references/api_reference.md +152 -0
  267. package/skills/programming-with-rust/references/practices-catalog.md +335 -0
  268. package/skills/programming-with-rust/scripts/review.py +142 -0
  269. package/skills/refactoring-ui/SKILL.md +362 -0
  270. package/skills/refactoring-ui/assets/example_asset.txt +1 -0
  271. package/skills/refactoring-ui/evals/evals.json +45 -0
  272. package/skills/refactoring-ui/evals/results.json +13 -0
  273. package/skills/refactoring-ui/examples/after.md +85 -0
  274. package/skills/refactoring-ui/examples/before.md +58 -0
  275. package/skills/refactoring-ui/references/api_reference.md +355 -0
  276. package/skills/refactoring-ui/references/review-checklist.md +114 -0
  277. package/skills/refactoring-ui/scripts/audit_css.py +250 -0
  278. package/skills/refactoring-ui/scripts/example.py +1 -0
  279. package/skills/rust-in-action/SKILL.md +350 -0
  280. package/skills/rust-in-action/evals/evals.json +38 -0
  281. package/skills/rust-in-action/evals/results.json +13 -0
  282. package/skills/rust-in-action/examples/after.md +156 -0
  283. package/skills/rust-in-action/examples/before.md +56 -0
  284. package/skills/rust-in-action/references/practices-catalog.md +346 -0
  285. package/skills/rust-in-action/scripts/review.py +147 -0
  286. package/skills/skill-router/SKILL.md +186 -0
  287. package/skills/skill-router/evals/evals.json +38 -0
  288. package/skills/skill-router/evals/results.json +13 -0
  289. package/skills/skill-router/examples/after.md +63 -0
  290. package/skills/skill-router/examples/before.md +39 -0
  291. package/skills/skill-router/references/api_reference.md +24 -0
  292. package/skills/skill-router/references/routing-heuristics.md +89 -0
  293. package/skills/skill-router/references/skill-catalog.md +174 -0
  294. package/skills/skill-router/scripts/route.py +266 -0
  295. package/skills/spring-boot-in-action/SKILL.md +340 -0
  296. package/skills/spring-boot-in-action/evals/evals.json +39 -0
  297. package/skills/spring-boot-in-action/evals/results.json +13 -0
  298. package/skills/spring-boot-in-action/examples/after.md +185 -0
  299. package/skills/spring-boot-in-action/examples/before.md +84 -0
  300. package/skills/spring-boot-in-action/references/practices-catalog.md +403 -0
  301. package/skills/spring-boot-in-action/scripts/review.py +184 -0
  302. package/skills/storytelling-with-data/SKILL.md +241 -0
  303. package/skills/storytelling-with-data/assets/example_asset.txt +1 -0
  304. package/skills/storytelling-with-data/evals/evals.json +47 -0
  305. package/skills/storytelling-with-data/evals/results.json +13 -0
  306. package/skills/storytelling-with-data/examples/after.md +50 -0
  307. package/skills/storytelling-with-data/examples/before.md +33 -0
  308. package/skills/storytelling-with-data/references/api_reference.md +379 -0
  309. package/skills/storytelling-with-data/references/review-checklist.md +111 -0
  310. package/skills/storytelling-with-data/scripts/chart_review.py +301 -0
  311. package/skills/storytelling-with-data/scripts/example.py +1 -0
  312. package/skills/system-design-interview/SKILL.md +233 -0
  313. package/skills/system-design-interview/assets/example_asset.txt +1 -0
  314. package/skills/system-design-interview/evals/evals.json +46 -0
  315. package/skills/system-design-interview/evals/results.json +13 -0
  316. package/skills/system-design-interview/examples/after.md +94 -0
  317. package/skills/system-design-interview/examples/before.md +27 -0
  318. package/skills/system-design-interview/references/api_reference.md +582 -0
  319. package/skills/system-design-interview/references/review-checklist.md +201 -0
  320. package/skills/system-design-interview/scripts/example.py +1 -0
  321. package/skills/system-design-interview/scripts/new_design.py +421 -0
  322. package/skills/using-asyncio-python/SKILL.md +290 -0
  323. package/skills/using-asyncio-python/assets/example_asset.txt +1 -0
  324. package/skills/using-asyncio-python/evals/evals.json +43 -0
  325. package/skills/using-asyncio-python/evals/results.json +13 -0
  326. package/skills/using-asyncio-python/examples/after.md +68 -0
  327. package/skills/using-asyncio-python/examples/before.md +39 -0
  328. package/skills/using-asyncio-python/references/api_reference.md +267 -0
  329. package/skills/using-asyncio-python/references/review-checklist.md +149 -0
  330. package/skills/using-asyncio-python/scripts/check_blocking.py +270 -0
  331. package/skills/using-asyncio-python/scripts/example.py +1 -0
  332. package/skills/web-scraping-python/SKILL.md +280 -0
  333. package/skills/web-scraping-python/assets/example_asset.txt +1 -0
  334. package/skills/web-scraping-python/evals/evals.json +46 -0
  335. package/skills/web-scraping-python/evals/results.json +13 -0
  336. package/skills/web-scraping-python/examples/after.md +109 -0
  337. package/skills/web-scraping-python/examples/before.md +40 -0
  338. package/skills/web-scraping-python/references/api_reference.md +393 -0
  339. package/skills/web-scraping-python/references/review-checklist.md +163 -0
  340. package/skills/web-scraping-python/scripts/example.py +1 -0
  341. package/skills/web-scraping-python/scripts/new_scraper.py +231 -0
  342. package/skills/writing-plans/audit.json +34 -0
  343. package/tests/agent-detector.test.js +83 -0
  344. package/tests/corrections.test.js +245 -0
  345. package/tests/doctor/hook-installer.test.js +72 -0
  346. package/tests/doctor/usage-tracker.test.js +140 -0
  347. package/tests/engine/benchmark-eval.test.js +31 -0
  348. package/tests/engine/bm25-index.test.js +85 -0
  349. package/tests/engine/capture-command.test.js +35 -0
  350. package/tests/engine/capture.test.js +17 -0
  351. package/tests/engine/graph-augmented-search.test.js +107 -0
  352. package/tests/engine/graph-injector.test.js +44 -0
  353. package/tests/engine/graph.test.js +216 -0
  354. package/tests/engine/hybrid-searcher.test.js +74 -0
  355. package/tests/engine/indexer-bm25.test.js +37 -0
  356. package/tests/engine/mcp-tools.test.js +73 -0
  357. package/tests/engine/project-initializer-mcp.test.js +99 -0
  358. package/tests/engine/query-expander.test.js +36 -0
  359. package/tests/engine/reranker.test.js +51 -0
  360. package/tests/engine/rrf.test.js +49 -0
  361. package/tests/engine/srag-prefix.test.js +47 -0
  362. package/tests/instinct-block.test.js +23 -0
  363. package/tests/mcp-config-writer.test.js +60 -0
  364. package/tests/project-initializer-new-agents.test.js +48 -0
  365. package/tests/rules/rules-manager.test.js +230 -0
  366. package/tests/well-known-builder.test.js +40 -0
  367. package/tests/wizard/integration-detector.test.js +31 -0
  368. package/tests/wizard/project-detector.test.js +51 -0
  369. package/tests/wizard/prompt-session.test.js +61 -0
  370. package/tests/wizard/prompt.test.js +16 -0
  371. package/tests/wizard/registry-embeddings.test.js +35 -0
  372. package/tests/wizard/skill-recommender.test.js +34 -0
  373. package/tests/wizard/slot-count.test.js +25 -0
  374. package/vercel.json +21 -0
@@ -0,0 +1,290 @@
1
+ ---
2
+ name: using-asyncio-python
3
+ version: "1.0"
4
+ license: MIT
5
+ tags: [python, async, concurrency]
6
+ description: >
7
+ Apply Using Asyncio in Python practices (Caleb Hattingh). Covers Introducing
8
+ Asyncio (Ch 1: what it is, I/O-bound concurrency), Threads (Ch 2: drawbacks,
9
+ race conditions, GIL, ThreadPoolExecutor), Asyncio Walk-Through (Ch 3: event
10
+ loop, coroutines, async def/await, tasks, futures, gather, wait, async with,
11
+ async for, async comprehensions, startup/shutdown, signal handling, executors),
12
+ Libraries (Ch 4: aiohttp, aiofiles, Sanic, aioredis, asyncpg), Concluding
13
+ Thoughts (Ch 5), History (App A: generators to async/await), Supplementary
14
+ (App B). Trigger on "asyncio", "async/await", "event loop", "coroutine",
15
+ "aiohttp", "async Python", "concurrent I/O", "non-blocking".
16
+ ---
17
+
18
+ # Using Asyncio in Python Skill
19
+
20
+ You are an expert Python async/concurrent programming engineer grounded in the
21
+ chapters from *Using Asyncio in Python* (Understanding Asynchronous Programming)
22
+ by Caleb Hattingh. You help developers in two modes:
23
+
24
+ 1. **Async Building** — Design and implement async Python code with idiomatic, production-ready patterns
25
+ 2. **Async Review** — Analyze existing async code against the book's practices and recommend improvements
26
+
27
+ ## How to Decide Which Mode
28
+
29
+ - If the user asks to *build*, *create*, *implement*, *write*, or *design* async code → **Async Building**
30
+ - If the user asks to *review*, *audit*, *improve*, *debug*, *optimize*, or *fix* async code → **Async Review**
31
+ - If ambiguous, ask briefly which mode they'd prefer
32
+
33
+ ---
34
+
35
+ ## Mode 1: Async Building
36
+
37
+ When designing or building async Python code, follow this decision flow:
38
+
39
+ ### Step 1 — Understand the Requirements
40
+
41
+ Ask (or infer from context):
42
+
43
+ - **What workload?** — I/O-bound (network, disk, database) or CPU-bound? Mixed?
44
+ - **What pattern?** — Single async function, producer-consumer, server, pipeline, background tasks?
45
+ - **What scale?** — Single coroutine, handful of tasks, thousands of concurrent connections?
46
+ - **What challenges?** — Graceful shutdown, cancellation, timeouts, blocking code integration?
47
+
48
+ ### Step 2 — Apply the Right Practices
49
+
50
+ Read `references/api_reference.md` for the full chapter-by-chapter catalog. Quick decision guide:
51
+
52
+ | Concern | Chapters to Apply |
53
+ |---------|-------------------|
54
+ | Understanding when to use asyncio | Ch 1: I/O-bound concurrency, single-threaded event loop, when threads aren't ideal |
55
+ | Threading vs asyncio decisions | Ch 2: Thread drawbacks, race conditions, GIL, when to use ThreadPoolExecutor |
56
+ | Core async patterns | Ch 3: asyncio.run(), event loop, coroutines, async def/await, create_task() |
57
+ | Task management | Ch 3: gather(), wait(), ensure_future(), Task cancellation, timeouts |
58
+ | Async iteration and context managers | Ch 3: async with, async for, async generators, async comprehensions |
59
+ | Startup and shutdown | Ch 3: Proper initialization, signal handling, executor shutdown, cleanup patterns |
60
+ | HTTP client/server | Ch 4: aiohttp ClientSession, aiohttp web server, connection pooling |
61
+ | Async file I/O | Ch 4: aiofiles for non-blocking file operations |
62
+ | Async web frameworks | Ch 4: Sanic for high-performance async web apps |
63
+ | Async databases | Ch 4: asyncpg for PostgreSQL, aioredis for Redis |
64
+ | Integrating blocking code | Ch 2-3: run_in_executor(), ThreadPoolExecutor, ProcessPoolExecutor |
65
+ | Historical context | App A: Evolution from generators → yield from → async/await |
66
+
67
+ ### Step 3 — Follow Asyncio Principles
68
+
69
+ <core_principles>
70
+ Every async implementation should honor these principles:
71
+
72
+ 1. **Use asyncio for I/O-bound work** — Asyncio excels at network calls, database queries, file I/O; use multiprocessing for CPU-bound
73
+ 2. **Prefer asyncio.run()** — Use it as the single entry point; avoid manual loop management
74
+ 3. **Use create_task() for concurrency** — Don't just await coroutines sequentially; create tasks for parallel I/O
75
+ 4. **Use gather() for fan-out** — Collect multiple coroutines and run them concurrently with return_exceptions=True
76
+ 5. **Always handle cancellation** — Wrap awaits in try/except CancelledError for graceful cleanup
77
+ 6. **Use async with for resources** — Async context managers ensure proper cleanup of connections, sessions, files
78
+ 7. **Never block the event loop** — Use run_in_executor() for any blocking call (disk I/O, CPU work, legacy libraries)
79
+ 8. **Implement graceful shutdown** — Handle SIGTERM/SIGINT, cancel pending tasks, wait for cleanup, close the loop
80
+ 9. **Use timeouts everywhere** — asyncio.wait_for() and asyncio.timeout() prevent indefinite hangs
81
+ 10. **Prefer async libraries** — Use aiohttp over requests, aiofiles over open(), asyncpg over psycopg2
82
+ 11. **Use asyncio.sleep() not time.sleep()** — Inside async functions, always use `await asyncio.sleep()` instead of `time.sleep()`; `time.sleep()` is a blocking call that freezes the entire event loop (Ch 3)
83
+ 12. **Use asyncio.get_running_loop() not get_event_loop()** — Inside async code, use `asyncio.get_running_loop()` to access the loop; `asyncio.get_event_loop()` is deprecated in async contexts and may create a new loop in Python 3.10+ (Ch 3)
84
+ 13. **Never call run_until_complete() from within async code** — `loop.run_until_complete()` cannot be called while the event loop is already running; doing so raises RuntimeError; use `await` or `create_task()` instead (Ch 3)
85
+ 14. **Use Semaphore for rate limiting** — Prefer `asyncio.Semaphore` to cap concurrency instead of `time.sleep()` for rate limiting; Semaphore is non-blocking and cooperative (Ch 3)
86
+ </core_principles>
87
+
88
+ ### Step 4 — Build the Async Code
89
+
90
+ Follow these guidelines:
91
+
92
+ - **Production-ready** — Include error handling, cancellation, timeouts, logging from the start
93
+ - **Structured concurrency** — Use TaskGroups (3.11+) or gather() to manage task lifetimes
94
+ - **Resource management** — Use async context managers for all connections, sessions, and files
95
+ - **Observable** — Log task creation, completion, errors, and timing
96
+ - **Testable** — Design coroutines as pure functions where possible; use pytest-asyncio for testing
97
+
98
+ When building async code, produce:
99
+
100
+ 1. **Approach identification** — Which chapters/concepts apply and why
101
+ 2. **Concurrency analysis** — What runs concurrently, what's sequential, where blocking happens
102
+ 3. **Implementation** — Production-ready code with error handling, cancellation, and timeouts
103
+ 4. **Shutdown strategy** — How the code handles signals, cancellation, and cleanup
104
+ 5. **Testing notes** — How to test the async code, mocking strategies
105
+
106
+ ### Async Building Examples
107
+
108
+ <examples>
109
+ <example id="1" title="Concurrent HTTP Fetching">
110
+
111
+ **Example 1 — Concurrent HTTP Fetching:**
112
+ ```
113
+ User: "Fetch data from 50 API endpoints concurrently"
114
+
115
+ Apply: Ch 3 (tasks, gather), Ch 4 (aiohttp ClientSession),
116
+ Ch 2 (why not threads)
117
+
118
+ Generate:
119
+ - aiohttp.ClientSession with connection pooling
120
+ - Semaphore to limit concurrent requests
121
+ - gather() with return_exceptions=True
122
+ - Timeout per request and overall
123
+ - Graceful error handling per URL
124
+ - Replace any time.sleep(delay) calls with await asyncio.sleep(delay) for polite delays
125
+ ```
126
+
127
+ </example>
128
+ <example id="2" title="Async Web Server">
129
+
130
+ **Example 2 — Async Web Server:**
131
+ ```
132
+ User: "Build an async web server that handles websockets"
133
+
134
+ Apply: Ch 4 (aiohttp server, Sanic), Ch 3 (tasks, async with),
135
+ Ch 3 (shutdown handling)
136
+
137
+ Generate:
138
+ - aiohttp or Sanic web application
139
+ - WebSocket handler with async for
140
+ - Background task management
141
+ - Graceful shutdown with cleanup
142
+ - Connection tracking
143
+ ```
144
+
145
+ </example>
146
+ <example id="3" title="Producer-Consumer Pipeline">
147
+
148
+ **Example 3 — Producer-Consumer Pipeline:**
149
+ ```
150
+ User: "Build a pipeline that reads from a queue, processes, and writes results"
151
+
152
+ Apply: Ch 3 (tasks, queues, async for), Ch 2 (executor for blocking),
153
+ Ch 3 (shutdown, cancellation)
154
+
155
+ Generate:
156
+ - asyncio.Queue for buffering
157
+ - Producer coroutine feeding the queue
158
+ - Consumer coroutines processing items
159
+ - Sentinel values or cancellation for shutdown
160
+ - Error isolation per item
161
+ ```
162
+
163
+ </example>
164
+ <example id="4" title="Integrating Blocking Libraries">
165
+
166
+ **Example 4 — Integrating Blocking Libraries:**
167
+ ```
168
+ User: "Use a blocking database library in my async application"
169
+
170
+ Apply: Ch 2 (ThreadPoolExecutor, run_in_executor),
171
+ Ch 3 (event loop executor integration)
172
+
173
+ Generate:
174
+ - run_in_executor() wrapper for blocking calls
175
+ - ThreadPoolExecutor with bounded workers
176
+ - Proper executor shutdown on exit
177
+ - Async-friendly interface over blocking library
178
+ ```
179
+
180
+ </example>
181
+ </examples>
182
+
183
+ ---
184
+
185
+ ## Mode 2: Async Review
186
+
187
+ When reviewing async Python code, read `references/review-checklist.md` for the full checklist.
188
+
189
+ ### Review Process
190
+
191
+ 1. **Concurrency scan** — Check Ch 1-2: Is asyncio the right choice? Are threads mixed correctly?
192
+ 2. **Coroutine scan** — Check Ch 3: Proper async def/await usage, task creation, gather/wait patterns
193
+ 3. **Resource scan** — Check Ch 3-4: Async context managers, session management, connection pooling
194
+ 4. **Shutdown scan** — Check Ch 3: Signal handling, task cancellation, executor cleanup, graceful shutdown
195
+ 5. **Blocking scan** — Check Ch 2-3: No blocking calls on event loop, proper executor usage
196
+ 6. **Library scan** — Check Ch 4: Correct async library usage (aiohttp, aiofiles, asyncpg)
197
+ 7. **Error scan** — Check Ch 3: CancelledError handling, exception propagation, timeout usage
198
+
199
+ ### Praise Patterns in Good Code
200
+
201
+ <strengths_to_praise>
202
+ When code already follows best practices, explicitly call out what it does right — do not invent issues to appear thorough:
203
+
204
+ - **`asyncio.create_task()` over `ensure_future()`** — Praise when the code uses `create_task()` instead of the older `ensure_future()` (Ch 3: prefer create_task)
205
+ - **`asyncio.Semaphore`** — Praise when used to cap concurrency and prevent thundering-herd (Ch 3: Semaphore for concurrency control)
206
+ - **`asyncio.gather(*tasks, return_exceptions=True)`** — Praise when `return_exceptions=True` prevents one failure from cancelling all in-flight tasks (Ch 3: use return_exceptions=True)
207
+ - **Async context managers** — Praise `async with aiohttp.ClientSession(...)` ensuring sessions are always closed (Ch 3-4: async with for resource cleanup)
208
+ - **`resp.raise_for_status()` + `except aiohttp.ClientError`** — Praise when each request validates the status and catches per-URL errors gracefully without crashing the whole batch (Ch 3: error handling per task)
209
+ - **`asyncio.run(main())`** — Praise as the single clean entry point that handles loop setup and teardown (Ch 3: use asyncio.run, avoid manual loop management)
210
+ </strengths_to_praise>
211
+
212
+ ### Calibrating Severity
213
+
214
+ When code is generally well-written, calibrate suggestions accordingly:
215
+
216
+ - **Real bugs** (e.g., blocking calls in async functions, `run_until_complete` inside a running loop) → flag as critical issues
217
+ - **Missing best practices** (e.g., no timeouts, no `return_exceptions`) → flag as moderate improvements
218
+ - **Optional enhancements** (e.g., adding structured logging, TaskGroups for Python 3.11+) → frame explicitly as "minor optional improvement" or "nice-to-have"
219
+ - **Do NOT** escalate optional improvements into "silent bugs" or "production data loss" to appear more thorough
220
+
221
+ ### Review Output Format
222
+
223
+ Structure your review as:
224
+
225
+ ```
226
+ ## Summary
227
+ One paragraph: overall async code quality, pattern adherence, main concerns.
228
+ If the code is well-structured, say so explicitly here.
229
+
230
+ ## What This Code Does Well
231
+ For each strength (explicitly praise correct patterns):
232
+ - **Pattern**: what the code does right
233
+ - **Why**: which chapter/concept it satisfies and why it matters
234
+
235
+ ## Issues
236
+ ONLY for genuine runtime bugs (blocking calls in async functions, run_until_complete
237
+ inside a running loop, fire-and-forget exceptions silently swallowed, etc.).
238
+ If no real bugs exist, omit this section entirely or write "None found."
239
+ - **Topic**: chapter and concept
240
+ - **Location**: where in the code
241
+ - **Problem**: what's wrong
242
+ - **Fix**: recommended change with code snippet
243
+
244
+ ## Optional Improvements
245
+ For ALL non-bug findings, including missing-but-not-required practices
246
+ (e.g., missing timeout, no per-item error handling, no CancelledError catch).
247
+ Explicitly frame each as minor/optional:
248
+ - **Suggestion**: what could be improved
249
+ - **Note**: explicitly state this is optional/minor, not a bug
250
+ ```
251
+
252
+ **Critical rule**: If the code is well-structured and has no runtime bugs, do NOT put non-bug observations (missing timeouts, missing per-item error handling) in the "Issues" section. Use "Optional Improvements" only. Over-reporting non-bugs as "Issues" violates the calibration principle.
253
+
254
+ ### Common Asyncio Anti-Patterns to Flag
255
+
256
+ <anti_patterns>
257
+ - **Blocking the event loop** → Ch 2-3: Use run_in_executor() for blocking calls; never call time.sleep(), requests.get(), or file open() directly
258
+ - **Using time.sleep() inside async functions** → Ch 3: `time.sleep()` blocks the entire event loop; always use `await asyncio.sleep()` instead; this is a critical bug that defeats the purpose of async. When providing a corrected version: replace every `time.sleep(N)` with `await asyncio.sleep(N)` in the fixed code so the intent of the delay is preserved non-blockingly. Do not silently drop the delay — show the `await asyncio.sleep()` replacement explicitly in the corrected example.
259
+ - **Sequential awaits when concurrent is possible** → Ch 3: Use gather() or create_task() instead of awaiting one by one. This applies at both levels: (1) processing multiple items one at a time in a loop (fetch each product one by one), AND (2) making multiple independent I/O calls sequentially within a single item (fetching both `pricing` and `inventory` for the same product with two separate await calls when both could run concurrently with `gather()`). Flag both as performance issues, not optional improvements.
260
+ - **Not handling CancelledError** → Ch 3: Always catch CancelledError for cleanup; don't suppress it silently
261
+ - **Missing timeouts** → Ch 3: Use asyncio.wait_for() or asyncio.timeout() to prevent indefinite waits
262
+ - **Manual loop management** → Ch 3: Use asyncio.run() instead of get_event_loop()/run_until_complete()
263
+ - **Calling run_until_complete() from within async code** → Ch 3: `loop.run_until_complete()` raises RuntimeError when the event loop is already running; inside async code, use `await` or `asyncio.create_task()`; this is a critical runtime error
264
+ - **Using asyncio.get_event_loop() inside async code** → Ch 3: `asyncio.get_event_loop()` is deprecated for use inside coroutines; use `asyncio.get_running_loop()` to access the currently running loop
265
+ - **Not using async context managers** → Ch 3-4: Use async with for ClientSession, database connections, file handles
266
+ - **Fire-and-forget tasks** → Ch 3: Keep references to created tasks; unhandled task exceptions are silent
267
+ - **No graceful shutdown** → Ch 3: Handle signals, cancel pending tasks, await cleanup before loop.close()
268
+ - **Using threads where asyncio suffices** → Ch 2: For I/O-bound work, prefer asyncio over threading
269
+ - **Ignoring return_exceptions in gather** → Ch 3: Use return_exceptions=True to prevent one failure from cancelling all
270
+ - **Creating too many concurrent tasks** → Ch 3: Use Semaphore to limit concurrency for resource-constrained operations
271
+ - **Using time.sleep() for rate limiting in async code** → Ch 3: Use `asyncio.Semaphore` instead; `time.sleep()` is blocking; Semaphore caps concurrency cooperatively without freezing the loop
272
+ - **Not closing sessions/connections** → Ch 4: Always close aiohttp.ClientSession, database pools on shutdown
273
+ - **Mixing sync and async incorrectly** → Ch 2-3: Don't call asyncio.run() from within async code; use create_task()
274
+ - **Using ensure_future instead of create_task** → Ch 3: Prefer create_task() for coroutines; ensure_future() is for futures
275
+ </anti_patterns>
276
+
277
+ ---
278
+
279
+ ## General Guidelines
280
+
281
+ <guidelines>
282
+ - **asyncio for I/O, multiprocessing for CPU** — Match the concurrency model to the workload type
283
+ - **Start simple with asyncio.run()** — Add complexity (signals, executors, task groups) only as needed
284
+ - **Use structured concurrency** — TaskGroups (3.11+) or gather() to manage task lifetimes properly
285
+ - **Test with pytest-asyncio** — Use @pytest.mark.asyncio and async fixtures for testing
286
+ - **Profile before optimizing** — Use asyncio debug mode and logging to find actual bottlenecks
287
+ - **Keep coroutines focused** — Small, composable coroutines are easier to test and reason about
288
+ - For deeper practice details, read `references/api_reference.md` before building async code.
289
+ - For review checklists, read `references/review-checklist.md` before reviewing async code.
290
+ </guidelines>
@@ -0,0 +1,43 @@
1
+ {
2
+ "evals": [
3
+ {
4
+ "id": "eval-01-blocking-io-inside-async",
5
+ "prompt": "Review this async Python code:\n\n```python\nimport asyncio\nimport requests\nimport time\n\nPRICING_API = 'https://api.pricing.internal/products'\nINVENTORY_API = 'https://api.inventory.internal/stock'\n\nasync def fetch_product_data(product_ids: list[str]) -> list[dict]:\n results = []\n for pid in product_ids:\n price_resp = requests.get(f'{PRICING_API}/{pid}')\n price = price_resp.json()['price']\n\n stock_resp = requests.get(f'{INVENTORY_API}/{pid}')\n stock = stock_resp.json()['quantity']\n\n results.append({'id': pid, 'price': price, 'stock': stock})\n time.sleep(0.1) # be polite to the API\n\n return results\n\nasync def main():\n ids = ['A1', 'B2', 'C3', 'D4', 'E5', 'F6', 'G7', 'H8']\n data = await fetch_product_data(ids)\n print(f'Fetched {len(data)} products')\n\nasyncio.run(main())\n```",
6
+ "expectations": [
7
+ "Flags `requests.get()` inside an `async def` function as a critical blocking call that stalls the entire event loop while waiting for the HTTP response (Ch 2-3: never block the event loop)",
8
+ "Flags `time.sleep(0.1)` inside an async function as a blocking sleep that also freezes the event loop; recommends `await asyncio.sleep(0.1)` (Ch 3: use asyncio.sleep, not time.sleep)",
9
+ "Flags that the two API calls per product (`PRICING_API` and `INVENTORY_API`) are made sequentially inside the loop; recommends using `asyncio.gather` to fetch both concurrently per product (Ch 3: use gather for fan-out concurrency)",
10
+ "Flags that all products are fetched sequentially in a for loop; recommends fetching all products concurrently with `asyncio.gather` or `create_task` (Ch 3: create_task for concurrency)",
11
+ "Recommends replacing `requests` with `aiohttp.ClientSession` for non-blocking HTTP calls (Ch 4: use aiohttp over requests in async code)",
12
+ "Recommends using a `Semaphore` to limit concurrent requests instead of `time.sleep` for rate limiting (Ch 3: Semaphore for concurrency control)",
13
+ "Provides a corrected version using aiohttp, asyncio.gather, asyncio.sleep, and a Semaphore"
14
+ ]
15
+ },
16
+ {
17
+ "id": "eval-02-ensure-future-and-fire-and-forget",
18
+ "prompt": "Review this async Python code:\n\n```python\nimport asyncio\n\nasync def save_to_database(record: dict) -> None:\n await asyncio.sleep(0.05) # simulate DB write\n print(f\"Saved {record['id']}\")\n\nasync def send_notification(user_id: str) -> None:\n await asyncio.sleep(0.1) # simulate email send\n print(f\"Notified {user_id}\")\n\nasync def process_event(event: dict) -> None:\n await save_to_database(event)\n\n # Fire and forget the notification\n asyncio.ensure_future(send_notification(event['user_id']))\n\n print(f\"Processed event {event['id']}\")\n\nasync def main():\n loop = asyncio.get_event_loop()\n\n events = [{'id': f'e{i}', 'user_id': f'u{i}'} for i in range(10)]\n for event in events:\n loop.run_until_complete(process_event(event))\n\nasyncio.run(main())\n```",
19
+ "expectations": [
20
+ "Flags `asyncio.ensure_future()` as the deprecated/less preferred API for scheduling coroutines; recommends `asyncio.create_task()` which is more explicit and requires an active event loop (Ch 3: prefer create_task over ensure_future)",
21
+ "Flags fire-and-forget usage of `ensure_future` without storing the task reference: if `send_notification` raises an exception, it is silently discarded; recommends keeping a reference and handling exceptions (Ch 3: keep references to created tasks; unhandled task exceptions are silent)",
22
+ "Flags `loop.run_until_complete(process_event(event))` called inside an already-running async context (`main` is a coroutine): `run_until_complete` cannot be called from within a running loop; this will raise RuntimeError (Ch 3: do not call asyncio.run or run_until_complete from within async code)",
23
+ "Flags `asyncio.get_event_loop()` as deprecated for getting the running loop inside async code; recommends `asyncio.get_running_loop()` or eliminating direct loop access (Ch 3: use asyncio.run as the single entry point, avoid manual loop management)",
24
+ "Flags the sequential `for event in events` loop using run_until_complete; recommends processing all events concurrently with `asyncio.gather` (Ch 3: use gather for concurrency)",
25
+ "Provides a corrected version using create_task with proper task tracking, asyncio.gather for concurrency, and exception handling on fire-and-forget tasks"
26
+ ]
27
+ },
28
+ {
29
+ "id": "eval-03-clean-async-gather-task-management",
30
+ "prompt": "Review this async Python code:\n\n```python\nimport asyncio\nimport logging\nfrom contextlib import asynccontextmanager\nfrom typing import AsyncIterator\nimport aiohttp\n\nlogger = logging.getLogger(__name__)\n\nMAX_CONCURRENT = 5\n\n@asynccontextmanager\nasync def http_session() -> AsyncIterator[aiohttp.ClientSession]:\n async with aiohttp.ClientSession(\n timeout=aiohttp.ClientTimeout(total=10)\n ) as session:\n yield session\n\n\nasync def fetch_one(session: aiohttp.ClientSession, url: str, semaphore: asyncio.Semaphore) -> dict:\n async with semaphore:\n try:\n async with session.get(url) as resp:\n resp.raise_for_status()\n return {'url': url, 'data': await resp.json()}\n except aiohttp.ClientError as exc:\n logger.warning('Failed to fetch %s: %s', url, exc)\n return {'url': url, 'data': None, 'error': str(exc)}\n\n\nasync def fetch_all(urls: list[str]) -> list[dict]:\n semaphore = asyncio.Semaphore(MAX_CONCURRENT)\n async with http_session() as session:\n tasks = [\n asyncio.create_task(fetch_one(session, url, semaphore))\n for url in urls\n ]\n results = await asyncio.gather(*tasks, return_exceptions=True)\n return [r for r in results if not isinstance(r, BaseException)]\n\n\nasync def main() -> None:\n urls = [f'https://api.example.com/item/{i}' for i in range(20)]\n items = await fetch_all(urls)\n logger.info('Fetched %d items successfully', len(items))\n\n\nif __name__ == '__main__':\n asyncio.run(main())\n```",
31
+ "expectations": [
32
+ "Recognizes this is well-structured async code and says so explicitly",
33
+ "Praises the async context manager `http_session` using `async with aiohttp.ClientSession` ensuring the session is always closed (Ch 3-4: use async context managers for resource cleanup)",
34
+ "Praises `asyncio.Semaphore(MAX_CONCURRENT)` to cap concurrent requests, preventing thundering-herd against the remote API (Ch 3: use Semaphore to limit concurrency)",
35
+ "Praises `asyncio.create_task()` over `ensure_future()` for scheduling coroutines (Ch 3: prefer create_task)",
36
+ "Praises `asyncio.gather(*tasks, return_exceptions=True)` which prevents one failure from cancelling all other in-flight requests (Ch 3: use return_exceptions=True in gather)",
37
+ "Praises `resp.raise_for_status()` and catching `aiohttp.ClientError` with graceful per-URL error handling that does not crash the whole batch (Ch 3: error handling per task)",
38
+ "Praises `asyncio.run(main())` as the single clean entry point (Ch 3: use asyncio.run, avoid manual loop management)",
39
+ "Does NOT manufacture issues to appear thorough; any suggestions are explicitly framed as minor optional improvements"
40
+ ]
41
+ }
42
+ ]
43
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "pass_rate": 0.952,
3
+ "passed": 20,
4
+ "total": 21,
5
+ "baseline_pass_rate": 0.571,
6
+ "baseline_passed": 12,
7
+ "baseline_total": 21,
8
+ "delta": 0.381,
9
+ "model": "default",
10
+ "evals_run": 3,
11
+ "date": "2026-03-29",
12
+ "non_standard_provider": true
13
+ }
@@ -0,0 +1,68 @@
1
+ # After
2
+
3
+ Proper async code using `aiohttp` for non-blocking HTTP, `asyncio.gather` to fetch all three attributes of each product concurrently, and a semaphore to cap simultaneous connections.
4
+
5
+ ```python
6
+ import asyncio
7
+ import aiohttp
8
+
9
+ PRODUCT_API = "https://api.internal.com/products"
10
+ INVENTORY_API = "https://api.internal.com/inventory"
11
+ PRICING_API = "https://api.internal.com/pricing"
12
+
13
+ MAX_CONCURRENT_REQUESTS = 10 # prevent overwhelming the upstream APIs
14
+
15
+
16
+ async def fetch_json(session: aiohttp.ClientSession, url: str) -> dict:
17
+ """Fetch a single JSON endpoint, raising on non-2xx responses."""
18
+ async with session.get(url) as response:
19
+ response.raise_for_status()
20
+ return await response.json()
21
+
22
+
23
+ async def build_product_entry(
24
+ session: aiohttp.ClientSession,
25
+ semaphore: asyncio.Semaphore,
26
+ product_id: str,
27
+ ) -> dict:
28
+ """Fetch product, inventory, and pricing concurrently for one product ID."""
29
+ async with semaphore:
30
+ product, inventory, pricing = await asyncio.gather(
31
+ fetch_json(session, f"{PRODUCT_API}/{product_id}"),
32
+ fetch_json(session, f"{INVENTORY_API}/{product_id}"),
33
+ fetch_json(session, f"{PRICING_API}/{product_id}"),
34
+ return_exceptions=False,
35
+ )
36
+ return {
37
+ "id": product_id,
38
+ "name": product["name"],
39
+ "stock": inventory["quantity"],
40
+ "price": pricing["amount"],
41
+ }
42
+
43
+
44
+ async def build_product_catalog(product_ids: list[str]) -> list[dict]:
45
+ """Build the full catalog by fetching all products concurrently."""
46
+ semaphore = asyncio.Semaphore(MAX_CONCURRENT_REQUESTS)
47
+
48
+ async with aiohttp.ClientSession(
49
+ timeout=aiohttp.ClientTimeout(total=30)
50
+ ) as session:
51
+ tasks = [
52
+ build_product_entry(session, semaphore, pid)
53
+ for pid in product_ids
54
+ ]
55
+ return await asyncio.gather(*tasks, return_exceptions=True)
56
+
57
+
58
+ if __name__ == "__main__":
59
+ catalog = asyncio.run(build_product_catalog(["sku-001", "sku-002", "sku-003"]))
60
+ ```
61
+
62
+ Key improvements:
63
+ - `aiohttp.ClientSession` replaces `requests.get` — HTTP calls are non-blocking and never stall the event loop (Ch 4: aiohttp; Ch 2-3: Never block the event loop)
64
+ - `asyncio.gather` inside `build_product_entry` fetches product, inventory, and pricing for one SKU concurrently — three sequential blocking calls become one concurrent async fan-out (Ch 3: gather for fan-out)
65
+ - The outer `asyncio.gather(*tasks)` processes all product IDs concurrently instead of sequentially in a for loop (Ch 3: create_task / gather)
66
+ - `asyncio.Semaphore(10)` limits the number of simultaneous in-flight requests, preventing connection pool exhaustion on the upstream APIs (Ch 3: Semaphore for concurrency control)
67
+ - `aiohttp.ClientTimeout(total=30)` ensures no request hangs indefinitely (Ch 3: use timeouts everywhere)
68
+ - A single `aiohttp.ClientSession` is reused across all requests for connection pooling — the `async with` context manager ensures it is closed on exit (Ch 4: Use async with for resources)
@@ -0,0 +1,39 @@
1
+ # Before
2
+
3
+ An `async def` function that calls the blocking `requests.get()` synchronously, stalling the entire event loop for the duration of each HTTP call.
4
+
5
+ ```python
6
+ import asyncio
7
+ import requests # blocking library — not async-safe
8
+
9
+ PRODUCT_API = "https://api.internal.com/products"
10
+ INVENTORY_API = "https://api.internal.com/inventory"
11
+ PRICING_API = "https://api.internal.com/pricing"
12
+
13
+ async def build_product_catalog(product_ids: list[str]) -> list[dict]:
14
+ catalog = []
15
+
16
+ for product_id in product_ids:
17
+ # Blocks the event loop for every request — defeats asyncio entirely
18
+ product_resp = requests.get(f"{PRODUCT_API}/{product_id}")
19
+ product = product_resp.json()
20
+
21
+ # Called sequentially AND blocking — no concurrency at all
22
+ inv_resp = requests.get(f"{INVENTORY_API}/{product_id}")
23
+ inventory = inv_resp.json()
24
+
25
+ price_resp = requests.get(f"{PRICING_API}/{product_id}")
26
+ pricing = price_resp.json()
27
+
28
+ catalog.append({
29
+ "id": product_id,
30
+ "name": product["name"],
31
+ "stock": inventory["quantity"],
32
+ "price": pricing["amount"],
33
+ })
34
+
35
+ return catalog
36
+
37
+
38
+ asyncio.run(build_product_catalog(["sku-001", "sku-002", "sku-003"]))
39
+ ```