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.
- package/__tests__/adapter-schema.test.js +2 -0
- package/__tests__/config.test.js +62 -1
- package/__tests__/help-json.test.js +2 -0
- package/__tests__/mcp-adapter.test.js +14 -4
- package/__tests__/mcp-local.test.js +159 -0
- package/__tests__/mcp-stdio-jsonrpc.test.js +105 -0
- package/__tests__/monty-plugin.test.js +121 -0
- package/__tests__/plugin-browser-use-uninstall.test.js +23 -0
- package/__tests__/plugin-browser-use.test.js +77 -0
- package/__tests__/plugins-command.test.js +92 -1
- package/__tests__/plugins-learn.test.js +62 -0
- package/__tests__/plugins-registry.test.js +3 -1
- package/__tests__/resend-plugin.test.js +122 -0
- package/__tests__/skills.test.js +4 -0
- package/cli/adapter-schema.js +3 -2
- package/cli/adapters/mcp.js +22 -3
- package/cli/adapters/process.js +34 -7
- package/cli/config.js +27 -1
- package/cli/help-json.js +2 -2
- package/cli/mcp-diagnostics.js +152 -0
- package/cli/mcp-discovery.js +221 -0
- package/cli/mcp-local.js +267 -25
- package/cli/mcp-stdio-jsonrpc.js +246 -0
- package/cli/plugin-install-guidance.js +25 -0
- package/cli/plugins-command.js +86 -3
- package/cli/plugins-learn.js +177 -0
- package/cli/plugins-manager.js +3 -0
- package/cli/plugins-registry.js +2 -1
- package/cli/skills-mcp.js +102 -0
- package/cli/skills.js +6 -40
- package/cli/supercli.js +7 -2
- package/docs/initial/mcp-local-mode.md +35 -0
- package/docs/mcp-cheatsheet.md +324 -0
- package/docs/plugins.md +7 -0
- package/package.json +1 -1
- package/plugins/browser-use/plugin.json +23 -0
- package/plugins/browser-use/scripts/post-install.js +146 -0
- package/plugins/browser-use/scripts/post-uninstall.js +28 -0
- package/plugins/browser-use/skills/quickstart/SKILL.md +47 -0
- package/plugins/monty/README.md +49 -0
- package/plugins/monty/plugin.json +69 -0
- package/plugins/monty/scripts/post-install.js +73 -0
- package/plugins/monty/scripts/post-uninstall.js +23 -0
- package/plugins/monty/scripts/run-python.js +140 -0
- package/plugins/monty/scripts/setup-monty.js +27 -0
- package/plugins/plugins.json +29 -0
- package/plugins/resend/plugin.json +371 -0
- package/plugins/resend/scripts/post-install.js +59 -0
- package/plugins/resend/scripts/post-uninstall.js +23 -0
- package/plugins/resend/scripts/setup-resend.js +27 -0
- package/plugins/resend/skills/quickstart/SKILL.md +80 -0
- package/ref-monty/.cargo/config.toml +3 -0
- package/ref-monty/.claude/settings.json +60 -0
- package/ref-monty/.claude/skills/fastmod/SKILL.md +22 -0
- package/ref-monty/.claude/skills/python-playground/SKILL.md +47 -0
- package/ref-monty/.codecov.yml +12 -0
- package/ref-monty/.github/actions/build-pgo-wheel/action.yml +72 -0
- package/ref-monty/.github/workflows/ci.yml +776 -0
- package/ref-monty/.github/workflows/codspeed.yml +45 -0
- package/ref-monty/.github/workflows/init-npm-packages.yml +82 -0
- package/ref-monty/.pre-commit-config.yaml +47 -0
- package/ref-monty/.python-version +1 -0
- package/ref-monty/.rustfmt.toml +4 -0
- package/ref-monty/.zed/settings.json +11 -0
- package/ref-monty/CLAUDE.md +535 -0
- package/ref-monty/Cargo.lock +3798 -0
- package/ref-monty/Cargo.toml +87 -0
- package/ref-monty/LICENSE +21 -0
- package/ref-monty/Makefile +216 -0
- package/ref-monty/README.md +430 -0
- package/ref-monty/RELEASING.md +47 -0
- package/ref-monty/crates/fuzz/Cargo.toml +30 -0
- package/ref-monty/crates/fuzz/fuzz_targets/string_input_panic.rs +37 -0
- package/ref-monty/crates/fuzz/fuzz_targets/tokens_input_panic.rs +552 -0
- package/ref-monty/crates/monty/Cargo.toml +68 -0
- package/ref-monty/crates/monty/benches/main.rs +247 -0
- package/ref-monty/crates/monty/build.rs +10 -0
- package/ref-monty/crates/monty/src/args.rs +733 -0
- package/ref-monty/crates/monty/src/asyncio.rs +179 -0
- package/ref-monty/crates/monty/src/builtins/abs.rs +55 -0
- package/ref-monty/crates/monty/src/builtins/all.rs +30 -0
- package/ref-monty/crates/monty/src/builtins/any.rs +30 -0
- package/ref-monty/crates/monty/src/builtins/bin.rs +59 -0
- package/ref-monty/crates/monty/src/builtins/chr.rs +46 -0
- package/ref-monty/crates/monty/src/builtins/divmod.rs +164 -0
- package/ref-monty/crates/monty/src/builtins/enumerate.rs +52 -0
- package/ref-monty/crates/monty/src/builtins/filter.rs +67 -0
- package/ref-monty/crates/monty/src/builtins/getattr.rs +65 -0
- package/ref-monty/crates/monty/src/builtins/hash.rs +28 -0
- package/ref-monty/crates/monty/src/builtins/hex.rs +58 -0
- package/ref-monty/crates/monty/src/builtins/id.rs +24 -0
- package/ref-monty/crates/monty/src/builtins/isinstance.rs +68 -0
- package/ref-monty/crates/monty/src/builtins/len.rs +25 -0
- package/ref-monty/crates/monty/src/builtins/map.rs +98 -0
- package/ref-monty/crates/monty/src/builtins/min_max.rs +113 -0
- package/ref-monty/crates/monty/src/builtins/mod.rs +246 -0
- package/ref-monty/crates/monty/src/builtins/next.rs +21 -0
- package/ref-monty/crates/monty/src/builtins/oct.rs +59 -0
- package/ref-monty/crates/monty/src/builtins/ord.rs +67 -0
- package/ref-monty/crates/monty/src/builtins/pow.rs +365 -0
- package/ref-monty/crates/monty/src/builtins/print.rs +141 -0
- package/ref-monty/crates/monty/src/builtins/repr.rs +16 -0
- package/ref-monty/crates/monty/src/builtins/reversed.rs +28 -0
- package/ref-monty/crates/monty/src/builtins/round.rs +174 -0
- package/ref-monty/crates/monty/src/builtins/sorted.rs +151 -0
- package/ref-monty/crates/monty/src/builtins/sum.rs +66 -0
- package/ref-monty/crates/monty/src/builtins/type_.rs +16 -0
- package/ref-monty/crates/monty/src/builtins/zip.rs +77 -0
- package/ref-monty/crates/monty/src/bytecode/builder.rs +699 -0
- package/ref-monty/crates/monty/src/bytecode/code.rs +310 -0
- package/ref-monty/crates/monty/src/bytecode/compiler.rs +3206 -0
- package/ref-monty/crates/monty/src/bytecode/mod.rs +24 -0
- package/ref-monty/crates/monty/src/bytecode/op.rs +617 -0
- package/ref-monty/crates/monty/src/bytecode/vm/async_exec.rs +1058 -0
- package/ref-monty/crates/monty/src/bytecode/vm/attr.rs +63 -0
- package/ref-monty/crates/monty/src/bytecode/vm/binary.rs +487 -0
- package/ref-monty/crates/monty/src/bytecode/vm/call.rs +767 -0
- package/ref-monty/crates/monty/src/bytecode/vm/collections.rs +741 -0
- package/ref-monty/crates/monty/src/bytecode/vm/compare.rs +147 -0
- package/ref-monty/crates/monty/src/bytecode/vm/exceptions.rs +297 -0
- package/ref-monty/crates/monty/src/bytecode/vm/format.rs +132 -0
- package/ref-monty/crates/monty/src/bytecode/vm/mod.rs +1958 -0
- package/ref-monty/crates/monty/src/bytecode/vm/scheduler.rs +620 -0
- package/ref-monty/crates/monty/src/exception_private.rs +1513 -0
- package/ref-monty/crates/monty/src/exception_public.rs +346 -0
- package/ref-monty/crates/monty/src/expressions.rs +694 -0
- package/ref-monty/crates/monty/src/fstring.rs +854 -0
- package/ref-monty/crates/monty/src/function.rs +119 -0
- package/ref-monty/crates/monty/src/heap.rs +1073 -0
- package/ref-monty/crates/monty/src/heap_data.rs +985 -0
- package/ref-monty/crates/monty/src/heap_traits.rs +312 -0
- package/ref-monty/crates/monty/src/intern.rs +837 -0
- package/ref-monty/crates/monty/src/io.rs +106 -0
- package/ref-monty/crates/monty/src/lib.rs +52 -0
- package/ref-monty/crates/monty/src/modules/asyncio.rs +144 -0
- package/ref-monty/crates/monty/src/modules/math.rs +1453 -0
- package/ref-monty/crates/monty/src/modules/mod.rs +120 -0
- package/ref-monty/crates/monty/src/modules/os.rs +116 -0
- package/ref-monty/crates/monty/src/modules/pathlib.rs +33 -0
- package/ref-monty/crates/monty/src/modules/re.rs +606 -0
- package/ref-monty/crates/monty/src/modules/sys.rs +60 -0
- package/ref-monty/crates/monty/src/modules/typing.rs +70 -0
- package/ref-monty/crates/monty/src/namespace.rs +21 -0
- package/ref-monty/crates/monty/src/object.rs +1040 -0
- package/ref-monty/crates/monty/src/os.rs +215 -0
- package/ref-monty/crates/monty/src/parse.rs +1730 -0
- package/ref-monty/crates/monty/src/prepare.rs +3015 -0
- package/ref-monty/crates/monty/src/repl.rs +1109 -0
- package/ref-monty/crates/monty/src/resource.rs +559 -0
- package/ref-monty/crates/monty/src/run.rs +457 -0
- package/ref-monty/crates/monty/src/run_progress.rs +821 -0
- package/ref-monty/crates/monty/src/signature.rs +651 -0
- package/ref-monty/crates/monty/src/sorting.rs +100 -0
- package/ref-monty/crates/monty/src/types/bytes.rs +2356 -0
- package/ref-monty/crates/monty/src/types/dataclass.rs +345 -0
- package/ref-monty/crates/monty/src/types/dict.rs +879 -0
- package/ref-monty/crates/monty/src/types/dict_view.rs +619 -0
- package/ref-monty/crates/monty/src/types/iter.rs +799 -0
- package/ref-monty/crates/monty/src/types/list.rs +929 -0
- package/ref-monty/crates/monty/src/types/long_int.rs +211 -0
- package/ref-monty/crates/monty/src/types/mod.rs +48 -0
- package/ref-monty/crates/monty/src/types/module.rs +146 -0
- package/ref-monty/crates/monty/src/types/namedtuple.rs +261 -0
- package/ref-monty/crates/monty/src/types/path.rs +596 -0
- package/ref-monty/crates/monty/src/types/property.rs +35 -0
- package/ref-monty/crates/monty/src/types/py_trait.rs +322 -0
- package/ref-monty/crates/monty/src/types/range.rs +285 -0
- package/ref-monty/crates/monty/src/types/re_match.rs +522 -0
- package/ref-monty/crates/monty/src/types/re_pattern.rs +726 -0
- package/ref-monty/crates/monty/src/types/set.rs +1373 -0
- package/ref-monty/crates/monty/src/types/slice.rs +257 -0
- package/ref-monty/crates/monty/src/types/str.rs +2051 -0
- package/ref-monty/crates/monty/src/types/tuple.rs +376 -0
- package/ref-monty/crates/monty/src/types/type.rs +407 -0
- package/ref-monty/crates/monty/src/value.rs +2558 -0
- package/ref-monty/crates/monty/test_cases/args__dict_get_no_args.py +3 -0
- package/ref-monty/crates/monty/test_cases/args__dict_get_too_many.py +3 -0
- package/ref-monty/crates/monty/test_cases/args__dict_items_with_args.py +3 -0
- package/ref-monty/crates/monty/test_cases/args__dict_keys_with_args.py +3 -0
- package/ref-monty/crates/monty/test_cases/args__dict_pop_no_args.py +3 -0
- package/ref-monty/crates/monty/test_cases/args__dict_pop_too_many.py +3 -0
- package/ref-monty/crates/monty/test_cases/args__dict_values_with_args.py +3 -0
- package/ref-monty/crates/monty/test_cases/args__id_too_many.py +2 -0
- package/ref-monty/crates/monty/test_cases/args__len_no_args.py +2 -0
- package/ref-monty/crates/monty/test_cases/args__len_too_many.py +2 -0
- package/ref-monty/crates/monty/test_cases/args__len_type_error_int.py +9 -0
- package/ref-monty/crates/monty/test_cases/args__len_type_error_none.py +9 -0
- package/ref-monty/crates/monty/test_cases/args__list_append_no_args.py +3 -0
- package/ref-monty/crates/monty/test_cases/args__list_append_too_many.py +3 -0
- package/ref-monty/crates/monty/test_cases/args__list_insert_too_few.py +3 -0
- package/ref-monty/crates/monty/test_cases/args__list_insert_too_many.py +3 -0
- package/ref-monty/crates/monty/test_cases/args__repr_no_args.py +2 -0
- package/ref-monty/crates/monty/test_cases/arith__div_zero_float.py +2 -0
- package/ref-monty/crates/monty/test_cases/arith__div_zero_int.py +2 -0
- package/ref-monty/crates/monty/test_cases/arith__floordiv_zero_float.py +2 -0
- package/ref-monty/crates/monty/test_cases/arith__floordiv_zero_int.py +2 -0
- package/ref-monty/crates/monty/test_cases/arith__pow_zero_neg.py +2 -0
- package/ref-monty/crates/monty/test_cases/arith__pow_zero_neg_builtin.py +9 -0
- package/ref-monty/crates/monty/test_cases/assert__expr_fail.py +2 -0
- package/ref-monty/crates/monty/test_cases/assert__fail.py +2 -0
- package/ref-monty/crates/monty/test_cases/assert__fail_msg.py +2 -0
- package/ref-monty/crates/monty/test_cases/assert__fn_fail.py +3 -0
- package/ref-monty/crates/monty/test_cases/assert__ops.py +11 -0
- package/ref-monty/crates/monty/test_cases/async__asyncio_run.py +47 -0
- package/ref-monty/crates/monty/test_cases/async__basic.py +10 -0
- package/ref-monty/crates/monty/test_cases/async__closure.py +14 -0
- package/ref-monty/crates/monty/test_cases/async__double_await_coroutine.py +16 -0
- package/ref-monty/crates/monty/test_cases/async__exception.py +10 -0
- package/ref-monty/crates/monty/test_cases/async__ext_call.py +73 -0
- package/ref-monty/crates/monty/test_cases/async__gather_all.py +85 -0
- package/ref-monty/crates/monty/test_cases/async__nested_await.py +15 -0
- package/ref-monty/crates/monty/test_cases/async__nested_gather_ext.py +37 -0
- package/ref-monty/crates/monty/test_cases/async__not_awaitable.py +10 -0
- package/ref-monty/crates/monty/test_cases/async__not_imported.py +14 -0
- package/ref-monty/crates/monty/test_cases/async__recursion_depth_isolation.py +27 -0
- package/ref-monty/crates/monty/test_cases/async__return_types.py +31 -0
- package/ref-monty/crates/monty/test_cases/async__sequential.py +16 -0
- package/ref-monty/crates/monty/test_cases/async__traceback.py +19 -0
- package/ref-monty/crates/monty/test_cases/async__with_args.py +14 -0
- package/ref-monty/crates/monty/test_cases/attr__get_int_error.py +9 -0
- package/ref-monty/crates/monty/test_cases/attr__get_list_error.py +9 -0
- package/ref-monty/crates/monty/test_cases/attr__set_frozen_nonfield.py +12 -0
- package/ref-monty/crates/monty/test_cases/attr__set_int_error.py +10 -0
- package/ref-monty/crates/monty/test_cases/attr__set_list_error.py +10 -0
- package/ref-monty/crates/monty/test_cases/bench__kitchen_sink.py +68 -0
- package/ref-monty/crates/monty/test_cases/bool__ops.py +20 -0
- package/ref-monty/crates/monty/test_cases/builtin__add_type_error.py +2 -0
- package/ref-monty/crates/monty/test_cases/builtin__filter.py +62 -0
- package/ref-monty/crates/monty/test_cases/builtin__filter_not_iterable.py +11 -0
- package/ref-monty/crates/monty/test_cases/builtin__getattr.py +84 -0
- package/ref-monty/crates/monty/test_cases/builtin__iter_funcs.py +42 -0
- package/ref-monty/crates/monty/test_cases/builtin__iter_next.py +66 -0
- package/ref-monty/crates/monty/test_cases/builtin__map.py +74 -0
- package/ref-monty/crates/monty/test_cases/builtin__map_not_iterable.py +11 -0
- package/ref-monty/crates/monty/test_cases/builtin__math_funcs.py +154 -0
- package/ref-monty/crates/monty/test_cases/builtin__more_iter_funcs.py +148 -0
- package/ref-monty/crates/monty/test_cases/builtin__next_stop_iteration.py +10 -0
- package/ref-monty/crates/monty/test_cases/builtin__print_invalid_kwarg.py +9 -0
- package/ref-monty/crates/monty/test_cases/builtin__print_kwargs.py +12 -0
- package/ref-monty/crates/monty/test_cases/builtin__repr.py +3 -0
- package/ref-monty/crates/monty/test_cases/builtin__string_funcs.py +73 -0
- package/ref-monty/crates/monty/test_cases/bytes__decode_invalid_utf8.py +18 -0
- package/ref-monty/crates/monty/test_cases/bytes__endswith_str_error.py +10 -0
- package/ref-monty/crates/monty/test_cases/bytes__getitem_index_error.py +10 -0
- package/ref-monty/crates/monty/test_cases/bytes__index_start_gt_end.py +10 -0
- package/ref-monty/crates/monty/test_cases/bytes__methods.py +394 -0
- package/ref-monty/crates/monty/test_cases/bytes__negative_count.py +9 -0
- package/ref-monty/crates/monty/test_cases/bytes__ops.py +90 -0
- package/ref-monty/crates/monty/test_cases/bytes__startswith_str_error.py +10 -0
- package/ref-monty/crates/monty/test_cases/call_object.py +3 -0
- package/ref-monty/crates/monty/test_cases/chain_comparison__all.py +79 -0
- package/ref-monty/crates/monty/test_cases/closure__param_shadows_outer.py +81 -0
- package/ref-monty/crates/monty/test_cases/closure__pep448.py +203 -0
- package/ref-monty/crates/monty/test_cases/closure__undefined_nonlocal.py +13 -0
- package/ref-monty/crates/monty/test_cases/compare__mixed_types.py +120 -0
- package/ref-monty/crates/monty/test_cases/comprehension__all.py +208 -0
- package/ref-monty/crates/monty/test_cases/comprehension__scope.py +7 -0
- package/ref-monty/crates/monty/test_cases/comprehension__unbound_local.py +14 -0
- package/ref-monty/crates/monty/test_cases/dataclass__basic.py +238 -0
- package/ref-monty/crates/monty/test_cases/dataclass__call_field_error.py +12 -0
- package/ref-monty/crates/monty/test_cases/dataclass__frozen_set_error.py +12 -0
- package/ref-monty/crates/monty/test_cases/dataclass__get_missing_attr_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/dict__get_unhashable_key.py +3 -0
- package/ref-monty/crates/monty/test_cases/dict__literal_unhashable_key.py +2 -0
- package/ref-monty/crates/monty/test_cases/dict__method_pop_missing_error.py +3 -0
- package/ref-monty/crates/monty/test_cases/dict__methods.py +151 -0
- package/ref-monty/crates/monty/test_cases/dict__ops.py +133 -0
- package/ref-monty/crates/monty/test_cases/dict__pop_unhashable_key.py +4 -0
- package/ref-monty/crates/monty/test_cases/dict__popitem_empty.py +9 -0
- package/ref-monty/crates/monty/test_cases/dict__subscript_missing_key.py +3 -0
- package/ref-monty/crates/monty/test_cases/dict__unhashable_dict_key.py +2 -0
- package/ref-monty/crates/monty/test_cases/dict__unhashable_list_key.py +2 -0
- package/ref-monty/crates/monty/test_cases/dict__unpack_type_error.py +2 -0
- package/ref-monty/crates/monty/test_cases/dict__views.py +165 -0
- package/ref-monty/crates/monty/test_cases/edge__all.py +26 -0
- package/ref-monty/crates/monty/test_cases/edge__float_int_mod.py +2 -0
- package/ref-monty/crates/monty/test_cases/edge__int_float_mod.py +2 -0
- package/ref-monty/crates/monty/test_cases/exc__args.py +16 -0
- package/ref-monty/crates/monty/test_cases/exc__str.py +15 -0
- package/ref-monty/crates/monty/test_cases/execute_ok__all.py +54 -0
- package/ref-monty/crates/monty/test_cases/execute_raise__error_instance_str.py +2 -0
- package/ref-monty/crates/monty/test_cases/execute_raise__error_no_args.py +2 -0
- package/ref-monty/crates/monty/test_cases/execute_raise__error_string_arg.py +2 -0
- package/ref-monty/crates/monty/test_cases/execute_raise__error_string_arg_quotes.py +2 -0
- package/ref-monty/crates/monty/test_cases/execute_raise__error_type.py +2 -0
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_instance_via_var.py +4 -0
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_list.py +2 -0
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_number.py +2 -0
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_type_call_via_var.py +4 -0
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_type_direct.py +3 -0
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_type_via_var.py +4 -0
- package/ref-monty/crates/monty/test_cases/ext_call__arg_side_effect_bug.py +22 -0
- package/ref-monty/crates/monty/test_cases/ext_call__augmented.py +17 -0
- package/ref-monty/crates/monty/test_cases/ext_call__augmented_refcount_bug.py +7 -0
- package/ref-monty/crates/monty/test_cases/ext_call__bare_raise_after_resume.py +34 -0
- package/ref-monty/crates/monty/test_cases/ext_call__basic.py +99 -0
- package/ref-monty/crates/monty/test_cases/ext_call__boolean.py +37 -0
- package/ref-monty/crates/monty/test_cases/ext_call__boolean_side_effect_hang.py +17 -0
- package/ref-monty/crates/monty/test_cases/ext_call__closure_bug.py +16 -0
- package/ref-monty/crates/monty/test_cases/ext_call__comparison.py +26 -0
- package/ref-monty/crates/monty/test_cases/ext_call__deep_call_stack.py +18 -0
- package/ref-monty/crates/monty/test_cases/ext_call__elif.py +171 -0
- package/ref-monty/crates/monty/test_cases/ext_call__exc.py +4 -0
- package/ref-monty/crates/monty/test_cases/ext_call__exc_deep_stack.py +39 -0
- package/ref-monty/crates/monty/test_cases/ext_call__exc_in_function.py +17 -0
- package/ref-monty/crates/monty/test_cases/ext_call__exc_nested_functions.py +31 -0
- package/ref-monty/crates/monty/test_cases/ext_call__ext_exc.py +171 -0
- package/ref-monty/crates/monty/test_cases/ext_call__for.py +114 -0
- package/ref-monty/crates/monty/test_cases/ext_call__fstring.py +12 -0
- package/ref-monty/crates/monty/test_cases/ext_call__if.py +135 -0
- package/ref-monty/crates/monty/test_cases/ext_call__if_condition.py +37 -0
- package/ref-monty/crates/monty/test_cases/ext_call__in_closure.py +14 -0
- package/ref-monty/crates/monty/test_cases/ext_call__in_function.py +40 -0
- package/ref-monty/crates/monty/test_cases/ext_call__in_function_simple.py +7 -0
- package/ref-monty/crates/monty/test_cases/ext_call__literals.py +17 -0
- package/ref-monty/crates/monty/test_cases/ext_call__multi_in_func.py +32 -0
- package/ref-monty/crates/monty/test_cases/ext_call__name_lookup.py +69 -0
- package/ref-monty/crates/monty/test_cases/ext_call__name_lookup_undefined.py +4 -0
- package/ref-monty/crates/monty/test_cases/ext_call__nested_calls.py +14 -0
- package/ref-monty/crates/monty/test_cases/ext_call__recursion_bug.py +19 -0
- package/ref-monty/crates/monty/test_cases/ext_call__return.py +28 -0
- package/ref-monty/crates/monty/test_cases/ext_call__side_effects.py +25 -0
- package/ref-monty/crates/monty/test_cases/ext_call__subscript.py +7 -0
- package/ref-monty/crates/monty/test_cases/ext_call__ternary.py +28 -0
- package/ref-monty/crates/monty/test_cases/ext_call__try.py +280 -0
- package/ref-monty/crates/monty/test_cases/ext_call__try_simple.py +10 -0
- package/ref-monty/crates/monty/test_cases/ext_call__unary.py +13 -0
- package/ref-monty/crates/monty/test_cases/frozenset__ops.py +178 -0
- package/ref-monty/crates/monty/test_cases/fstring__all.py +236 -0
- package/ref-monty/crates/monty/test_cases/fstring__error_eq_align_on_str.py +3 -0
- package/ref-monty/crates/monty/test_cases/fstring__error_float_f_on_str.py +3 -0
- package/ref-monty/crates/monty/test_cases/fstring__error_int_d_on_float.py +3 -0
- package/ref-monty/crates/monty/test_cases/fstring__error_int_d_on_str.py +3 -0
- package/ref-monty/crates/monty/test_cases/fstring__error_invalid_spec.py +4 -0
- package/ref-monty/crates/monty/test_cases/fstring__error_invalid_spec_dynamic.py +4 -0
- package/ref-monty/crates/monty/test_cases/fstring__error_invalid_spec_str.py +4 -0
- package/ref-monty/crates/monty/test_cases/fstring__error_str_s_on_int.py +3 -0
- package/ref-monty/crates/monty/test_cases/function__call_duplicate_kwargs.py +6 -0
- package/ref-monty/crates/monty/test_cases/function__call_unpack.py +42 -0
- package/ref-monty/crates/monty/test_cases/function__defaults.py +117 -0
- package/ref-monty/crates/monty/test_cases/function__err_duplicate_arg.py +7 -0
- package/ref-monty/crates/monty/test_cases/function__err_duplicate_first_arg.py +7 -0
- package/ref-monty/crates/monty/test_cases/function__err_duplicate_kwarg_cleanup.py +9 -0
- package/ref-monty/crates/monty/test_cases/function__err_kwonly_as_positional.py +7 -0
- package/ref-monty/crates/monty/test_cases/function__err_missing_all_posonly.py +7 -0
- package/ref-monty/crates/monty/test_cases/function__err_missing_heap_cleanup.py +9 -0
- package/ref-monty/crates/monty/test_cases/function__err_missing_kwonly.py +7 -0
- package/ref-monty/crates/monty/test_cases/function__err_missing_posonly_with_kwarg.py +7 -0
- package/ref-monty/crates/monty/test_cases/function__err_missing_with_posonly.py +7 -0
- package/ref-monty/crates/monty/test_cases/function__err_posonly_as_kwarg.py +7 -0
- package/ref-monty/crates/monty/test_cases/function__err_posonly_first_as_kwarg.py +7 -0
- package/ref-monty/crates/monty/test_cases/function__err_too_many_posonly.py +7 -0
- package/ref-monty/crates/monty/test_cases/function__err_too_many_with_kwonly.py +7 -0
- package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg.py +7 -0
- package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg_cleanup.py +9 -0
- package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg_quote.py +13 -0
- package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg_simple.py +7 -0
- package/ref-monty/crates/monty/test_cases/function__err_unpack_duplicate_arg.py +6 -0
- package/ref-monty/crates/monty/test_cases/function__err_unpack_duplicate_heap.py +8 -0
- package/ref-monty/crates/monty/test_cases/function__err_unpack_int.py +6 -0
- package/ref-monty/crates/monty/test_cases/function__err_unpack_nonstring_key.py +6 -0
- package/ref-monty/crates/monty/test_cases/function__err_unpack_not_mapping.py +6 -0
- package/ref-monty/crates/monty/test_cases/function__kwargs_unpacking.py +173 -0
- package/ref-monty/crates/monty/test_cases/function__ops.py +294 -0
- package/ref-monty/crates/monty/test_cases/function__return_none.py +42 -0
- package/ref-monty/crates/monty/test_cases/function__signatures.py +47 -0
- package/ref-monty/crates/monty/test_cases/function__too_few_args_all.py +6 -0
- package/ref-monty/crates/monty/test_cases/function__too_few_args_one.py +6 -0
- package/ref-monty/crates/monty/test_cases/function__too_few_args_two.py +6 -0
- package/ref-monty/crates/monty/test_cases/function__too_many_args_one.py +6 -0
- package/ref-monty/crates/monty/test_cases/function__too_many_args_two.py +6 -0
- package/ref-monty/crates/monty/test_cases/function__too_many_args_zero.py +6 -0
- package/ref-monty/crates/monty/test_cases/global__error_assigned_before.py +7 -0
- package/ref-monty/crates/monty/test_cases/global__ops.py +163 -0
- package/ref-monty/crates/monty/test_cases/hash__dict_unhashable.py +2 -0
- package/ref-monty/crates/monty/test_cases/hash__list_unhashable.py +2 -0
- package/ref-monty/crates/monty/test_cases/hash__ops.py +153 -0
- package/ref-monty/crates/monty/test_cases/id__bytes_literals_distinct.py +3 -0
- package/ref-monty/crates/monty/test_cases/id__int_copy_distinct.py +5 -0
- package/ref-monty/crates/monty/test_cases/id__is_number_is_number.py +3 -0
- package/ref-monty/crates/monty/test_cases/id__non_overlapping_lifetimes_distinct_types.py +10 -0
- package/ref-monty/crates/monty/test_cases/id__non_overlapping_lifetimes_same_types.py +6 -0
- package/ref-monty/crates/monty/test_cases/id__ops.py +97 -0
- package/ref-monty/crates/monty/test_cases/id__str_literals_same.py +3 -0
- package/ref-monty/crates/monty/test_cases/if__elif_else.py +207 -0
- package/ref-monty/crates/monty/test_cases/if__raise_elif.py +11 -0
- package/ref-monty/crates/monty/test_cases/if__raise_else.py +13 -0
- package/ref-monty/crates/monty/test_cases/if__raise_if.py +9 -0
- package/ref-monty/crates/monty/test_cases/if__raise_in_elif_condition.py +18 -0
- package/ref-monty/crates/monty/test_cases/if__raise_in_if_condition.py +16 -0
- package/ref-monty/crates/monty/test_cases/if_else_expr__all.py +55 -0
- package/ref-monty/crates/monty/test_cases/import__error_cannot_import.py +9 -0
- package/ref-monty/crates/monty/test_cases/import__error_module_not_found.py +9 -0
- package/ref-monty/crates/monty/test_cases/import__local_scope.py +68 -0
- package/ref-monty/crates/monty/test_cases/import__os.py +25 -0
- package/ref-monty/crates/monty/test_cases/import__relative_error.py +9 -0
- package/ref-monty/crates/monty/test_cases/import__relative_no_module_error.py +9 -0
- package/ref-monty/crates/monty/test_cases/import__runtime_error_when_executed.py +14 -0
- package/ref-monty/crates/monty/test_cases/import__star_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/import__sys.py +47 -0
- package/ref-monty/crates/monty/test_cases/import__sys_monty.py +28 -0
- package/ref-monty/crates/monty/test_cases/import__type_checking_guard.py +37 -0
- package/ref-monty/crates/monty/test_cases/import__typing.py +25 -0
- package/ref-monty/crates/monty/test_cases/import__typing_type_ignore.py +4 -0
- package/ref-monty/crates/monty/test_cases/int__bigint.py +467 -0
- package/ref-monty/crates/monty/test_cases/int__bigint_errors.py +260 -0
- package/ref-monty/crates/monty/test_cases/int__ops.py +219 -0
- package/ref-monty/crates/monty/test_cases/int__overflow_division.py +84 -0
- package/ref-monty/crates/monty/test_cases/is_variant__all.py +36 -0
- package/ref-monty/crates/monty/test_cases/isinstance__arg2_list_error.py +2 -0
- package/ref-monty/crates/monty/test_cases/isinstance__arg2_type_error.py +2 -0
- package/ref-monty/crates/monty/test_cases/iter__dict_mutation.py +4 -0
- package/ref-monty/crates/monty/test_cases/iter__for.py +243 -0
- package/ref-monty/crates/monty/test_cases/iter__for_loop_unpacking.py +66 -0
- package/ref-monty/crates/monty/test_cases/iter__generator_expr.py +20 -0
- package/ref-monty/crates/monty/test_cases/iter__generator_expr_type.py +7 -0
- package/ref-monty/crates/monty/test_cases/iter__not_iterable.py +3 -0
- package/ref-monty/crates/monty/test_cases/lambda__all.py +145 -0
- package/ref-monty/crates/monty/test_cases/list__extend_not_iterable.py +7 -0
- package/ref-monty/crates/monty/test_cases/list__getitem_out_of_bounds.py +3 -0
- package/ref-monty/crates/monty/test_cases/list__index_not_found.py +9 -0
- package/ref-monty/crates/monty/test_cases/list__index_start_gt_end.py +10 -0
- package/ref-monty/crates/monty/test_cases/list__ops.py +473 -0
- package/ref-monty/crates/monty/test_cases/list__pop_empty.py +9 -0
- package/ref-monty/crates/monty/test_cases/list__pop_out_of_range.py +9 -0
- package/ref-monty/crates/monty/test_cases/list__pop_type_error.py +9 -0
- package/ref-monty/crates/monty/test_cases/list__remove_not_found.py +9 -0
- package/ref-monty/crates/monty/test_cases/list__setitem_dict_index.py +13 -0
- package/ref-monty/crates/monty/test_cases/list__setitem_huge_int_index.py +13 -0
- package/ref-monty/crates/monty/test_cases/list__setitem_index_error.py +10 -0
- package/ref-monty/crates/monty/test_cases/list__setitem_type_error.py +10 -0
- package/ref-monty/crates/monty/test_cases/list__unpack_type_error.py +2 -0
- package/ref-monty/crates/monty/test_cases/longint__index_error.py +3 -0
- package/ref-monty/crates/monty/test_cases/longint__repeat_error.py +3 -0
- package/ref-monty/crates/monty/test_cases/loop__break_continue.py +113 -0
- package/ref-monty/crates/monty/test_cases/loop__break_finally.py +69 -0
- package/ref-monty/crates/monty/test_cases/loop__break_in_function_error.py +13 -0
- package/ref-monty/crates/monty/test_cases/loop__break_in_if_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/loop__break_nested_except_clears.py +55 -0
- package/ref-monty/crates/monty/test_cases/loop__break_outside_error.py +9 -0
- package/ref-monty/crates/monty/test_cases/loop__continue_finally.py +81 -0
- package/ref-monty/crates/monty/test_cases/loop__continue_in_function_error.py +13 -0
- package/ref-monty/crates/monty/test_cases/loop__continue_in_if_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/loop__continue_nested_except_clears.py +60 -0
- package/ref-monty/crates/monty/test_cases/loop__continue_outside_error.py +9 -0
- package/ref-monty/crates/monty/test_cases/math__acos_domain_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__acosh_domain_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__asin_domain_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__atanh_domain_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__cos_inf_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__cosh_overflow_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__exp_overflow_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__factorial_float_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__factorial_negative_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__floor_inf_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__floor_nan_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__floor_str_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__fmod_inf_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__gamma_neg_int_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__gcd_float_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__isqrt_negative_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__ldexp_overflow_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__log1p_domain_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__log_base1_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__log_zero_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__module.py +1432 -0
- package/ref-monty/crates/monty/test_cases/math__pow_domain_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__sin_inf_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__sqrt_negative_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__tan_inf_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__trunc_str_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/method__args_kwargs_unpacking.py +259 -0
- package/ref-monty/crates/monty/test_cases/name_error__unbound_local_func.py +19 -0
- package/ref-monty/crates/monty/test_cases/name_error__unbound_local_module.py +12 -0
- package/ref-monty/crates/monty/test_cases/name_error__undefined_call_chained.py +9 -0
- package/ref-monty/crates/monty/test_cases/name_error__undefined_call_in_expr.py +9 -0
- package/ref-monty/crates/monty/test_cases/name_error__undefined_call_in_function.py +16 -0
- package/ref-monty/crates/monty/test_cases/name_error__undefined_call_with_args.py +9 -0
- package/ref-monty/crates/monty/test_cases/name_error__undefined_global.py +10 -0
- package/ref-monty/crates/monty/test_cases/namedtuple__missing_attr.py +11 -0
- package/ref-monty/crates/monty/test_cases/namedtuple__ops.py +34 -0
- package/ref-monty/crates/monty/test_cases/nonlocal__error_module_level.py +3 -0
- package/ref-monty/crates/monty/test_cases/nonlocal__ops.py +353 -0
- package/ref-monty/crates/monty/test_cases/os__environ.py +40 -0
- package/ref-monty/crates/monty/test_cases/os__getenv_key_list_error.py +5 -0
- package/ref-monty/crates/monty/test_cases/os__getenv_key_type_error.py +5 -0
- package/ref-monty/crates/monty/test_cases/parse_error__complex.py +3 -0
- package/ref-monty/crates/monty/test_cases/pathlib__import.py +11 -0
- package/ref-monty/crates/monty/test_cases/pathlib__os.py +136 -0
- package/ref-monty/crates/monty/test_cases/pathlib__os_read_error.py +12 -0
- package/ref-monty/crates/monty/test_cases/pathlib__pure.py +81 -0
- package/ref-monty/crates/monty/test_cases/pyobject__cycle_dict_self.py +5 -0
- package/ref-monty/crates/monty/test_cases/pyobject__cycle_list_dict.py +6 -0
- package/ref-monty/crates/monty/test_cases/pyobject__cycle_list_self.py +5 -0
- package/ref-monty/crates/monty/test_cases/pyobject__cycle_multiple_refs.py +6 -0
- package/ref-monty/crates/monty/test_cases/range__error_no_args.py +2 -0
- package/ref-monty/crates/monty/test_cases/range__error_step_zero.py +2 -0
- package/ref-monty/crates/monty/test_cases/range__error_too_many_args.py +2 -0
- package/ref-monty/crates/monty/test_cases/range__getitem_index_error.py +10 -0
- package/ref-monty/crates/monty/test_cases/range__ops.py +236 -0
- package/ref-monty/crates/monty/test_cases/re__basic.py +756 -0
- package/ref-monty/crates/monty/test_cases/re__grouping.py +241 -0
- package/ref-monty/crates/monty/test_cases/re__match.py +148 -0
- package/ref-monty/crates/monty/test_cases/recursion__deep_drop.py +26 -0
- package/ref-monty/crates/monty/test_cases/recursion__deep_eq.py +23 -0
- package/ref-monty/crates/monty/test_cases/recursion__deep_hash.py +46 -0
- package/ref-monty/crates/monty/test_cases/recursion__deep_repr.py +12 -0
- package/ref-monty/crates/monty/test_cases/recursion__function_depth.py +13 -0
- package/ref-monty/crates/monty/test_cases/refcount__cycle_mutual_reference.py +18 -0
- package/ref-monty/crates/monty/test_cases/refcount__cycle_self_reference.py +12 -0
- package/ref-monty/crates/monty/test_cases/refcount__dict_basic.py +5 -0
- package/ref-monty/crates/monty/test_cases/refcount__dict_get.py +5 -0
- package/ref-monty/crates/monty/test_cases/refcount__dict_keys_and.py +14 -0
- package/ref-monty/crates/monty/test_cases/refcount__dict_overwrite.py +6 -0
- package/ref-monty/crates/monty/test_cases/refcount__gather_cleanup.py +16 -0
- package/ref-monty/crates/monty/test_cases/refcount__gather_exception.py +18 -0
- package/ref-monty/crates/monty/test_cases/refcount__gather_nested_cancel.py +25 -0
- package/ref-monty/crates/monty/test_cases/refcount__immediate_skipped.py +4 -0
- package/ref-monty/crates/monty/test_cases/refcount__kwargs_unpacking.py +27 -0
- package/ref-monty/crates/monty/test_cases/refcount__list_append_multiple.py +6 -0
- package/ref-monty/crates/monty/test_cases/refcount__list_append_ref.py +5 -0
- package/ref-monty/crates/monty/test_cases/refcount__list_concat.py +5 -0
- package/ref-monty/crates/monty/test_cases/refcount__list_getitem.py +5 -0
- package/ref-monty/crates/monty/test_cases/refcount__list_iadd.py +5 -0
- package/ref-monty/crates/monty/test_cases/refcount__nested_list.py +4 -0
- package/ref-monty/crates/monty/test_cases/refcount__re_pattern_sub_error_paths.py +37 -0
- package/ref-monty/crates/monty/test_cases/refcount__re_search_match.py +34 -0
- package/ref-monty/crates/monty/test_cases/refcount__re_sub_error_paths.py +31 -0
- package/ref-monty/crates/monty/test_cases/refcount__shared_reference.py +4 -0
- package/ref-monty/crates/monty/test_cases/refcount__single_list.py +3 -0
- package/ref-monty/crates/monty/test_cases/repr__cycle_detection.py +24 -0
- package/ref-monty/crates/monty/test_cases/set__ops.py +191 -0
- package/ref-monty/crates/monty/test_cases/set__review_bugs.py +35 -0
- package/ref-monty/crates/monty/test_cases/set__unpack_type_error.py +2 -0
- package/ref-monty/crates/monty/test_cases/slice__invalid_indices.py +2 -0
- package/ref-monty/crates/monty/test_cases/slice__kwargs.py +9 -0
- package/ref-monty/crates/monty/test_cases/slice__no_args.py +9 -0
- package/ref-monty/crates/monty/test_cases/slice__ops.py +149 -0
- package/ref-monty/crates/monty/test_cases/slice__step_zero.py +9 -0
- package/ref-monty/crates/monty/test_cases/slice__step_zero_bytes.py +9 -0
- package/ref-monty/crates/monty/test_cases/slice__step_zero_range.py +9 -0
- package/ref-monty/crates/monty/test_cases/slice__step_zero_str.py +9 -0
- package/ref-monty/crates/monty/test_cases/slice__step_zero_tuple.py +9 -0
- package/ref-monty/crates/monty/test_cases/slice__too_many_args.py +9 -0
- package/ref-monty/crates/monty/test_cases/str__getitem_index_error.py +10 -0
- package/ref-monty/crates/monty/test_cases/str__index_not_found.py +9 -0
- package/ref-monty/crates/monty/test_cases/str__join_no_args.py +9 -0
- package/ref-monty/crates/monty/test_cases/str__join_non_string.py +9 -0
- package/ref-monty/crates/monty/test_cases/str__join_not_iterable.py +9 -0
- package/ref-monty/crates/monty/test_cases/str__join_too_many_args.py +9 -0
- package/ref-monty/crates/monty/test_cases/str__methods.py +327 -0
- package/ref-monty/crates/monty/test_cases/str__ops.py +162 -0
- package/ref-monty/crates/monty/test_cases/str__partition_empty.py +9 -0
- package/ref-monty/crates/monty/test_cases/str__rsplit_empty_sep.py +9 -0
- package/ref-monty/crates/monty/test_cases/str__split_empty_sep.py +9 -0
- package/ref-monty/crates/monty/test_cases/sys__types.py +7 -0
- package/ref-monty/crates/monty/test_cases/traceback__division_error.py +30 -0
- package/ref-monty/crates/monty/test_cases/traceback__index_error.py +17 -0
- package/ref-monty/crates/monty/test_cases/traceback__insert_as_int.py +10 -0
- package/ref-monty/crates/monty/test_cases/traceback__nested_call.py +29 -0
- package/ref-monty/crates/monty/test_cases/traceback__nonlocal_module_scope.py +10 -0
- package/ref-monty/crates/monty/test_cases/traceback__nonlocal_unbound.py +24 -0
- package/ref-monty/crates/monty/test_cases/traceback__range_as_int.py +9 -0
- package/ref-monty/crates/monty/test_cases/traceback__recursion_error.py +23 -0
- package/ref-monty/crates/monty/test_cases/traceback__set_mutation.py +11 -0
- package/ref-monty/crates/monty/test_cases/traceback__undefined_attr_call.py +16 -0
- package/ref-monty/crates/monty/test_cases/traceback__undefined_call.py +16 -0
- package/ref-monty/crates/monty/test_cases/traceback__undefined_raise.py +16 -0
- package/ref-monty/crates/monty/test_cases/try_except__all.py +472 -0
- package/ref-monty/crates/monty/test_cases/try_except__bare_raise_no_context.py +2 -0
- package/ref-monty/crates/monty/test_cases/try_except__invalid_type.py +5 -0
- package/ref-monty/crates/monty/test_cases/tuple__getitem_out_of_bounds.py +3 -0
- package/ref-monty/crates/monty/test_cases/tuple__index_not_found.py +9 -0
- package/ref-monty/crates/monty/test_cases/tuple__index_start_gt_end.py +10 -0
- package/ref-monty/crates/monty/test_cases/tuple__methods.py +19 -0
- package/ref-monty/crates/monty/test_cases/tuple__ops.py +133 -0
- package/ref-monty/crates/monty/test_cases/tuple__unpack_type_error.py +2 -0
- package/ref-monty/crates/monty/test_cases/type__builtin_attr_error.py +9 -0
- package/ref-monty/crates/monty/test_cases/type__bytes_negative.py +2 -0
- package/ref-monty/crates/monty/test_cases/type__cell_not_builtin.py +9 -0
- package/ref-monty/crates/monty/test_cases/type__exception_attr_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/type__float_conversion_error.py +2 -0
- package/ref-monty/crates/monty/test_cases/type__float_repr_both_quotes.py +9 -0
- package/ref-monty/crates/monty/test_cases/type__float_repr_newline.py +9 -0
- package/ref-monty/crates/monty/test_cases/type__float_repr_single_quote.py +9 -0
- package/ref-monty/crates/monty/test_cases/type__int_conversion_error.py +2 -0
- package/ref-monty/crates/monty/test_cases/type__list_not_iterable.py +2 -0
- package/ref-monty/crates/monty/test_cases/type__non_builtin_name_error.py +9 -0
- package/ref-monty/crates/monty/test_cases/type__ops.py +200 -0
- package/ref-monty/crates/monty/test_cases/type__shadow_exc.py +3 -0
- package/ref-monty/crates/monty/test_cases/type__shadow_int.py +9 -0
- package/ref-monty/crates/monty/test_cases/type__shadow_len.py +3 -0
- package/ref-monty/crates/monty/test_cases/type__tuple_not_iterable.py +2 -0
- package/ref-monty/crates/monty/test_cases/type_error__int_add_list.py +2 -0
- package/ref-monty/crates/monty/test_cases/type_error__int_div_str.py +2 -0
- package/ref-monty/crates/monty/test_cases/type_error__int_floordiv_str.py +2 -0
- package/ref-monty/crates/monty/test_cases/type_error__int_iadd_str.py +3 -0
- package/ref-monty/crates/monty/test_cases/type_error__int_mod_str.py +2 -0
- package/ref-monty/crates/monty/test_cases/type_error__int_pow_str.py +2 -0
- package/ref-monty/crates/monty/test_cases/type_error__int_sub_str.py +2 -0
- package/ref-monty/crates/monty/test_cases/type_error__list_add_int.py +2 -0
- package/ref-monty/crates/monty/test_cases/type_error__list_add_str.py +2 -0
- package/ref-monty/crates/monty/test_cases/type_error__list_iadd_int.py +6 -0
- package/ref-monty/crates/monty/test_cases/type_error__str_add_int.py +2 -0
- package/ref-monty/crates/monty/test_cases/type_error__str_iadd_int.py +3 -0
- package/ref-monty/crates/monty/test_cases/type_error__unary_invert_str.py +3 -0
- package/ref-monty/crates/monty/test_cases/type_error__unary_minus_str.py +4 -0
- package/ref-monty/crates/monty/test_cases/type_error__unary_neg_str.py +3 -0
- package/ref-monty/crates/monty/test_cases/type_error__unary_plus_str.py +4 -0
- package/ref-monty/crates/monty/test_cases/typing__types.py +24 -0
- package/ref-monty/crates/monty/test_cases/unpack__nested.py +48 -0
- package/ref-monty/crates/monty/test_cases/unpack__non_sequence.py +9 -0
- package/ref-monty/crates/monty/test_cases/unpack__not_enough.py +9 -0
- package/ref-monty/crates/monty/test_cases/unpack__ops.py +153 -0
- package/ref-monty/crates/monty/test_cases/unpack__star_not_enough.py +9 -0
- package/ref-monty/crates/monty/test_cases/unpack__too_many.py +9 -0
- package/ref-monty/crates/monty/test_cases/version__cpython.py +4 -0
- package/ref-monty/crates/monty/test_cases/walrus__all.py +178 -0
- package/ref-monty/crates/monty/test_cases/while__all.py +206 -0
- package/ref-monty/crates/monty/tests/asyncio.rs +764 -0
- package/ref-monty/crates/monty/tests/binary_serde.rs +185 -0
- package/ref-monty/crates/monty/tests/bytecode_limits.rs +248 -0
- package/ref-monty/crates/monty/tests/datatest_runner.rs +2029 -0
- package/ref-monty/crates/monty/tests/inputs.rs +420 -0
- package/ref-monty/crates/monty/tests/json_serde.rs +250 -0
- package/ref-monty/crates/monty/tests/main.rs +71 -0
- package/ref-monty/crates/monty/tests/math_module.rs +114 -0
- package/ref-monty/crates/monty/tests/name_lookup.rs +482 -0
- package/ref-monty/crates/monty/tests/os_tests.rs +459 -0
- package/ref-monty/crates/monty/tests/parse_errors.rs +441 -0
- package/ref-monty/crates/monty/tests/print_writer.rs +238 -0
- package/ref-monty/crates/monty/tests/py_object.rs +121 -0
- package/ref-monty/crates/monty/tests/regex.rs +90 -0
- package/ref-monty/crates/monty/tests/repl.rs +344 -0
- package/ref-monty/crates/monty/tests/resource_limits.rs +1826 -0
- package/ref-monty/crates/monty/tests/try_from.rs +167 -0
- package/ref-monty/crates/monty-cli/Cargo.toml +25 -0
- package/ref-monty/crates/monty-cli/src/main.rs +541 -0
- package/ref-monty/crates/monty-js/.cargo/config.toml +2 -0
- package/ref-monty/crates/monty-js/.prettierignore +8 -0
- package/ref-monty/crates/monty-js/Cargo.toml +32 -0
- package/ref-monty/crates/monty-js/README.md +207 -0
- package/ref-monty/crates/monty-js/__test__/async.spec.ts +350 -0
- package/ref-monty/crates/monty-js/__test__/basic.spec.ts +114 -0
- package/ref-monty/crates/monty-js/__test__/exceptions.spec.ts +427 -0
- package/ref-monty/crates/monty-js/__test__/external.spec.ts +354 -0
- package/ref-monty/crates/monty-js/__test__/inputs.spec.ts +143 -0
- package/ref-monty/crates/monty-js/__test__/limits.spec.ts +162 -0
- package/ref-monty/crates/monty-js/__test__/package.json +3 -0
- package/ref-monty/crates/monty-js/__test__/print.spec.ts +229 -0
- package/ref-monty/crates/monty-js/__test__/repl.spec.ts +34 -0
- package/ref-monty/crates/monty-js/__test__/serialize.spec.ts +205 -0
- package/ref-monty/crates/monty-js/__test__/start.spec.ts +443 -0
- package/ref-monty/crates/monty-js/__test__/type_check.spec.ts +147 -0
- package/ref-monty/crates/monty-js/__test__/types.spec.ts +319 -0
- package/ref-monty/crates/monty-js/build.rs +61 -0
- package/ref-monty/crates/monty-js/index-header.d.ts +3 -0
- package/ref-monty/crates/monty-js/package-lock.json +4694 -0
- package/ref-monty/crates/monty-js/package.json +100 -0
- package/ref-monty/crates/monty-js/scripts/smoke-test.sh +69 -0
- package/ref-monty/crates/monty-js/smoke-test/package.json +17 -0
- package/ref-monty/crates/monty-js/smoke-test/test.ts +171 -0
- package/ref-monty/crates/monty-js/smoke-test/tsconfig.json +11 -0
- package/ref-monty/crates/monty-js/src/convert.rs +648 -0
- package/ref-monty/crates/monty-js/src/exceptions.rs +293 -0
- package/ref-monty/crates/monty-js/src/lib.rs +41 -0
- package/ref-monty/crates/monty-js/src/limits.rs +53 -0
- package/ref-monty/crates/monty-js/src/monty_cls.rs +1407 -0
- package/ref-monty/crates/monty-js/tsconfig.json +17 -0
- package/ref-monty/crates/monty-js/wrapper.ts +701 -0
- package/ref-monty/crates/monty-python/Cargo.toml +38 -0
- package/ref-monty/crates/monty-python/README.md +134 -0
- package/ref-monty/crates/monty-python/build.rs +4 -0
- package/ref-monty/crates/monty-python/example.py +40 -0
- package/ref-monty/crates/monty-python/exercise.py +46 -0
- package/ref-monty/crates/monty-python/pyproject.toml +57 -0
- package/ref-monty/crates/monty-python/python/pydantic_monty/__init__.py +281 -0
- package/ref-monty/crates/monty-python/python/pydantic_monty/_monty.pyi +677 -0
- package/ref-monty/crates/monty-python/python/pydantic_monty/os_access.py +933 -0
- package/ref-monty/crates/monty-python/python/pydantic_monty/py.typed +0 -0
- package/ref-monty/crates/monty-python/src/convert.rs +273 -0
- package/ref-monty/crates/monty-python/src/dataclass.rs +461 -0
- package/ref-monty/crates/monty-python/src/exceptions.rs +557 -0
- package/ref-monty/crates/monty-python/src/external.rs +165 -0
- package/ref-monty/crates/monty-python/src/lib.rs +77 -0
- package/ref-monty/crates/monty-python/src/limits.rs +142 -0
- package/ref-monty/crates/monty-python/src/monty_cls.rs +1650 -0
- package/ref-monty/crates/monty-python/src/repl.rs +470 -0
- package/ref-monty/crates/monty-python/src/serialization.rs +761 -0
- package/ref-monty/crates/monty-python/tests/test_async.py +1201 -0
- package/ref-monty/crates/monty-python/tests/test_basic.py +66 -0
- package/ref-monty/crates/monty-python/tests/test_dataclasses.py +971 -0
- package/ref-monty/crates/monty-python/tests/test_exceptions.py +361 -0
- package/ref-monty/crates/monty-python/tests/test_external.py +367 -0
- package/ref-monty/crates/monty-python/tests/test_inputs.py +126 -0
- package/ref-monty/crates/monty-python/tests/test_limits.py +257 -0
- package/ref-monty/crates/monty-python/tests/test_os_access.py +1286 -0
- package/ref-monty/crates/monty-python/tests/test_os_access_compat.py +731 -0
- package/ref-monty/crates/monty-python/tests/test_os_access_raw.py +483 -0
- package/ref-monty/crates/monty-python/tests/test_os_calls.py +819 -0
- package/ref-monty/crates/monty-python/tests/test_print.py +208 -0
- package/ref-monty/crates/monty-python/tests/test_re.py +170 -0
- package/ref-monty/crates/monty-python/tests/test_readme_examples.py +20 -0
- package/ref-monty/crates/monty-python/tests/test_repl.py +749 -0
- package/ref-monty/crates/monty-python/tests/test_serialize.py +284 -0
- package/ref-monty/crates/monty-python/tests/test_start.py +346 -0
- package/ref-monty/crates/monty-python/tests/test_threading.py +163 -0
- package/ref-monty/crates/monty-python/tests/test_type_check.py +344 -0
- package/ref-monty/crates/monty-python/tests/test_types.py +553 -0
- package/ref-monty/crates/monty-type-checking/Cargo.toml +32 -0
- package/ref-monty/crates/monty-type-checking/src/db.rs +116 -0
- package/ref-monty/crates/monty-type-checking/src/lib.rs +4 -0
- package/ref-monty/crates/monty-type-checking/src/type_check.rs +280 -0
- package/ref-monty/crates/monty-type-checking/tests/bad_types.py +109 -0
- package/ref-monty/crates/monty-type-checking/tests/bad_types_output.txt +21 -0
- package/ref-monty/crates/monty-type-checking/tests/good_types.py +475 -0
- package/ref-monty/crates/monty-type-checking/tests/main.rs +205 -0
- package/ref-monty/crates/monty-type-checking/tests/reveal_types.py +56 -0
- package/ref-monty/crates/monty-type-checking/tests/reveal_types_output.txt +41 -0
- package/ref-monty/crates/monty-typeshed/Cargo.toml +29 -0
- package/ref-monty/crates/monty-typeshed/README.md +11 -0
- package/ref-monty/crates/monty-typeshed/build.rs +101 -0
- package/ref-monty/crates/monty-typeshed/custom/README.md +1 -0
- package/ref-monty/crates/monty-typeshed/custom/asyncio.pyi +138 -0
- package/ref-monty/crates/monty-typeshed/custom/os.pyi +87 -0
- package/ref-monty/crates/monty-typeshed/custom/sys.pyi +33 -0
- package/ref-monty/crates/monty-typeshed/src/lib.rs +56 -0
- package/ref-monty/crates/monty-typeshed/update.py +321 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/source_commit.txt +1 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/VERSIONS +20 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/_collections_abc.pyi +105 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/_typeshed/__init__.pyi +394 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/asyncio.pyi +138 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/builtins.pyi +1434 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/collections/__init__.pyi +527 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/collections/abc.pyi +2 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/dataclasses.pyi +502 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/enum.pyi +376 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/math.pyi +149 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/os.pyi +87 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/pathlib/__init__.pyi +395 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/pathlib/types.pyi +8 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/re.pyi +337 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/sys.pyi +33 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/types.pyi +741 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/typing.pyi +1217 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/typing_extensions.pyi +716 -0
- package/ref-monty/docs/usage-guide.md +117 -0
- package/ref-monty/examples/README.md +3 -0
- package/ref-monty/examples/expense_analysis/README.md +3 -0
- package/ref-monty/examples/expense_analysis/data.py +124 -0
- package/ref-monty/examples/expense_analysis/main.py +115 -0
- package/ref-monty/examples/sql_playground/README.md +20 -0
- package/ref-monty/examples/sql_playground/external_functions.py +129 -0
- package/ref-monty/examples/sql_playground/main.py +81 -0
- package/ref-monty/examples/sql_playground/sandbox_code.py +82 -0
- package/ref-monty/examples/sql_playground/type_stubs.pyi +14 -0
- package/ref-monty/examples/web_scraper/README.md +15 -0
- package/ref-monty/examples/web_scraper/browser.py +56 -0
- package/ref-monty/examples/web_scraper/example_code.py +59 -0
- package/ref-monty/examples/web_scraper/external_functions.py +324 -0
- package/ref-monty/examples/web_scraper/main.py +193 -0
- package/ref-monty/examples/web_scraper/sub_agent.py +79 -0
- package/ref-monty/monty-npm.md +235 -0
- package/ref-monty/pyproject.toml +162 -0
- package/ref-monty/scripts/check_imports.py +91 -0
- package/ref-monty/scripts/codecov_diff.py +412 -0
- package/ref-monty/scripts/complete_tests.py +146 -0
- package/ref-monty/scripts/flamegraph_to_text.py +208 -0
- package/ref-monty/scripts/iter_test_methods.py +540 -0
- package/ref-monty/scripts/run_traceback.py +180 -0
- package/ref-monty/scripts/startup_performance.py +130 -0
- package/ref-monty/uv.lock +1779 -0
- package/temp_resend_cli/repo/.github/scripts/pr-title-check.js +34 -0
- package/temp_resend_cli/repo/.github/workflows/ci.yml +67 -0
- package/temp_resend_cli/repo/.github/workflows/post-release.yml +51 -0
- package/temp_resend_cli/repo/.github/workflows/pr-title-check.yml +13 -0
- package/temp_resend_cli/repo/.github/workflows/release.yml +175 -0
- package/temp_resend_cli/repo/.github/workflows/test-install-unix.yml +34 -0
- package/temp_resend_cli/repo/.github/workflows/test-install-windows.yml +48 -0
- package/temp_resend_cli/repo/CHANGELOG.md +31 -0
- package/temp_resend_cli/repo/LICENSE +21 -0
- package/temp_resend_cli/repo/README.md +450 -0
- package/temp_resend_cli/repo/biome.json +36 -0
- package/temp_resend_cli/repo/install.ps1 +141 -0
- package/temp_resend_cli/repo/install.sh +301 -0
- package/temp_resend_cli/repo/package.json +61 -0
- package/temp_resend_cli/repo/pnpm-lock.yaml +2439 -0
- package/temp_resend_cli/repo/renovate.json +4 -0
- package/temp_resend_cli/repo/src/cli.ts +98 -0
- package/temp_resend_cli/repo/src/commands/api-keys/create.ts +114 -0
- package/temp_resend_cli/repo/src/commands/api-keys/delete.ts +47 -0
- package/temp_resend_cli/repo/src/commands/api-keys/index.ts +26 -0
- package/temp_resend_cli/repo/src/commands/api-keys/list.ts +35 -0
- package/temp_resend_cli/repo/src/commands/api-keys/utils.ts +8 -0
- package/temp_resend_cli/repo/src/commands/auth/index.ts +20 -0
- package/temp_resend_cli/repo/src/commands/auth/login.ts +234 -0
- package/temp_resend_cli/repo/src/commands/auth/logout.ts +105 -0
- package/temp_resend_cli/repo/src/commands/broadcasts/create.ts +196 -0
- package/temp_resend_cli/repo/src/commands/broadcasts/delete.ts +46 -0
- package/temp_resend_cli/repo/src/commands/broadcasts/get.ts +59 -0
- package/temp_resend_cli/repo/src/commands/broadcasts/index.ts +43 -0
- package/temp_resend_cli/repo/src/commands/broadcasts/list.ts +60 -0
- package/temp_resend_cli/repo/src/commands/broadcasts/send.ts +56 -0
- package/temp_resend_cli/repo/src/commands/broadcasts/update.ts +95 -0
- package/temp_resend_cli/repo/src/commands/broadcasts/utils.ts +35 -0
- package/temp_resend_cli/repo/src/commands/contact-properties/create.ts +118 -0
- package/temp_resend_cli/repo/src/commands/contact-properties/delete.ts +48 -0
- package/temp_resend_cli/repo/src/commands/contact-properties/get.ts +46 -0
- package/temp_resend_cli/repo/src/commands/contact-properties/index.ts +48 -0
- package/temp_resend_cli/repo/src/commands/contact-properties/list.ts +68 -0
- package/temp_resend_cli/repo/src/commands/contact-properties/update.ts +88 -0
- package/temp_resend_cli/repo/src/commands/contact-properties/utils.ts +17 -0
- package/temp_resend_cli/repo/src/commands/contacts/add-segment.ts +78 -0
- package/temp_resend_cli/repo/src/commands/contacts/create.ts +122 -0
- package/temp_resend_cli/repo/src/commands/contacts/delete.ts +49 -0
- package/temp_resend_cli/repo/src/commands/contacts/get.ts +53 -0
- package/temp_resend_cli/repo/src/commands/contacts/index.ts +58 -0
- package/temp_resend_cli/repo/src/commands/contacts/list.ts +57 -0
- package/temp_resend_cli/repo/src/commands/contacts/remove-segment.ts +48 -0
- package/temp_resend_cli/repo/src/commands/contacts/segments.ts +39 -0
- package/temp_resend_cli/repo/src/commands/contacts/topics.ts +45 -0
- package/temp_resend_cli/repo/src/commands/contacts/update-topics.ts +90 -0
- package/temp_resend_cli/repo/src/commands/contacts/update.ts +77 -0
- package/temp_resend_cli/repo/src/commands/contacts/utils.ts +119 -0
- package/temp_resend_cli/repo/src/commands/doctor.ts +216 -0
- package/temp_resend_cli/repo/src/commands/domains/create.ts +83 -0
- package/temp_resend_cli/repo/src/commands/domains/delete.ts +42 -0
- package/temp_resend_cli/repo/src/commands/domains/get.ts +47 -0
- package/temp_resend_cli/repo/src/commands/domains/index.ts +35 -0
- package/temp_resend_cli/repo/src/commands/domains/list.ts +53 -0
- package/temp_resend_cli/repo/src/commands/domains/update.ts +75 -0
- package/temp_resend_cli/repo/src/commands/domains/utils.ts +44 -0
- package/temp_resend_cli/repo/src/commands/domains/verify.ts +38 -0
- package/temp_resend_cli/repo/src/commands/emails/batch.ts +140 -0
- package/temp_resend_cli/repo/src/commands/emails/get.ts +44 -0
- package/temp_resend_cli/repo/src/commands/emails/index.ts +30 -0
- package/temp_resend_cli/repo/src/commands/emails/list.ts +84 -0
- package/temp_resend_cli/repo/src/commands/emails/receiving/attachment.ts +55 -0
- package/temp_resend_cli/repo/src/commands/emails/receiving/attachments.ts +68 -0
- package/temp_resend_cli/repo/src/commands/emails/receiving/get.ts +58 -0
- package/temp_resend_cli/repo/src/commands/emails/receiving/index.ts +28 -0
- package/temp_resend_cli/repo/src/commands/emails/receiving/list.ts +59 -0
- package/temp_resend_cli/repo/src/commands/emails/receiving/utils.ts +38 -0
- package/temp_resend_cli/repo/src/commands/emails/send.ts +189 -0
- package/temp_resend_cli/repo/src/commands/open.ts +27 -0
- package/temp_resend_cli/repo/src/commands/segments/create.ts +50 -0
- package/temp_resend_cli/repo/src/commands/segments/delete.ts +47 -0
- package/temp_resend_cli/repo/src/commands/segments/get.ts +38 -0
- package/temp_resend_cli/repo/src/commands/segments/index.ts +36 -0
- package/temp_resend_cli/repo/src/commands/segments/list.ts +58 -0
- package/temp_resend_cli/repo/src/commands/segments/utils.ts +7 -0
- package/temp_resend_cli/repo/src/commands/teams/index.ts +10 -0
- package/temp_resend_cli/repo/src/commands/teams/list.ts +35 -0
- package/temp_resend_cli/repo/src/commands/teams/remove.ts +86 -0
- package/temp_resend_cli/repo/src/commands/teams/switch.ts +76 -0
- package/temp_resend_cli/repo/src/commands/topics/create.ts +73 -0
- package/temp_resend_cli/repo/src/commands/topics/delete.ts +47 -0
- package/temp_resend_cli/repo/src/commands/topics/get.ts +42 -0
- package/temp_resend_cli/repo/src/commands/topics/index.ts +42 -0
- package/temp_resend_cli/repo/src/commands/topics/list.ts +34 -0
- package/temp_resend_cli/repo/src/commands/topics/update.ts +59 -0
- package/temp_resend_cli/repo/src/commands/topics/utils.ts +16 -0
- package/temp_resend_cli/repo/src/commands/webhooks/create.ts +128 -0
- package/temp_resend_cli/repo/src/commands/webhooks/delete.ts +49 -0
- package/temp_resend_cli/repo/src/commands/webhooks/get.ts +42 -0
- package/temp_resend_cli/repo/src/commands/webhooks/index.ts +42 -0
- package/temp_resend_cli/repo/src/commands/webhooks/list.ts +55 -0
- package/temp_resend_cli/repo/src/commands/webhooks/listen.ts +379 -0
- package/temp_resend_cli/repo/src/commands/webhooks/update.ts +83 -0
- package/temp_resend_cli/repo/src/commands/webhooks/utils.ts +36 -0
- package/temp_resend_cli/repo/src/commands/whoami.ts +71 -0
- package/temp_resend_cli/repo/src/lib/actions.ts +157 -0
- package/temp_resend_cli/repo/src/lib/client.ts +37 -0
- package/temp_resend_cli/repo/src/lib/config.ts +217 -0
- package/temp_resend_cli/repo/src/lib/files.ts +15 -0
- package/temp_resend_cli/repo/src/lib/help-text.ts +38 -0
- package/temp_resend_cli/repo/src/lib/output.ts +56 -0
- package/temp_resend_cli/repo/src/lib/pagination.ts +36 -0
- package/temp_resend_cli/repo/src/lib/prompts.ts +149 -0
- package/temp_resend_cli/repo/src/lib/spinner.ts +100 -0
- package/temp_resend_cli/repo/src/lib/table.ts +57 -0
- package/temp_resend_cli/repo/src/lib/tty.ts +28 -0
- package/temp_resend_cli/repo/src/lib/update-check.ts +169 -0
- package/temp_resend_cli/repo/src/lib/version.ts +4 -0
- package/temp_resend_cli/repo/tests/commands/api-keys/create.test.ts +196 -0
- package/temp_resend_cli/repo/tests/commands/api-keys/delete.test.ts +157 -0
- package/temp_resend_cli/repo/tests/commands/api-keys/list.test.ts +134 -0
- package/temp_resend_cli/repo/tests/commands/auth/login.test.ts +153 -0
- package/temp_resend_cli/repo/tests/commands/auth/logout.test.ts +153 -0
- package/temp_resend_cli/repo/tests/commands/broadcasts/create.test.ts +454 -0
- package/temp_resend_cli/repo/tests/commands/broadcasts/delete.test.ts +183 -0
- package/temp_resend_cli/repo/tests/commands/broadcasts/get.test.ts +147 -0
- package/temp_resend_cli/repo/tests/commands/broadcasts/list.test.ts +199 -0
- package/temp_resend_cli/repo/tests/commands/broadcasts/send.test.ts +162 -0
- package/temp_resend_cli/repo/tests/commands/broadcasts/update.test.ts +288 -0
- package/temp_resend_cli/repo/tests/commands/contact-properties/create.test.ts +251 -0
- package/temp_resend_cli/repo/tests/commands/contact-properties/delete.test.ts +184 -0
- package/temp_resend_cli/repo/tests/commands/contact-properties/get.test.ts +145 -0
- package/temp_resend_cli/repo/tests/commands/contact-properties/list.test.ts +181 -0
- package/temp_resend_cli/repo/tests/commands/contact-properties/update.test.ts +217 -0
- package/temp_resend_cli/repo/tests/commands/contacts/add-segment.test.ts +189 -0
- package/temp_resend_cli/repo/tests/commands/contacts/create.test.ts +271 -0
- package/temp_resend_cli/repo/tests/commands/contacts/delete.test.ts +193 -0
- package/temp_resend_cli/repo/tests/commands/contacts/get.test.ts +149 -0
- package/temp_resend_cli/repo/tests/commands/contacts/list.test.ts +176 -0
- package/temp_resend_cli/repo/tests/commands/contacts/remove-segment.test.ts +167 -0
- package/temp_resend_cli/repo/tests/commands/contacts/segments.test.ts +168 -0
- package/temp_resend_cli/repo/tests/commands/contacts/topics.test.ts +164 -0
- package/temp_resend_cli/repo/tests/commands/contacts/update-topics.test.ts +248 -0
- package/temp_resend_cli/repo/tests/commands/contacts/update.test.ts +206 -0
- package/temp_resend_cli/repo/tests/commands/doctor.test.ts +164 -0
- package/temp_resend_cli/repo/tests/commands/domains/create.test.ts +193 -0
- package/temp_resend_cli/repo/tests/commands/domains/delete.test.ts +157 -0
- package/temp_resend_cli/repo/tests/commands/domains/get.test.ts +138 -0
- package/temp_resend_cli/repo/tests/commands/domains/list.test.ts +165 -0
- package/temp_resend_cli/repo/tests/commands/domains/update.test.ts +224 -0
- package/temp_resend_cli/repo/tests/commands/domains/verify.test.ts +118 -0
- package/temp_resend_cli/repo/tests/commands/emails/batch.test.ts +324 -0
- package/temp_resend_cli/repo/tests/commands/emails/get.test.ts +132 -0
- package/temp_resend_cli/repo/tests/commands/emails/receiving/attachment.test.ts +141 -0
- package/temp_resend_cli/repo/tests/commands/emails/receiving/attachments.test.ts +169 -0
- package/temp_resend_cli/repo/tests/commands/emails/receiving/get.test.ts +141 -0
- package/temp_resend_cli/repo/tests/commands/emails/receiving/list.test.ts +182 -0
- package/temp_resend_cli/repo/tests/commands/emails/send.test.ts +312 -0
- package/temp_resend_cli/repo/tests/commands/segments/create.test.ts +164 -0
- package/temp_resend_cli/repo/tests/commands/segments/delete.test.ts +183 -0
- package/temp_resend_cli/repo/tests/commands/segments/get.test.ts +138 -0
- package/temp_resend_cli/repo/tests/commands/segments/list.test.ts +174 -0
- package/temp_resend_cli/repo/tests/commands/teams/list.test.ts +62 -0
- package/temp_resend_cli/repo/tests/commands/teams/remove.test.ts +110 -0
- package/temp_resend_cli/repo/tests/commands/teams/switch.test.ts +103 -0
- package/temp_resend_cli/repo/tests/commands/topics/create.test.ts +192 -0
- package/temp_resend_cli/repo/tests/commands/topics/delete.test.ts +157 -0
- package/temp_resend_cli/repo/tests/commands/topics/get.test.ts +126 -0
- package/temp_resend_cli/repo/tests/commands/topics/list.test.ts +125 -0
- package/temp_resend_cli/repo/tests/commands/topics/update.test.ts +178 -0
- package/temp_resend_cli/repo/tests/commands/webhooks/create.test.ts +225 -0
- package/temp_resend_cli/repo/tests/commands/webhooks/delete.test.ts +157 -0
- package/temp_resend_cli/repo/tests/commands/webhooks/get.test.ts +126 -0
- package/temp_resend_cli/repo/tests/commands/webhooks/list.test.ts +178 -0
- package/temp_resend_cli/repo/tests/commands/webhooks/update.test.ts +207 -0
- package/temp_resend_cli/repo/tests/commands/whoami.test.ts +98 -0
- package/temp_resend_cli/repo/tests/e2e/smoke.test.ts +93 -0
- package/temp_resend_cli/repo/tests/helpers.ts +86 -0
- package/temp_resend_cli/repo/tests/lib/client.test.ts +71 -0
- package/temp_resend_cli/repo/tests/lib/config.test.ts +451 -0
- package/temp_resend_cli/repo/tests/lib/files.test.ts +73 -0
- package/temp_resend_cli/repo/tests/lib/help-text.test.ts +97 -0
- package/temp_resend_cli/repo/tests/lib/output.test.ts +136 -0
- package/temp_resend_cli/repo/tests/lib/prompts.test.ts +185 -0
- package/temp_resend_cli/repo/tests/lib/spinner.test.ts +166 -0
- package/temp_resend_cli/repo/tests/lib/table.test.ts +63 -0
- package/temp_resend_cli/repo/tests/lib/tty.test.ts +89 -0
- package/temp_resend_cli/repo/tests/lib/update-check.test.ts +179 -0
- package/temp_resend_cli/repo/tsconfig.json +14 -0
- package/temp_resend_cli/repo/vitest.config.e2e.ts +8 -0
- package/temp_resend_cli/repo/vitest.config.ts +10 -0
- package/tests/test-mcp-browser-use-smoke.sh +28 -56
- package/tests/test-monty-smoke.sh +32 -0
- 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
|
+
}
|