superacli 1.1.4 → 1.1.6

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 (961) hide show
  1. package/__tests__/adapter-schema.test.js +2 -0
  2. package/__tests__/config.test.js +62 -1
  3. package/__tests__/help-json.test.js +2 -0
  4. package/__tests__/mcp-adapter.test.js +14 -4
  5. package/__tests__/mcp-local.test.js +159 -0
  6. package/__tests__/mcp-stdio-jsonrpc.test.js +105 -0
  7. package/__tests__/monty-plugin.test.js +121 -0
  8. package/__tests__/plugin-browser-use-uninstall.test.js +23 -0
  9. package/__tests__/plugin-browser-use.test.js +77 -0
  10. package/__tests__/plugins-command.test.js +92 -1
  11. package/__tests__/plugins-learn.test.js +62 -0
  12. package/__tests__/plugins-registry.test.js +3 -1
  13. package/__tests__/resend-plugin.test.js +122 -0
  14. package/__tests__/skills.test.js +4 -0
  15. package/cli/adapter-schema.js +3 -2
  16. package/cli/adapters/mcp.js +22 -3
  17. package/cli/adapters/process.js +34 -7
  18. package/cli/config.js +27 -1
  19. package/cli/help-json.js +2 -2
  20. package/cli/mcp-diagnostics.js +152 -0
  21. package/cli/mcp-discovery.js +221 -0
  22. package/cli/mcp-local.js +267 -25
  23. package/cli/mcp-stdio-jsonrpc.js +246 -0
  24. package/cli/plugin-install-guidance.js +25 -0
  25. package/cli/plugins-command.js +86 -3
  26. package/cli/plugins-learn.js +177 -0
  27. package/cli/plugins-manager.js +3 -0
  28. package/cli/plugins-registry.js +2 -1
  29. package/cli/skills-mcp.js +102 -0
  30. package/cli/skills.js +6 -40
  31. package/cli/supercli.js +7 -2
  32. package/docs/initial/mcp-local-mode.md +35 -0
  33. package/docs/mcp-cheatsheet.md +324 -0
  34. package/docs/plugins.md +7 -0
  35. package/package.json +1 -1
  36. package/plugins/browser-use/plugin.json +23 -0
  37. package/plugins/browser-use/scripts/post-install.js +146 -0
  38. package/plugins/browser-use/scripts/post-uninstall.js +28 -0
  39. package/plugins/browser-use/skills/quickstart/SKILL.md +47 -0
  40. package/plugins/monty/README.md +49 -0
  41. package/plugins/monty/plugin.json +69 -0
  42. package/plugins/monty/scripts/post-install.js +73 -0
  43. package/plugins/monty/scripts/post-uninstall.js +23 -0
  44. package/plugins/monty/scripts/run-python.js +140 -0
  45. package/plugins/monty/scripts/setup-monty.js +27 -0
  46. package/plugins/plugins.json +29 -0
  47. package/plugins/resend/plugin.json +371 -0
  48. package/plugins/resend/scripts/post-install.js +59 -0
  49. package/plugins/resend/scripts/post-uninstall.js +23 -0
  50. package/plugins/resend/scripts/setup-resend.js +27 -0
  51. package/plugins/resend/skills/quickstart/SKILL.md +80 -0
  52. package/ref-monty/.cargo/config.toml +3 -0
  53. package/ref-monty/.claude/settings.json +60 -0
  54. package/ref-monty/.claude/skills/fastmod/SKILL.md +22 -0
  55. package/ref-monty/.claude/skills/python-playground/SKILL.md +47 -0
  56. package/ref-monty/.codecov.yml +12 -0
  57. package/ref-monty/.github/actions/build-pgo-wheel/action.yml +72 -0
  58. package/ref-monty/.github/workflows/ci.yml +776 -0
  59. package/ref-monty/.github/workflows/codspeed.yml +45 -0
  60. package/ref-monty/.github/workflows/init-npm-packages.yml +82 -0
  61. package/ref-monty/.pre-commit-config.yaml +47 -0
  62. package/ref-monty/.python-version +1 -0
  63. package/ref-monty/.rustfmt.toml +4 -0
  64. package/ref-monty/.zed/settings.json +11 -0
  65. package/ref-monty/CLAUDE.md +535 -0
  66. package/ref-monty/Cargo.lock +3798 -0
  67. package/ref-monty/Cargo.toml +87 -0
  68. package/ref-monty/LICENSE +21 -0
  69. package/ref-monty/Makefile +216 -0
  70. package/ref-monty/README.md +430 -0
  71. package/ref-monty/RELEASING.md +47 -0
  72. package/ref-monty/crates/fuzz/Cargo.toml +30 -0
  73. package/ref-monty/crates/fuzz/fuzz_targets/string_input_panic.rs +37 -0
  74. package/ref-monty/crates/fuzz/fuzz_targets/tokens_input_panic.rs +552 -0
  75. package/ref-monty/crates/monty/Cargo.toml +68 -0
  76. package/ref-monty/crates/monty/benches/main.rs +247 -0
  77. package/ref-monty/crates/monty/build.rs +10 -0
  78. package/ref-monty/crates/monty/src/args.rs +733 -0
  79. package/ref-monty/crates/monty/src/asyncio.rs +179 -0
  80. package/ref-monty/crates/monty/src/builtins/abs.rs +55 -0
  81. package/ref-monty/crates/monty/src/builtins/all.rs +30 -0
  82. package/ref-monty/crates/monty/src/builtins/any.rs +30 -0
  83. package/ref-monty/crates/monty/src/builtins/bin.rs +59 -0
  84. package/ref-monty/crates/monty/src/builtins/chr.rs +46 -0
  85. package/ref-monty/crates/monty/src/builtins/divmod.rs +164 -0
  86. package/ref-monty/crates/monty/src/builtins/enumerate.rs +52 -0
  87. package/ref-monty/crates/monty/src/builtins/filter.rs +67 -0
  88. package/ref-monty/crates/monty/src/builtins/getattr.rs +65 -0
  89. package/ref-monty/crates/monty/src/builtins/hash.rs +28 -0
  90. package/ref-monty/crates/monty/src/builtins/hex.rs +58 -0
  91. package/ref-monty/crates/monty/src/builtins/id.rs +24 -0
  92. package/ref-monty/crates/monty/src/builtins/isinstance.rs +68 -0
  93. package/ref-monty/crates/monty/src/builtins/len.rs +25 -0
  94. package/ref-monty/crates/monty/src/builtins/map.rs +98 -0
  95. package/ref-monty/crates/monty/src/builtins/min_max.rs +113 -0
  96. package/ref-monty/crates/monty/src/builtins/mod.rs +246 -0
  97. package/ref-monty/crates/monty/src/builtins/next.rs +21 -0
  98. package/ref-monty/crates/monty/src/builtins/oct.rs +59 -0
  99. package/ref-monty/crates/monty/src/builtins/ord.rs +67 -0
  100. package/ref-monty/crates/monty/src/builtins/pow.rs +365 -0
  101. package/ref-monty/crates/monty/src/builtins/print.rs +141 -0
  102. package/ref-monty/crates/monty/src/builtins/repr.rs +16 -0
  103. package/ref-monty/crates/monty/src/builtins/reversed.rs +28 -0
  104. package/ref-monty/crates/monty/src/builtins/round.rs +174 -0
  105. package/ref-monty/crates/monty/src/builtins/sorted.rs +151 -0
  106. package/ref-monty/crates/monty/src/builtins/sum.rs +66 -0
  107. package/ref-monty/crates/monty/src/builtins/type_.rs +16 -0
  108. package/ref-monty/crates/monty/src/builtins/zip.rs +77 -0
  109. package/ref-monty/crates/monty/src/bytecode/builder.rs +699 -0
  110. package/ref-monty/crates/monty/src/bytecode/code.rs +310 -0
  111. package/ref-monty/crates/monty/src/bytecode/compiler.rs +3206 -0
  112. package/ref-monty/crates/monty/src/bytecode/mod.rs +24 -0
  113. package/ref-monty/crates/monty/src/bytecode/op.rs +617 -0
  114. package/ref-monty/crates/monty/src/bytecode/vm/async_exec.rs +1058 -0
  115. package/ref-monty/crates/monty/src/bytecode/vm/attr.rs +63 -0
  116. package/ref-monty/crates/monty/src/bytecode/vm/binary.rs +487 -0
  117. package/ref-monty/crates/monty/src/bytecode/vm/call.rs +767 -0
  118. package/ref-monty/crates/monty/src/bytecode/vm/collections.rs +741 -0
  119. package/ref-monty/crates/monty/src/bytecode/vm/compare.rs +147 -0
  120. package/ref-monty/crates/monty/src/bytecode/vm/exceptions.rs +297 -0
  121. package/ref-monty/crates/monty/src/bytecode/vm/format.rs +132 -0
  122. package/ref-monty/crates/monty/src/bytecode/vm/mod.rs +1958 -0
  123. package/ref-monty/crates/monty/src/bytecode/vm/scheduler.rs +620 -0
  124. package/ref-monty/crates/monty/src/exception_private.rs +1513 -0
  125. package/ref-monty/crates/monty/src/exception_public.rs +346 -0
  126. package/ref-monty/crates/monty/src/expressions.rs +694 -0
  127. package/ref-monty/crates/monty/src/fstring.rs +854 -0
  128. package/ref-monty/crates/monty/src/function.rs +119 -0
  129. package/ref-monty/crates/monty/src/heap.rs +1073 -0
  130. package/ref-monty/crates/monty/src/heap_data.rs +985 -0
  131. package/ref-monty/crates/monty/src/heap_traits.rs +312 -0
  132. package/ref-monty/crates/monty/src/intern.rs +837 -0
  133. package/ref-monty/crates/monty/src/io.rs +106 -0
  134. package/ref-monty/crates/monty/src/lib.rs +52 -0
  135. package/ref-monty/crates/monty/src/modules/asyncio.rs +144 -0
  136. package/ref-monty/crates/monty/src/modules/math.rs +1453 -0
  137. package/ref-monty/crates/monty/src/modules/mod.rs +120 -0
  138. package/ref-monty/crates/monty/src/modules/os.rs +116 -0
  139. package/ref-monty/crates/monty/src/modules/pathlib.rs +33 -0
  140. package/ref-monty/crates/monty/src/modules/re.rs +606 -0
  141. package/ref-monty/crates/monty/src/modules/sys.rs +60 -0
  142. package/ref-monty/crates/monty/src/modules/typing.rs +70 -0
  143. package/ref-monty/crates/monty/src/namespace.rs +21 -0
  144. package/ref-monty/crates/monty/src/object.rs +1040 -0
  145. package/ref-monty/crates/monty/src/os.rs +215 -0
  146. package/ref-monty/crates/monty/src/parse.rs +1730 -0
  147. package/ref-monty/crates/monty/src/prepare.rs +3015 -0
  148. package/ref-monty/crates/monty/src/repl.rs +1109 -0
  149. package/ref-monty/crates/monty/src/resource.rs +559 -0
  150. package/ref-monty/crates/monty/src/run.rs +457 -0
  151. package/ref-monty/crates/monty/src/run_progress.rs +821 -0
  152. package/ref-monty/crates/monty/src/signature.rs +651 -0
  153. package/ref-monty/crates/monty/src/sorting.rs +100 -0
  154. package/ref-monty/crates/monty/src/types/bytes.rs +2356 -0
  155. package/ref-monty/crates/monty/src/types/dataclass.rs +345 -0
  156. package/ref-monty/crates/monty/src/types/dict.rs +879 -0
  157. package/ref-monty/crates/monty/src/types/dict_view.rs +619 -0
  158. package/ref-monty/crates/monty/src/types/iter.rs +799 -0
  159. package/ref-monty/crates/monty/src/types/list.rs +929 -0
  160. package/ref-monty/crates/monty/src/types/long_int.rs +211 -0
  161. package/ref-monty/crates/monty/src/types/mod.rs +48 -0
  162. package/ref-monty/crates/monty/src/types/module.rs +146 -0
  163. package/ref-monty/crates/monty/src/types/namedtuple.rs +261 -0
  164. package/ref-monty/crates/monty/src/types/path.rs +596 -0
  165. package/ref-monty/crates/monty/src/types/property.rs +35 -0
  166. package/ref-monty/crates/monty/src/types/py_trait.rs +322 -0
  167. package/ref-monty/crates/monty/src/types/range.rs +285 -0
  168. package/ref-monty/crates/monty/src/types/re_match.rs +522 -0
  169. package/ref-monty/crates/monty/src/types/re_pattern.rs +726 -0
  170. package/ref-monty/crates/monty/src/types/set.rs +1373 -0
  171. package/ref-monty/crates/monty/src/types/slice.rs +257 -0
  172. package/ref-monty/crates/monty/src/types/str.rs +2051 -0
  173. package/ref-monty/crates/monty/src/types/tuple.rs +376 -0
  174. package/ref-monty/crates/monty/src/types/type.rs +407 -0
  175. package/ref-monty/crates/monty/src/value.rs +2558 -0
  176. package/ref-monty/crates/monty/test_cases/args__dict_get_no_args.py +3 -0
  177. package/ref-monty/crates/monty/test_cases/args__dict_get_too_many.py +3 -0
  178. package/ref-monty/crates/monty/test_cases/args__dict_items_with_args.py +3 -0
  179. package/ref-monty/crates/monty/test_cases/args__dict_keys_with_args.py +3 -0
  180. package/ref-monty/crates/monty/test_cases/args__dict_pop_no_args.py +3 -0
  181. package/ref-monty/crates/monty/test_cases/args__dict_pop_too_many.py +3 -0
  182. package/ref-monty/crates/monty/test_cases/args__dict_values_with_args.py +3 -0
  183. package/ref-monty/crates/monty/test_cases/args__id_too_many.py +2 -0
  184. package/ref-monty/crates/monty/test_cases/args__len_no_args.py +2 -0
  185. package/ref-monty/crates/monty/test_cases/args__len_too_many.py +2 -0
  186. package/ref-monty/crates/monty/test_cases/args__len_type_error_int.py +9 -0
  187. package/ref-monty/crates/monty/test_cases/args__len_type_error_none.py +9 -0
  188. package/ref-monty/crates/monty/test_cases/args__list_append_no_args.py +3 -0
  189. package/ref-monty/crates/monty/test_cases/args__list_append_too_many.py +3 -0
  190. package/ref-monty/crates/monty/test_cases/args__list_insert_too_few.py +3 -0
  191. package/ref-monty/crates/monty/test_cases/args__list_insert_too_many.py +3 -0
  192. package/ref-monty/crates/monty/test_cases/args__repr_no_args.py +2 -0
  193. package/ref-monty/crates/monty/test_cases/arith__div_zero_float.py +2 -0
  194. package/ref-monty/crates/monty/test_cases/arith__div_zero_int.py +2 -0
  195. package/ref-monty/crates/monty/test_cases/arith__floordiv_zero_float.py +2 -0
  196. package/ref-monty/crates/monty/test_cases/arith__floordiv_zero_int.py +2 -0
  197. package/ref-monty/crates/monty/test_cases/arith__pow_zero_neg.py +2 -0
  198. package/ref-monty/crates/monty/test_cases/arith__pow_zero_neg_builtin.py +9 -0
  199. package/ref-monty/crates/monty/test_cases/assert__expr_fail.py +2 -0
  200. package/ref-monty/crates/monty/test_cases/assert__fail.py +2 -0
  201. package/ref-monty/crates/monty/test_cases/assert__fail_msg.py +2 -0
  202. package/ref-monty/crates/monty/test_cases/assert__fn_fail.py +3 -0
  203. package/ref-monty/crates/monty/test_cases/assert__ops.py +11 -0
  204. package/ref-monty/crates/monty/test_cases/async__asyncio_run.py +47 -0
  205. package/ref-monty/crates/monty/test_cases/async__basic.py +10 -0
  206. package/ref-monty/crates/monty/test_cases/async__closure.py +14 -0
  207. package/ref-monty/crates/monty/test_cases/async__double_await_coroutine.py +16 -0
  208. package/ref-monty/crates/monty/test_cases/async__exception.py +10 -0
  209. package/ref-monty/crates/monty/test_cases/async__ext_call.py +73 -0
  210. package/ref-monty/crates/monty/test_cases/async__gather_all.py +85 -0
  211. package/ref-monty/crates/monty/test_cases/async__nested_await.py +15 -0
  212. package/ref-monty/crates/monty/test_cases/async__nested_gather_ext.py +37 -0
  213. package/ref-monty/crates/monty/test_cases/async__not_awaitable.py +10 -0
  214. package/ref-monty/crates/monty/test_cases/async__not_imported.py +14 -0
  215. package/ref-monty/crates/monty/test_cases/async__recursion_depth_isolation.py +27 -0
  216. package/ref-monty/crates/monty/test_cases/async__return_types.py +31 -0
  217. package/ref-monty/crates/monty/test_cases/async__sequential.py +16 -0
  218. package/ref-monty/crates/monty/test_cases/async__traceback.py +19 -0
  219. package/ref-monty/crates/monty/test_cases/async__with_args.py +14 -0
  220. package/ref-monty/crates/monty/test_cases/attr__get_int_error.py +9 -0
  221. package/ref-monty/crates/monty/test_cases/attr__get_list_error.py +9 -0
  222. package/ref-monty/crates/monty/test_cases/attr__set_frozen_nonfield.py +12 -0
  223. package/ref-monty/crates/monty/test_cases/attr__set_int_error.py +10 -0
  224. package/ref-monty/crates/monty/test_cases/attr__set_list_error.py +10 -0
  225. package/ref-monty/crates/monty/test_cases/bench__kitchen_sink.py +68 -0
  226. package/ref-monty/crates/monty/test_cases/bool__ops.py +20 -0
  227. package/ref-monty/crates/monty/test_cases/builtin__add_type_error.py +2 -0
  228. package/ref-monty/crates/monty/test_cases/builtin__filter.py +62 -0
  229. package/ref-monty/crates/monty/test_cases/builtin__filter_not_iterable.py +11 -0
  230. package/ref-monty/crates/monty/test_cases/builtin__getattr.py +84 -0
  231. package/ref-monty/crates/monty/test_cases/builtin__iter_funcs.py +42 -0
  232. package/ref-monty/crates/monty/test_cases/builtin__iter_next.py +66 -0
  233. package/ref-monty/crates/monty/test_cases/builtin__map.py +74 -0
  234. package/ref-monty/crates/monty/test_cases/builtin__map_not_iterable.py +11 -0
  235. package/ref-monty/crates/monty/test_cases/builtin__math_funcs.py +154 -0
  236. package/ref-monty/crates/monty/test_cases/builtin__more_iter_funcs.py +148 -0
  237. package/ref-monty/crates/monty/test_cases/builtin__next_stop_iteration.py +10 -0
  238. package/ref-monty/crates/monty/test_cases/builtin__print_invalid_kwarg.py +9 -0
  239. package/ref-monty/crates/monty/test_cases/builtin__print_kwargs.py +12 -0
  240. package/ref-monty/crates/monty/test_cases/builtin__repr.py +3 -0
  241. package/ref-monty/crates/monty/test_cases/builtin__string_funcs.py +73 -0
  242. package/ref-monty/crates/monty/test_cases/bytes__decode_invalid_utf8.py +18 -0
  243. package/ref-monty/crates/monty/test_cases/bytes__endswith_str_error.py +10 -0
  244. package/ref-monty/crates/monty/test_cases/bytes__getitem_index_error.py +10 -0
  245. package/ref-monty/crates/monty/test_cases/bytes__index_start_gt_end.py +10 -0
  246. package/ref-monty/crates/monty/test_cases/bytes__methods.py +394 -0
  247. package/ref-monty/crates/monty/test_cases/bytes__negative_count.py +9 -0
  248. package/ref-monty/crates/monty/test_cases/bytes__ops.py +90 -0
  249. package/ref-monty/crates/monty/test_cases/bytes__startswith_str_error.py +10 -0
  250. package/ref-monty/crates/monty/test_cases/call_object.py +3 -0
  251. package/ref-monty/crates/monty/test_cases/chain_comparison__all.py +79 -0
  252. package/ref-monty/crates/monty/test_cases/closure__param_shadows_outer.py +81 -0
  253. package/ref-monty/crates/monty/test_cases/closure__pep448.py +203 -0
  254. package/ref-monty/crates/monty/test_cases/closure__undefined_nonlocal.py +13 -0
  255. package/ref-monty/crates/monty/test_cases/compare__mixed_types.py +120 -0
  256. package/ref-monty/crates/monty/test_cases/comprehension__all.py +208 -0
  257. package/ref-monty/crates/monty/test_cases/comprehension__scope.py +7 -0
  258. package/ref-monty/crates/monty/test_cases/comprehension__unbound_local.py +14 -0
  259. package/ref-monty/crates/monty/test_cases/dataclass__basic.py +238 -0
  260. package/ref-monty/crates/monty/test_cases/dataclass__call_field_error.py +12 -0
  261. package/ref-monty/crates/monty/test_cases/dataclass__frozen_set_error.py +12 -0
  262. package/ref-monty/crates/monty/test_cases/dataclass__get_missing_attr_error.py +11 -0
  263. package/ref-monty/crates/monty/test_cases/dict__get_unhashable_key.py +3 -0
  264. package/ref-monty/crates/monty/test_cases/dict__literal_unhashable_key.py +2 -0
  265. package/ref-monty/crates/monty/test_cases/dict__method_pop_missing_error.py +3 -0
  266. package/ref-monty/crates/monty/test_cases/dict__methods.py +151 -0
  267. package/ref-monty/crates/monty/test_cases/dict__ops.py +133 -0
  268. package/ref-monty/crates/monty/test_cases/dict__pop_unhashable_key.py +4 -0
  269. package/ref-monty/crates/monty/test_cases/dict__popitem_empty.py +9 -0
  270. package/ref-monty/crates/monty/test_cases/dict__subscript_missing_key.py +3 -0
  271. package/ref-monty/crates/monty/test_cases/dict__unhashable_dict_key.py +2 -0
  272. package/ref-monty/crates/monty/test_cases/dict__unhashable_list_key.py +2 -0
  273. package/ref-monty/crates/monty/test_cases/dict__unpack_type_error.py +2 -0
  274. package/ref-monty/crates/monty/test_cases/dict__views.py +165 -0
  275. package/ref-monty/crates/monty/test_cases/edge__all.py +26 -0
  276. package/ref-monty/crates/monty/test_cases/edge__float_int_mod.py +2 -0
  277. package/ref-monty/crates/monty/test_cases/edge__int_float_mod.py +2 -0
  278. package/ref-monty/crates/monty/test_cases/exc__args.py +16 -0
  279. package/ref-monty/crates/monty/test_cases/exc__str.py +15 -0
  280. package/ref-monty/crates/monty/test_cases/execute_ok__all.py +54 -0
  281. package/ref-monty/crates/monty/test_cases/execute_raise__error_instance_str.py +2 -0
  282. package/ref-monty/crates/monty/test_cases/execute_raise__error_no_args.py +2 -0
  283. package/ref-monty/crates/monty/test_cases/execute_raise__error_string_arg.py +2 -0
  284. package/ref-monty/crates/monty/test_cases/execute_raise__error_string_arg_quotes.py +2 -0
  285. package/ref-monty/crates/monty/test_cases/execute_raise__error_type.py +2 -0
  286. package/ref-monty/crates/monty/test_cases/execute_raise__raise_instance_via_var.py +4 -0
  287. package/ref-monty/crates/monty/test_cases/execute_raise__raise_list.py +2 -0
  288. package/ref-monty/crates/monty/test_cases/execute_raise__raise_number.py +2 -0
  289. package/ref-monty/crates/monty/test_cases/execute_raise__raise_type_call_via_var.py +4 -0
  290. package/ref-monty/crates/monty/test_cases/execute_raise__raise_type_direct.py +3 -0
  291. package/ref-monty/crates/monty/test_cases/execute_raise__raise_type_via_var.py +4 -0
  292. package/ref-monty/crates/monty/test_cases/ext_call__arg_side_effect_bug.py +22 -0
  293. package/ref-monty/crates/monty/test_cases/ext_call__augmented.py +17 -0
  294. package/ref-monty/crates/monty/test_cases/ext_call__augmented_refcount_bug.py +7 -0
  295. package/ref-monty/crates/monty/test_cases/ext_call__bare_raise_after_resume.py +34 -0
  296. package/ref-monty/crates/monty/test_cases/ext_call__basic.py +99 -0
  297. package/ref-monty/crates/monty/test_cases/ext_call__boolean.py +37 -0
  298. package/ref-monty/crates/monty/test_cases/ext_call__boolean_side_effect_hang.py +17 -0
  299. package/ref-monty/crates/monty/test_cases/ext_call__closure_bug.py +16 -0
  300. package/ref-monty/crates/monty/test_cases/ext_call__comparison.py +26 -0
  301. package/ref-monty/crates/monty/test_cases/ext_call__deep_call_stack.py +18 -0
  302. package/ref-monty/crates/monty/test_cases/ext_call__elif.py +171 -0
  303. package/ref-monty/crates/monty/test_cases/ext_call__exc.py +4 -0
  304. package/ref-monty/crates/monty/test_cases/ext_call__exc_deep_stack.py +39 -0
  305. package/ref-monty/crates/monty/test_cases/ext_call__exc_in_function.py +17 -0
  306. package/ref-monty/crates/monty/test_cases/ext_call__exc_nested_functions.py +31 -0
  307. package/ref-monty/crates/monty/test_cases/ext_call__ext_exc.py +171 -0
  308. package/ref-monty/crates/monty/test_cases/ext_call__for.py +114 -0
  309. package/ref-monty/crates/monty/test_cases/ext_call__fstring.py +12 -0
  310. package/ref-monty/crates/monty/test_cases/ext_call__if.py +135 -0
  311. package/ref-monty/crates/monty/test_cases/ext_call__if_condition.py +37 -0
  312. package/ref-monty/crates/monty/test_cases/ext_call__in_closure.py +14 -0
  313. package/ref-monty/crates/monty/test_cases/ext_call__in_function.py +40 -0
  314. package/ref-monty/crates/monty/test_cases/ext_call__in_function_simple.py +7 -0
  315. package/ref-monty/crates/monty/test_cases/ext_call__literals.py +17 -0
  316. package/ref-monty/crates/monty/test_cases/ext_call__multi_in_func.py +32 -0
  317. package/ref-monty/crates/monty/test_cases/ext_call__name_lookup.py +69 -0
  318. package/ref-monty/crates/monty/test_cases/ext_call__name_lookup_undefined.py +4 -0
  319. package/ref-monty/crates/monty/test_cases/ext_call__nested_calls.py +14 -0
  320. package/ref-monty/crates/monty/test_cases/ext_call__recursion_bug.py +19 -0
  321. package/ref-monty/crates/monty/test_cases/ext_call__return.py +28 -0
  322. package/ref-monty/crates/monty/test_cases/ext_call__side_effects.py +25 -0
  323. package/ref-monty/crates/monty/test_cases/ext_call__subscript.py +7 -0
  324. package/ref-monty/crates/monty/test_cases/ext_call__ternary.py +28 -0
  325. package/ref-monty/crates/monty/test_cases/ext_call__try.py +280 -0
  326. package/ref-monty/crates/monty/test_cases/ext_call__try_simple.py +10 -0
  327. package/ref-monty/crates/monty/test_cases/ext_call__unary.py +13 -0
  328. package/ref-monty/crates/monty/test_cases/frozenset__ops.py +178 -0
  329. package/ref-monty/crates/monty/test_cases/fstring__all.py +236 -0
  330. package/ref-monty/crates/monty/test_cases/fstring__error_eq_align_on_str.py +3 -0
  331. package/ref-monty/crates/monty/test_cases/fstring__error_float_f_on_str.py +3 -0
  332. package/ref-monty/crates/monty/test_cases/fstring__error_int_d_on_float.py +3 -0
  333. package/ref-monty/crates/monty/test_cases/fstring__error_int_d_on_str.py +3 -0
  334. package/ref-monty/crates/monty/test_cases/fstring__error_invalid_spec.py +4 -0
  335. package/ref-monty/crates/monty/test_cases/fstring__error_invalid_spec_dynamic.py +4 -0
  336. package/ref-monty/crates/monty/test_cases/fstring__error_invalid_spec_str.py +4 -0
  337. package/ref-monty/crates/monty/test_cases/fstring__error_str_s_on_int.py +3 -0
  338. package/ref-monty/crates/monty/test_cases/function__call_duplicate_kwargs.py +6 -0
  339. package/ref-monty/crates/monty/test_cases/function__call_unpack.py +42 -0
  340. package/ref-monty/crates/monty/test_cases/function__defaults.py +117 -0
  341. package/ref-monty/crates/monty/test_cases/function__err_duplicate_arg.py +7 -0
  342. package/ref-monty/crates/monty/test_cases/function__err_duplicate_first_arg.py +7 -0
  343. package/ref-monty/crates/monty/test_cases/function__err_duplicate_kwarg_cleanup.py +9 -0
  344. package/ref-monty/crates/monty/test_cases/function__err_kwonly_as_positional.py +7 -0
  345. package/ref-monty/crates/monty/test_cases/function__err_missing_all_posonly.py +7 -0
  346. package/ref-monty/crates/monty/test_cases/function__err_missing_heap_cleanup.py +9 -0
  347. package/ref-monty/crates/monty/test_cases/function__err_missing_kwonly.py +7 -0
  348. package/ref-monty/crates/monty/test_cases/function__err_missing_posonly_with_kwarg.py +7 -0
  349. package/ref-monty/crates/monty/test_cases/function__err_missing_with_posonly.py +7 -0
  350. package/ref-monty/crates/monty/test_cases/function__err_posonly_as_kwarg.py +7 -0
  351. package/ref-monty/crates/monty/test_cases/function__err_posonly_first_as_kwarg.py +7 -0
  352. package/ref-monty/crates/monty/test_cases/function__err_too_many_posonly.py +7 -0
  353. package/ref-monty/crates/monty/test_cases/function__err_too_many_with_kwonly.py +7 -0
  354. package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg.py +7 -0
  355. package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg_cleanup.py +9 -0
  356. package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg_quote.py +13 -0
  357. package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg_simple.py +7 -0
  358. package/ref-monty/crates/monty/test_cases/function__err_unpack_duplicate_arg.py +6 -0
  359. package/ref-monty/crates/monty/test_cases/function__err_unpack_duplicate_heap.py +8 -0
  360. package/ref-monty/crates/monty/test_cases/function__err_unpack_int.py +6 -0
  361. package/ref-monty/crates/monty/test_cases/function__err_unpack_nonstring_key.py +6 -0
  362. package/ref-monty/crates/monty/test_cases/function__err_unpack_not_mapping.py +6 -0
  363. package/ref-monty/crates/monty/test_cases/function__kwargs_unpacking.py +173 -0
  364. package/ref-monty/crates/monty/test_cases/function__ops.py +294 -0
  365. package/ref-monty/crates/monty/test_cases/function__return_none.py +42 -0
  366. package/ref-monty/crates/monty/test_cases/function__signatures.py +47 -0
  367. package/ref-monty/crates/monty/test_cases/function__too_few_args_all.py +6 -0
  368. package/ref-monty/crates/monty/test_cases/function__too_few_args_one.py +6 -0
  369. package/ref-monty/crates/monty/test_cases/function__too_few_args_two.py +6 -0
  370. package/ref-monty/crates/monty/test_cases/function__too_many_args_one.py +6 -0
  371. package/ref-monty/crates/monty/test_cases/function__too_many_args_two.py +6 -0
  372. package/ref-monty/crates/monty/test_cases/function__too_many_args_zero.py +6 -0
  373. package/ref-monty/crates/monty/test_cases/global__error_assigned_before.py +7 -0
  374. package/ref-monty/crates/monty/test_cases/global__ops.py +163 -0
  375. package/ref-monty/crates/monty/test_cases/hash__dict_unhashable.py +2 -0
  376. package/ref-monty/crates/monty/test_cases/hash__list_unhashable.py +2 -0
  377. package/ref-monty/crates/monty/test_cases/hash__ops.py +153 -0
  378. package/ref-monty/crates/monty/test_cases/id__bytes_literals_distinct.py +3 -0
  379. package/ref-monty/crates/monty/test_cases/id__int_copy_distinct.py +5 -0
  380. package/ref-monty/crates/monty/test_cases/id__is_number_is_number.py +3 -0
  381. package/ref-monty/crates/monty/test_cases/id__non_overlapping_lifetimes_distinct_types.py +10 -0
  382. package/ref-monty/crates/monty/test_cases/id__non_overlapping_lifetimes_same_types.py +6 -0
  383. package/ref-monty/crates/monty/test_cases/id__ops.py +97 -0
  384. package/ref-monty/crates/monty/test_cases/id__str_literals_same.py +3 -0
  385. package/ref-monty/crates/monty/test_cases/if__elif_else.py +207 -0
  386. package/ref-monty/crates/monty/test_cases/if__raise_elif.py +11 -0
  387. package/ref-monty/crates/monty/test_cases/if__raise_else.py +13 -0
  388. package/ref-monty/crates/monty/test_cases/if__raise_if.py +9 -0
  389. package/ref-monty/crates/monty/test_cases/if__raise_in_elif_condition.py +18 -0
  390. package/ref-monty/crates/monty/test_cases/if__raise_in_if_condition.py +16 -0
  391. package/ref-monty/crates/monty/test_cases/if_else_expr__all.py +55 -0
  392. package/ref-monty/crates/monty/test_cases/import__error_cannot_import.py +9 -0
  393. package/ref-monty/crates/monty/test_cases/import__error_module_not_found.py +9 -0
  394. package/ref-monty/crates/monty/test_cases/import__local_scope.py +68 -0
  395. package/ref-monty/crates/monty/test_cases/import__os.py +25 -0
  396. package/ref-monty/crates/monty/test_cases/import__relative_error.py +9 -0
  397. package/ref-monty/crates/monty/test_cases/import__relative_no_module_error.py +9 -0
  398. package/ref-monty/crates/monty/test_cases/import__runtime_error_when_executed.py +14 -0
  399. package/ref-monty/crates/monty/test_cases/import__star_error.py +11 -0
  400. package/ref-monty/crates/monty/test_cases/import__sys.py +47 -0
  401. package/ref-monty/crates/monty/test_cases/import__sys_monty.py +28 -0
  402. package/ref-monty/crates/monty/test_cases/import__type_checking_guard.py +37 -0
  403. package/ref-monty/crates/monty/test_cases/import__typing.py +25 -0
  404. package/ref-monty/crates/monty/test_cases/import__typing_type_ignore.py +4 -0
  405. package/ref-monty/crates/monty/test_cases/int__bigint.py +467 -0
  406. package/ref-monty/crates/monty/test_cases/int__bigint_errors.py +260 -0
  407. package/ref-monty/crates/monty/test_cases/int__ops.py +219 -0
  408. package/ref-monty/crates/monty/test_cases/int__overflow_division.py +84 -0
  409. package/ref-monty/crates/monty/test_cases/is_variant__all.py +36 -0
  410. package/ref-monty/crates/monty/test_cases/isinstance__arg2_list_error.py +2 -0
  411. package/ref-monty/crates/monty/test_cases/isinstance__arg2_type_error.py +2 -0
  412. package/ref-monty/crates/monty/test_cases/iter__dict_mutation.py +4 -0
  413. package/ref-monty/crates/monty/test_cases/iter__for.py +243 -0
  414. package/ref-monty/crates/monty/test_cases/iter__for_loop_unpacking.py +66 -0
  415. package/ref-monty/crates/monty/test_cases/iter__generator_expr.py +20 -0
  416. package/ref-monty/crates/monty/test_cases/iter__generator_expr_type.py +7 -0
  417. package/ref-monty/crates/monty/test_cases/iter__not_iterable.py +3 -0
  418. package/ref-monty/crates/monty/test_cases/lambda__all.py +145 -0
  419. package/ref-monty/crates/monty/test_cases/list__extend_not_iterable.py +7 -0
  420. package/ref-monty/crates/monty/test_cases/list__getitem_out_of_bounds.py +3 -0
  421. package/ref-monty/crates/monty/test_cases/list__index_not_found.py +9 -0
  422. package/ref-monty/crates/monty/test_cases/list__index_start_gt_end.py +10 -0
  423. package/ref-monty/crates/monty/test_cases/list__ops.py +473 -0
  424. package/ref-monty/crates/monty/test_cases/list__pop_empty.py +9 -0
  425. package/ref-monty/crates/monty/test_cases/list__pop_out_of_range.py +9 -0
  426. package/ref-monty/crates/monty/test_cases/list__pop_type_error.py +9 -0
  427. package/ref-monty/crates/monty/test_cases/list__remove_not_found.py +9 -0
  428. package/ref-monty/crates/monty/test_cases/list__setitem_dict_index.py +13 -0
  429. package/ref-monty/crates/monty/test_cases/list__setitem_huge_int_index.py +13 -0
  430. package/ref-monty/crates/monty/test_cases/list__setitem_index_error.py +10 -0
  431. package/ref-monty/crates/monty/test_cases/list__setitem_type_error.py +10 -0
  432. package/ref-monty/crates/monty/test_cases/list__unpack_type_error.py +2 -0
  433. package/ref-monty/crates/monty/test_cases/longint__index_error.py +3 -0
  434. package/ref-monty/crates/monty/test_cases/longint__repeat_error.py +3 -0
  435. package/ref-monty/crates/monty/test_cases/loop__break_continue.py +113 -0
  436. package/ref-monty/crates/monty/test_cases/loop__break_finally.py +69 -0
  437. package/ref-monty/crates/monty/test_cases/loop__break_in_function_error.py +13 -0
  438. package/ref-monty/crates/monty/test_cases/loop__break_in_if_error.py +11 -0
  439. package/ref-monty/crates/monty/test_cases/loop__break_nested_except_clears.py +55 -0
  440. package/ref-monty/crates/monty/test_cases/loop__break_outside_error.py +9 -0
  441. package/ref-monty/crates/monty/test_cases/loop__continue_finally.py +81 -0
  442. package/ref-monty/crates/monty/test_cases/loop__continue_in_function_error.py +13 -0
  443. package/ref-monty/crates/monty/test_cases/loop__continue_in_if_error.py +11 -0
  444. package/ref-monty/crates/monty/test_cases/loop__continue_nested_except_clears.py +60 -0
  445. package/ref-monty/crates/monty/test_cases/loop__continue_outside_error.py +9 -0
  446. package/ref-monty/crates/monty/test_cases/math__acos_domain_error.py +11 -0
  447. package/ref-monty/crates/monty/test_cases/math__acosh_domain_error.py +11 -0
  448. package/ref-monty/crates/monty/test_cases/math__asin_domain_error.py +11 -0
  449. package/ref-monty/crates/monty/test_cases/math__atanh_domain_error.py +11 -0
  450. package/ref-monty/crates/monty/test_cases/math__cos_inf_error.py +11 -0
  451. package/ref-monty/crates/monty/test_cases/math__cosh_overflow_error.py +11 -0
  452. package/ref-monty/crates/monty/test_cases/math__exp_overflow_error.py +11 -0
  453. package/ref-monty/crates/monty/test_cases/math__factorial_float_error.py +11 -0
  454. package/ref-monty/crates/monty/test_cases/math__factorial_negative_error.py +11 -0
  455. package/ref-monty/crates/monty/test_cases/math__floor_inf_error.py +11 -0
  456. package/ref-monty/crates/monty/test_cases/math__floor_nan_error.py +11 -0
  457. package/ref-monty/crates/monty/test_cases/math__floor_str_error.py +11 -0
  458. package/ref-monty/crates/monty/test_cases/math__fmod_inf_error.py +11 -0
  459. package/ref-monty/crates/monty/test_cases/math__gamma_neg_int_error.py +11 -0
  460. package/ref-monty/crates/monty/test_cases/math__gcd_float_error.py +11 -0
  461. package/ref-monty/crates/monty/test_cases/math__isqrt_negative_error.py +11 -0
  462. package/ref-monty/crates/monty/test_cases/math__ldexp_overflow_error.py +11 -0
  463. package/ref-monty/crates/monty/test_cases/math__log1p_domain_error.py +11 -0
  464. package/ref-monty/crates/monty/test_cases/math__log_base1_error.py +11 -0
  465. package/ref-monty/crates/monty/test_cases/math__log_zero_error.py +11 -0
  466. package/ref-monty/crates/monty/test_cases/math__module.py +1432 -0
  467. package/ref-monty/crates/monty/test_cases/math__pow_domain_error.py +11 -0
  468. package/ref-monty/crates/monty/test_cases/math__sin_inf_error.py +11 -0
  469. package/ref-monty/crates/monty/test_cases/math__sqrt_negative_error.py +11 -0
  470. package/ref-monty/crates/monty/test_cases/math__tan_inf_error.py +11 -0
  471. package/ref-monty/crates/monty/test_cases/math__trunc_str_error.py +11 -0
  472. package/ref-monty/crates/monty/test_cases/method__args_kwargs_unpacking.py +259 -0
  473. package/ref-monty/crates/monty/test_cases/name_error__unbound_local_func.py +19 -0
  474. package/ref-monty/crates/monty/test_cases/name_error__unbound_local_module.py +12 -0
  475. package/ref-monty/crates/monty/test_cases/name_error__undefined_call_chained.py +9 -0
  476. package/ref-monty/crates/monty/test_cases/name_error__undefined_call_in_expr.py +9 -0
  477. package/ref-monty/crates/monty/test_cases/name_error__undefined_call_in_function.py +16 -0
  478. package/ref-monty/crates/monty/test_cases/name_error__undefined_call_with_args.py +9 -0
  479. package/ref-monty/crates/monty/test_cases/name_error__undefined_global.py +10 -0
  480. package/ref-monty/crates/monty/test_cases/namedtuple__missing_attr.py +11 -0
  481. package/ref-monty/crates/monty/test_cases/namedtuple__ops.py +34 -0
  482. package/ref-monty/crates/monty/test_cases/nonlocal__error_module_level.py +3 -0
  483. package/ref-monty/crates/monty/test_cases/nonlocal__ops.py +353 -0
  484. package/ref-monty/crates/monty/test_cases/os__environ.py +40 -0
  485. package/ref-monty/crates/monty/test_cases/os__getenv_key_list_error.py +5 -0
  486. package/ref-monty/crates/monty/test_cases/os__getenv_key_type_error.py +5 -0
  487. package/ref-monty/crates/monty/test_cases/parse_error__complex.py +3 -0
  488. package/ref-monty/crates/monty/test_cases/pathlib__import.py +11 -0
  489. package/ref-monty/crates/monty/test_cases/pathlib__os.py +136 -0
  490. package/ref-monty/crates/monty/test_cases/pathlib__os_read_error.py +12 -0
  491. package/ref-monty/crates/monty/test_cases/pathlib__pure.py +81 -0
  492. package/ref-monty/crates/monty/test_cases/pyobject__cycle_dict_self.py +5 -0
  493. package/ref-monty/crates/monty/test_cases/pyobject__cycle_list_dict.py +6 -0
  494. package/ref-monty/crates/monty/test_cases/pyobject__cycle_list_self.py +5 -0
  495. package/ref-monty/crates/monty/test_cases/pyobject__cycle_multiple_refs.py +6 -0
  496. package/ref-monty/crates/monty/test_cases/range__error_no_args.py +2 -0
  497. package/ref-monty/crates/monty/test_cases/range__error_step_zero.py +2 -0
  498. package/ref-monty/crates/monty/test_cases/range__error_too_many_args.py +2 -0
  499. package/ref-monty/crates/monty/test_cases/range__getitem_index_error.py +10 -0
  500. package/ref-monty/crates/monty/test_cases/range__ops.py +236 -0
  501. package/ref-monty/crates/monty/test_cases/re__basic.py +756 -0
  502. package/ref-monty/crates/monty/test_cases/re__grouping.py +241 -0
  503. package/ref-monty/crates/monty/test_cases/re__match.py +148 -0
  504. package/ref-monty/crates/monty/test_cases/recursion__deep_drop.py +26 -0
  505. package/ref-monty/crates/monty/test_cases/recursion__deep_eq.py +23 -0
  506. package/ref-monty/crates/monty/test_cases/recursion__deep_hash.py +46 -0
  507. package/ref-monty/crates/monty/test_cases/recursion__deep_repr.py +12 -0
  508. package/ref-monty/crates/monty/test_cases/recursion__function_depth.py +13 -0
  509. package/ref-monty/crates/monty/test_cases/refcount__cycle_mutual_reference.py +18 -0
  510. package/ref-monty/crates/monty/test_cases/refcount__cycle_self_reference.py +12 -0
  511. package/ref-monty/crates/monty/test_cases/refcount__dict_basic.py +5 -0
  512. package/ref-monty/crates/monty/test_cases/refcount__dict_get.py +5 -0
  513. package/ref-monty/crates/monty/test_cases/refcount__dict_keys_and.py +14 -0
  514. package/ref-monty/crates/monty/test_cases/refcount__dict_overwrite.py +6 -0
  515. package/ref-monty/crates/monty/test_cases/refcount__gather_cleanup.py +16 -0
  516. package/ref-monty/crates/monty/test_cases/refcount__gather_exception.py +18 -0
  517. package/ref-monty/crates/monty/test_cases/refcount__gather_nested_cancel.py +25 -0
  518. package/ref-monty/crates/monty/test_cases/refcount__immediate_skipped.py +4 -0
  519. package/ref-monty/crates/monty/test_cases/refcount__kwargs_unpacking.py +27 -0
  520. package/ref-monty/crates/monty/test_cases/refcount__list_append_multiple.py +6 -0
  521. package/ref-monty/crates/monty/test_cases/refcount__list_append_ref.py +5 -0
  522. package/ref-monty/crates/monty/test_cases/refcount__list_concat.py +5 -0
  523. package/ref-monty/crates/monty/test_cases/refcount__list_getitem.py +5 -0
  524. package/ref-monty/crates/monty/test_cases/refcount__list_iadd.py +5 -0
  525. package/ref-monty/crates/monty/test_cases/refcount__nested_list.py +4 -0
  526. package/ref-monty/crates/monty/test_cases/refcount__re_pattern_sub_error_paths.py +37 -0
  527. package/ref-monty/crates/monty/test_cases/refcount__re_search_match.py +34 -0
  528. package/ref-monty/crates/monty/test_cases/refcount__re_sub_error_paths.py +31 -0
  529. package/ref-monty/crates/monty/test_cases/refcount__shared_reference.py +4 -0
  530. package/ref-monty/crates/monty/test_cases/refcount__single_list.py +3 -0
  531. package/ref-monty/crates/monty/test_cases/repr__cycle_detection.py +24 -0
  532. package/ref-monty/crates/monty/test_cases/set__ops.py +191 -0
  533. package/ref-monty/crates/monty/test_cases/set__review_bugs.py +35 -0
  534. package/ref-monty/crates/monty/test_cases/set__unpack_type_error.py +2 -0
  535. package/ref-monty/crates/monty/test_cases/slice__invalid_indices.py +2 -0
  536. package/ref-monty/crates/monty/test_cases/slice__kwargs.py +9 -0
  537. package/ref-monty/crates/monty/test_cases/slice__no_args.py +9 -0
  538. package/ref-monty/crates/monty/test_cases/slice__ops.py +149 -0
  539. package/ref-monty/crates/monty/test_cases/slice__step_zero.py +9 -0
  540. package/ref-monty/crates/monty/test_cases/slice__step_zero_bytes.py +9 -0
  541. package/ref-monty/crates/monty/test_cases/slice__step_zero_range.py +9 -0
  542. package/ref-monty/crates/monty/test_cases/slice__step_zero_str.py +9 -0
  543. package/ref-monty/crates/monty/test_cases/slice__step_zero_tuple.py +9 -0
  544. package/ref-monty/crates/monty/test_cases/slice__too_many_args.py +9 -0
  545. package/ref-monty/crates/monty/test_cases/str__getitem_index_error.py +10 -0
  546. package/ref-monty/crates/monty/test_cases/str__index_not_found.py +9 -0
  547. package/ref-monty/crates/monty/test_cases/str__join_no_args.py +9 -0
  548. package/ref-monty/crates/monty/test_cases/str__join_non_string.py +9 -0
  549. package/ref-monty/crates/monty/test_cases/str__join_not_iterable.py +9 -0
  550. package/ref-monty/crates/monty/test_cases/str__join_too_many_args.py +9 -0
  551. package/ref-monty/crates/monty/test_cases/str__methods.py +327 -0
  552. package/ref-monty/crates/monty/test_cases/str__ops.py +162 -0
  553. package/ref-monty/crates/monty/test_cases/str__partition_empty.py +9 -0
  554. package/ref-monty/crates/monty/test_cases/str__rsplit_empty_sep.py +9 -0
  555. package/ref-monty/crates/monty/test_cases/str__split_empty_sep.py +9 -0
  556. package/ref-monty/crates/monty/test_cases/sys__types.py +7 -0
  557. package/ref-monty/crates/monty/test_cases/traceback__division_error.py +30 -0
  558. package/ref-monty/crates/monty/test_cases/traceback__index_error.py +17 -0
  559. package/ref-monty/crates/monty/test_cases/traceback__insert_as_int.py +10 -0
  560. package/ref-monty/crates/monty/test_cases/traceback__nested_call.py +29 -0
  561. package/ref-monty/crates/monty/test_cases/traceback__nonlocal_module_scope.py +10 -0
  562. package/ref-monty/crates/monty/test_cases/traceback__nonlocal_unbound.py +24 -0
  563. package/ref-monty/crates/monty/test_cases/traceback__range_as_int.py +9 -0
  564. package/ref-monty/crates/monty/test_cases/traceback__recursion_error.py +23 -0
  565. package/ref-monty/crates/monty/test_cases/traceback__set_mutation.py +11 -0
  566. package/ref-monty/crates/monty/test_cases/traceback__undefined_attr_call.py +16 -0
  567. package/ref-monty/crates/monty/test_cases/traceback__undefined_call.py +16 -0
  568. package/ref-monty/crates/monty/test_cases/traceback__undefined_raise.py +16 -0
  569. package/ref-monty/crates/monty/test_cases/try_except__all.py +472 -0
  570. package/ref-monty/crates/monty/test_cases/try_except__bare_raise_no_context.py +2 -0
  571. package/ref-monty/crates/monty/test_cases/try_except__invalid_type.py +5 -0
  572. package/ref-monty/crates/monty/test_cases/tuple__getitem_out_of_bounds.py +3 -0
  573. package/ref-monty/crates/monty/test_cases/tuple__index_not_found.py +9 -0
  574. package/ref-monty/crates/monty/test_cases/tuple__index_start_gt_end.py +10 -0
  575. package/ref-monty/crates/monty/test_cases/tuple__methods.py +19 -0
  576. package/ref-monty/crates/monty/test_cases/tuple__ops.py +133 -0
  577. package/ref-monty/crates/monty/test_cases/tuple__unpack_type_error.py +2 -0
  578. package/ref-monty/crates/monty/test_cases/type__builtin_attr_error.py +9 -0
  579. package/ref-monty/crates/monty/test_cases/type__bytes_negative.py +2 -0
  580. package/ref-monty/crates/monty/test_cases/type__cell_not_builtin.py +9 -0
  581. package/ref-monty/crates/monty/test_cases/type__exception_attr_error.py +11 -0
  582. package/ref-monty/crates/monty/test_cases/type__float_conversion_error.py +2 -0
  583. package/ref-monty/crates/monty/test_cases/type__float_repr_both_quotes.py +9 -0
  584. package/ref-monty/crates/monty/test_cases/type__float_repr_newline.py +9 -0
  585. package/ref-monty/crates/monty/test_cases/type__float_repr_single_quote.py +9 -0
  586. package/ref-monty/crates/monty/test_cases/type__int_conversion_error.py +2 -0
  587. package/ref-monty/crates/monty/test_cases/type__list_not_iterable.py +2 -0
  588. package/ref-monty/crates/monty/test_cases/type__non_builtin_name_error.py +9 -0
  589. package/ref-monty/crates/monty/test_cases/type__ops.py +200 -0
  590. package/ref-monty/crates/monty/test_cases/type__shadow_exc.py +3 -0
  591. package/ref-monty/crates/monty/test_cases/type__shadow_int.py +9 -0
  592. package/ref-monty/crates/monty/test_cases/type__shadow_len.py +3 -0
  593. package/ref-monty/crates/monty/test_cases/type__tuple_not_iterable.py +2 -0
  594. package/ref-monty/crates/monty/test_cases/type_error__int_add_list.py +2 -0
  595. package/ref-monty/crates/monty/test_cases/type_error__int_div_str.py +2 -0
  596. package/ref-monty/crates/monty/test_cases/type_error__int_floordiv_str.py +2 -0
  597. package/ref-monty/crates/monty/test_cases/type_error__int_iadd_str.py +3 -0
  598. package/ref-monty/crates/monty/test_cases/type_error__int_mod_str.py +2 -0
  599. package/ref-monty/crates/monty/test_cases/type_error__int_pow_str.py +2 -0
  600. package/ref-monty/crates/monty/test_cases/type_error__int_sub_str.py +2 -0
  601. package/ref-monty/crates/monty/test_cases/type_error__list_add_int.py +2 -0
  602. package/ref-monty/crates/monty/test_cases/type_error__list_add_str.py +2 -0
  603. package/ref-monty/crates/monty/test_cases/type_error__list_iadd_int.py +6 -0
  604. package/ref-monty/crates/monty/test_cases/type_error__str_add_int.py +2 -0
  605. package/ref-monty/crates/monty/test_cases/type_error__str_iadd_int.py +3 -0
  606. package/ref-monty/crates/monty/test_cases/type_error__unary_invert_str.py +3 -0
  607. package/ref-monty/crates/monty/test_cases/type_error__unary_minus_str.py +4 -0
  608. package/ref-monty/crates/monty/test_cases/type_error__unary_neg_str.py +3 -0
  609. package/ref-monty/crates/monty/test_cases/type_error__unary_plus_str.py +4 -0
  610. package/ref-monty/crates/monty/test_cases/typing__types.py +24 -0
  611. package/ref-monty/crates/monty/test_cases/unpack__nested.py +48 -0
  612. package/ref-monty/crates/monty/test_cases/unpack__non_sequence.py +9 -0
  613. package/ref-monty/crates/monty/test_cases/unpack__not_enough.py +9 -0
  614. package/ref-monty/crates/monty/test_cases/unpack__ops.py +153 -0
  615. package/ref-monty/crates/monty/test_cases/unpack__star_not_enough.py +9 -0
  616. package/ref-monty/crates/monty/test_cases/unpack__too_many.py +9 -0
  617. package/ref-monty/crates/monty/test_cases/version__cpython.py +4 -0
  618. package/ref-monty/crates/monty/test_cases/walrus__all.py +178 -0
  619. package/ref-monty/crates/monty/test_cases/while__all.py +206 -0
  620. package/ref-monty/crates/monty/tests/asyncio.rs +764 -0
  621. package/ref-monty/crates/monty/tests/binary_serde.rs +185 -0
  622. package/ref-monty/crates/monty/tests/bytecode_limits.rs +248 -0
  623. package/ref-monty/crates/monty/tests/datatest_runner.rs +2029 -0
  624. package/ref-monty/crates/monty/tests/inputs.rs +420 -0
  625. package/ref-monty/crates/monty/tests/json_serde.rs +250 -0
  626. package/ref-monty/crates/monty/tests/main.rs +71 -0
  627. package/ref-monty/crates/monty/tests/math_module.rs +114 -0
  628. package/ref-monty/crates/monty/tests/name_lookup.rs +482 -0
  629. package/ref-monty/crates/monty/tests/os_tests.rs +459 -0
  630. package/ref-monty/crates/monty/tests/parse_errors.rs +441 -0
  631. package/ref-monty/crates/monty/tests/print_writer.rs +238 -0
  632. package/ref-monty/crates/monty/tests/py_object.rs +121 -0
  633. package/ref-monty/crates/monty/tests/regex.rs +90 -0
  634. package/ref-monty/crates/monty/tests/repl.rs +344 -0
  635. package/ref-monty/crates/monty/tests/resource_limits.rs +1826 -0
  636. package/ref-monty/crates/monty/tests/try_from.rs +167 -0
  637. package/ref-monty/crates/monty-cli/Cargo.toml +25 -0
  638. package/ref-monty/crates/monty-cli/src/main.rs +541 -0
  639. package/ref-monty/crates/monty-js/.cargo/config.toml +2 -0
  640. package/ref-monty/crates/monty-js/.prettierignore +8 -0
  641. package/ref-monty/crates/monty-js/Cargo.toml +32 -0
  642. package/ref-monty/crates/monty-js/README.md +207 -0
  643. package/ref-monty/crates/monty-js/__test__/async.spec.ts +350 -0
  644. package/ref-monty/crates/monty-js/__test__/basic.spec.ts +114 -0
  645. package/ref-monty/crates/monty-js/__test__/exceptions.spec.ts +427 -0
  646. package/ref-monty/crates/monty-js/__test__/external.spec.ts +354 -0
  647. package/ref-monty/crates/monty-js/__test__/inputs.spec.ts +143 -0
  648. package/ref-monty/crates/monty-js/__test__/limits.spec.ts +162 -0
  649. package/ref-monty/crates/monty-js/__test__/package.json +3 -0
  650. package/ref-monty/crates/monty-js/__test__/print.spec.ts +229 -0
  651. package/ref-monty/crates/monty-js/__test__/repl.spec.ts +34 -0
  652. package/ref-monty/crates/monty-js/__test__/serialize.spec.ts +205 -0
  653. package/ref-monty/crates/monty-js/__test__/start.spec.ts +443 -0
  654. package/ref-monty/crates/monty-js/__test__/type_check.spec.ts +147 -0
  655. package/ref-monty/crates/monty-js/__test__/types.spec.ts +319 -0
  656. package/ref-monty/crates/monty-js/build.rs +61 -0
  657. package/ref-monty/crates/monty-js/index-header.d.ts +3 -0
  658. package/ref-monty/crates/monty-js/package-lock.json +4694 -0
  659. package/ref-monty/crates/monty-js/package.json +100 -0
  660. package/ref-monty/crates/monty-js/scripts/smoke-test.sh +69 -0
  661. package/ref-monty/crates/monty-js/smoke-test/package.json +17 -0
  662. package/ref-monty/crates/monty-js/smoke-test/test.ts +171 -0
  663. package/ref-monty/crates/monty-js/smoke-test/tsconfig.json +11 -0
  664. package/ref-monty/crates/monty-js/src/convert.rs +648 -0
  665. package/ref-monty/crates/monty-js/src/exceptions.rs +293 -0
  666. package/ref-monty/crates/monty-js/src/lib.rs +41 -0
  667. package/ref-monty/crates/monty-js/src/limits.rs +53 -0
  668. package/ref-monty/crates/monty-js/src/monty_cls.rs +1407 -0
  669. package/ref-monty/crates/monty-js/tsconfig.json +17 -0
  670. package/ref-monty/crates/monty-js/wrapper.ts +701 -0
  671. package/ref-monty/crates/monty-python/Cargo.toml +38 -0
  672. package/ref-monty/crates/monty-python/README.md +134 -0
  673. package/ref-monty/crates/monty-python/build.rs +4 -0
  674. package/ref-monty/crates/monty-python/example.py +40 -0
  675. package/ref-monty/crates/monty-python/exercise.py +46 -0
  676. package/ref-monty/crates/monty-python/pyproject.toml +57 -0
  677. package/ref-monty/crates/monty-python/python/pydantic_monty/__init__.py +281 -0
  678. package/ref-monty/crates/monty-python/python/pydantic_monty/_monty.pyi +677 -0
  679. package/ref-monty/crates/monty-python/python/pydantic_monty/os_access.py +933 -0
  680. package/ref-monty/crates/monty-python/python/pydantic_monty/py.typed +0 -0
  681. package/ref-monty/crates/monty-python/src/convert.rs +273 -0
  682. package/ref-monty/crates/monty-python/src/dataclass.rs +461 -0
  683. package/ref-monty/crates/monty-python/src/exceptions.rs +557 -0
  684. package/ref-monty/crates/monty-python/src/external.rs +165 -0
  685. package/ref-monty/crates/monty-python/src/lib.rs +77 -0
  686. package/ref-monty/crates/monty-python/src/limits.rs +142 -0
  687. package/ref-monty/crates/monty-python/src/monty_cls.rs +1650 -0
  688. package/ref-monty/crates/monty-python/src/repl.rs +470 -0
  689. package/ref-monty/crates/monty-python/src/serialization.rs +761 -0
  690. package/ref-monty/crates/monty-python/tests/test_async.py +1201 -0
  691. package/ref-monty/crates/monty-python/tests/test_basic.py +66 -0
  692. package/ref-monty/crates/monty-python/tests/test_dataclasses.py +971 -0
  693. package/ref-monty/crates/monty-python/tests/test_exceptions.py +361 -0
  694. package/ref-monty/crates/monty-python/tests/test_external.py +367 -0
  695. package/ref-monty/crates/monty-python/tests/test_inputs.py +126 -0
  696. package/ref-monty/crates/monty-python/tests/test_limits.py +257 -0
  697. package/ref-monty/crates/monty-python/tests/test_os_access.py +1286 -0
  698. package/ref-monty/crates/monty-python/tests/test_os_access_compat.py +731 -0
  699. package/ref-monty/crates/monty-python/tests/test_os_access_raw.py +483 -0
  700. package/ref-monty/crates/monty-python/tests/test_os_calls.py +819 -0
  701. package/ref-monty/crates/monty-python/tests/test_print.py +208 -0
  702. package/ref-monty/crates/monty-python/tests/test_re.py +170 -0
  703. package/ref-monty/crates/monty-python/tests/test_readme_examples.py +20 -0
  704. package/ref-monty/crates/monty-python/tests/test_repl.py +749 -0
  705. package/ref-monty/crates/monty-python/tests/test_serialize.py +284 -0
  706. package/ref-monty/crates/monty-python/tests/test_start.py +346 -0
  707. package/ref-monty/crates/monty-python/tests/test_threading.py +163 -0
  708. package/ref-monty/crates/monty-python/tests/test_type_check.py +344 -0
  709. package/ref-monty/crates/monty-python/tests/test_types.py +553 -0
  710. package/ref-monty/crates/monty-type-checking/Cargo.toml +32 -0
  711. package/ref-monty/crates/monty-type-checking/src/db.rs +116 -0
  712. package/ref-monty/crates/monty-type-checking/src/lib.rs +4 -0
  713. package/ref-monty/crates/monty-type-checking/src/type_check.rs +280 -0
  714. package/ref-monty/crates/monty-type-checking/tests/bad_types.py +109 -0
  715. package/ref-monty/crates/monty-type-checking/tests/bad_types_output.txt +21 -0
  716. package/ref-monty/crates/monty-type-checking/tests/good_types.py +475 -0
  717. package/ref-monty/crates/monty-type-checking/tests/main.rs +205 -0
  718. package/ref-monty/crates/monty-type-checking/tests/reveal_types.py +56 -0
  719. package/ref-monty/crates/monty-type-checking/tests/reveal_types_output.txt +41 -0
  720. package/ref-monty/crates/monty-typeshed/Cargo.toml +29 -0
  721. package/ref-monty/crates/monty-typeshed/README.md +11 -0
  722. package/ref-monty/crates/monty-typeshed/build.rs +101 -0
  723. package/ref-monty/crates/monty-typeshed/custom/README.md +1 -0
  724. package/ref-monty/crates/monty-typeshed/custom/asyncio.pyi +138 -0
  725. package/ref-monty/crates/monty-typeshed/custom/os.pyi +87 -0
  726. package/ref-monty/crates/monty-typeshed/custom/sys.pyi +33 -0
  727. package/ref-monty/crates/monty-typeshed/src/lib.rs +56 -0
  728. package/ref-monty/crates/monty-typeshed/update.py +321 -0
  729. package/ref-monty/crates/monty-typeshed/vendor/typeshed/source_commit.txt +1 -0
  730. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/VERSIONS +20 -0
  731. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/_collections_abc.pyi +105 -0
  732. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/_typeshed/__init__.pyi +394 -0
  733. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/asyncio.pyi +138 -0
  734. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/builtins.pyi +1434 -0
  735. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/collections/__init__.pyi +527 -0
  736. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/collections/abc.pyi +2 -0
  737. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/dataclasses.pyi +502 -0
  738. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/enum.pyi +376 -0
  739. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/math.pyi +149 -0
  740. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/os.pyi +87 -0
  741. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/pathlib/__init__.pyi +395 -0
  742. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/pathlib/types.pyi +8 -0
  743. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/re.pyi +337 -0
  744. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/sys.pyi +33 -0
  745. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/types.pyi +741 -0
  746. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/typing.pyi +1217 -0
  747. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/typing_extensions.pyi +716 -0
  748. package/ref-monty/docs/usage-guide.md +117 -0
  749. package/ref-monty/examples/README.md +3 -0
  750. package/ref-monty/examples/expense_analysis/README.md +3 -0
  751. package/ref-monty/examples/expense_analysis/data.py +124 -0
  752. package/ref-monty/examples/expense_analysis/main.py +115 -0
  753. package/ref-monty/examples/sql_playground/README.md +20 -0
  754. package/ref-monty/examples/sql_playground/external_functions.py +129 -0
  755. package/ref-monty/examples/sql_playground/main.py +81 -0
  756. package/ref-monty/examples/sql_playground/sandbox_code.py +82 -0
  757. package/ref-monty/examples/sql_playground/type_stubs.pyi +14 -0
  758. package/ref-monty/examples/web_scraper/README.md +15 -0
  759. package/ref-monty/examples/web_scraper/browser.py +56 -0
  760. package/ref-monty/examples/web_scraper/example_code.py +59 -0
  761. package/ref-monty/examples/web_scraper/external_functions.py +324 -0
  762. package/ref-monty/examples/web_scraper/main.py +193 -0
  763. package/ref-monty/examples/web_scraper/sub_agent.py +79 -0
  764. package/ref-monty/monty-npm.md +235 -0
  765. package/ref-monty/pyproject.toml +162 -0
  766. package/ref-monty/scripts/check_imports.py +91 -0
  767. package/ref-monty/scripts/codecov_diff.py +412 -0
  768. package/ref-monty/scripts/complete_tests.py +146 -0
  769. package/ref-monty/scripts/flamegraph_to_text.py +208 -0
  770. package/ref-monty/scripts/iter_test_methods.py +540 -0
  771. package/ref-monty/scripts/run_traceback.py +180 -0
  772. package/ref-monty/scripts/startup_performance.py +130 -0
  773. package/ref-monty/uv.lock +1779 -0
  774. package/temp_resend_cli/repo/.github/scripts/pr-title-check.js +34 -0
  775. package/temp_resend_cli/repo/.github/workflows/ci.yml +67 -0
  776. package/temp_resend_cli/repo/.github/workflows/post-release.yml +51 -0
  777. package/temp_resend_cli/repo/.github/workflows/pr-title-check.yml +13 -0
  778. package/temp_resend_cli/repo/.github/workflows/release.yml +175 -0
  779. package/temp_resend_cli/repo/.github/workflows/test-install-unix.yml +34 -0
  780. package/temp_resend_cli/repo/.github/workflows/test-install-windows.yml +48 -0
  781. package/temp_resend_cli/repo/CHANGELOG.md +31 -0
  782. package/temp_resend_cli/repo/LICENSE +21 -0
  783. package/temp_resend_cli/repo/README.md +450 -0
  784. package/temp_resend_cli/repo/biome.json +36 -0
  785. package/temp_resend_cli/repo/install.ps1 +141 -0
  786. package/temp_resend_cli/repo/install.sh +301 -0
  787. package/temp_resend_cli/repo/package.json +61 -0
  788. package/temp_resend_cli/repo/pnpm-lock.yaml +2439 -0
  789. package/temp_resend_cli/repo/renovate.json +4 -0
  790. package/temp_resend_cli/repo/src/cli.ts +98 -0
  791. package/temp_resend_cli/repo/src/commands/api-keys/create.ts +114 -0
  792. package/temp_resend_cli/repo/src/commands/api-keys/delete.ts +47 -0
  793. package/temp_resend_cli/repo/src/commands/api-keys/index.ts +26 -0
  794. package/temp_resend_cli/repo/src/commands/api-keys/list.ts +35 -0
  795. package/temp_resend_cli/repo/src/commands/api-keys/utils.ts +8 -0
  796. package/temp_resend_cli/repo/src/commands/auth/index.ts +20 -0
  797. package/temp_resend_cli/repo/src/commands/auth/login.ts +234 -0
  798. package/temp_resend_cli/repo/src/commands/auth/logout.ts +105 -0
  799. package/temp_resend_cli/repo/src/commands/broadcasts/create.ts +196 -0
  800. package/temp_resend_cli/repo/src/commands/broadcasts/delete.ts +46 -0
  801. package/temp_resend_cli/repo/src/commands/broadcasts/get.ts +59 -0
  802. package/temp_resend_cli/repo/src/commands/broadcasts/index.ts +43 -0
  803. package/temp_resend_cli/repo/src/commands/broadcasts/list.ts +60 -0
  804. package/temp_resend_cli/repo/src/commands/broadcasts/send.ts +56 -0
  805. package/temp_resend_cli/repo/src/commands/broadcasts/update.ts +95 -0
  806. package/temp_resend_cli/repo/src/commands/broadcasts/utils.ts +35 -0
  807. package/temp_resend_cli/repo/src/commands/contact-properties/create.ts +118 -0
  808. package/temp_resend_cli/repo/src/commands/contact-properties/delete.ts +48 -0
  809. package/temp_resend_cli/repo/src/commands/contact-properties/get.ts +46 -0
  810. package/temp_resend_cli/repo/src/commands/contact-properties/index.ts +48 -0
  811. package/temp_resend_cli/repo/src/commands/contact-properties/list.ts +68 -0
  812. package/temp_resend_cli/repo/src/commands/contact-properties/update.ts +88 -0
  813. package/temp_resend_cli/repo/src/commands/contact-properties/utils.ts +17 -0
  814. package/temp_resend_cli/repo/src/commands/contacts/add-segment.ts +78 -0
  815. package/temp_resend_cli/repo/src/commands/contacts/create.ts +122 -0
  816. package/temp_resend_cli/repo/src/commands/contacts/delete.ts +49 -0
  817. package/temp_resend_cli/repo/src/commands/contacts/get.ts +53 -0
  818. package/temp_resend_cli/repo/src/commands/contacts/index.ts +58 -0
  819. package/temp_resend_cli/repo/src/commands/contacts/list.ts +57 -0
  820. package/temp_resend_cli/repo/src/commands/contacts/remove-segment.ts +48 -0
  821. package/temp_resend_cli/repo/src/commands/contacts/segments.ts +39 -0
  822. package/temp_resend_cli/repo/src/commands/contacts/topics.ts +45 -0
  823. package/temp_resend_cli/repo/src/commands/contacts/update-topics.ts +90 -0
  824. package/temp_resend_cli/repo/src/commands/contacts/update.ts +77 -0
  825. package/temp_resend_cli/repo/src/commands/contacts/utils.ts +119 -0
  826. package/temp_resend_cli/repo/src/commands/doctor.ts +216 -0
  827. package/temp_resend_cli/repo/src/commands/domains/create.ts +83 -0
  828. package/temp_resend_cli/repo/src/commands/domains/delete.ts +42 -0
  829. package/temp_resend_cli/repo/src/commands/domains/get.ts +47 -0
  830. package/temp_resend_cli/repo/src/commands/domains/index.ts +35 -0
  831. package/temp_resend_cli/repo/src/commands/domains/list.ts +53 -0
  832. package/temp_resend_cli/repo/src/commands/domains/update.ts +75 -0
  833. package/temp_resend_cli/repo/src/commands/domains/utils.ts +44 -0
  834. package/temp_resend_cli/repo/src/commands/domains/verify.ts +38 -0
  835. package/temp_resend_cli/repo/src/commands/emails/batch.ts +140 -0
  836. package/temp_resend_cli/repo/src/commands/emails/get.ts +44 -0
  837. package/temp_resend_cli/repo/src/commands/emails/index.ts +30 -0
  838. package/temp_resend_cli/repo/src/commands/emails/list.ts +84 -0
  839. package/temp_resend_cli/repo/src/commands/emails/receiving/attachment.ts +55 -0
  840. package/temp_resend_cli/repo/src/commands/emails/receiving/attachments.ts +68 -0
  841. package/temp_resend_cli/repo/src/commands/emails/receiving/get.ts +58 -0
  842. package/temp_resend_cli/repo/src/commands/emails/receiving/index.ts +28 -0
  843. package/temp_resend_cli/repo/src/commands/emails/receiving/list.ts +59 -0
  844. package/temp_resend_cli/repo/src/commands/emails/receiving/utils.ts +38 -0
  845. package/temp_resend_cli/repo/src/commands/emails/send.ts +189 -0
  846. package/temp_resend_cli/repo/src/commands/open.ts +27 -0
  847. package/temp_resend_cli/repo/src/commands/segments/create.ts +50 -0
  848. package/temp_resend_cli/repo/src/commands/segments/delete.ts +47 -0
  849. package/temp_resend_cli/repo/src/commands/segments/get.ts +38 -0
  850. package/temp_resend_cli/repo/src/commands/segments/index.ts +36 -0
  851. package/temp_resend_cli/repo/src/commands/segments/list.ts +58 -0
  852. package/temp_resend_cli/repo/src/commands/segments/utils.ts +7 -0
  853. package/temp_resend_cli/repo/src/commands/teams/index.ts +10 -0
  854. package/temp_resend_cli/repo/src/commands/teams/list.ts +35 -0
  855. package/temp_resend_cli/repo/src/commands/teams/remove.ts +86 -0
  856. package/temp_resend_cli/repo/src/commands/teams/switch.ts +76 -0
  857. package/temp_resend_cli/repo/src/commands/topics/create.ts +73 -0
  858. package/temp_resend_cli/repo/src/commands/topics/delete.ts +47 -0
  859. package/temp_resend_cli/repo/src/commands/topics/get.ts +42 -0
  860. package/temp_resend_cli/repo/src/commands/topics/index.ts +42 -0
  861. package/temp_resend_cli/repo/src/commands/topics/list.ts +34 -0
  862. package/temp_resend_cli/repo/src/commands/topics/update.ts +59 -0
  863. package/temp_resend_cli/repo/src/commands/topics/utils.ts +16 -0
  864. package/temp_resend_cli/repo/src/commands/webhooks/create.ts +128 -0
  865. package/temp_resend_cli/repo/src/commands/webhooks/delete.ts +49 -0
  866. package/temp_resend_cli/repo/src/commands/webhooks/get.ts +42 -0
  867. package/temp_resend_cli/repo/src/commands/webhooks/index.ts +42 -0
  868. package/temp_resend_cli/repo/src/commands/webhooks/list.ts +55 -0
  869. package/temp_resend_cli/repo/src/commands/webhooks/listen.ts +379 -0
  870. package/temp_resend_cli/repo/src/commands/webhooks/update.ts +83 -0
  871. package/temp_resend_cli/repo/src/commands/webhooks/utils.ts +36 -0
  872. package/temp_resend_cli/repo/src/commands/whoami.ts +71 -0
  873. package/temp_resend_cli/repo/src/lib/actions.ts +157 -0
  874. package/temp_resend_cli/repo/src/lib/client.ts +37 -0
  875. package/temp_resend_cli/repo/src/lib/config.ts +217 -0
  876. package/temp_resend_cli/repo/src/lib/files.ts +15 -0
  877. package/temp_resend_cli/repo/src/lib/help-text.ts +38 -0
  878. package/temp_resend_cli/repo/src/lib/output.ts +56 -0
  879. package/temp_resend_cli/repo/src/lib/pagination.ts +36 -0
  880. package/temp_resend_cli/repo/src/lib/prompts.ts +149 -0
  881. package/temp_resend_cli/repo/src/lib/spinner.ts +100 -0
  882. package/temp_resend_cli/repo/src/lib/table.ts +57 -0
  883. package/temp_resend_cli/repo/src/lib/tty.ts +28 -0
  884. package/temp_resend_cli/repo/src/lib/update-check.ts +169 -0
  885. package/temp_resend_cli/repo/src/lib/version.ts +4 -0
  886. package/temp_resend_cli/repo/tests/commands/api-keys/create.test.ts +196 -0
  887. package/temp_resend_cli/repo/tests/commands/api-keys/delete.test.ts +157 -0
  888. package/temp_resend_cli/repo/tests/commands/api-keys/list.test.ts +134 -0
  889. package/temp_resend_cli/repo/tests/commands/auth/login.test.ts +153 -0
  890. package/temp_resend_cli/repo/tests/commands/auth/logout.test.ts +153 -0
  891. package/temp_resend_cli/repo/tests/commands/broadcasts/create.test.ts +454 -0
  892. package/temp_resend_cli/repo/tests/commands/broadcasts/delete.test.ts +183 -0
  893. package/temp_resend_cli/repo/tests/commands/broadcasts/get.test.ts +147 -0
  894. package/temp_resend_cli/repo/tests/commands/broadcasts/list.test.ts +199 -0
  895. package/temp_resend_cli/repo/tests/commands/broadcasts/send.test.ts +162 -0
  896. package/temp_resend_cli/repo/tests/commands/broadcasts/update.test.ts +288 -0
  897. package/temp_resend_cli/repo/tests/commands/contact-properties/create.test.ts +251 -0
  898. package/temp_resend_cli/repo/tests/commands/contact-properties/delete.test.ts +184 -0
  899. package/temp_resend_cli/repo/tests/commands/contact-properties/get.test.ts +145 -0
  900. package/temp_resend_cli/repo/tests/commands/contact-properties/list.test.ts +181 -0
  901. package/temp_resend_cli/repo/tests/commands/contact-properties/update.test.ts +217 -0
  902. package/temp_resend_cli/repo/tests/commands/contacts/add-segment.test.ts +189 -0
  903. package/temp_resend_cli/repo/tests/commands/contacts/create.test.ts +271 -0
  904. package/temp_resend_cli/repo/tests/commands/contacts/delete.test.ts +193 -0
  905. package/temp_resend_cli/repo/tests/commands/contacts/get.test.ts +149 -0
  906. package/temp_resend_cli/repo/tests/commands/contacts/list.test.ts +176 -0
  907. package/temp_resend_cli/repo/tests/commands/contacts/remove-segment.test.ts +167 -0
  908. package/temp_resend_cli/repo/tests/commands/contacts/segments.test.ts +168 -0
  909. package/temp_resend_cli/repo/tests/commands/contacts/topics.test.ts +164 -0
  910. package/temp_resend_cli/repo/tests/commands/contacts/update-topics.test.ts +248 -0
  911. package/temp_resend_cli/repo/tests/commands/contacts/update.test.ts +206 -0
  912. package/temp_resend_cli/repo/tests/commands/doctor.test.ts +164 -0
  913. package/temp_resend_cli/repo/tests/commands/domains/create.test.ts +193 -0
  914. package/temp_resend_cli/repo/tests/commands/domains/delete.test.ts +157 -0
  915. package/temp_resend_cli/repo/tests/commands/domains/get.test.ts +138 -0
  916. package/temp_resend_cli/repo/tests/commands/domains/list.test.ts +165 -0
  917. package/temp_resend_cli/repo/tests/commands/domains/update.test.ts +224 -0
  918. package/temp_resend_cli/repo/tests/commands/domains/verify.test.ts +118 -0
  919. package/temp_resend_cli/repo/tests/commands/emails/batch.test.ts +324 -0
  920. package/temp_resend_cli/repo/tests/commands/emails/get.test.ts +132 -0
  921. package/temp_resend_cli/repo/tests/commands/emails/receiving/attachment.test.ts +141 -0
  922. package/temp_resend_cli/repo/tests/commands/emails/receiving/attachments.test.ts +169 -0
  923. package/temp_resend_cli/repo/tests/commands/emails/receiving/get.test.ts +141 -0
  924. package/temp_resend_cli/repo/tests/commands/emails/receiving/list.test.ts +182 -0
  925. package/temp_resend_cli/repo/tests/commands/emails/send.test.ts +312 -0
  926. package/temp_resend_cli/repo/tests/commands/segments/create.test.ts +164 -0
  927. package/temp_resend_cli/repo/tests/commands/segments/delete.test.ts +183 -0
  928. package/temp_resend_cli/repo/tests/commands/segments/get.test.ts +138 -0
  929. package/temp_resend_cli/repo/tests/commands/segments/list.test.ts +174 -0
  930. package/temp_resend_cli/repo/tests/commands/teams/list.test.ts +62 -0
  931. package/temp_resend_cli/repo/tests/commands/teams/remove.test.ts +110 -0
  932. package/temp_resend_cli/repo/tests/commands/teams/switch.test.ts +103 -0
  933. package/temp_resend_cli/repo/tests/commands/topics/create.test.ts +192 -0
  934. package/temp_resend_cli/repo/tests/commands/topics/delete.test.ts +157 -0
  935. package/temp_resend_cli/repo/tests/commands/topics/get.test.ts +126 -0
  936. package/temp_resend_cli/repo/tests/commands/topics/list.test.ts +125 -0
  937. package/temp_resend_cli/repo/tests/commands/topics/update.test.ts +178 -0
  938. package/temp_resend_cli/repo/tests/commands/webhooks/create.test.ts +225 -0
  939. package/temp_resend_cli/repo/tests/commands/webhooks/delete.test.ts +157 -0
  940. package/temp_resend_cli/repo/tests/commands/webhooks/get.test.ts +126 -0
  941. package/temp_resend_cli/repo/tests/commands/webhooks/list.test.ts +178 -0
  942. package/temp_resend_cli/repo/tests/commands/webhooks/update.test.ts +207 -0
  943. package/temp_resend_cli/repo/tests/commands/whoami.test.ts +98 -0
  944. package/temp_resend_cli/repo/tests/e2e/smoke.test.ts +93 -0
  945. package/temp_resend_cli/repo/tests/helpers.ts +86 -0
  946. package/temp_resend_cli/repo/tests/lib/client.test.ts +71 -0
  947. package/temp_resend_cli/repo/tests/lib/config.test.ts +451 -0
  948. package/temp_resend_cli/repo/tests/lib/files.test.ts +73 -0
  949. package/temp_resend_cli/repo/tests/lib/help-text.test.ts +97 -0
  950. package/temp_resend_cli/repo/tests/lib/output.test.ts +136 -0
  951. package/temp_resend_cli/repo/tests/lib/prompts.test.ts +185 -0
  952. package/temp_resend_cli/repo/tests/lib/spinner.test.ts +166 -0
  953. package/temp_resend_cli/repo/tests/lib/table.test.ts +63 -0
  954. package/temp_resend_cli/repo/tests/lib/tty.test.ts +89 -0
  955. package/temp_resend_cli/repo/tests/lib/update-check.test.ts +179 -0
  956. package/temp_resend_cli/repo/tsconfig.json +14 -0
  957. package/temp_resend_cli/repo/vitest.config.e2e.ts +8 -0
  958. package/temp_resend_cli/repo/vitest.config.ts +10 -0
  959. package/tests/test-mcp-browser-use-smoke.sh +28 -56
  960. package/tests/test-monty-smoke.sh +32 -0
  961. package/tests/test-resend-smoke.sh +36 -0
@@ -0,0 +1,1201 @@
1
+ import asyncio
2
+
3
+ import pytest
4
+ from dirty_equals import IsList
5
+ from inline_snapshot import snapshot
6
+
7
+ import pydantic_monty
8
+ from pydantic_monty import run_monty_async, run_repl_async
9
+
10
+
11
+ def test_async():
12
+ code = 'await foobar(1, 2)'
13
+ m = pydantic_monty.Monty(code)
14
+ progress = m.start()
15
+ assert isinstance(progress, pydantic_monty.FunctionSnapshot)
16
+ assert progress.function_name == snapshot('foobar')
17
+ assert progress.args == snapshot((1, 2))
18
+ call_id = progress.call_id
19
+ progress = progress.resume(future=...)
20
+ assert isinstance(progress, pydantic_monty.FutureSnapshot)
21
+ assert progress.pending_call_ids == snapshot([call_id])
22
+ progress = progress.resume({call_id: {'return_value': 3}})
23
+ assert isinstance(progress, pydantic_monty.MontyComplete)
24
+ assert progress.output == snapshot(3)
25
+
26
+
27
+ def test_asyncio_gather():
28
+ code = """
29
+ import asyncio
30
+
31
+ await asyncio.gather(foo(1), bar(2))
32
+ """
33
+ m = pydantic_monty.Monty(code)
34
+ progress = m.start()
35
+ assert isinstance(progress, pydantic_monty.FunctionSnapshot)
36
+ assert progress.function_name == snapshot('foo')
37
+ assert progress.args == snapshot((1,))
38
+ foo_call_ids = progress.call_id
39
+
40
+ progress = progress.resume(future=...)
41
+ assert isinstance(progress, pydantic_monty.FunctionSnapshot)
42
+ assert progress.function_name == snapshot('bar')
43
+ assert progress.args == snapshot((2,))
44
+ bar_call_ids = progress.call_id
45
+ progress = progress.resume(future=...)
46
+
47
+ assert isinstance(progress, pydantic_monty.FutureSnapshot)
48
+ dump_progress = progress.dump()
49
+
50
+ assert progress.pending_call_ids == IsList(foo_call_ids, bar_call_ids, check_order=False)
51
+ progress = progress.resume({foo_call_ids: {'return_value': 3}, bar_call_ids: {'return_value': 4}})
52
+ assert isinstance(progress, pydantic_monty.MontyComplete)
53
+ assert progress.output == snapshot([3, 4])
54
+
55
+ progress2 = pydantic_monty.load_snapshot(dump_progress)
56
+ assert isinstance(progress2, pydantic_monty.FutureSnapshot)
57
+ assert progress2.pending_call_ids == IsList(foo_call_ids, bar_call_ids, check_order=False)
58
+ progress = progress2.resume({bar_call_ids: {'return_value': 14}, foo_call_ids: {'return_value': 13}})
59
+ assert isinstance(progress, pydantic_monty.MontyComplete)
60
+ assert progress.output == snapshot([13, 14])
61
+
62
+ progress3 = pydantic_monty.load_snapshot(dump_progress)
63
+ assert isinstance(progress3, pydantic_monty.FutureSnapshot)
64
+ progress = progress3.resume({bar_call_ids: {'return_value': 14}, foo_call_ids: {'future': ...}})
65
+ assert isinstance(progress, pydantic_monty.FutureSnapshot)
66
+
67
+ assert progress.pending_call_ids == [foo_call_ids]
68
+ progress = progress.resume({foo_call_ids: {'return_value': 144}})
69
+ assert isinstance(progress, pydantic_monty.MontyComplete)
70
+ assert progress.output == snapshot([144, 14])
71
+
72
+
73
+ # === Tests for run_monty_async ===
74
+
75
+
76
+ async def test_run_monty_async_sync_function():
77
+ """Test run_monty_async with a basic sync external function."""
78
+ m = pydantic_monty.Monty('get_value()')
79
+
80
+ def get_value():
81
+ return 42
82
+
83
+ result = await run_monty_async(m, external_functions={'get_value': get_value})
84
+ assert result == snapshot(42)
85
+
86
+
87
+ async def test_run_monty_async_async_function():
88
+ """Test run_monty_async with a basic async external function."""
89
+ m = pydantic_monty.Monty('await fetch_data()')
90
+
91
+ async def fetch_data():
92
+ await asyncio.sleep(0.001)
93
+ return 'async result'
94
+
95
+ result = await run_monty_async(m, external_functions={'fetch_data': fetch_data})
96
+ assert result == snapshot('async result')
97
+
98
+
99
+ async def test_run_monty_async_function_not_found():
100
+ """Test that missing external function raises wrapped error."""
101
+ m = pydantic_monty.Monty('missing_func()')
102
+
103
+ with pytest.raises(pydantic_monty.MontyRuntimeError) as exc_info:
104
+ await run_monty_async(m, external_functions={})
105
+ inner = exc_info.value.exception()
106
+ assert isinstance(inner, LookupError)
107
+ assert inner.args[0] == snapshot("Unable to find 'missing_func' in external functions dict")
108
+
109
+
110
+ async def test_run_monty_async_sync_exception():
111
+ """Test that sync function exceptions propagate correctly."""
112
+ m = pydantic_monty.Monty('fail()')
113
+
114
+ def fail():
115
+ raise ValueError('sync error')
116
+
117
+ with pytest.raises(pydantic_monty.MontyRuntimeError) as exc_info:
118
+ await run_monty_async(m, external_functions={'fail': fail})
119
+ inner = exc_info.value.exception()
120
+ assert isinstance(inner, ValueError)
121
+ assert inner.args[0] == snapshot('sync error')
122
+
123
+
124
+ async def test_run_monty_async_async_exception():
125
+ """Test that async function exceptions propagate correctly."""
126
+ m = pydantic_monty.Monty('await async_fail()')
127
+
128
+ async def async_fail():
129
+ await asyncio.sleep(0.001)
130
+ raise RuntimeError('async error')
131
+
132
+ with pytest.raises(pydantic_monty.MontyRuntimeError) as exc_info:
133
+ await run_monty_async(m, external_functions={'async_fail': async_fail})
134
+ inner = exc_info.value.exception()
135
+ assert isinstance(inner, RuntimeError)
136
+ assert inner.args[0] == snapshot('async error')
137
+
138
+
139
+ async def test_run_monty_async_exception_caught():
140
+ """Test that exceptions caught in try/except don't propagate."""
141
+ code = """
142
+ try:
143
+ fail()
144
+ except ValueError:
145
+ caught = True
146
+ caught
147
+ """
148
+ m = pydantic_monty.Monty(code)
149
+
150
+ def fail():
151
+ raise ValueError('caught error')
152
+
153
+ result = await run_monty_async(m, external_functions={'fail': fail})
154
+ assert result == snapshot(True)
155
+
156
+
157
+ async def test_run_monty_async_multiple_async_functions():
158
+ """Test asyncio.gather with multiple async functions."""
159
+ code = """
160
+ import asyncio
161
+ await asyncio.gather(fetch_a(), fetch_b())
162
+ """
163
+ m = pydantic_monty.Monty(code)
164
+
165
+ async def fetch_a():
166
+ await asyncio.sleep(0.01)
167
+ return 'a'
168
+
169
+ async def fetch_b():
170
+ await asyncio.sleep(0.005)
171
+ return 'b'
172
+
173
+ result = await run_monty_async(m, external_functions={'fetch_a': fetch_a, 'fetch_b': fetch_b})
174
+ assert result == snapshot(['a', 'b'])
175
+
176
+
177
+ async def test_run_monty_async_mixed_sync_async():
178
+ """Test mix of sync and async external functions."""
179
+ code = """
180
+ sync_val = sync_func()
181
+ async_val = await async_func()
182
+ sync_val + async_val
183
+ """
184
+ m = pydantic_monty.Monty(code)
185
+
186
+ def sync_func():
187
+ return 10
188
+
189
+ async def async_func():
190
+ await asyncio.sleep(0.001)
191
+ return 5
192
+
193
+ result = await run_monty_async(m, external_functions={'sync_func': sync_func, 'async_func': async_func})
194
+ assert result == snapshot(15)
195
+
196
+
197
+ async def test_run_monty_async_with_inputs():
198
+ """Test run_monty_async with inputs parameter."""
199
+ m = pydantic_monty.Monty('process(x, y)', inputs=['x', 'y'])
200
+
201
+ def process(a: int, b: int) -> int:
202
+ return a * b
203
+
204
+ result = await run_monty_async(m, inputs={'x': 6, 'y': 7}, external_functions={'process': process})
205
+ assert result == snapshot(42)
206
+
207
+
208
+ async def test_run_monty_async_with_print_callback():
209
+ """Test run_monty_async with print_callback parameter."""
210
+ output: list[tuple[str, str]] = []
211
+
212
+ def callback(stream: str, text: str) -> None:
213
+ output.append((stream, text))
214
+
215
+ m = pydantic_monty.Monty('print("hello from async")')
216
+ result = await run_monty_async(m, print_callback=callback)
217
+ assert result is None
218
+ assert output == snapshot([('stdout', 'hello from async'), ('stdout', '\n')])
219
+
220
+
221
+ async def test_run_monty_async_function_returning_none():
222
+ """Test async function that returns None."""
223
+ m = pydantic_monty.Monty('do_nothing()')
224
+
225
+ def do_nothing():
226
+ return None
227
+
228
+ result = await run_monty_async(m, external_functions={'do_nothing': do_nothing})
229
+ assert result is None
230
+
231
+
232
+ async def test_run_monty_async_no_external_calls():
233
+ """Test run_monty_async when code has no external calls."""
234
+ m = pydantic_monty.Monty('1 + 2 + 3')
235
+ result = await run_monty_async(m)
236
+ assert result == snapshot(6)
237
+
238
+
239
+ # === Tests for run_monty_async with os parameter ===
240
+
241
+
242
+ async def test_run_monty_async_with_os():
243
+ """run_monty_async can use OSAccess for file operations."""
244
+ from pydantic_monty import MemoryFile, OSAccess
245
+
246
+ fs = OSAccess([MemoryFile('/test.txt', content='hello world')])
247
+
248
+ m = pydantic_monty.Monty(
249
+ """
250
+ from pathlib import Path
251
+ Path('/test.txt').read_text()
252
+ """,
253
+ )
254
+
255
+ result = await run_monty_async(m, os=fs)
256
+ assert result == snapshot('hello world')
257
+
258
+
259
+ async def test_run_monty_async_os_with_external_functions():
260
+ """run_monty_async can combine OSAccess with external functions."""
261
+ from pydantic_monty import MemoryFile, OSAccess
262
+
263
+ fs = OSAccess([MemoryFile('/data.txt', content='test data')])
264
+
265
+ async def process(text: str) -> str:
266
+ return text.upper()
267
+
268
+ m = pydantic_monty.Monty(
269
+ """
270
+ from pathlib import Path
271
+ content = Path('/data.txt').read_text()
272
+ await process(content)
273
+ """,
274
+ )
275
+
276
+ result = await run_monty_async(
277
+ m,
278
+ external_functions={'process': process},
279
+ os=fs,
280
+ )
281
+ assert result == snapshot('TEST DATA')
282
+
283
+
284
+ async def test_run_monty_async_os_file_not_found():
285
+ """run_monty_async propagates OS errors correctly."""
286
+ from pydantic_monty import OSAccess
287
+
288
+ fs = OSAccess()
289
+
290
+ m = pydantic_monty.Monty(
291
+ """
292
+ from pathlib import Path
293
+ Path('/missing.txt').read_text()
294
+ """,
295
+ )
296
+
297
+ with pytest.raises(pydantic_monty.MontyRuntimeError) as exc_info:
298
+ await run_monty_async(m, os=fs)
299
+ assert str(exc_info.value) == snapshot("FileNotFoundError: [Errno 2] No such file or directory: '/missing.txt'")
300
+
301
+
302
+ async def test_run_monty_async_os_not_provided():
303
+ """run_monty_async raises error when OS function called without os handler."""
304
+ m = pydantic_monty.Monty(
305
+ """
306
+ from pathlib import Path
307
+ Path('/test.txt').exists()
308
+ """,
309
+ )
310
+
311
+ with pytest.raises(pydantic_monty.MontyRuntimeError) as exc_info:
312
+ await run_monty_async(m)
313
+ inner = exc_info.value.exception()
314
+ assert isinstance(inner, RuntimeError)
315
+ assert 'OS function' in inner.args[0]
316
+ assert 'no os handler provided' in inner.args[0]
317
+
318
+
319
+ async def test_run_monty_async_nested_gather_with_external_functions():
320
+ """Test nested asyncio.gather with spawned tasks and external async functions.
321
+
322
+ https://github.com/pydantic/monty/pull/174
323
+
324
+ Reproduces the pattern from stack_overflow.py: outer gather spawns 3 coroutine tasks,
325
+ each doing a sequential await then an inner gather with 2 external futures.
326
+ """
327
+ code = """\
328
+ import asyncio
329
+
330
+ async def get_city_weather(city_name: str):
331
+ coords = await get_lat_lng(location_description=city_name)
332
+ lat, lng = coords['lat'], coords['lng']
333
+ temp_task = get_temp(lat=lat, lng=lng)
334
+ desc_task = get_weather_description(lat=lat, lng=lng)
335
+ temp, desc = await asyncio.gather(temp_task, desc_task)
336
+ return {
337
+ 'city': city_name,
338
+ 'temp': temp,
339
+ 'description': desc
340
+ }
341
+
342
+ async def main():
343
+ cities = ['London', 'Paris', 'Tokyo']
344
+ results = await asyncio.gather(*(get_city_weather(city) for city in cities))
345
+ return results
346
+
347
+ await main()
348
+ """
349
+ m = pydantic_monty.Monty(code)
350
+
351
+ city_coords = {
352
+ 'London': {'lat': 51.5, 'lng': -0.1},
353
+ 'Paris': {'lat': 48.9, 'lng': 2.3},
354
+ 'Tokyo': {'lat': 35.7, 'lng': 139.7},
355
+ }
356
+ city_temps = {
357
+ (51.5, -0.1): 15.0,
358
+ (48.9, 2.3): 18.0,
359
+ (35.7, 139.7): 22.0,
360
+ }
361
+ city_descs = {
362
+ (51.5, -0.1): 'Cloudy',
363
+ (48.9, 2.3): 'Sunny',
364
+ (35.7, 139.7): 'Humid',
365
+ }
366
+
367
+ async def get_lat_lng(location_description: str):
368
+ return city_coords[location_description]
369
+
370
+ async def get_temp(lat: float, lng: float):
371
+ return city_temps[(lat, lng)]
372
+
373
+ async def get_weather_description(lat: float, lng: float):
374
+ return city_descs[(lat, lng)]
375
+
376
+ result = await run_monty_async(
377
+ m,
378
+ external_functions={
379
+ 'get_lat_lng': get_lat_lng,
380
+ 'get_temp': get_temp,
381
+ 'get_weather_description': get_weather_description,
382
+ },
383
+ )
384
+ assert result == snapshot(
385
+ [
386
+ {'city': 'London', 'temp': 15.0, 'description': 'Cloudy'},
387
+ {'city': 'Paris', 'temp': 18.0, 'description': 'Sunny'},
388
+ {'city': 'Tokyo', 'temp': 22.0, 'description': 'Humid'},
389
+ ]
390
+ )
391
+
392
+
393
+ async def test_run_monty_async_os_write_and_read():
394
+ """run_monty_async supports both reading and writing files."""
395
+ from pydantic_monty import MemoryFile, OSAccess
396
+
397
+ fs = OSAccess([MemoryFile('/file.txt', content='original')])
398
+
399
+ m = pydantic_monty.Monty(
400
+ """
401
+ from pathlib import Path
402
+ p = Path('/file.txt')
403
+ p.write_text('updated')
404
+ p.read_text()
405
+ """,
406
+ )
407
+
408
+ result = await run_monty_async(m, os=fs)
409
+ assert result == snapshot('updated')
410
+
411
+
412
+ # === Tests for MontyRepl.feed_start() with async patterns ===
413
+
414
+
415
+ def test_repl_feed_start_async_gather():
416
+ """MontyRepl.feed_start supports asyncio.gather with multiple futures."""
417
+ code = """
418
+ import asyncio
419
+
420
+ await asyncio.gather(foo(1), bar(2))
421
+ """
422
+ repl = pydantic_monty.MontyRepl()
423
+ progress = repl.feed_start(code)
424
+ assert isinstance(progress, pydantic_monty.FunctionSnapshot)
425
+ assert progress.function_name == snapshot('foo')
426
+ foo_call_id = progress.call_id
427
+
428
+ progress = progress.resume(future=...)
429
+ assert isinstance(progress, pydantic_monty.FunctionSnapshot)
430
+ assert progress.function_name == snapshot('bar')
431
+ bar_call_id = progress.call_id
432
+ progress = progress.resume(future=...)
433
+
434
+ assert isinstance(progress, pydantic_monty.FutureSnapshot)
435
+ from dirty_equals import IsList
436
+
437
+ assert progress.pending_call_ids == IsList(foo_call_id, bar_call_id, check_order=False)
438
+ progress = progress.resume({foo_call_id: {'return_value': 3}, bar_call_id: {'return_value': 4}})
439
+ assert isinstance(progress, pydantic_monty.MontyComplete)
440
+ assert progress.output == snapshot([3, 4])
441
+
442
+ # REPL should still be usable after async completion
443
+ assert repl.feed_run('1 + 1') == snapshot(2)
444
+
445
+
446
+ def test_repl_feed_start_async_state_persistence():
447
+ """MontyRepl.feed_start async: REPL state persists across async snippets."""
448
+ repl = pydantic_monty.MontyRepl()
449
+ repl.feed_run('x = 10')
450
+
451
+ progress = repl.feed_start('result = await fetch(x)')
452
+ assert isinstance(progress, pydantic_monty.FunctionSnapshot)
453
+ assert progress.function_name == snapshot('fetch')
454
+ assert progress.args == snapshot((10,))
455
+ call_id = progress.call_id
456
+
457
+ progress = progress.resume(future=...)
458
+ assert isinstance(progress, pydantic_monty.FutureSnapshot)
459
+ progress = progress.resume({call_id: {'return_value': 'fetched'}})
460
+ assert isinstance(progress, pydantic_monty.MontyComplete)
461
+ assert progress.output is None # assignment, not expression
462
+
463
+ assert repl.feed_run('result') == snapshot('fetched')
464
+ assert repl.feed_run('x') == snapshot(10)
465
+
466
+
467
+ # === Tests for run_repl_async ===
468
+
469
+
470
+ async def test_run_repl_async_sync_function():
471
+ """run_repl_async with a basic sync external function."""
472
+ repl = pydantic_monty.MontyRepl()
473
+
474
+ def get_value():
475
+ return 42
476
+
477
+ result = await run_repl_async(repl, 'get_value()', external_functions={'get_value': get_value})
478
+ assert result == snapshot(42)
479
+
480
+
481
+ async def test_run_repl_async_async_function():
482
+ """run_repl_async with a basic async external function."""
483
+ repl = pydantic_monty.MontyRepl()
484
+
485
+ async def fetch_data():
486
+ await asyncio.sleep(0.001)
487
+ return 'async result'
488
+
489
+ result = await run_repl_async(repl, 'await fetch_data()', external_functions={'fetch_data': fetch_data})
490
+ assert result == snapshot('async result')
491
+
492
+
493
+ async def test_run_repl_async_state_persists():
494
+ """REPL state persists across multiple run_repl_async calls."""
495
+ repl = pydantic_monty.MontyRepl()
496
+
497
+ def double(x: int) -> int:
498
+ return x * 2
499
+
500
+ ext = {'double': double}
501
+ await run_repl_async(repl, 'x = 10', external_functions=ext)
502
+ await run_repl_async(repl, 'y = double(x)', external_functions=ext)
503
+ result = await run_repl_async(repl, 'y', external_functions=ext)
504
+ assert result == snapshot(20)
505
+
506
+
507
+ async def test_run_repl_async_async_state_persists():
508
+ """REPL state persists across async calls with await."""
509
+ repl = pydantic_monty.MontyRepl()
510
+
511
+ async def fetch(key: str) -> str:
512
+ return f'value_{key}'
513
+
514
+ ext = {'fetch': fetch}
515
+ await run_repl_async(repl, "a = await fetch('one')", external_functions=ext)
516
+ await run_repl_async(repl, "b = await fetch('two')", external_functions=ext)
517
+ result = await run_repl_async(repl, 'a + b', external_functions=ext)
518
+ assert result == snapshot('value_onevalue_two')
519
+
520
+
521
+ async def test_run_repl_async_gather():
522
+ """run_repl_async handles asyncio.gather with multiple futures."""
523
+ repl = pydantic_monty.MontyRepl()
524
+
525
+ async def fetch_a():
526
+ await asyncio.sleep(0.01)
527
+ return 'a'
528
+
529
+ async def fetch_b():
530
+ await asyncio.sleep(0.005)
531
+ return 'b'
532
+
533
+ code = """\
534
+ import asyncio
535
+ await asyncio.gather(fetch_a(), fetch_b())
536
+ """
537
+ result = await run_repl_async(repl, code, external_functions={'fetch_a': fetch_a, 'fetch_b': fetch_b})
538
+ assert result == snapshot(['a', 'b'])
539
+
540
+
541
+ async def test_run_repl_async_function_not_found():
542
+ """run_repl_async raises error for missing external function."""
543
+ repl = pydantic_monty.MontyRepl()
544
+
545
+ with pytest.raises(pydantic_monty.MontyRuntimeError) as exc_info:
546
+ await run_repl_async(repl, 'missing_func()', external_functions={})
547
+ inner = exc_info.value.exception()
548
+ assert isinstance(inner, LookupError)
549
+ assert inner.args[0] == snapshot("Unable to find 'missing_func' in external functions dict")
550
+
551
+
552
+ async def test_run_repl_async_error_preserves_state():
553
+ """REPL state is preserved after an error in run_repl_async."""
554
+ repl = pydantic_monty.MontyRepl()
555
+ await run_repl_async(repl, 'x = 42')
556
+
557
+ def fail():
558
+ raise ValueError('oops')
559
+
560
+ with pytest.raises(pydantic_monty.MontyRuntimeError):
561
+ await run_repl_async(repl, 'fail()', external_functions={'fail': fail})
562
+
563
+ result = await run_repl_async(repl, 'x')
564
+ assert result == snapshot(42)
565
+
566
+
567
+ async def test_run_repl_async_with_inputs():
568
+ """run_repl_async supports inputs parameter."""
569
+ repl = pydantic_monty.MontyRepl()
570
+
571
+ def add(a: int, b: int) -> int:
572
+ return a + b
573
+
574
+ result = await run_repl_async(repl, 'add(x, y)', inputs={'x': 3, 'y': 4}, external_functions={'add': add})
575
+ assert result == snapshot(7)
576
+
577
+
578
+ async def test_run_repl_async_with_print_callback():
579
+ """run_repl_async supports print_callback parameter."""
580
+ repl = pydantic_monty.MontyRepl()
581
+ output: list[str] = []
582
+
583
+ def callback(stream: str, text: str) -> None:
584
+ output.append(text)
585
+
586
+ await run_repl_async(repl, 'print("hello from repl")', print_callback=callback)
587
+ assert output == snapshot(['hello from repl', '\n'])
588
+
589
+
590
+ async def test_run_repl_async_with_os():
591
+ """run_repl_async supports OS access."""
592
+ from pydantic_monty import MemoryFile, OSAccess
593
+
594
+ repl = pydantic_monty.MontyRepl()
595
+ fs = OSAccess([MemoryFile('/test.txt', content='repl content')])
596
+
597
+ code = """\
598
+ from pathlib import Path
599
+ Path('/test.txt').read_text()
600
+ """
601
+ result = await run_repl_async(repl, code, os=fs)
602
+ assert result == snapshot('repl content')
603
+
604
+
605
+ async def test_run_repl_async_mixed_sync_async():
606
+ """run_repl_async handles mix of sync and async functions."""
607
+ repl = pydantic_monty.MontyRepl()
608
+
609
+ def sync_func():
610
+ return 10
611
+
612
+ async def async_func():
613
+ await asyncio.sleep(0.001)
614
+ return 5
615
+
616
+ code = """\
617
+ sync_val = sync_func()
618
+ async_val = await async_func()
619
+ sync_val + async_val
620
+ """
621
+ result = await run_repl_async(repl, code, external_functions={'sync_func': sync_func, 'async_func': async_func})
622
+ assert result == snapshot(15)
623
+
624
+
625
+ async def test_run_repl_async_no_external_calls():
626
+ """run_repl_async works when code has no external calls."""
627
+ repl = pydantic_monty.MontyRepl()
628
+ result = await run_repl_async(repl, '1 + 2 + 3')
629
+ assert result == snapshot(6)
630
+
631
+
632
+ # === LLM agent patterns: realistic run_repl_async scenarios ===
633
+
634
+
635
+ async def test_repl_llm_iterative_data_collection():
636
+ """LLM defines a helper, collects data in batches, accumulates results across snippets."""
637
+ repl = pydantic_monty.MontyRepl()
638
+
639
+ responses: dict[int, list[dict[str, object]]] = {
640
+ 0: [{'id': 1, 'name': 'Alice'}, {'id': 2, 'name': 'Bob'}],
641
+ 2: [{'id': 3, 'name': 'Charlie'}],
642
+ 3: [],
643
+ }
644
+
645
+ async def fetch_users(offset: int, limit: int) -> list[dict[str, object]]:
646
+ return responses.get(offset, [])
647
+
648
+ ext = {'fetch_users': fetch_users}
649
+
650
+ # Snippet 1: LLM sets up accumulator
651
+ await run_repl_async(repl, 'all_users = []', external_functions=ext)
652
+
653
+ # Snippet 2: LLM fetches first batch
654
+ await run_repl_async(
655
+ repl,
656
+ """\
657
+ batch = await fetch_users(0, 2)
658
+ all_users = all_users + batch
659
+ len(batch)
660
+ """,
661
+ external_functions=ext,
662
+ )
663
+
664
+ # Snippet 3: LLM fetches next batch using state
665
+ await run_repl_async(
666
+ repl,
667
+ """\
668
+ batch = await fetch_users(len(all_users), 2)
669
+ all_users = all_users + batch
670
+ len(batch)
671
+ """,
672
+ external_functions=ext,
673
+ )
674
+
675
+ # Snippet 4: LLM fetches again, gets empty — realizes done
676
+ await run_repl_async(
677
+ repl,
678
+ """\
679
+ batch = await fetch_users(len(all_users), 2)
680
+ all_users = all_users + batch
681
+ len(batch)
682
+ """,
683
+ external_functions=ext,
684
+ )
685
+
686
+ # Snippet 5: LLM extracts final result
687
+ result = await run_repl_async(repl, '[u["name"] for u in all_users]', external_functions=ext)
688
+ assert result == snapshot(['Alice', 'Bob', 'Charlie'])
689
+
690
+
691
+ async def test_repl_llm_error_recovery_retry():
692
+ """LLM catches an error, adjusts approach, retries successfully."""
693
+ repl = pydantic_monty.MontyRepl()
694
+ call_count = 0
695
+
696
+ async def flaky_api(query: str) -> str:
697
+ nonlocal call_count
698
+ call_count += 1
699
+ if call_count == 1:
700
+ raise ConnectionError('server unavailable')
701
+ return f'result for {query}'
702
+
703
+ ext = {'flaky_api': flaky_api}
704
+
705
+ # Snippet 1: LLM tries, gets error
706
+ with pytest.raises(pydantic_monty.MontyRuntimeError):
707
+ await run_repl_async(repl, "data = await flaky_api('test')", external_functions=ext)
708
+
709
+ # Snippet 2: LLM wraps in try/except and retries
710
+ result = await run_repl_async(
711
+ repl,
712
+ """\
713
+ try:
714
+ data = await flaky_api('test')
715
+ except Exception as e:
716
+ data = 'fallback'
717
+ data
718
+ """,
719
+ external_functions=ext,
720
+ )
721
+ assert result == snapshot('result for test')
722
+
723
+
724
+ async def test_repl_llm_redefine_helper_function():
725
+ """LLM defines a function, uses it, then redefines it with improvements."""
726
+ repl = pydantic_monty.MontyRepl()
727
+
728
+ async def fetch(url: str) -> str:
729
+ return f'<html>{url}</html>'
730
+
731
+ ext = {'fetch': fetch}
732
+
733
+ # Snippet 1: LLM defines initial parser
734
+ await run_repl_async(
735
+ repl,
736
+ """\
737
+ def parse_title(html):
738
+ return html
739
+ """,
740
+ external_functions=ext,
741
+ )
742
+
743
+ # Snippet 2: LLM uses it, gets raw html back
744
+ result = await run_repl_async(
745
+ repl,
746
+ """\
747
+ html = await fetch('example.com')
748
+ parse_title(html)
749
+ """,
750
+ external_functions=ext,
751
+ )
752
+ assert result == snapshot('<html>example.com</html>')
753
+
754
+ # Snippet 3: LLM redefines parser with better logic
755
+ await run_repl_async(
756
+ repl,
757
+ """\
758
+ def parse_title(html):
759
+ start = html.find('>') + 1
760
+ end = html.rfind('<')
761
+ return html[start:end]
762
+ """,
763
+ external_functions=ext,
764
+ )
765
+
766
+ # Snippet 4: uses improved parser on previously fetched data
767
+ result = await run_repl_async(repl, 'parse_title(html)', external_functions=ext)
768
+ assert result == snapshot('example.com')
769
+
770
+
771
+ async def test_repl_llm_sequential_async_pipeline():
772
+ """LLM builds a data pipeline: fetch -> transform -> store, each step depends on previous."""
773
+ repl = pydantic_monty.MontyRepl()
774
+
775
+ async def search(query: str) -> list[str]:
776
+ return [f'{query}_result_1', f'{query}_result_2']
777
+
778
+ async def summarize(text: str) -> str:
779
+ return f'summary({text})'
780
+
781
+ records: list[str] = []
782
+
783
+ def record(item: str) -> None:
784
+ records.append(item)
785
+
786
+ ext = {'search': search, 'summarize': summarize, 'record': record}
787
+
788
+ code = """\
789
+ results = await search('python async')
790
+ summaries = []
791
+ for r in results:
792
+ s = await summarize(r)
793
+ summaries.append(s)
794
+ record(s)
795
+ summaries
796
+ """
797
+ result = await run_repl_async(repl, code, external_functions=ext)
798
+ assert result == snapshot(['summary(python async_result_1)', 'summary(python async_result_2)'])
799
+ assert records == snapshot(['summary(python async_result_1)', 'summary(python async_result_2)'])
800
+
801
+
802
+ async def test_repl_llm_gather_fan_out():
803
+ """LLM uses asyncio.gather to fan out many concurrent requests."""
804
+ repl = pydantic_monty.MontyRepl()
805
+
806
+ async def fetch_price(item: str) -> float:
807
+ prices = {'apple': 1.5, 'banana': 0.75, 'cherry': 3.0, 'date': 5.0, 'elderberry': 8.0}
808
+ return prices[item]
809
+
810
+ ext = {'fetch_price': fetch_price}
811
+
812
+ code = """\
813
+ import asyncio
814
+
815
+ items = ['apple', 'banana', 'cherry', 'date', 'elderberry']
816
+ prices = await asyncio.gather(*(fetch_price(item) for item in items))
817
+ dict(zip(items, prices))
818
+ """
819
+ result = await run_repl_async(repl, code, external_functions=ext)
820
+ assert result == snapshot({'apple': 1.5, 'banana': 0.75, 'cherry': 3.0, 'date': 5.0, 'elderberry': 8.0})
821
+
822
+
823
+ async def test_repl_llm_try_except_around_external():
824
+ """LLM wraps individual external calls in try/except for graceful degradation."""
825
+ repl = pydantic_monty.MontyRepl()
826
+
827
+ def fetch_data(key: str) -> str:
828
+ if key == 'bad':
829
+ raise KeyError(f'no data for {key}')
830
+ return f'data_{key}'
831
+
832
+ ext = {'fetch_data': fetch_data}
833
+
834
+ code = """\
835
+ results = {}
836
+ for key in ['good', 'bad', 'also_good']:
837
+ try:
838
+ results[key] = fetch_data(key)
839
+ except KeyError:
840
+ results[key] = 'missing'
841
+ results
842
+ """
843
+ result = await run_repl_async(repl, code, external_functions=ext)
844
+ assert result == snapshot({'good': 'data_good', 'bad': 'missing', 'also_good': 'data_also_good'})
845
+
846
+
847
+ async def test_repl_llm_conditional_external_call():
848
+ """LLM only calls external function when a condition is met."""
849
+ repl = pydantic_monty.MontyRepl()
850
+ call_count = 0
851
+
852
+ async def expensive_lookup(key: str) -> str:
853
+ nonlocal call_count
854
+ call_count += 1
855
+ return f'looked up {key}'
856
+
857
+ ext = {'expensive_lookup': expensive_lookup}
858
+
859
+ # Snippet 1: set up a cache
860
+ await run_repl_async(repl, "cache = {'x': 'cached_x'}", external_functions=ext)
861
+
862
+ # Snippet 2: LLM checks cache before calling
863
+ code = """\
864
+ results = []
865
+ for key in ['x', 'y', 'x']:
866
+ if key in cache:
867
+ results.append(cache[key])
868
+ else:
869
+ val = await expensive_lookup(key)
870
+ cache[key] = val
871
+ results.append(val)
872
+ results
873
+ """
874
+ result = await run_repl_async(repl, code, external_functions=ext)
875
+ assert result == snapshot(['cached_x', 'looked up y', 'cached_x'])
876
+ assert call_count == 1 # only 'y' triggered a call
877
+
878
+
879
+ async def test_repl_llm_side_effect_recording():
880
+ """LLM uses a side-effect-only external function to record structured data."""
881
+ repl = pydantic_monty.MontyRepl()
882
+ recorded: list[dict[str, object]] = []
883
+
884
+ def record_model(name: str, params: str, price: float) -> None:
885
+ recorded.append({'name': name, 'params': params, 'price': price})
886
+
887
+ async def get_models() -> list[dict[str, str]]:
888
+ return [
889
+ {'name': 'gpt-4', 'params': '1.7T'},
890
+ {'name': 'claude-3', 'params': '???'},
891
+ ]
892
+
893
+ ext = {'record_model': record_model, 'get_models': get_models}
894
+
895
+ code = """\
896
+ models = await get_models()
897
+ for m in models:
898
+ record_model(m['name'], m['params'], 0.01)
899
+ len(models)
900
+ """
901
+ result = await run_repl_async(repl, code, external_functions=ext)
902
+ assert result == snapshot(2)
903
+ assert recorded == snapshot(
904
+ [{'name': 'gpt-4', 'params': '1.7T', 'price': 0.01}, {'name': 'claude-3', 'params': '???', 'price': 0.01}]
905
+ )
906
+
907
+
908
+ async def test_repl_llm_helper_wrapping_externals_with_retry():
909
+ """LLM defines a helper function that wraps external calls with retry logic."""
910
+ repl = pydantic_monty.MontyRepl()
911
+ attempt_counts: dict[str, int] = {}
912
+
913
+ def unreliable_fetch(url: str) -> str:
914
+ attempt_counts.setdefault(url, 0)
915
+ attempt_counts[url] += 1
916
+ if attempt_counts[url] < 2:
917
+ raise ValueError('temporary failure')
918
+ return f'content of {url}'
919
+
920
+ ext = {'unreliable_fetch': unreliable_fetch}
921
+
922
+ # Snippet 1: LLM defines retry helper
923
+ await run_repl_async(
924
+ repl,
925
+ """\
926
+ def fetch_with_retry(url, max_retries=3):
927
+ for i in range(max_retries):
928
+ try:
929
+ return unreliable_fetch(url)
930
+ except ValueError:
931
+ if i == max_retries - 1:
932
+ raise
933
+ raise ValueError('should not reach here')
934
+ """,
935
+ external_functions=ext,
936
+ )
937
+
938
+ # Snippet 2: LLM uses the retry helper
939
+ result = await run_repl_async(repl, "fetch_with_retry('example.com')", external_functions=ext)
940
+ assert result == snapshot('content of example.com')
941
+ assert attempt_counts == snapshot({'example.com': 2})
942
+
943
+
944
+ async def test_repl_llm_nested_gather_with_sequential_deps():
945
+ """LLM does gather of tasks where each task has sequential async steps internally."""
946
+ repl = pydantic_monty.MontyRepl()
947
+
948
+ async def get_user(user_id: int) -> dict[str, object]:
949
+ return {'id': user_id, 'name': f'user_{user_id}'}
950
+
951
+ async def get_posts(user_id: int) -> list[str]:
952
+ return [f'post_{user_id}_1', f'post_{user_id}_2']
953
+
954
+ ext = {'get_user': get_user, 'get_posts': get_posts}
955
+
956
+ code = """\
957
+ import asyncio
958
+
959
+ async def get_user_with_posts(uid):
960
+ user = await get_user(uid)
961
+ posts = await get_posts(uid)
962
+ user['posts'] = posts
963
+ return user
964
+
965
+ results = await asyncio.gather(
966
+ get_user_with_posts(1),
967
+ get_user_with_posts(2),
968
+ get_user_with_posts(3),
969
+ )
970
+ results
971
+ """
972
+ result = await run_repl_async(repl, code, external_functions=ext)
973
+ assert result == snapshot(
974
+ [
975
+ {'id': 1, 'name': 'user_1', 'posts': ['post_1_1', 'post_1_2']},
976
+ {'id': 2, 'name': 'user_2', 'posts': ['post_2_1', 'post_2_2']},
977
+ {'id': 3, 'name': 'user_3', 'posts': ['post_3_1', 'post_3_2']},
978
+ ]
979
+ )
980
+
981
+
982
+ async def test_repl_llm_external_returns_complex_nested_structure():
983
+ """LLM processes deeply nested API response from external function."""
984
+ repl = pydantic_monty.MontyRepl()
985
+
986
+ async def get_api_response() -> dict[str, object]:
987
+ return {
988
+ 'status': 'ok',
989
+ 'data': {
990
+ 'users': [
991
+ {'name': 'Alice', 'scores': [95, 87, 92]},
992
+ {'name': 'Bob', 'scores': [78, 85, 90]},
993
+ ],
994
+ 'metadata': {'page': 1, 'total': 2},
995
+ },
996
+ }
997
+
998
+ ext = {'get_api_response': get_api_response}
999
+
1000
+ # Snippet 1: fetch and store
1001
+ await run_repl_async(repl, 'response = await get_api_response()', external_functions=ext)
1002
+
1003
+ # Snippet 2: LLM navigates nested structure
1004
+ result = await run_repl_async(
1005
+ repl,
1006
+ """\
1007
+ users = response['data']['users']
1008
+ averages = {}
1009
+ for u in users:
1010
+ avg = sum(u['scores']) / len(u['scores'])
1011
+ averages[u['name']] = round(avg, 1)
1012
+ averages
1013
+ """,
1014
+ external_functions=ext,
1015
+ )
1016
+ assert result == snapshot({'Alice': 91.3, 'Bob': 84.3})
1017
+
1018
+
1019
+ async def test_repl_llm_external_with_kwargs():
1020
+ """LLM calls external functions using keyword arguments."""
1021
+ repl = pydantic_monty.MontyRepl()
1022
+
1023
+ async def search(query: str, limit: int = 10, offset: int = 0) -> dict[str, object]:
1024
+ return {'query': query, 'limit': limit, 'offset': offset, 'results': [f'{query}_{i}' for i in range(limit)]}
1025
+
1026
+ ext = {'search': search}
1027
+
1028
+ code = """\
1029
+ page1 = await search('test', limit=2, offset=0)
1030
+ page2 = await search('test', limit=2, offset=2)
1031
+ page1['results'] + page2['results']
1032
+ """
1033
+ result = await run_repl_async(repl, code, external_functions=ext)
1034
+ assert result == snapshot(['test_0', 'test_1', 'test_0', 'test_1'])
1035
+
1036
+
1037
+ async def test_repl_llm_os_read_then_process_with_external():
1038
+ """LLM reads a file via OS, then processes content with an async external function."""
1039
+ from pydantic_monty import MemoryFile, OSAccess
1040
+
1041
+ repl = pydantic_monty.MontyRepl()
1042
+ fs = OSAccess([MemoryFile('/data.csv', content='alice,95\nbob,87\ncharlie,92')])
1043
+
1044
+ async def analyze(text: str) -> dict[str, int]:
1045
+ rows = text.strip().split('\n')
1046
+ return {name: int(score) for name, score in (r.split(',') for r in rows)}
1047
+
1048
+ ext = {'analyze': analyze}
1049
+
1050
+ # Snippet 1: read file
1051
+ await run_repl_async(
1052
+ repl,
1053
+ """\
1054
+ from pathlib import Path
1055
+ raw = Path('/data.csv').read_text()
1056
+ """,
1057
+ external_functions=ext,
1058
+ os=fs,
1059
+ )
1060
+
1061
+ # Snippet 2: process with external
1062
+ result = await run_repl_async(repl, 'await analyze(raw)', external_functions=ext, os=fs)
1063
+ assert result == snapshot({'alice': 95, 'bob': 87, 'charlie': 92})
1064
+
1065
+
1066
+ async def test_repl_llm_long_multi_step_session():
1067
+ """Simulates a multi-step LLM agent session: setup, explore, process, summarize."""
1068
+ repl = pydantic_monty.MontyRepl()
1069
+
1070
+ db: dict[str, list[dict[str, object]]] = {
1071
+ 'products': [
1072
+ {'name': 'Widget', 'price': 9.99, 'category': 'tools'},
1073
+ {'name': 'Gadget', 'price': 24.99, 'category': 'electronics'},
1074
+ {'name': 'Doohickey', 'price': 4.99, 'category': 'tools'},
1075
+ {'name': 'Thingamajig', 'price': 49.99, 'category': 'electronics'},
1076
+ ],
1077
+ }
1078
+
1079
+ async def query_db(table: str, filters: dict[str, str] | None = None) -> list[dict[str, object]]:
1080
+ rows = db.get(table, [])
1081
+ if filters:
1082
+ for k, v in filters.items():
1083
+ rows = [r for r in rows if r.get(k) == v]
1084
+ return rows
1085
+
1086
+ ext = {'query_db': query_db}
1087
+
1088
+ # Step 1: LLM explores what's available
1089
+ result = await run_repl_async(repl, 'await query_db("products")', external_functions=ext)
1090
+ assert len(result) == 4
1091
+
1092
+ # Step 2: LLM filters by category
1093
+ await run_repl_async(
1094
+ repl,
1095
+ "tools = await query_db('products', filters={'category': 'tools'})",
1096
+ external_functions=ext,
1097
+ )
1098
+
1099
+ # Step 3: LLM computes stats
1100
+ result = await run_repl_async(
1101
+ repl,
1102
+ """\
1103
+ total = sum(p['price'] for p in tools)
1104
+ avg = total / len(tools)
1105
+ {'count': len(tools), 'total': round(total, 2), 'average': round(avg, 2)}
1106
+ """,
1107
+ external_functions=ext,
1108
+ )
1109
+ assert result == snapshot({'count': 2, 'total': 14.98, 'average': 7.49})
1110
+
1111
+ # Step 4: LLM also checks electronics
1112
+ await run_repl_async(
1113
+ repl,
1114
+ "electronics = await query_db('products', filters={'category': 'electronics'})",
1115
+ external_functions=ext,
1116
+ )
1117
+
1118
+ # Step 5: LLM builds final summary from accumulated state
1119
+ result = await run_repl_async(
1120
+ repl,
1121
+ """\
1122
+ summary = {}
1123
+ for cat, items in [('tools', tools), ('electronics', electronics)]:
1124
+ summary[cat] = {
1125
+ 'count': len(items),
1126
+ 'total': round(sum(i['price'] for i in items), 2),
1127
+ 'items': [i['name'] for i in items],
1128
+ }
1129
+ summary
1130
+ """,
1131
+ external_functions=ext,
1132
+ )
1133
+ assert result == snapshot(
1134
+ {
1135
+ 'tools': {'count': 2, 'total': 14.98, 'items': ['Widget', 'Doohickey']},
1136
+ 'electronics': {'count': 2, 'total': 74.98, 'items': ['Gadget', 'Thingamajig']},
1137
+ }
1138
+ )
1139
+
1140
+
1141
+ async def test_repl_llm_string_manipulation_of_external_result():
1142
+ """LLM fetches HTML-like content and does string processing across snippets."""
1143
+ repl = pydantic_monty.MontyRepl()
1144
+
1145
+ async def fetch_page(url: str) -> str:
1146
+ return '<title>Test Page</title><body><p>Hello</p><p>World</p></body>'
1147
+
1148
+ ext = {'fetch_page': fetch_page}
1149
+
1150
+ await run_repl_async(repl, "html = await fetch_page('example.com')", external_functions=ext)
1151
+
1152
+ # LLM extracts title
1153
+ result = await run_repl_async(
1154
+ repl,
1155
+ """\
1156
+ start = html.find('<title>') + len('<title>')
1157
+ end = html.find('</title>')
1158
+ title = html[start:end]
1159
+ title
1160
+ """,
1161
+ external_functions=ext,
1162
+ )
1163
+ assert result == snapshot('Test Page')
1164
+
1165
+ # LLM extracts paragraphs
1166
+ result = await run_repl_async(
1167
+ repl,
1168
+ """\
1169
+ paragraphs = []
1170
+ remaining = html
1171
+ while '<p>' in remaining:
1172
+ s = remaining.find('<p>') + 3
1173
+ e = remaining.find('</p>')
1174
+ paragraphs.append(remaining[s:e])
1175
+ remaining = remaining[e + 4:]
1176
+ paragraphs
1177
+ """,
1178
+ external_functions=ext,
1179
+ )
1180
+ assert result == snapshot(['Hello', 'World'])
1181
+
1182
+
1183
+ async def test_repl_llm_syntax_error_then_fix():
1184
+ """LLM writes code with a syntax error, then fixes it in the next snippet."""
1185
+ repl = pydantic_monty.MontyRepl()
1186
+
1187
+ def add(a: int, b: int) -> int:
1188
+ return a + b
1189
+
1190
+ ext = {'add': add}
1191
+
1192
+ # Snippet 1: set up state
1193
+ await run_repl_async(repl, 'x = 10', external_functions=ext)
1194
+
1195
+ # Snippet 2: syntax error
1196
+ with pytest.raises(pydantic_monty.MontySyntaxError):
1197
+ await run_repl_async(repl, 'y = add(x,', external_functions=ext)
1198
+
1199
+ # Snippet 3: state preserved, LLM fixes the code
1200
+ result = await run_repl_async(repl, 'y = add(x, 5)\ny', external_functions=ext)
1201
+ assert result == snapshot(15)