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,1650 @@
1
+ use std::{
2
+ borrow::Cow,
3
+ fmt::Write,
4
+ sync::{Mutex, PoisonError},
5
+ };
6
+
7
+ // Use `::monty` to refer to the external crate (not the pymodule)
8
+ use ::monty::{
9
+ ExtFunctionResult, FunctionCall, LimitedTracker, MontyException, MontyObject, MontyRun, NameLookupResult,
10
+ NoLimitTracker, OsCall, PrintWriter, PrintWriterCallback, ReplFunctionCall, ReplNameLookup, ReplOsCall,
11
+ ReplProgress, ReplResolveFutures, ReplStartError, ResolveFutures, ResourceTracker, RunProgress,
12
+ };
13
+ use monty::{ExcType, NameLookup};
14
+ use monty_type_checking::{SourceFile, type_check};
15
+ use pyo3::{
16
+ IntoPyObjectExt,
17
+ exceptions::{PyKeyError, PyRuntimeError, PyTypeError, PyValueError},
18
+ intern,
19
+ prelude::*,
20
+ types::{PyBytes, PyDict, PyList, PyTuple, PyType},
21
+ };
22
+ use send_wrapper::SendWrapper;
23
+
24
+ use crate::{
25
+ convert::{get_docstring, monty_to_py, py_to_monty},
26
+ dataclass::DcRegistry,
27
+ exceptions::{MontyError, MontyTypingError, exc_py_to_monty},
28
+ external::{ExternalFunctionRegistry, dispatch_method_call},
29
+ limits::{PySignalTracker, extract_limits},
30
+ repl::{EitherRepl, FromCoreRepl, PyMontyRepl},
31
+ };
32
+
33
+ /// A sandboxed Python interpreter instance.
34
+ ///
35
+ /// Parses and compiles Python code on initialization, then can be run
36
+ /// multiple times with different input values. This separates the parsing
37
+ /// cost from execution, making repeated runs more efficient.
38
+ #[pyclass(name = "Monty", module = "pydantic_monty")]
39
+ #[derive(Debug)]
40
+ pub struct PyMonty {
41
+ /// The compiled code snapshot, ready to execute.
42
+ runner: MontyRun,
43
+ /// The artificial name of the python code "file"
44
+ script_name: String,
45
+ /// Names of input variables expected by the code.
46
+ input_names: Vec<String>,
47
+ /// Registry of dataclass types for reconstructing original types on output.
48
+ ///
49
+ /// Maps type pointer identity (`u64`) to the original Python type, allowing
50
+ /// `isinstance(result, OriginalClass)` to work correctly after round-tripping through Monty.
51
+ dc_registry: DcRegistry,
52
+ }
53
+
54
+ #[pymethods]
55
+ impl PyMonty {
56
+ /// Creates a new Monty interpreter by parsing the given code.
57
+ ///
58
+ /// # Arguments
59
+ /// * `code` - Python code to execute
60
+ /// * `inputs` - List of input variable names available in the code
61
+ /// * `type_check` - Whether to perform type checking on the code
62
+ /// * `type_check_stubs` - Prefix code to be executed before type checking
63
+ /// * `dataclass_registry` - Registry of dataclass types for reconstructing original types on output.
64
+ #[new]
65
+ #[pyo3(signature = (code, *, script_name="main.py", inputs=None, type_check=false, type_check_stubs=None, dataclass_registry=None))]
66
+ fn new(
67
+ py: Python<'_>,
68
+ code: String,
69
+ script_name: &str,
70
+ inputs: Option<&Bound<'_, PyList>>,
71
+ type_check: bool,
72
+ type_check_stubs: Option<&str>,
73
+ dataclass_registry: Option<&Bound<'_, PyList>>,
74
+ ) -> PyResult<Self> {
75
+ let input_names = list_str(inputs, "inputs")?;
76
+
77
+ if type_check {
78
+ py_type_check(py, &code, script_name, type_check_stubs)?;
79
+ }
80
+
81
+ // Create the snapshot (parses the code)
82
+ let runner = MontyRun::new(code, script_name, input_names.clone()).map_err(|e| MontyError::new_err(py, e))?;
83
+
84
+ Ok(Self {
85
+ runner,
86
+ script_name: script_name.to_string(),
87
+ input_names,
88
+ dc_registry: DcRegistry::from_list(py, dataclass_registry)?,
89
+ })
90
+ }
91
+
92
+ /// Registers a dataclass type for proper isinstance() support on output.
93
+ ///
94
+ /// When a dataclass passes through Monty and is returned, it becomes a `MontyDataclass`.
95
+ /// By registering the original type, `isinstance(result, OriginalClass)` will return `True`.
96
+ ///
97
+ /// # Arguments
98
+ /// * `cls` - The dataclass type to register
99
+ ///
100
+ /// # Raises
101
+ /// * `TypeError` if the argument is not a dataclass type
102
+ fn register_dataclass(&self, cls: &Bound<'_, PyType>) -> PyResult<()> {
103
+ self.dc_registry.insert(cls)
104
+ }
105
+
106
+ /// Performs static type checking on the code.
107
+ ///
108
+ /// Analyzes the code for type errors without executing it. This uses
109
+ /// a subset of Python's type system supported by Monty.
110
+ ///
111
+ /// # Args
112
+ /// * `prefix_code` - Optional prefix to prepend to the code before type checking,
113
+ /// e.g. with inputs and external function signatures
114
+ ///
115
+ /// # Raises
116
+ /// * `RuntimeError` if type checking infrastructure fails
117
+ /// * `MontyTypingError` if type errors are found
118
+ #[pyo3(signature = (prefix_code=None))]
119
+ fn type_check(&self, py: Python<'_>, prefix_code: Option<&str>) -> PyResult<()> {
120
+ py_type_check(py, self.runner.code(), &self.script_name, prefix_code)
121
+ }
122
+
123
+ /// Executes the code and returns the result.
124
+ ///
125
+ /// # Returns
126
+ /// The result of the last expression in the code
127
+ ///
128
+ /// # Raises
129
+ /// Various Python exceptions matching what the code would raise
130
+ #[pyo3(signature = (*, inputs=None, limits=None, external_functions=None, print_callback=None, os=None))]
131
+ fn run(
132
+ &self,
133
+ py: Python<'_>,
134
+ inputs: Option<&Bound<'_, PyDict>>,
135
+ limits: Option<&Bound<'_, PyDict>>,
136
+ external_functions: Option<&Bound<'_, PyDict>>,
137
+ print_callback: Option<&Bound<'_, PyAny>>,
138
+ os: Option<&Bound<'_, PyAny>>,
139
+ ) -> PyResult<Py<PyAny>> {
140
+ // Clone the Arc handle — all clones share the same underlying registry,
141
+ // so auto-registrations during execution are visible to all users.
142
+ let input_values = self.extract_input_values(inputs, &self.dc_registry)?;
143
+
144
+ if let Some(os_callback) = os
145
+ && !os_callback.is_callable()
146
+ {
147
+ let msg = format!("TypeError: '{}' object is not callable", os_callback.get_type().name()?);
148
+ return Err(PyTypeError::new_err(msg));
149
+ }
150
+
151
+ // Build print writer
152
+ let mut print_cb;
153
+ let print_writer = match print_callback {
154
+ Some(cb) => {
155
+ print_cb = CallbackStringPrint::new(cb);
156
+ PrintWriter::Callback(&mut print_cb)
157
+ }
158
+ None => PrintWriter::Stdout,
159
+ };
160
+
161
+ // Run with appropriate tracker type (must branch due to different generic types)
162
+ if let Some(limits) = limits {
163
+ let tracker = PySignalTracker::new(LimitedTracker::new(extract_limits(limits)?));
164
+ self.run_impl(py, input_values, tracker, external_functions, os, print_writer)
165
+ } else {
166
+ let tracker = PySignalTracker::new(NoLimitTracker);
167
+ self.run_impl(py, input_values, tracker, external_functions, os, print_writer)
168
+ }
169
+ }
170
+
171
+ #[pyo3(signature = (*, inputs=None, limits=None, print_callback=None))]
172
+ fn start<'py>(
173
+ &self,
174
+ py: Python<'py>,
175
+ inputs: Option<&Bound<'py, PyDict>>,
176
+ limits: Option<&Bound<'py, PyDict>>,
177
+ print_callback: Option<Bound<'_, PyAny>>,
178
+ ) -> PyResult<Bound<'py, PyAny>> {
179
+ // Clone the Arc handle — shares the same underlying registry
180
+ let dc_registry = self.dc_registry.clone_ref(py);
181
+ let input_values = self.extract_input_values(inputs, &dc_registry)?;
182
+
183
+ // Build print writer - CallbackStringPrint is Send so GIL can be released
184
+ let mut print_cb;
185
+ let print_writer = match &print_callback {
186
+ Some(cb) => {
187
+ print_cb = CallbackStringPrint::new(cb);
188
+ PrintWriter::Callback(&mut print_cb)
189
+ }
190
+ None => PrintWriter::Stdout,
191
+ };
192
+
193
+ let runner = self.runner.clone();
194
+ let print_writer = SendWrapper::new(print_writer);
195
+
196
+ // Helper macro to start execution with GIL released
197
+ macro_rules! start_impl {
198
+ ($tracker:expr) => {{
199
+ py.detach(|| runner.start(input_values, $tracker, print_writer.take()))
200
+ .map_err(|e| MontyError::new_err(py, e))?
201
+ }};
202
+ }
203
+
204
+ // Branch on limits (different generic types)
205
+ let progress = if let Some(limits) = limits {
206
+ let tracker = PySignalTracker::new(LimitedTracker::new(extract_limits(limits)?));
207
+ EitherProgress::Limited(start_impl!(tracker))
208
+ } else {
209
+ let tracker = PySignalTracker::new(NoLimitTracker);
210
+ EitherProgress::NoLimit(start_impl!(tracker))
211
+ };
212
+ progress.progress_or_complete(
213
+ py,
214
+ self.script_name.clone(),
215
+ print_callback.map(Bound::unbind),
216
+ dc_registry,
217
+ )
218
+ }
219
+
220
+ /// Serializes the Monty instance to a binary format.
221
+ ///
222
+ /// The serialized data can be stored and later restored with `Monty.load()`.
223
+ /// This allows caching parsed code to avoid re-parsing on subsequent runs.
224
+ ///
225
+ /// # Returns
226
+ /// Bytes containing the serialized Monty instance.
227
+ ///
228
+ /// # Raises
229
+ /// `ValueError` if serialization fails.
230
+ fn dump<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
231
+ let serialized = SerializedMonty {
232
+ runner: self.runner.clone(),
233
+ script_name: self.script_name.clone(),
234
+ input_names: self.input_names.clone(),
235
+ };
236
+ let bytes = postcard::to_allocvec(&serialized).map_err(|e| PyValueError::new_err(e.to_string()))?;
237
+ Ok(PyBytes::new(py, &bytes))
238
+ }
239
+
240
+ /// Deserializes a Monty instance from binary format.
241
+ ///
242
+ /// # Arguments
243
+ /// * `data` - The serialized Monty data from `dump()`
244
+ /// * `dataclass_registry` - Optional list of dataclasses to register
245
+ ///
246
+ /// # Returns
247
+ /// A new Monty instance.
248
+ ///
249
+ /// # Raises
250
+ /// `ValueError` if deserialization fails.
251
+ #[staticmethod]
252
+ #[pyo3(signature = (data, *, dataclass_registry=None))]
253
+ fn load(
254
+ py: Python<'_>,
255
+ data: &Bound<'_, PyBytes>,
256
+ dataclass_registry: Option<&Bound<'_, PyList>>,
257
+ ) -> PyResult<Self> {
258
+ let bytes = data.as_bytes();
259
+ let serialized: SerializedMonty =
260
+ postcard::from_bytes(bytes).map_err(|e| PyValueError::new_err(e.to_string()))?;
261
+
262
+ Ok(Self {
263
+ runner: serialized.runner,
264
+ script_name: serialized.script_name,
265
+ input_names: serialized.input_names,
266
+ dc_registry: DcRegistry::from_list(py, dataclass_registry)?,
267
+ })
268
+ }
269
+
270
+ fn __repr__(&self) -> String {
271
+ let lines = self.runner.code().lines().count();
272
+ let mut s = format!(
273
+ "Monty(<{} line{} of code>, script_name='{}'",
274
+ lines,
275
+ if lines == 1 { "" } else { "s" },
276
+ self.script_name
277
+ );
278
+ if !self.input_names.is_empty() {
279
+ write!(s, ", inputs={:?}", self.input_names).unwrap();
280
+ }
281
+ s.push(')');
282
+ s
283
+ }
284
+ }
285
+
286
+ fn py_type_check(py: Python<'_>, code: &str, script_name: &str, type_stubs: Option<&str>) -> PyResult<()> {
287
+ let type_stubs = type_stubs.map(|type_stubs| SourceFile::new(type_stubs, "type_stubs.pyi"));
288
+
289
+ let opt_diagnostics =
290
+ type_check(&SourceFile::new(code, script_name), type_stubs.as_ref()).map_err(PyRuntimeError::new_err)?;
291
+
292
+ if let Some(diagnostic) = opt_diagnostics {
293
+ Err(MontyTypingError::new_err(py, diagnostic))
294
+ } else {
295
+ Ok(())
296
+ }
297
+ }
298
+
299
+ impl PyMonty {
300
+ /// Extracts input values from a Python dict in the order they were declared.
301
+ ///
302
+ /// Validates that all required inputs are provided. Any dataclass inputs are
303
+ /// automatically registered in `dc_registry` via `py_to_monty` so they can be
304
+ /// properly reconstructed on output.
305
+ fn extract_input_values(
306
+ &self,
307
+ inputs: Option<&Bound<'_, PyDict>>,
308
+ dc_registry: &DcRegistry,
309
+ ) -> PyResult<Vec<::monty::MontyObject>> {
310
+ if self.input_names.is_empty() {
311
+ if inputs.is_some() {
312
+ return Err(PyTypeError::new_err(
313
+ "No input variables declared but inputs dict was provided",
314
+ ));
315
+ }
316
+ return Ok(vec![]);
317
+ }
318
+
319
+ let Some(inputs) = inputs else {
320
+ return Err(PyTypeError::new_err(format!(
321
+ "Missing required inputs: {:?}",
322
+ self.input_names
323
+ )));
324
+ };
325
+
326
+ // Extract values in declaration order
327
+ self.input_names
328
+ .iter()
329
+ .map(|name| {
330
+ let value = inputs
331
+ .get_item(name)?
332
+ .ok_or_else(|| PyKeyError::new_err(format!("Missing required input: '{name}'")))?;
333
+ py_to_monty(&value, dc_registry)
334
+ })
335
+ .collect::<PyResult<_>>()
336
+ }
337
+
338
+ /// Runs code with a generic resource tracker, releasing the GIL during execution.
339
+ ///
340
+ /// Takes explicit field references instead of `&mut self` so that `run()` can
341
+ /// remain `&self` (required for concurrent thread access in PyO3).
342
+ fn run_impl(
343
+ &self,
344
+ py: Python<'_>,
345
+ input_values: Vec<MontyObject>,
346
+ tracker: impl ResourceTracker + Send,
347
+ external_functions: Option<&Bound<'_, PyDict>>,
348
+ os: Option<&Bound<'_, PyAny>>,
349
+ print_output: PrintWriter<'_>,
350
+ ) -> PyResult<Py<PyAny>> {
351
+ // wrap print_output in SendWrapper so that it can be accessed inside the py.detach calls despite
352
+ // no `Send` bound - py.detach() is overly restrictive to prevent `Bound` types going inside
353
+ let mut print_output = SendWrapper::new(print_output);
354
+
355
+ // Check if any inputs contain dataclasses (including nested in containers) —
356
+ // if so, we need the iterative path because method calls could happen lazily
357
+ // and need to be dispatched to the host.
358
+ let has_dataclass_inputs = || input_values.iter().any(contains_dataclass);
359
+
360
+ if external_functions.is_none() && os.is_none() && !has_dataclass_inputs() {
361
+ return match py.detach(|| self.runner.run(input_values, tracker, print_output.reborrow())) {
362
+ Ok(v) => monty_to_py(py, &v, &self.dc_registry),
363
+ Err(err) => Err(MontyError::new_err(py, err)),
364
+ };
365
+ }
366
+ // Clone the runner since start() consumes it - allows reuse of the parsed code
367
+ let runner = self.runner.clone();
368
+ let mut progress = py
369
+ .detach(|| runner.start(input_values, tracker, print_output.reborrow()))
370
+ .map_err(|e| MontyError::new_err(py, e))?;
371
+
372
+ loop {
373
+ match progress {
374
+ RunProgress::Complete(result) => return monty_to_py(py, &result, &self.dc_registry),
375
+ RunProgress::FunctionCall(call) => {
376
+ // Dataclass method calls have method_call=true and the first arg is the instance
377
+ let return_value = if call.method_call {
378
+ dispatch_method_call(py, &call.function_name, &call.args, &call.kwargs, &self.dc_registry)
379
+ } else if let Some(ext_fns) = external_functions {
380
+ let registry = ExternalFunctionRegistry::new(py, ext_fns, &self.dc_registry);
381
+ registry.call(&call.function_name, &call.args, &call.kwargs)
382
+ } else {
383
+ return Err(PyRuntimeError::new_err(format!(
384
+ "External function '{}' called but no external_functions provided",
385
+ call.function_name
386
+ )));
387
+ };
388
+
389
+ progress = py
390
+ .detach(|| call.resume(return_value, print_output.reborrow()))
391
+ .map_err(|e| MontyError::new_err(py, e))?;
392
+ }
393
+ RunProgress::NameLookup(lookup) => {
394
+ let result = if let Some(ext_fns) = external_functions
395
+ && let Some(value) = ext_fns.get_item(&lookup.name)?
396
+ {
397
+ NameLookupResult::Value(MontyObject::Function {
398
+ name: lookup.name.clone(),
399
+ docstring: get_docstring(&value),
400
+ })
401
+ } else {
402
+ NameLookupResult::Undefined
403
+ };
404
+
405
+ progress = py
406
+ .detach(|| lookup.resume(result, print_output.reborrow()))
407
+ .map_err(|e| MontyError::new_err(py, e))?;
408
+ }
409
+ RunProgress::ResolveFutures(_) => {
410
+ return Err(PyRuntimeError::new_err("async futures not supported with `Monty.run`"));
411
+ }
412
+ RunProgress::OsCall(call) => {
413
+ let result: ExtFunctionResult = if let Some(os_callback) = os {
414
+ // Convert args to Python
415
+ let py_args: Vec<Py<PyAny>> = call
416
+ .args
417
+ .iter()
418
+ .map(|arg| monty_to_py(py, arg, &self.dc_registry))
419
+ .collect::<PyResult<_>>()?;
420
+ let py_args_tuple = PyTuple::new(py, py_args)?;
421
+
422
+ // Convert kwargs to Python dict
423
+ let py_kwargs = PyDict::new(py);
424
+ for (k, v) in &call.kwargs {
425
+ py_kwargs.set_item(
426
+ monty_to_py(py, k, &self.dc_registry)?,
427
+ monty_to_py(py, v, &self.dc_registry)?,
428
+ )?;
429
+ }
430
+
431
+ // call the os callback, if an exception is raised, return it to monty
432
+ match os_callback.call1((call.function.to_string(), py_args_tuple, py_kwargs)) {
433
+ Ok(result) => py_to_monty(&result, &self.dc_registry)?.into(),
434
+ Err(err) => exc_py_to_monty(py, &err).into(),
435
+ }
436
+ } else {
437
+ MontyException::new(
438
+ ExcType::NotImplementedError,
439
+ Some(format!("OS function '{}' not implemented", call.function)),
440
+ )
441
+ .into()
442
+ };
443
+
444
+ progress = py
445
+ .detach(|| call.resume(result, print_output.reborrow()))
446
+ .map_err(|e| MontyError::new_err(py, e))?;
447
+ }
448
+ }
449
+ }
450
+ }
451
+ }
452
+
453
+ /// pyclass doesn't support generic types, hence hard coding the generics
454
+ #[derive(Debug)]
455
+ pub(crate) enum EitherProgress {
456
+ NoLimit(RunProgress<PySignalTracker<NoLimitTracker>>),
457
+ Limited(RunProgress<PySignalTracker<LimitedTracker>>),
458
+ /// REPL progress with back-reference to the owning `PyMontyRepl` for auto-restore.
459
+ ReplNoLimit(ReplProgress<PySignalTracker<NoLimitTracker>>, Py<PyMontyRepl>),
460
+ /// REPL progress with back-reference to the owning `PyMontyRepl` for auto-restore.
461
+ ReplLimited(ReplProgress<PySignalTracker<LimitedTracker>>, Py<PyMontyRepl>),
462
+ }
463
+
464
+ impl EitherProgress {
465
+ /// Converts progress into the appropriate Python object:
466
+ /// function snapshot, name lookup snapshot, future snapshot, or complete.
467
+ pub(crate) fn progress_or_complete(
468
+ self,
469
+ py: Python<'_>,
470
+ script_name: String,
471
+ print_callback: Option<Py<PyAny>>,
472
+ dc_registry: DcRegistry,
473
+ ) -> PyResult<Bound<'_, PyAny>> {
474
+ match self {
475
+ Self::NoLimit(p) => run_progress_to_py(py, p, script_name, print_callback, dc_registry),
476
+ Self::Limited(p) => run_progress_to_py(py, p, script_name, print_callback, dc_registry),
477
+ Self::ReplNoLimit(p, owner) => repl_progress_to_py(py, p, script_name, print_callback, dc_registry, owner),
478
+ Self::ReplLimited(p, owner) => repl_progress_to_py(py, p, script_name, print_callback, dc_registry, owner),
479
+ }
480
+ }
481
+ }
482
+
483
+ /// Converts a `RunProgress<T>` into the appropriate Python snapshot type.
484
+ fn run_progress_to_py<T: ResourceTracker>(
485
+ py: Python<'_>,
486
+ progress: RunProgress<T>,
487
+ script_name: String,
488
+ print_callback: Option<Py<PyAny>>,
489
+ dc_registry: DcRegistry,
490
+ ) -> PyResult<Bound<'_, PyAny>>
491
+ where
492
+ EitherFunctionSnapshot: FromFunctionCall<T> + FromOsCall<T>,
493
+ EitherLookupSnapshot: FromNameLookup<T>,
494
+ EitherFutureSnapshot: FromResolveFutures<T>,
495
+ {
496
+ match progress {
497
+ RunProgress::Complete(result) => PyMontyComplete::create(py, &result, &dc_registry),
498
+ RunProgress::FunctionCall(call) => {
499
+ PyFunctionSnapshot::function_call(py, call, script_name, print_callback, dc_registry)
500
+ }
501
+ RunProgress::OsCall(call) => PyFunctionSnapshot::os_call(py, call, script_name, print_callback, dc_registry),
502
+ RunProgress::ResolveFutures(state) => {
503
+ PyFutureSnapshot::new_py_any(py, state, script_name, print_callback, dc_registry)
504
+ }
505
+ RunProgress::NameLookup(lookup) => {
506
+ PyNameLookupSnapshot::new_py_any(py, lookup, script_name, print_callback, dc_registry)
507
+ }
508
+ }
509
+ }
510
+
511
+ /// Converts a `ReplProgress<T>` into the appropriate Python snapshot type.
512
+ ///
513
+ /// On completion, restores the REPL state into `repl_owner` before returning `MontyComplete`.
514
+ /// The `repl_owner` is propagated into snapshot enum variants so the chain can continue.
515
+ fn repl_progress_to_py<T: ResourceTracker>(
516
+ py: Python<'_>,
517
+ progress: ReplProgress<T>,
518
+ script_name: String,
519
+ print_callback: Option<Py<PyAny>>,
520
+ dc_registry: DcRegistry,
521
+ repl_owner: Py<PyMontyRepl>,
522
+ ) -> PyResult<Bound<'_, PyAny>>
523
+ where
524
+ EitherFunctionSnapshot: FromReplFunctionCall<T> + FromReplOsCall<T>,
525
+ EitherLookupSnapshot: FromReplNameLookup<T>,
526
+ EitherFutureSnapshot: FromReplResolveFutures<T>,
527
+ EitherRepl: FromCoreRepl<T>,
528
+ {
529
+ match progress {
530
+ ReplProgress::Complete { repl, value } => {
531
+ repl_owner.get().put_repl(EitherRepl::from_core(repl));
532
+ PyMontyComplete::create(py, &value, &dc_registry)
533
+ }
534
+ ReplProgress::FunctionCall(call) => {
535
+ PyFunctionSnapshot::repl_function_call(py, call, script_name, print_callback, dc_registry, repl_owner)
536
+ }
537
+ ReplProgress::OsCall(call) => {
538
+ PyFunctionSnapshot::repl_os_call(py, call, script_name, print_callback, dc_registry, repl_owner)
539
+ }
540
+ ReplProgress::NameLookup(lookup) => {
541
+ let variable_name = lookup.name.clone();
542
+ PyNameLookupSnapshot::repl_name_lookup(
543
+ py,
544
+ lookup,
545
+ script_name,
546
+ print_callback,
547
+ dc_registry,
548
+ repl_owner,
549
+ variable_name,
550
+ )
551
+ }
552
+ ReplProgress::ResolveFutures(state) => {
553
+ PyFutureSnapshot::repl_resolve_futures(py, state, script_name, print_callback, dc_registry, repl_owner)
554
+ }
555
+ }
556
+ }
557
+
558
+ /// Runtime execution snapshot, holds either a `FunctionCall` or `OsCall` for both
559
+ /// resource tracker variants since pyclass structs can't be generic.
560
+ ///
561
+ /// Also holds REPL variants (`ReplFunctionCall`, `ReplOsCall`) for `MontyRepl.feed_start()`.
562
+ /// REPL variants carry a `Py<PyMontyRepl>` back-reference so the REPL can be auto-restored
563
+ /// on completion or error.
564
+ ///
565
+ /// Used internally by `PyFunctionSnapshot` to store execution state. Both `FunctionCall`
566
+ /// and `OsCall` have the same `resume()` signature, so we dispatch to the appropriate
567
+ /// inner type based on the variant.
568
+ ///
569
+ /// The `Done` variant indicates the snapshot has been consumed.
570
+ ///
571
+ /// Serde: REPL variants serialize as their non-REPL counterparts (stripping the owner).
572
+ /// Deserialization always produces non-REPL variants.
573
+ #[derive(Debug)]
574
+ pub(crate) enum EitherFunctionSnapshot {
575
+ // Run variants (from Monty.start())
576
+ NoLimitFn(FunctionCall<PySignalTracker<NoLimitTracker>>),
577
+ NoLimitOs(OsCall<PySignalTracker<NoLimitTracker>>),
578
+ LimitedFn(FunctionCall<PySignalTracker<LimitedTracker>>),
579
+ LimitedOs(OsCall<PySignalTracker<LimitedTracker>>),
580
+ // REPL variants (from MontyRepl.feed_start()) — carry the REPL owner
581
+ ReplNoLimitFn(ReplFunctionCall<PySignalTracker<NoLimitTracker>>, Py<PyMontyRepl>),
582
+ ReplNoLimitOs(ReplOsCall<PySignalTracker<NoLimitTracker>>, Py<PyMontyRepl>),
583
+ ReplLimitedFn(ReplFunctionCall<PySignalTracker<LimitedTracker>>, Py<PyMontyRepl>),
584
+ ReplLimitedOs(ReplOsCall<PySignalTracker<LimitedTracker>>, Py<PyMontyRepl>),
585
+ /// Sentinel indicating the snapshot has been consumed via `resume()`.
586
+ Done,
587
+ }
588
+
589
+ /// Helper trait for wrapping `FunctionCall<T>` into `EitherFunctionSnapshot`.
590
+ trait FromFunctionCall<T: ResourceTracker> {
591
+ /// Wraps a function call into the appropriate variant.
592
+ fn from_fn(call: FunctionCall<T>) -> Self;
593
+ }
594
+
595
+ impl FromFunctionCall<PySignalTracker<NoLimitTracker>> for EitherFunctionSnapshot {
596
+ fn from_fn(call: FunctionCall<PySignalTracker<NoLimitTracker>>) -> Self {
597
+ Self::NoLimitFn(call)
598
+ }
599
+ }
600
+
601
+ impl FromFunctionCall<PySignalTracker<LimitedTracker>> for EitherFunctionSnapshot {
602
+ fn from_fn(call: FunctionCall<PySignalTracker<LimitedTracker>>) -> Self {
603
+ Self::LimitedFn(call)
604
+ }
605
+ }
606
+
607
+ /// Helper trait for wrapping `OsCall<T>` into `EitherFunctionSnapshot`.
608
+ trait FromOsCall<T: ResourceTracker> {
609
+ /// Wraps an OS call into the appropriate variant.
610
+ fn from_os(call: OsCall<T>) -> Self;
611
+ }
612
+
613
+ impl FromOsCall<PySignalTracker<NoLimitTracker>> for EitherFunctionSnapshot {
614
+ fn from_os(call: OsCall<PySignalTracker<NoLimitTracker>>) -> Self {
615
+ Self::NoLimitOs(call)
616
+ }
617
+ }
618
+
619
+ impl FromOsCall<PySignalTracker<LimitedTracker>> for EitherFunctionSnapshot {
620
+ fn from_os(call: OsCall<PySignalTracker<LimitedTracker>>) -> Self {
621
+ Self::LimitedOs(call)
622
+ }
623
+ }
624
+
625
+ /// Helper trait for wrapping `ReplFunctionCall<T>` into `EitherFunctionSnapshot`.
626
+ trait FromReplFunctionCall<T: ResourceTracker> {
627
+ /// Wraps a REPL function call into the appropriate variant.
628
+ fn from_repl_fn(call: ReplFunctionCall<T>, owner: Py<PyMontyRepl>) -> Self;
629
+ }
630
+
631
+ impl FromReplFunctionCall<PySignalTracker<NoLimitTracker>> for EitherFunctionSnapshot {
632
+ fn from_repl_fn(call: ReplFunctionCall<PySignalTracker<NoLimitTracker>>, owner: Py<PyMontyRepl>) -> Self {
633
+ Self::ReplNoLimitFn(call, owner)
634
+ }
635
+ }
636
+
637
+ impl FromReplFunctionCall<PySignalTracker<LimitedTracker>> for EitherFunctionSnapshot {
638
+ fn from_repl_fn(call: ReplFunctionCall<PySignalTracker<LimitedTracker>>, owner: Py<PyMontyRepl>) -> Self {
639
+ Self::ReplLimitedFn(call, owner)
640
+ }
641
+ }
642
+
643
+ /// Helper trait for wrapping `ReplOsCall<T>` into `EitherFunctionSnapshot`.
644
+ trait FromReplOsCall<T: ResourceTracker> {
645
+ /// Wraps a REPL OS call into the appropriate variant.
646
+ fn from_repl_os(call: ReplOsCall<T>, owner: Py<PyMontyRepl>) -> Self;
647
+ }
648
+
649
+ impl FromReplOsCall<PySignalTracker<NoLimitTracker>> for EitherFunctionSnapshot {
650
+ fn from_repl_os(call: ReplOsCall<PySignalTracker<NoLimitTracker>>, owner: Py<PyMontyRepl>) -> Self {
651
+ Self::ReplNoLimitOs(call, owner)
652
+ }
653
+ }
654
+
655
+ impl FromReplOsCall<PySignalTracker<LimitedTracker>> for EitherFunctionSnapshot {
656
+ fn from_repl_os(call: ReplOsCall<PySignalTracker<LimitedTracker>>, owner: Py<PyMontyRepl>) -> Self {
657
+ Self::ReplLimitedOs(call, owner)
658
+ }
659
+ }
660
+
661
+ /// Snapshot generated during execution when monty yields to the host for a function call.
662
+ #[pyclass(name = "FunctionSnapshot", module = "pydantic_monty")]
663
+ #[derive(Debug)]
664
+ pub struct PyFunctionSnapshot {
665
+ snapshot: Mutex<EitherFunctionSnapshot>,
666
+ print_callback: Option<Py<PyAny>>,
667
+ dc_registry: DcRegistry,
668
+
669
+ /// Name of the script being executed
670
+ #[pyo3(get)]
671
+ pub script_name: String,
672
+
673
+ /// Whether this call refers to an OS function
674
+ #[pyo3(get)]
675
+ pub is_os_function: bool,
676
+
677
+ /// Whether this call is a dataclass method call (first arg is `self`)
678
+ #[pyo3(get)]
679
+ pub is_method_call: bool,
680
+
681
+ /// The name of the function being called.
682
+ #[pyo3(get)]
683
+ pub function_name: String,
684
+ /// The positional arguments passed to the function.
685
+ #[pyo3(get)]
686
+ pub args: Py<PyTuple>,
687
+ /// The keyword arguments passed to the function (key, value pairs).
688
+ #[pyo3(get)]
689
+ pub kwargs: Py<PyDict>,
690
+ /// The unique identifier for this call
691
+ #[pyo3(get)]
692
+ pub call_id: u32,
693
+ }
694
+
695
+ impl PyFunctionSnapshot {
696
+ /// Creates a `PyFunctionSnapshot` for an external function call.
697
+ ///
698
+ /// Extracts display fields from the `FunctionCall` before moving it into
699
+ /// `EitherSnapshot` via the provided `wrap` closure.
700
+ fn function_call<T: ResourceTracker>(
701
+ py: Python<'_>,
702
+ call: FunctionCall<T>,
703
+ script_name: String,
704
+ print_callback: Option<Py<PyAny>>,
705
+ dc_registry: DcRegistry,
706
+ ) -> PyResult<Bound<'_, PyAny>>
707
+ where
708
+ EitherFunctionSnapshot: FromFunctionCall<T>,
709
+ {
710
+ let function_name = call.function_name.clone();
711
+ let call_id = call.call_id;
712
+ let method_call = call.method_call;
713
+ let items: PyResult<Vec<Py<PyAny>>> = call
714
+ .args
715
+ .iter()
716
+ .map(|item| monty_to_py(py, item, &dc_registry))
717
+ .collect();
718
+ let dict = PyDict::new(py);
719
+ for (k, v) in &call.kwargs {
720
+ dict.set_item(monty_to_py(py, k, &dc_registry)?, monty_to_py(py, v, &dc_registry)?)?;
721
+ }
722
+
723
+ let slf = Self {
724
+ snapshot: Mutex::new(EitherFunctionSnapshot::from_fn(call)),
725
+ print_callback,
726
+ script_name,
727
+ is_os_function: false,
728
+ is_method_call: method_call,
729
+ function_name,
730
+ args: PyTuple::new(py, items?)?.unbind(),
731
+ kwargs: dict.unbind(),
732
+ call_id,
733
+ dc_registry,
734
+ };
735
+ slf.into_bound_py_any(py)
736
+ }
737
+
738
+ /// Creates a `PyFunctionSnapshot` for an OS-level call.
739
+ ///
740
+ /// Extracts display fields from the `OsCall` before moving it into
741
+ /// `EitherSnapshot` via the provided `wrap` closure.
742
+ fn os_call<T: ResourceTracker>(
743
+ py: Python<'_>,
744
+ call: OsCall<T>,
745
+ script_name: String,
746
+ print_callback: Option<Py<PyAny>>,
747
+ dc_registry: DcRegistry,
748
+ ) -> PyResult<Bound<'_, PyAny>>
749
+ where
750
+ EitherFunctionSnapshot: FromOsCall<T>,
751
+ {
752
+ let function_name = call.function.to_string();
753
+ let call_id = call.call_id;
754
+ let items: PyResult<Vec<Py<PyAny>>> = call
755
+ .args
756
+ .iter()
757
+ .map(|item| monty_to_py(py, item, &dc_registry))
758
+ .collect();
759
+ let dict = PyDict::new(py);
760
+ for (k, v) in &call.kwargs {
761
+ dict.set_item(monty_to_py(py, k, &dc_registry)?, monty_to_py(py, v, &dc_registry)?)?;
762
+ }
763
+
764
+ let slf = Self {
765
+ snapshot: Mutex::new(EitherFunctionSnapshot::from_os(call)),
766
+ print_callback,
767
+ script_name,
768
+ is_os_function: true,
769
+ is_method_call: false,
770
+ function_name,
771
+ args: PyTuple::new(py, items?)?.unbind(),
772
+ kwargs: dict.unbind(),
773
+ call_id,
774
+ dc_registry,
775
+ };
776
+ slf.into_bound_py_any(py)
777
+ }
778
+
779
+ /// Creates a `PyFunctionSnapshot` for a REPL external function call.
780
+ fn repl_function_call<T: ResourceTracker>(
781
+ py: Python<'_>,
782
+ call: ReplFunctionCall<T>,
783
+ script_name: String,
784
+ print_callback: Option<Py<PyAny>>,
785
+ dc_registry: DcRegistry,
786
+ repl_owner: Py<PyMontyRepl>,
787
+ ) -> PyResult<Bound<'_, PyAny>>
788
+ where
789
+ EitherFunctionSnapshot: FromReplFunctionCall<T>,
790
+ {
791
+ let function_name = call.function_name.clone();
792
+ let call_id = call.call_id;
793
+ let method_call = call.method_call;
794
+ let items: PyResult<Vec<Py<PyAny>>> = call
795
+ .args
796
+ .iter()
797
+ .map(|item| monty_to_py(py, item, &dc_registry))
798
+ .collect();
799
+ let dict = PyDict::new(py);
800
+ for (k, v) in &call.kwargs {
801
+ dict.set_item(monty_to_py(py, k, &dc_registry)?, monty_to_py(py, v, &dc_registry)?)?;
802
+ }
803
+
804
+ let slf = Self {
805
+ snapshot: Mutex::new(EitherFunctionSnapshot::from_repl_fn(call, repl_owner)),
806
+ print_callback,
807
+ script_name,
808
+ is_os_function: false,
809
+ is_method_call: method_call,
810
+ function_name,
811
+ args: PyTuple::new(py, items?)?.unbind(),
812
+ kwargs: dict.unbind(),
813
+ call_id,
814
+ dc_registry,
815
+ };
816
+ slf.into_bound_py_any(py)
817
+ }
818
+
819
+ /// Creates a `PyFunctionSnapshot` for a REPL OS-level call.
820
+ fn repl_os_call<T: ResourceTracker>(
821
+ py: Python<'_>,
822
+ call: ReplOsCall<T>,
823
+ script_name: String,
824
+ print_callback: Option<Py<PyAny>>,
825
+ dc_registry: DcRegistry,
826
+ repl_owner: Py<PyMontyRepl>,
827
+ ) -> PyResult<Bound<'_, PyAny>>
828
+ where
829
+ EitherFunctionSnapshot: FromReplOsCall<T>,
830
+ {
831
+ let function_name = call.function.to_string();
832
+ let call_id = call.call_id;
833
+ let items: PyResult<Vec<Py<PyAny>>> = call
834
+ .args
835
+ .iter()
836
+ .map(|item| monty_to_py(py, item, &dc_registry))
837
+ .collect();
838
+ let dict = PyDict::new(py);
839
+ for (k, v) in &call.kwargs {
840
+ dict.set_item(monty_to_py(py, k, &dc_registry)?, monty_to_py(py, v, &dc_registry)?)?;
841
+ }
842
+
843
+ let slf = Self {
844
+ snapshot: Mutex::new(EitherFunctionSnapshot::from_repl_os(call, repl_owner)),
845
+ print_callback,
846
+ script_name,
847
+ is_os_function: true,
848
+ is_method_call: false,
849
+ function_name,
850
+ args: PyTuple::new(py, items?)?.unbind(),
851
+ kwargs: dict.unbind(),
852
+ call_id,
853
+ dc_registry,
854
+ };
855
+ slf.into_bound_py_any(py)
856
+ }
857
+
858
+ /// Constructs a `PyFunctionSnapshot` from deserialized parts.
859
+ ///
860
+ /// Used by `load_snapshot` and `load_repl_snapshot` to reconstruct snapshot objects.
861
+ #[expect(clippy::too_many_arguments)]
862
+ pub(crate) fn from_deserialized(
863
+ py: Python<'_>,
864
+ snapshot: EitherFunctionSnapshot,
865
+ print_callback: Option<Py<PyAny>>,
866
+ dc_registry: DcRegistry,
867
+ script_name: String,
868
+ is_os_function: bool,
869
+ is_method_call: bool,
870
+ function_name: String,
871
+ args: Py<PyTuple>,
872
+ kwargs: Py<PyDict>,
873
+ call_id: u32,
874
+ ) -> PyResult<Bound<'_, PyAny>> {
875
+ let slf = Self {
876
+ snapshot: Mutex::new(snapshot),
877
+ print_callback,
878
+ dc_registry,
879
+ script_name,
880
+ is_os_function,
881
+ is_method_call,
882
+ function_name,
883
+ args,
884
+ kwargs,
885
+ call_id,
886
+ };
887
+ slf.into_bound_py_any(py)
888
+ }
889
+ }
890
+
891
+ #[pymethods]
892
+ impl PyFunctionSnapshot {
893
+ /// Resumes execution with either a return value, exception or future.
894
+ ///
895
+ /// Exactly one of `return_value`, `exception` or `future` must be provided as a keyword argument.
896
+ ///
897
+ /// # Raises
898
+ /// * `TypeError` if both arguments are provided, or neither
899
+ /// * `RuntimeError` if the snapshot has already been resumed
900
+ #[pyo3(signature = (**kwargs))]
901
+ pub fn resume<'py>(&self, py: Python<'py>, kwargs: Option<&Bound<'_, PyDict>>) -> PyResult<Bound<'py, PyAny>> {
902
+ const ARGS_ERROR: &str = "resume() accepts either return_value or exception, not both";
903
+
904
+ let mut snapshot = self
905
+ .snapshot
906
+ .lock()
907
+ .map_err(|_| PyRuntimeError::new_err("Snapshot is currently being resumed by another thread"))?;
908
+
909
+ let snapshot = std::mem::replace(&mut *snapshot, EitherFunctionSnapshot::Done);
910
+ let Some(kwargs) = kwargs else {
911
+ return Err(PyTypeError::new_err(ARGS_ERROR));
912
+ };
913
+ let external_result = extract_external_result(py, kwargs, ARGS_ERROR, &self.dc_registry, self.call_id)?;
914
+
915
+ // Build print writer before detaching - clone_ref needs py token
916
+ let mut print_cb;
917
+ let print_writer = match &self.print_callback {
918
+ Some(cb) => {
919
+ print_cb = CallbackStringPrint::from_py(cb.clone_ref(py));
920
+ PrintWriter::Callback(&mut print_cb)
921
+ }
922
+ None => PrintWriter::Stdout,
923
+ };
924
+ // wrap print_writer in SendWrapper so that it can be accessed inside the py.detach calls despite
925
+ // no `Send` bound - py.detach() is overly restrictive to prevent `Bound` types going inside
926
+ let mut print_writer = SendWrapper::new(print_writer);
927
+
928
+ let progress = match snapshot {
929
+ EitherFunctionSnapshot::NoLimitFn(call) => {
930
+ let result = py.detach(|| call.resume(external_result, print_writer.reborrow()));
931
+ EitherProgress::NoLimit(result.map_err(|e| MontyError::new_err(py, e))?)
932
+ }
933
+ EitherFunctionSnapshot::NoLimitOs(call) => {
934
+ let result = py.detach(|| call.resume(external_result, print_writer.reborrow()));
935
+ EitherProgress::NoLimit(result.map_err(|e| MontyError::new_err(py, e))?)
936
+ }
937
+ EitherFunctionSnapshot::LimitedFn(call) => {
938
+ let result = py.detach(|| call.resume(external_result, print_writer.reborrow()));
939
+ EitherProgress::Limited(result.map_err(|e| MontyError::new_err(py, e))?)
940
+ }
941
+ EitherFunctionSnapshot::LimitedOs(call) => {
942
+ let result = py.detach(|| call.resume(external_result, print_writer.reborrow()));
943
+ EitherProgress::Limited(result.map_err(|e| MontyError::new_err(py, e))?)
944
+ }
945
+ EitherFunctionSnapshot::ReplNoLimitFn(call, owner) => {
946
+ let result = py
947
+ .detach(|| call.resume(external_result, print_writer.reborrow()))
948
+ .map_err(|e| restore_repl_from_repl_start_error(py, &owner, *e))?;
949
+ EitherProgress::ReplNoLimit(result, owner)
950
+ }
951
+ EitherFunctionSnapshot::ReplNoLimitOs(call, owner) => {
952
+ let result = py
953
+ .detach(|| call.resume(external_result, print_writer.reborrow()))
954
+ .map_err(|e| restore_repl_from_repl_start_error(py, &owner, *e))?;
955
+ EitherProgress::ReplNoLimit(result, owner)
956
+ }
957
+ EitherFunctionSnapshot::ReplLimitedFn(call, owner) => {
958
+ let result = py
959
+ .detach(|| call.resume(external_result, print_writer.reborrow()))
960
+ .map_err(|e| restore_repl_from_repl_start_error(py, &owner, *e))?;
961
+ EitherProgress::ReplLimited(result, owner)
962
+ }
963
+ EitherFunctionSnapshot::ReplLimitedOs(call, owner) => {
964
+ let result = py
965
+ .detach(|| call.resume(external_result, print_writer.reborrow()))
966
+ .map_err(|e| restore_repl_from_repl_start_error(py, &owner, *e))?;
967
+ EitherProgress::ReplLimited(result, owner)
968
+ }
969
+ EitherFunctionSnapshot::Done => return Err(PyRuntimeError::new_err("Progress already resumed")),
970
+ };
971
+
972
+ let dc_registry = self.dc_registry.clone_ref(py);
973
+ progress.progress_or_complete(
974
+ py,
975
+ self.script_name.clone(),
976
+ self.print_callback.as_ref().map(|cb| cb.clone_ref(py)),
977
+ dc_registry,
978
+ )
979
+ }
980
+
981
+ /// Serializes the FunctionSnapshot instance to a binary format.
982
+ ///
983
+ /// The serialized data can be stored and later restored with `load_snapshot()`
984
+ /// or `load_repl_snapshot()`. REPL snapshots automatically include the REPL state.
985
+ ///
986
+ /// Note: The `print_callback` is not serialized and must be re-provided when loading.
987
+ ///
988
+ /// # Returns
989
+ /// Bytes containing the serialized FunctionSnapshot instance.
990
+ ///
991
+ /// # Raises
992
+ /// `ValueError` if serialization fails.
993
+ /// `RuntimeError` if the progress has already been resumed.
994
+ fn dump<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
995
+ let bytes = crate::serialization::dump_function_snapshot(
996
+ py,
997
+ &self.snapshot,
998
+ &self.script_name,
999
+ self.is_os_function,
1000
+ self.is_method_call,
1001
+ &self.function_name,
1002
+ &self.args,
1003
+ &self.kwargs,
1004
+ self.call_id,
1005
+ &self.dc_registry,
1006
+ )?;
1007
+ Ok(PyBytes::new(py, &bytes))
1008
+ }
1009
+
1010
+ fn __repr__(&self, py: Python<'_>) -> PyResult<String> {
1011
+ Ok(format!(
1012
+ "FunctionSnapshot(script_name='{}', function_name='{}', args={}, kwargs={})",
1013
+ self.script_name,
1014
+ self.function_name,
1015
+ self.args.bind(py).repr()?,
1016
+ self.kwargs.bind(py).repr()?
1017
+ ))
1018
+ }
1019
+ }
1020
+
1021
+ /// Runtime execution snapshot, holds a `NameLookup` for both
1022
+ /// resource tracker variants since pyclass structs can't be generic.
1023
+ ///
1024
+ /// Also holds REPL variants with `Py<PyMontyRepl>` for `MontyRepl.feed_start()`.
1025
+ ///
1026
+ /// The `Done` variant indicates the snapshot has been consumed.
1027
+ #[derive(Debug)]
1028
+ pub(crate) enum EitherLookupSnapshot {
1029
+ NoLimit(NameLookup<PySignalTracker<NoLimitTracker>>),
1030
+ Limited(NameLookup<PySignalTracker<LimitedTracker>>),
1031
+ ReplNoLimit(ReplNameLookup<PySignalTracker<NoLimitTracker>>, Py<PyMontyRepl>),
1032
+ ReplLimited(ReplNameLookup<PySignalTracker<LimitedTracker>>, Py<PyMontyRepl>),
1033
+ /// Sentinel indicating the snapshot has been consumed via `resume()`.
1034
+ Done,
1035
+ }
1036
+
1037
+ /// Helper trait for wrapping `NameLookup<T>` into `EitherLookupSnapshot`.
1038
+ trait FromNameLookup<T: ResourceTracker> {
1039
+ /// Wraps a name lookup into the appropriate variant.
1040
+ fn from_name_lookup(lookup: NameLookup<T>) -> Self;
1041
+ }
1042
+
1043
+ impl FromNameLookup<PySignalTracker<NoLimitTracker>> for EitherLookupSnapshot {
1044
+ fn from_name_lookup(lookup: NameLookup<PySignalTracker<NoLimitTracker>>) -> Self {
1045
+ Self::NoLimit(lookup)
1046
+ }
1047
+ }
1048
+
1049
+ impl FromNameLookup<PySignalTracker<LimitedTracker>> for EitherLookupSnapshot {
1050
+ fn from_name_lookup(lookup: NameLookup<PySignalTracker<LimitedTracker>>) -> Self {
1051
+ Self::Limited(lookup)
1052
+ }
1053
+ }
1054
+
1055
+ /// Helper trait for wrapping `ReplNameLookup<T>` into `EitherLookupSnapshot`.
1056
+ trait FromReplNameLookup<T: ResourceTracker> {
1057
+ /// Wraps a REPL name lookup into the appropriate variant.
1058
+ fn from_repl_name_lookup(lookup: ReplNameLookup<T>, owner: Py<PyMontyRepl>) -> Self;
1059
+ }
1060
+
1061
+ impl FromReplNameLookup<PySignalTracker<NoLimitTracker>> for EitherLookupSnapshot {
1062
+ fn from_repl_name_lookup(lookup: ReplNameLookup<PySignalTracker<NoLimitTracker>>, owner: Py<PyMontyRepl>) -> Self {
1063
+ Self::ReplNoLimit(lookup, owner)
1064
+ }
1065
+ }
1066
+
1067
+ impl FromReplNameLookup<PySignalTracker<LimitedTracker>> for EitherLookupSnapshot {
1068
+ fn from_repl_name_lookup(lookup: ReplNameLookup<PySignalTracker<LimitedTracker>>, owner: Py<PyMontyRepl>) -> Self {
1069
+ Self::ReplLimited(lookup, owner)
1070
+ }
1071
+ }
1072
+
1073
+ /// Snapshot generated during execution when monty yields to the host for a name lookup.
1074
+ #[pyclass(name = "NameLookupSnapshot", module = "pydantic_monty")]
1075
+ #[derive(Debug)]
1076
+ pub struct PyNameLookupSnapshot {
1077
+ snapshot: Mutex<EitherLookupSnapshot>,
1078
+ print_callback: Option<Py<PyAny>>,
1079
+ dc_registry: DcRegistry,
1080
+
1081
+ /// Name of the script being executed
1082
+ #[pyo3(get)]
1083
+ pub script_name: String,
1084
+
1085
+ /// Name of the variable being looked up
1086
+ #[pyo3(get)]
1087
+ pub variable_name: String,
1088
+ }
1089
+
1090
+ impl PyNameLookupSnapshot {
1091
+ /// Creates a `PyNameLookupSnapshot` for an external function call.
1092
+ ///
1093
+ /// Extracts display fields from the `FunctionCall` before moving it into
1094
+ /// `EitherSnapshot` via the provided `wrap` closure.
1095
+ fn new_py_any<T: ResourceTracker>(
1096
+ py: Python<'_>,
1097
+ lookup: NameLookup<T>,
1098
+ script_name: String,
1099
+ print_callback: Option<Py<PyAny>>,
1100
+ dc_registry: DcRegistry,
1101
+ ) -> PyResult<Bound<'_, PyAny>>
1102
+ where
1103
+ EitherLookupSnapshot: FromNameLookup<T>,
1104
+ {
1105
+ let variable_name = lookup.name.clone();
1106
+
1107
+ let slf = Self {
1108
+ snapshot: Mutex::new(EitherLookupSnapshot::from_name_lookup(lookup)),
1109
+ print_callback,
1110
+ dc_registry,
1111
+ script_name,
1112
+ variable_name,
1113
+ };
1114
+ slf.into_bound_py_any(py)
1115
+ }
1116
+
1117
+ /// Creates a `PyNameLookupSnapshot` for a REPL name lookup.
1118
+ fn repl_name_lookup<T: ResourceTracker>(
1119
+ py: Python<'_>,
1120
+ lookup: ReplNameLookup<T>,
1121
+ script_name: String,
1122
+ print_callback: Option<Py<PyAny>>,
1123
+ dc_registry: DcRegistry,
1124
+ repl_owner: Py<PyMontyRepl>,
1125
+ variable_name: String,
1126
+ ) -> PyResult<Bound<'_, PyAny>>
1127
+ where
1128
+ EitherLookupSnapshot: FromReplNameLookup<T>,
1129
+ {
1130
+ let slf = Self {
1131
+ snapshot: Mutex::new(EitherLookupSnapshot::from_repl_name_lookup(lookup, repl_owner)),
1132
+ print_callback,
1133
+ dc_registry,
1134
+ script_name,
1135
+ variable_name,
1136
+ };
1137
+ slf.into_bound_py_any(py)
1138
+ }
1139
+
1140
+ /// Constructs a `PyNameLookupSnapshot` from deserialized parts.
1141
+ pub(crate) fn from_deserialized(
1142
+ py: Python<'_>,
1143
+ snapshot: EitherLookupSnapshot,
1144
+ print_callback: Option<Py<PyAny>>,
1145
+ dc_registry: DcRegistry,
1146
+ script_name: String,
1147
+ variable_name: String,
1148
+ ) -> PyResult<Bound<'_, PyAny>> {
1149
+ let slf = Self {
1150
+ snapshot: Mutex::new(snapshot),
1151
+ print_callback,
1152
+ dc_registry,
1153
+ script_name,
1154
+ variable_name,
1155
+ };
1156
+ slf.into_bound_py_any(py)
1157
+ }
1158
+ }
1159
+
1160
+ #[pymethods]
1161
+ impl PyNameLookupSnapshot {
1162
+ /// Resumes execution with either a value or undefined.
1163
+ #[pyo3(signature = (**kwargs))]
1164
+ pub fn resume<'py>(&self, py: Python<'py>, kwargs: Option<&Bound<'_, PyDict>>) -> PyResult<Bound<'py, PyAny>> {
1165
+ let mut snapshot = self
1166
+ .snapshot
1167
+ .lock()
1168
+ .map_err(|_| PyRuntimeError::new_err("Snapshot is currently being resumed by another thread"))?;
1169
+
1170
+ let snapshot = std::mem::replace(&mut *snapshot, EitherLookupSnapshot::Done);
1171
+ let lookup_result = if let Some(kwargs) = kwargs
1172
+ && let Some(value) = kwargs.get_item(intern!(py, "value"))?
1173
+ {
1174
+ NameLookupResult::Value(py_to_monty(&value, &self.dc_registry)?)
1175
+ } else {
1176
+ NameLookupResult::Undefined
1177
+ };
1178
+
1179
+ // Build print writer before detaching - clone_ref needs py token
1180
+ let mut print_cb;
1181
+ let print_writer = match &self.print_callback {
1182
+ Some(cb) => {
1183
+ print_cb = CallbackStringPrint::from_py(cb.clone_ref(py));
1184
+ PrintWriter::Callback(&mut print_cb)
1185
+ }
1186
+ None => PrintWriter::Stdout,
1187
+ };
1188
+ let mut print_writer = SendWrapper::new(print_writer);
1189
+
1190
+ let progress = match snapshot {
1191
+ EitherLookupSnapshot::NoLimit(snapshot) => {
1192
+ let result = py.detach(|| snapshot.resume(lookup_result, print_writer.reborrow()));
1193
+ EitherProgress::NoLimit(result.map_err(|e| MontyError::new_err(py, e))?)
1194
+ }
1195
+ EitherLookupSnapshot::Limited(snapshot) => {
1196
+ let result = py.detach(|| snapshot.resume(lookup_result, print_writer.reborrow()));
1197
+ EitherProgress::Limited(result.map_err(|e| MontyError::new_err(py, e))?)
1198
+ }
1199
+ EitherLookupSnapshot::ReplNoLimit(snapshot, owner) => {
1200
+ let result = py
1201
+ .detach(|| snapshot.resume(lookup_result, print_writer.reborrow()))
1202
+ .map_err(|e| restore_repl_from_repl_start_error(py, &owner, *e))?;
1203
+ EitherProgress::ReplNoLimit(result, owner)
1204
+ }
1205
+ EitherLookupSnapshot::ReplLimited(snapshot, owner) => {
1206
+ let result = py
1207
+ .detach(|| snapshot.resume(lookup_result, print_writer.reborrow()))
1208
+ .map_err(|e| restore_repl_from_repl_start_error(py, &owner, *e))?;
1209
+ EitherProgress::ReplLimited(result, owner)
1210
+ }
1211
+ EitherLookupSnapshot::Done => return Err(PyRuntimeError::new_err("Progress already resumed")),
1212
+ };
1213
+
1214
+ // Clone the Arc handle for the next snapshot/complete
1215
+ let dc_registry = self.dc_registry.clone_ref(py);
1216
+ progress.progress_or_complete(
1217
+ py,
1218
+ self.script_name.clone(),
1219
+ self.print_callback.as_ref().map(|cb| cb.clone_ref(py)),
1220
+ dc_registry,
1221
+ )
1222
+ }
1223
+
1224
+ /// Serializes the NameLookupSnapshot instance to a binary format.
1225
+ ///
1226
+ /// The serialized data can be stored and later restored with `load_snapshot()`
1227
+ /// or `load_repl_snapshot()`. REPL snapshots automatically include the REPL state.
1228
+ ///
1229
+ /// Note: The `print_callback` is not serialized and must be re-provided when loading.
1230
+ ///
1231
+ /// # Returns
1232
+ /// Bytes containing the serialized NameLookupSnapshot instance.
1233
+ ///
1234
+ /// # Raises
1235
+ /// `ValueError` if serialization fails.
1236
+ /// `RuntimeError` if the progress has already been resumed.
1237
+ fn dump<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
1238
+ let bytes = crate::serialization::dump_lookup_snapshot(&self.snapshot, &self.script_name, &self.variable_name)?;
1239
+ Ok(PyBytes::new(py, &bytes))
1240
+ }
1241
+
1242
+ fn __repr__(&self) -> String {
1243
+ format!(
1244
+ "NameLookupSnapshot(script_name='{}', variable_name={:?})",
1245
+ self.script_name, self.variable_name
1246
+ )
1247
+ }
1248
+ }
1249
+
1250
+ /// Holds a `ResolveFutures` for either resource tracker variant.
1251
+ ///
1252
+ /// Also holds REPL variants with `Py<PyMontyRepl>` for `MontyRepl.feed_start()`.
1253
+ ///
1254
+ /// Used internally by `PyFutureSnapshot` to store execution state when
1255
+ /// awaiting resolution of pending async external calls.
1256
+ #[derive(Debug)]
1257
+ pub(crate) enum EitherFutureSnapshot {
1258
+ NoLimit(ResolveFutures<PySignalTracker<NoLimitTracker>>),
1259
+ Limited(ResolveFutures<PySignalTracker<LimitedTracker>>),
1260
+ ReplNoLimit(ReplResolveFutures<PySignalTracker<NoLimitTracker>>, Py<PyMontyRepl>),
1261
+ ReplLimited(ReplResolveFutures<PySignalTracker<LimitedTracker>>, Py<PyMontyRepl>),
1262
+ /// Sentinel indicating the snapshot has been consumed via `resume()`.
1263
+ Done,
1264
+ }
1265
+
1266
+ /// Helper trait for wrapping `ResolveFutures<T>` into `EitherFutureSnapshot`.
1267
+ trait FromResolveFutures<T: ResourceTracker> {
1268
+ /// Wraps a resolve-futures state into the appropriate variant.
1269
+ fn from_resolve_futures(state: ResolveFutures<T>) -> Self;
1270
+ }
1271
+
1272
+ impl FromResolveFutures<PySignalTracker<NoLimitTracker>> for EitherFutureSnapshot {
1273
+ fn from_resolve_futures(state: ResolveFutures<PySignalTracker<NoLimitTracker>>) -> Self {
1274
+ Self::NoLimit(state)
1275
+ }
1276
+ }
1277
+
1278
+ impl FromResolveFutures<PySignalTracker<LimitedTracker>> for EitherFutureSnapshot {
1279
+ fn from_resolve_futures(state: ResolveFutures<PySignalTracker<LimitedTracker>>) -> Self {
1280
+ Self::Limited(state)
1281
+ }
1282
+ }
1283
+
1284
+ /// Helper trait for wrapping `ReplResolveFutures<T>` into `EitherFutureSnapshot`.
1285
+ trait FromReplResolveFutures<T: ResourceTracker> {
1286
+ /// Wraps a REPL resolve-futures state into the appropriate variant.
1287
+ fn from_repl_resolve_futures(state: ReplResolveFutures<T>, owner: Py<PyMontyRepl>) -> Self;
1288
+ }
1289
+
1290
+ impl FromReplResolveFutures<PySignalTracker<NoLimitTracker>> for EitherFutureSnapshot {
1291
+ fn from_repl_resolve_futures(
1292
+ state: ReplResolveFutures<PySignalTracker<NoLimitTracker>>,
1293
+ owner: Py<PyMontyRepl>,
1294
+ ) -> Self {
1295
+ Self::ReplNoLimit(state, owner)
1296
+ }
1297
+ }
1298
+
1299
+ impl FromReplResolveFutures<PySignalTracker<LimitedTracker>> for EitherFutureSnapshot {
1300
+ fn from_repl_resolve_futures(
1301
+ state: ReplResolveFutures<PySignalTracker<LimitedTracker>>,
1302
+ owner: Py<PyMontyRepl>,
1303
+ ) -> Self {
1304
+ Self::ReplLimited(state, owner)
1305
+ }
1306
+ }
1307
+
1308
+ /// Snapshot generated during execution when monty yields to the host to resolve a future.
1309
+ ///
1310
+ /// Works for both `Monty.start()` and `MontyRepl.feed_start()`.
1311
+ #[pyclass(name = "FutureSnapshot", module = "pydantic_monty", frozen)]
1312
+ #[derive(Debug)]
1313
+ pub struct PyFutureSnapshot {
1314
+ snapshot: Mutex<EitherFutureSnapshot>,
1315
+ print_callback: Option<Py<PyAny>>,
1316
+ dc_registry: DcRegistry,
1317
+
1318
+ /// Name of the script being executed
1319
+ #[pyo3(get)]
1320
+ pub script_name: String,
1321
+ }
1322
+
1323
+ impl PyFutureSnapshot {
1324
+ fn new_py_any<T: ResourceTracker>(
1325
+ py: Python<'_>,
1326
+ state: ResolveFutures<T>,
1327
+ script_name: String,
1328
+ print_callback: Option<Py<PyAny>>,
1329
+ dc_registry: DcRegistry,
1330
+ ) -> PyResult<Bound<'_, PyAny>>
1331
+ where
1332
+ EitherFutureSnapshot: FromResolveFutures<T>,
1333
+ {
1334
+ let slf = Self {
1335
+ snapshot: Mutex::new(EitherFutureSnapshot::from_resolve_futures(state)),
1336
+ print_callback,
1337
+ dc_registry,
1338
+ script_name,
1339
+ };
1340
+ slf.into_bound_py_any(py)
1341
+ }
1342
+
1343
+ /// Constructs a `PyFutureSnapshot` from deserialized parts.
1344
+ ///
1345
+ /// Used by `load_snapshot` and `load_repl_snapshot` to reconstruct snapshot objects.
1346
+ pub(crate) fn from_deserialized(
1347
+ py: Python<'_>,
1348
+ snapshot: EitherFutureSnapshot,
1349
+ print_callback: Option<Py<PyAny>>,
1350
+ dc_registry: DcRegistry,
1351
+ script_name: String,
1352
+ ) -> PyResult<Bound<'_, PyAny>> {
1353
+ let slf = Self {
1354
+ snapshot: Mutex::new(snapshot),
1355
+ print_callback,
1356
+ dc_registry,
1357
+ script_name,
1358
+ };
1359
+ slf.into_bound_py_any(py)
1360
+ }
1361
+
1362
+ /// Creates a `PyFutureSnapshot` for a REPL resolve-futures state.
1363
+ fn repl_resolve_futures<T: ResourceTracker>(
1364
+ py: Python<'_>,
1365
+ state: ReplResolveFutures<T>,
1366
+ script_name: String,
1367
+ print_callback: Option<Py<PyAny>>,
1368
+ dc_registry: DcRegistry,
1369
+ repl_owner: Py<PyMontyRepl>,
1370
+ ) -> PyResult<Bound<'_, PyAny>>
1371
+ where
1372
+ EitherFutureSnapshot: FromReplResolveFutures<T>,
1373
+ {
1374
+ let slf = Self {
1375
+ snapshot: Mutex::new(EitherFutureSnapshot::from_repl_resolve_futures(state, repl_owner)),
1376
+ print_callback,
1377
+ dc_registry,
1378
+ script_name,
1379
+ };
1380
+ slf.into_bound_py_any(py)
1381
+ }
1382
+ }
1383
+
1384
+ #[pymethods]
1385
+ impl PyFutureSnapshot {
1386
+ /// Resumes execution with results for one or more futures.
1387
+ #[pyo3(signature = (results))]
1388
+ pub fn resume<'py>(&self, py: Python<'py>, results: &Bound<'_, PyDict>) -> PyResult<Bound<'py, PyAny>> {
1389
+ const ARGS_ERROR: &str = "results values must be a dict with either 'return_value' or 'exception', not both";
1390
+
1391
+ let mut snapshot = self
1392
+ .snapshot
1393
+ .lock()
1394
+ .map_err(|_| PyRuntimeError::new_err("Snapshot is currently being resumed by another thread"))?;
1395
+
1396
+ let snapshot = std::mem::replace(&mut *snapshot, EitherFutureSnapshot::Done);
1397
+
1398
+ let external_results = results
1399
+ .iter()
1400
+ .map(|(key, value)| {
1401
+ let call_id = key.extract::<u32>()?;
1402
+ let dict = value.cast::<PyDict>()?;
1403
+ let value = extract_external_result(py, dict, ARGS_ERROR, &self.dc_registry, call_id)?;
1404
+ Ok((call_id, value))
1405
+ })
1406
+ .collect::<PyResult<Vec<_>>>()?;
1407
+
1408
+ // Build print writer before detaching - clone_ref needs py token
1409
+ let mut print_cb;
1410
+ let print_writer = match &self.print_callback {
1411
+ Some(cb) => {
1412
+ print_cb = CallbackStringPrint::from_py(cb.clone_ref(py));
1413
+ PrintWriter::Callback(&mut print_cb)
1414
+ }
1415
+ None => PrintWriter::Stdout,
1416
+ };
1417
+ let mut print_writer = SendWrapper::new(print_writer);
1418
+
1419
+ let progress = match snapshot {
1420
+ EitherFutureSnapshot::NoLimit(snapshot) => {
1421
+ let result = py.detach(|| snapshot.resume(external_results, print_writer.reborrow()));
1422
+ EitherProgress::NoLimit(result.map_err(|e| MontyError::new_err(py, e))?)
1423
+ }
1424
+ EitherFutureSnapshot::Limited(snapshot) => {
1425
+ let result = py.detach(|| snapshot.resume(external_results, print_writer.reborrow()));
1426
+ EitherProgress::Limited(result.map_err(|e| MontyError::new_err(py, e))?)
1427
+ }
1428
+ EitherFutureSnapshot::ReplNoLimit(snapshot, owner) => {
1429
+ let result = py
1430
+ .detach(|| snapshot.resume(external_results, print_writer.reborrow()))
1431
+ .map_err(|e| restore_repl_from_repl_start_error(py, &owner, *e))?;
1432
+ EitherProgress::ReplNoLimit(result, owner)
1433
+ }
1434
+ EitherFutureSnapshot::ReplLimited(snapshot, owner) => {
1435
+ let result = py
1436
+ .detach(|| snapshot.resume(external_results, print_writer.reborrow()))
1437
+ .map_err(|e| restore_repl_from_repl_start_error(py, &owner, *e))?;
1438
+ EitherProgress::ReplLimited(result, owner)
1439
+ }
1440
+ EitherFutureSnapshot::Done => return Err(PyRuntimeError::new_err("Progress already resumed")),
1441
+ };
1442
+
1443
+ // Clone the Arc handle for the next snapshot/complete
1444
+ let dc_registry = self.dc_registry.clone_ref(py);
1445
+ progress.progress_or_complete(
1446
+ py,
1447
+ self.script_name.clone(),
1448
+ self.print_callback.as_ref().map(|cb| cb.clone_ref(py)),
1449
+ dc_registry,
1450
+ )
1451
+ }
1452
+
1453
+ /// Returns the pending call IDs associated with the FutureSnapshot instance.
1454
+ ///
1455
+ /// # Returns
1456
+ /// A slice of pending call IDs.
1457
+ #[getter]
1458
+ fn pending_call_ids<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyList>> {
1459
+ let snapshot = self.snapshot.lock().unwrap_or_else(PoisonError::into_inner);
1460
+ match &*snapshot {
1461
+ EitherFutureSnapshot::NoLimit(snapshot) => PyList::new(py, snapshot.pending_call_ids()),
1462
+ EitherFutureSnapshot::Limited(snapshot) => PyList::new(py, snapshot.pending_call_ids()),
1463
+ EitherFutureSnapshot::ReplNoLimit(snapshot, _) => PyList::new(py, snapshot.pending_call_ids()),
1464
+ EitherFutureSnapshot::ReplLimited(snapshot, _) => PyList::new(py, snapshot.pending_call_ids()),
1465
+ EitherFutureSnapshot::Done => Err(PyRuntimeError::new_err("FutureSnapshot already resumed")),
1466
+ }
1467
+ }
1468
+
1469
+ /// Serializes the FutureSnapshot instance to a binary format.
1470
+ ///
1471
+ /// The serialized data can be stored and later restored with `load_snapshot()`
1472
+ /// or `load_repl_snapshot()`. REPL snapshots automatically include the REPL state.
1473
+ ///
1474
+ /// Note: The `print_callback` is not serialized and must be re-provided when loading.
1475
+ ///
1476
+ /// # Returns
1477
+ /// Bytes containing the serialized FutureSnapshot instance.
1478
+ ///
1479
+ /// # Raises
1480
+ /// `ValueError` if serialization fails.
1481
+ /// `RuntimeError` if the progress has already been resumed.
1482
+ fn dump<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
1483
+ let bytes = crate::serialization::dump_future_snapshot(&self.snapshot, &self.script_name)?;
1484
+ Ok(PyBytes::new(py, &bytes))
1485
+ }
1486
+
1487
+ fn __repr__(&self) -> String {
1488
+ let snapshot = self.snapshot.lock().unwrap_or_else(PoisonError::into_inner);
1489
+ let pending_call_ids = match &*snapshot {
1490
+ EitherFutureSnapshot::NoLimit(s) => s.pending_call_ids(),
1491
+ EitherFutureSnapshot::Limited(s) => s.pending_call_ids(),
1492
+ EitherFutureSnapshot::ReplNoLimit(s, _) => s.pending_call_ids(),
1493
+ EitherFutureSnapshot::ReplLimited(s, _) => s.pending_call_ids(),
1494
+ EitherFutureSnapshot::Done => &[],
1495
+ };
1496
+ format!(
1497
+ "FutureSnapshot(script_name='{}', pending_call_ids={pending_call_ids:?})",
1498
+ self.script_name,
1499
+ )
1500
+ }
1501
+ }
1502
+
1503
+ #[pyclass(name = "MontyComplete", module = "pydantic_monty", frozen)]
1504
+ pub struct PyMontyComplete {
1505
+ #[pyo3(get)]
1506
+ pub output: Py<PyAny>,
1507
+ // TODO we might want to add stats on execution here like time, allocations, etc.
1508
+ }
1509
+
1510
+ impl PyMontyComplete {
1511
+ fn create<'py>(py: Python<'py>, output: &MontyObject, dc_registry: &DcRegistry) -> PyResult<Bound<'py, PyAny>> {
1512
+ let output = monty_to_py(py, output, dc_registry)?;
1513
+ let slf = Self { output };
1514
+ slf.into_bound_py_any(py)
1515
+ }
1516
+ }
1517
+
1518
+ #[pymethods]
1519
+ impl PyMontyComplete {
1520
+ fn __repr__(&self, py: Python<'_>) -> PyResult<String> {
1521
+ Ok(format!("MontyComplete(output={})", self.output.bind(py).repr()?))
1522
+ }
1523
+ }
1524
+
1525
+ fn list_str(arg: Option<&Bound<'_, PyList>>, name: &str) -> PyResult<Vec<String>> {
1526
+ if let Some(names) = arg {
1527
+ names
1528
+ .iter()
1529
+ .map(|item| item.extract::<String>())
1530
+ .collect::<PyResult<Vec<_>>>()
1531
+ .map_err(|e| PyTypeError::new_err(format!("{name}: {e}")))
1532
+ } else {
1533
+ Ok(vec![])
1534
+ }
1535
+ }
1536
+
1537
+ /// A `PrintWriter` implementation that calls a Python callback for each print output.
1538
+ ///
1539
+ /// This struct holds a GIL-independent `Py<PyAny>` reference to the callback,
1540
+ /// allowing it to be used across GIL release boundaries. The GIL is re-acquired
1541
+ /// briefly for each callback invocation.
1542
+ #[derive(Debug)]
1543
+ pub(crate) struct CallbackStringPrint(Py<PyAny>);
1544
+
1545
+ impl CallbackStringPrint {
1546
+ /// Creates a new `CallbackStringPrint` from a borrowed Python callback.
1547
+ fn new(callback: &Bound<'_, PyAny>) -> Self {
1548
+ Self(callback.clone().unbind())
1549
+ }
1550
+
1551
+ /// Creates a new `CallbackStringPrint` from an owned `Py<PyAny>`.
1552
+ pub(crate) fn from_py(callback: Py<PyAny>) -> Self {
1553
+ Self(callback)
1554
+ }
1555
+ }
1556
+
1557
+ impl PrintWriterCallback for CallbackStringPrint {
1558
+ fn stdout_write(&mut self, output: Cow<'_, str>) -> Result<(), MontyException> {
1559
+ Python::attach(|py| {
1560
+ self.0.bind(py).call1(("stdout", output.as_ref()))?;
1561
+ Ok::<_, PyErr>(())
1562
+ })
1563
+ .map_err(|e| Python::attach(|py| exc_py_to_monty(py, &e)))
1564
+ }
1565
+
1566
+ fn stdout_push(&mut self, end: char) -> Result<(), MontyException> {
1567
+ Python::attach(|py| {
1568
+ self.0.bind(py).call1(("stdout", end.to_string()))?;
1569
+ Ok::<_, PyErr>(())
1570
+ })
1571
+ .map_err(|e| Python::attach(|py| exc_py_to_monty(py, &e)))
1572
+ }
1573
+ }
1574
+
1575
+ /// Recursively checks whether a `MontyObject` contains a dataclass, including
1576
+ /// inside containers like `List`, `Tuple`, and `Dict`.
1577
+ ///
1578
+ /// This is used to decide whether to take the iterative execution path: dataclass
1579
+ /// method calls need host dispatch, so if any input (even nested) is a dataclass
1580
+ /// we must use the iterative runner rather than the non-iterative `run()`.
1581
+ fn contains_dataclass(obj: &MontyObject) -> bool {
1582
+ match obj {
1583
+ MontyObject::Dataclass { .. } => true,
1584
+ MontyObject::List(items) | MontyObject::Tuple(items) => items.iter().any(contains_dataclass),
1585
+ MontyObject::Dict(pairs) => pairs
1586
+ .into_iter()
1587
+ .any(|(k, v)| contains_dataclass(k) || contains_dataclass(v)),
1588
+ _ => false,
1589
+ }
1590
+ }
1591
+
1592
+ /// Serialization wrapper for `PyMonty` that includes all fields needed for reconstruction.
1593
+ #[derive(serde::Serialize, serde::Deserialize)]
1594
+ struct SerializedMonty {
1595
+ runner: MontyRun,
1596
+ script_name: String,
1597
+ input_names: Vec<String>,
1598
+ }
1599
+
1600
+ /// Extract an external result (object or exception) from a dictionary.
1601
+ ///
1602
+ /// Any dataclass return values are automatically registered in the `dc_registry` via `py_to_monty`
1603
+ /// so they can be properly reconstructed on output.
1604
+ /// Extracts an `ExternalResult` from a Python dict with a single key.
1605
+ ///
1606
+ /// Accepts `return_value`, `exception`, or `future` (with value `...`).
1607
+ /// The `call_id` is required for `future` results to track the pending call.
1608
+ fn extract_external_result(
1609
+ py: Python<'_>,
1610
+ dict: &Bound<'_, PyDict>,
1611
+ error_msg: &'static str,
1612
+ dc_registry: &DcRegistry,
1613
+ call_id: u32,
1614
+ ) -> PyResult<ExtFunctionResult> {
1615
+ if dict.len() != 1 {
1616
+ Err(PyTypeError::new_err(error_msg))
1617
+ } else if let Some(rv) = dict.get_item(intern!(py, "return_value"))? {
1618
+ // Return value provided
1619
+ Ok(py_to_monty(&rv, dc_registry)?.into())
1620
+ } else if let Some(exc) = dict.get_item(intern!(py, "exception"))? {
1621
+ // Exception provided
1622
+ let py_err = PyErr::from_value(exc.into_any());
1623
+ Ok(exc_py_to_monty(py, &py_err).into())
1624
+ } else if let Some(exc) = dict.get_item(intern!(py, "future"))? {
1625
+ if exc.eq(py.Ellipsis()).unwrap_or_default() {
1626
+ Ok(ExtFunctionResult::Future(call_id))
1627
+ } else {
1628
+ Err(PyTypeError::new_err(
1629
+ "Value for the 'future' key must be Ellipsis (...)",
1630
+ ))
1631
+ }
1632
+ } else {
1633
+ // wrong key in kwargs
1634
+ Err(PyTypeError::new_err(error_msg))
1635
+ }
1636
+ }
1637
+
1638
+ /// Extracts the REPL from a `ReplStartError`, restores it into the owner,
1639
+ /// and returns the Python exception.
1640
+ fn restore_repl_from_repl_start_error<T: ResourceTracker>(
1641
+ py: Python<'_>,
1642
+ repl_owner: &Py<PyMontyRepl>,
1643
+ err: ReplStartError<T>,
1644
+ ) -> PyErr
1645
+ where
1646
+ EitherRepl: FromCoreRepl<T>,
1647
+ {
1648
+ repl_owner.get().put_repl(EitherRepl::from_core(err.repl));
1649
+ MontyError::new_err(py, err.error)
1650
+ }