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,1201 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from dirty_equals import IsList
|
|
5
|
+
from inline_snapshot import snapshot
|
|
6
|
+
|
|
7
|
+
import pydantic_monty
|
|
8
|
+
from pydantic_monty import run_monty_async, run_repl_async
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_async():
|
|
12
|
+
code = 'await foobar(1, 2)'
|
|
13
|
+
m = pydantic_monty.Monty(code)
|
|
14
|
+
progress = m.start()
|
|
15
|
+
assert isinstance(progress, pydantic_monty.FunctionSnapshot)
|
|
16
|
+
assert progress.function_name == snapshot('foobar')
|
|
17
|
+
assert progress.args == snapshot((1, 2))
|
|
18
|
+
call_id = progress.call_id
|
|
19
|
+
progress = progress.resume(future=...)
|
|
20
|
+
assert isinstance(progress, pydantic_monty.FutureSnapshot)
|
|
21
|
+
assert progress.pending_call_ids == snapshot([call_id])
|
|
22
|
+
progress = progress.resume({call_id: {'return_value': 3}})
|
|
23
|
+
assert isinstance(progress, pydantic_monty.MontyComplete)
|
|
24
|
+
assert progress.output == snapshot(3)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_asyncio_gather():
|
|
28
|
+
code = """
|
|
29
|
+
import asyncio
|
|
30
|
+
|
|
31
|
+
await asyncio.gather(foo(1), bar(2))
|
|
32
|
+
"""
|
|
33
|
+
m = pydantic_monty.Monty(code)
|
|
34
|
+
progress = m.start()
|
|
35
|
+
assert isinstance(progress, pydantic_monty.FunctionSnapshot)
|
|
36
|
+
assert progress.function_name == snapshot('foo')
|
|
37
|
+
assert progress.args == snapshot((1,))
|
|
38
|
+
foo_call_ids = progress.call_id
|
|
39
|
+
|
|
40
|
+
progress = progress.resume(future=...)
|
|
41
|
+
assert isinstance(progress, pydantic_monty.FunctionSnapshot)
|
|
42
|
+
assert progress.function_name == snapshot('bar')
|
|
43
|
+
assert progress.args == snapshot((2,))
|
|
44
|
+
bar_call_ids = progress.call_id
|
|
45
|
+
progress = progress.resume(future=...)
|
|
46
|
+
|
|
47
|
+
assert isinstance(progress, pydantic_monty.FutureSnapshot)
|
|
48
|
+
dump_progress = progress.dump()
|
|
49
|
+
|
|
50
|
+
assert progress.pending_call_ids == IsList(foo_call_ids, bar_call_ids, check_order=False)
|
|
51
|
+
progress = progress.resume({foo_call_ids: {'return_value': 3}, bar_call_ids: {'return_value': 4}})
|
|
52
|
+
assert isinstance(progress, pydantic_monty.MontyComplete)
|
|
53
|
+
assert progress.output == snapshot([3, 4])
|
|
54
|
+
|
|
55
|
+
progress2 = pydantic_monty.load_snapshot(dump_progress)
|
|
56
|
+
assert isinstance(progress2, pydantic_monty.FutureSnapshot)
|
|
57
|
+
assert progress2.pending_call_ids == IsList(foo_call_ids, bar_call_ids, check_order=False)
|
|
58
|
+
progress = progress2.resume({bar_call_ids: {'return_value': 14}, foo_call_ids: {'return_value': 13}})
|
|
59
|
+
assert isinstance(progress, pydantic_monty.MontyComplete)
|
|
60
|
+
assert progress.output == snapshot([13, 14])
|
|
61
|
+
|
|
62
|
+
progress3 = pydantic_monty.load_snapshot(dump_progress)
|
|
63
|
+
assert isinstance(progress3, pydantic_monty.FutureSnapshot)
|
|
64
|
+
progress = progress3.resume({bar_call_ids: {'return_value': 14}, foo_call_ids: {'future': ...}})
|
|
65
|
+
assert isinstance(progress, pydantic_monty.FutureSnapshot)
|
|
66
|
+
|
|
67
|
+
assert progress.pending_call_ids == [foo_call_ids]
|
|
68
|
+
progress = progress.resume({foo_call_ids: {'return_value': 144}})
|
|
69
|
+
assert isinstance(progress, pydantic_monty.MontyComplete)
|
|
70
|
+
assert progress.output == snapshot([144, 14])
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# === Tests for run_monty_async ===
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
async def test_run_monty_async_sync_function():
|
|
77
|
+
"""Test run_monty_async with a basic sync external function."""
|
|
78
|
+
m = pydantic_monty.Monty('get_value()')
|
|
79
|
+
|
|
80
|
+
def get_value():
|
|
81
|
+
return 42
|
|
82
|
+
|
|
83
|
+
result = await run_monty_async(m, external_functions={'get_value': get_value})
|
|
84
|
+
assert result == snapshot(42)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
async def test_run_monty_async_async_function():
|
|
88
|
+
"""Test run_monty_async with a basic async external function."""
|
|
89
|
+
m = pydantic_monty.Monty('await fetch_data()')
|
|
90
|
+
|
|
91
|
+
async def fetch_data():
|
|
92
|
+
await asyncio.sleep(0.001)
|
|
93
|
+
return 'async result'
|
|
94
|
+
|
|
95
|
+
result = await run_monty_async(m, external_functions={'fetch_data': fetch_data})
|
|
96
|
+
assert result == snapshot('async result')
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
async def test_run_monty_async_function_not_found():
|
|
100
|
+
"""Test that missing external function raises wrapped error."""
|
|
101
|
+
m = pydantic_monty.Monty('missing_func()')
|
|
102
|
+
|
|
103
|
+
with pytest.raises(pydantic_monty.MontyRuntimeError) as exc_info:
|
|
104
|
+
await run_monty_async(m, external_functions={})
|
|
105
|
+
inner = exc_info.value.exception()
|
|
106
|
+
assert isinstance(inner, LookupError)
|
|
107
|
+
assert inner.args[0] == snapshot("Unable to find 'missing_func' in external functions dict")
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
async def test_run_monty_async_sync_exception():
|
|
111
|
+
"""Test that sync function exceptions propagate correctly."""
|
|
112
|
+
m = pydantic_monty.Monty('fail()')
|
|
113
|
+
|
|
114
|
+
def fail():
|
|
115
|
+
raise ValueError('sync error')
|
|
116
|
+
|
|
117
|
+
with pytest.raises(pydantic_monty.MontyRuntimeError) as exc_info:
|
|
118
|
+
await run_monty_async(m, external_functions={'fail': fail})
|
|
119
|
+
inner = exc_info.value.exception()
|
|
120
|
+
assert isinstance(inner, ValueError)
|
|
121
|
+
assert inner.args[0] == snapshot('sync error')
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
async def test_run_monty_async_async_exception():
|
|
125
|
+
"""Test that async function exceptions propagate correctly."""
|
|
126
|
+
m = pydantic_monty.Monty('await async_fail()')
|
|
127
|
+
|
|
128
|
+
async def async_fail():
|
|
129
|
+
await asyncio.sleep(0.001)
|
|
130
|
+
raise RuntimeError('async error')
|
|
131
|
+
|
|
132
|
+
with pytest.raises(pydantic_monty.MontyRuntimeError) as exc_info:
|
|
133
|
+
await run_monty_async(m, external_functions={'async_fail': async_fail})
|
|
134
|
+
inner = exc_info.value.exception()
|
|
135
|
+
assert isinstance(inner, RuntimeError)
|
|
136
|
+
assert inner.args[0] == snapshot('async error')
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
async def test_run_monty_async_exception_caught():
|
|
140
|
+
"""Test that exceptions caught in try/except don't propagate."""
|
|
141
|
+
code = """
|
|
142
|
+
try:
|
|
143
|
+
fail()
|
|
144
|
+
except ValueError:
|
|
145
|
+
caught = True
|
|
146
|
+
caught
|
|
147
|
+
"""
|
|
148
|
+
m = pydantic_monty.Monty(code)
|
|
149
|
+
|
|
150
|
+
def fail():
|
|
151
|
+
raise ValueError('caught error')
|
|
152
|
+
|
|
153
|
+
result = await run_monty_async(m, external_functions={'fail': fail})
|
|
154
|
+
assert result == snapshot(True)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
async def test_run_monty_async_multiple_async_functions():
|
|
158
|
+
"""Test asyncio.gather with multiple async functions."""
|
|
159
|
+
code = """
|
|
160
|
+
import asyncio
|
|
161
|
+
await asyncio.gather(fetch_a(), fetch_b())
|
|
162
|
+
"""
|
|
163
|
+
m = pydantic_monty.Monty(code)
|
|
164
|
+
|
|
165
|
+
async def fetch_a():
|
|
166
|
+
await asyncio.sleep(0.01)
|
|
167
|
+
return 'a'
|
|
168
|
+
|
|
169
|
+
async def fetch_b():
|
|
170
|
+
await asyncio.sleep(0.005)
|
|
171
|
+
return 'b'
|
|
172
|
+
|
|
173
|
+
result = await run_monty_async(m, external_functions={'fetch_a': fetch_a, 'fetch_b': fetch_b})
|
|
174
|
+
assert result == snapshot(['a', 'b'])
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
async def test_run_monty_async_mixed_sync_async():
|
|
178
|
+
"""Test mix of sync and async external functions."""
|
|
179
|
+
code = """
|
|
180
|
+
sync_val = sync_func()
|
|
181
|
+
async_val = await async_func()
|
|
182
|
+
sync_val + async_val
|
|
183
|
+
"""
|
|
184
|
+
m = pydantic_monty.Monty(code)
|
|
185
|
+
|
|
186
|
+
def sync_func():
|
|
187
|
+
return 10
|
|
188
|
+
|
|
189
|
+
async def async_func():
|
|
190
|
+
await asyncio.sleep(0.001)
|
|
191
|
+
return 5
|
|
192
|
+
|
|
193
|
+
result = await run_monty_async(m, external_functions={'sync_func': sync_func, 'async_func': async_func})
|
|
194
|
+
assert result == snapshot(15)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
async def test_run_monty_async_with_inputs():
|
|
198
|
+
"""Test run_monty_async with inputs parameter."""
|
|
199
|
+
m = pydantic_monty.Monty('process(x, y)', inputs=['x', 'y'])
|
|
200
|
+
|
|
201
|
+
def process(a: int, b: int) -> int:
|
|
202
|
+
return a * b
|
|
203
|
+
|
|
204
|
+
result = await run_monty_async(m, inputs={'x': 6, 'y': 7}, external_functions={'process': process})
|
|
205
|
+
assert result == snapshot(42)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
async def test_run_monty_async_with_print_callback():
|
|
209
|
+
"""Test run_monty_async with print_callback parameter."""
|
|
210
|
+
output: list[tuple[str, str]] = []
|
|
211
|
+
|
|
212
|
+
def callback(stream: str, text: str) -> None:
|
|
213
|
+
output.append((stream, text))
|
|
214
|
+
|
|
215
|
+
m = pydantic_monty.Monty('print("hello from async")')
|
|
216
|
+
result = await run_monty_async(m, print_callback=callback)
|
|
217
|
+
assert result is None
|
|
218
|
+
assert output == snapshot([('stdout', 'hello from async'), ('stdout', '\n')])
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
async def test_run_monty_async_function_returning_none():
|
|
222
|
+
"""Test async function that returns None."""
|
|
223
|
+
m = pydantic_monty.Monty('do_nothing()')
|
|
224
|
+
|
|
225
|
+
def do_nothing():
|
|
226
|
+
return None
|
|
227
|
+
|
|
228
|
+
result = await run_monty_async(m, external_functions={'do_nothing': do_nothing})
|
|
229
|
+
assert result is None
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
async def test_run_monty_async_no_external_calls():
|
|
233
|
+
"""Test run_monty_async when code has no external calls."""
|
|
234
|
+
m = pydantic_monty.Monty('1 + 2 + 3')
|
|
235
|
+
result = await run_monty_async(m)
|
|
236
|
+
assert result == snapshot(6)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
# === Tests for run_monty_async with os parameter ===
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
async def test_run_monty_async_with_os():
|
|
243
|
+
"""run_monty_async can use OSAccess for file operations."""
|
|
244
|
+
from pydantic_monty import MemoryFile, OSAccess
|
|
245
|
+
|
|
246
|
+
fs = OSAccess([MemoryFile('/test.txt', content='hello world')])
|
|
247
|
+
|
|
248
|
+
m = pydantic_monty.Monty(
|
|
249
|
+
"""
|
|
250
|
+
from pathlib import Path
|
|
251
|
+
Path('/test.txt').read_text()
|
|
252
|
+
""",
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
result = await run_monty_async(m, os=fs)
|
|
256
|
+
assert result == snapshot('hello world')
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
async def test_run_monty_async_os_with_external_functions():
|
|
260
|
+
"""run_monty_async can combine OSAccess with external functions."""
|
|
261
|
+
from pydantic_monty import MemoryFile, OSAccess
|
|
262
|
+
|
|
263
|
+
fs = OSAccess([MemoryFile('/data.txt', content='test data')])
|
|
264
|
+
|
|
265
|
+
async def process(text: str) -> str:
|
|
266
|
+
return text.upper()
|
|
267
|
+
|
|
268
|
+
m = pydantic_monty.Monty(
|
|
269
|
+
"""
|
|
270
|
+
from pathlib import Path
|
|
271
|
+
content = Path('/data.txt').read_text()
|
|
272
|
+
await process(content)
|
|
273
|
+
""",
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
result = await run_monty_async(
|
|
277
|
+
m,
|
|
278
|
+
external_functions={'process': process},
|
|
279
|
+
os=fs,
|
|
280
|
+
)
|
|
281
|
+
assert result == snapshot('TEST DATA')
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
async def test_run_monty_async_os_file_not_found():
|
|
285
|
+
"""run_monty_async propagates OS errors correctly."""
|
|
286
|
+
from pydantic_monty import OSAccess
|
|
287
|
+
|
|
288
|
+
fs = OSAccess()
|
|
289
|
+
|
|
290
|
+
m = pydantic_monty.Monty(
|
|
291
|
+
"""
|
|
292
|
+
from pathlib import Path
|
|
293
|
+
Path('/missing.txt').read_text()
|
|
294
|
+
""",
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
with pytest.raises(pydantic_monty.MontyRuntimeError) as exc_info:
|
|
298
|
+
await run_monty_async(m, os=fs)
|
|
299
|
+
assert str(exc_info.value) == snapshot("FileNotFoundError: [Errno 2] No such file or directory: '/missing.txt'")
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
async def test_run_monty_async_os_not_provided():
|
|
303
|
+
"""run_monty_async raises error when OS function called without os handler."""
|
|
304
|
+
m = pydantic_monty.Monty(
|
|
305
|
+
"""
|
|
306
|
+
from pathlib import Path
|
|
307
|
+
Path('/test.txt').exists()
|
|
308
|
+
""",
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
with pytest.raises(pydantic_monty.MontyRuntimeError) as exc_info:
|
|
312
|
+
await run_monty_async(m)
|
|
313
|
+
inner = exc_info.value.exception()
|
|
314
|
+
assert isinstance(inner, RuntimeError)
|
|
315
|
+
assert 'OS function' in inner.args[0]
|
|
316
|
+
assert 'no os handler provided' in inner.args[0]
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
async def test_run_monty_async_nested_gather_with_external_functions():
|
|
320
|
+
"""Test nested asyncio.gather with spawned tasks and external async functions.
|
|
321
|
+
|
|
322
|
+
https://github.com/pydantic/monty/pull/174
|
|
323
|
+
|
|
324
|
+
Reproduces the pattern from stack_overflow.py: outer gather spawns 3 coroutine tasks,
|
|
325
|
+
each doing a sequential await then an inner gather with 2 external futures.
|
|
326
|
+
"""
|
|
327
|
+
code = """\
|
|
328
|
+
import asyncio
|
|
329
|
+
|
|
330
|
+
async def get_city_weather(city_name: str):
|
|
331
|
+
coords = await get_lat_lng(location_description=city_name)
|
|
332
|
+
lat, lng = coords['lat'], coords['lng']
|
|
333
|
+
temp_task = get_temp(lat=lat, lng=lng)
|
|
334
|
+
desc_task = get_weather_description(lat=lat, lng=lng)
|
|
335
|
+
temp, desc = await asyncio.gather(temp_task, desc_task)
|
|
336
|
+
return {
|
|
337
|
+
'city': city_name,
|
|
338
|
+
'temp': temp,
|
|
339
|
+
'description': desc
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
async def main():
|
|
343
|
+
cities = ['London', 'Paris', 'Tokyo']
|
|
344
|
+
results = await asyncio.gather(*(get_city_weather(city) for city in cities))
|
|
345
|
+
return results
|
|
346
|
+
|
|
347
|
+
await main()
|
|
348
|
+
"""
|
|
349
|
+
m = pydantic_monty.Monty(code)
|
|
350
|
+
|
|
351
|
+
city_coords = {
|
|
352
|
+
'London': {'lat': 51.5, 'lng': -0.1},
|
|
353
|
+
'Paris': {'lat': 48.9, 'lng': 2.3},
|
|
354
|
+
'Tokyo': {'lat': 35.7, 'lng': 139.7},
|
|
355
|
+
}
|
|
356
|
+
city_temps = {
|
|
357
|
+
(51.5, -0.1): 15.0,
|
|
358
|
+
(48.9, 2.3): 18.0,
|
|
359
|
+
(35.7, 139.7): 22.0,
|
|
360
|
+
}
|
|
361
|
+
city_descs = {
|
|
362
|
+
(51.5, -0.1): 'Cloudy',
|
|
363
|
+
(48.9, 2.3): 'Sunny',
|
|
364
|
+
(35.7, 139.7): 'Humid',
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
async def get_lat_lng(location_description: str):
|
|
368
|
+
return city_coords[location_description]
|
|
369
|
+
|
|
370
|
+
async def get_temp(lat: float, lng: float):
|
|
371
|
+
return city_temps[(lat, lng)]
|
|
372
|
+
|
|
373
|
+
async def get_weather_description(lat: float, lng: float):
|
|
374
|
+
return city_descs[(lat, lng)]
|
|
375
|
+
|
|
376
|
+
result = await run_monty_async(
|
|
377
|
+
m,
|
|
378
|
+
external_functions={
|
|
379
|
+
'get_lat_lng': get_lat_lng,
|
|
380
|
+
'get_temp': get_temp,
|
|
381
|
+
'get_weather_description': get_weather_description,
|
|
382
|
+
},
|
|
383
|
+
)
|
|
384
|
+
assert result == snapshot(
|
|
385
|
+
[
|
|
386
|
+
{'city': 'London', 'temp': 15.0, 'description': 'Cloudy'},
|
|
387
|
+
{'city': 'Paris', 'temp': 18.0, 'description': 'Sunny'},
|
|
388
|
+
{'city': 'Tokyo', 'temp': 22.0, 'description': 'Humid'},
|
|
389
|
+
]
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
async def test_run_monty_async_os_write_and_read():
|
|
394
|
+
"""run_monty_async supports both reading and writing files."""
|
|
395
|
+
from pydantic_monty import MemoryFile, OSAccess
|
|
396
|
+
|
|
397
|
+
fs = OSAccess([MemoryFile('/file.txt', content='original')])
|
|
398
|
+
|
|
399
|
+
m = pydantic_monty.Monty(
|
|
400
|
+
"""
|
|
401
|
+
from pathlib import Path
|
|
402
|
+
p = Path('/file.txt')
|
|
403
|
+
p.write_text('updated')
|
|
404
|
+
p.read_text()
|
|
405
|
+
""",
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
result = await run_monty_async(m, os=fs)
|
|
409
|
+
assert result == snapshot('updated')
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
# === Tests for MontyRepl.feed_start() with async patterns ===
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def test_repl_feed_start_async_gather():
|
|
416
|
+
"""MontyRepl.feed_start supports asyncio.gather with multiple futures."""
|
|
417
|
+
code = """
|
|
418
|
+
import asyncio
|
|
419
|
+
|
|
420
|
+
await asyncio.gather(foo(1), bar(2))
|
|
421
|
+
"""
|
|
422
|
+
repl = pydantic_monty.MontyRepl()
|
|
423
|
+
progress = repl.feed_start(code)
|
|
424
|
+
assert isinstance(progress, pydantic_monty.FunctionSnapshot)
|
|
425
|
+
assert progress.function_name == snapshot('foo')
|
|
426
|
+
foo_call_id = progress.call_id
|
|
427
|
+
|
|
428
|
+
progress = progress.resume(future=...)
|
|
429
|
+
assert isinstance(progress, pydantic_monty.FunctionSnapshot)
|
|
430
|
+
assert progress.function_name == snapshot('bar')
|
|
431
|
+
bar_call_id = progress.call_id
|
|
432
|
+
progress = progress.resume(future=...)
|
|
433
|
+
|
|
434
|
+
assert isinstance(progress, pydantic_monty.FutureSnapshot)
|
|
435
|
+
from dirty_equals import IsList
|
|
436
|
+
|
|
437
|
+
assert progress.pending_call_ids == IsList(foo_call_id, bar_call_id, check_order=False)
|
|
438
|
+
progress = progress.resume({foo_call_id: {'return_value': 3}, bar_call_id: {'return_value': 4}})
|
|
439
|
+
assert isinstance(progress, pydantic_monty.MontyComplete)
|
|
440
|
+
assert progress.output == snapshot([3, 4])
|
|
441
|
+
|
|
442
|
+
# REPL should still be usable after async completion
|
|
443
|
+
assert repl.feed_run('1 + 1') == snapshot(2)
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
def test_repl_feed_start_async_state_persistence():
|
|
447
|
+
"""MontyRepl.feed_start async: REPL state persists across async snippets."""
|
|
448
|
+
repl = pydantic_monty.MontyRepl()
|
|
449
|
+
repl.feed_run('x = 10')
|
|
450
|
+
|
|
451
|
+
progress = repl.feed_start('result = await fetch(x)')
|
|
452
|
+
assert isinstance(progress, pydantic_monty.FunctionSnapshot)
|
|
453
|
+
assert progress.function_name == snapshot('fetch')
|
|
454
|
+
assert progress.args == snapshot((10,))
|
|
455
|
+
call_id = progress.call_id
|
|
456
|
+
|
|
457
|
+
progress = progress.resume(future=...)
|
|
458
|
+
assert isinstance(progress, pydantic_monty.FutureSnapshot)
|
|
459
|
+
progress = progress.resume({call_id: {'return_value': 'fetched'}})
|
|
460
|
+
assert isinstance(progress, pydantic_monty.MontyComplete)
|
|
461
|
+
assert progress.output is None # assignment, not expression
|
|
462
|
+
|
|
463
|
+
assert repl.feed_run('result') == snapshot('fetched')
|
|
464
|
+
assert repl.feed_run('x') == snapshot(10)
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
# === Tests for run_repl_async ===
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
async def test_run_repl_async_sync_function():
|
|
471
|
+
"""run_repl_async with a basic sync external function."""
|
|
472
|
+
repl = pydantic_monty.MontyRepl()
|
|
473
|
+
|
|
474
|
+
def get_value():
|
|
475
|
+
return 42
|
|
476
|
+
|
|
477
|
+
result = await run_repl_async(repl, 'get_value()', external_functions={'get_value': get_value})
|
|
478
|
+
assert result == snapshot(42)
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
async def test_run_repl_async_async_function():
|
|
482
|
+
"""run_repl_async with a basic async external function."""
|
|
483
|
+
repl = pydantic_monty.MontyRepl()
|
|
484
|
+
|
|
485
|
+
async def fetch_data():
|
|
486
|
+
await asyncio.sleep(0.001)
|
|
487
|
+
return 'async result'
|
|
488
|
+
|
|
489
|
+
result = await run_repl_async(repl, 'await fetch_data()', external_functions={'fetch_data': fetch_data})
|
|
490
|
+
assert result == snapshot('async result')
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
async def test_run_repl_async_state_persists():
|
|
494
|
+
"""REPL state persists across multiple run_repl_async calls."""
|
|
495
|
+
repl = pydantic_monty.MontyRepl()
|
|
496
|
+
|
|
497
|
+
def double(x: int) -> int:
|
|
498
|
+
return x * 2
|
|
499
|
+
|
|
500
|
+
ext = {'double': double}
|
|
501
|
+
await run_repl_async(repl, 'x = 10', external_functions=ext)
|
|
502
|
+
await run_repl_async(repl, 'y = double(x)', external_functions=ext)
|
|
503
|
+
result = await run_repl_async(repl, 'y', external_functions=ext)
|
|
504
|
+
assert result == snapshot(20)
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
async def test_run_repl_async_async_state_persists():
|
|
508
|
+
"""REPL state persists across async calls with await."""
|
|
509
|
+
repl = pydantic_monty.MontyRepl()
|
|
510
|
+
|
|
511
|
+
async def fetch(key: str) -> str:
|
|
512
|
+
return f'value_{key}'
|
|
513
|
+
|
|
514
|
+
ext = {'fetch': fetch}
|
|
515
|
+
await run_repl_async(repl, "a = await fetch('one')", external_functions=ext)
|
|
516
|
+
await run_repl_async(repl, "b = await fetch('two')", external_functions=ext)
|
|
517
|
+
result = await run_repl_async(repl, 'a + b', external_functions=ext)
|
|
518
|
+
assert result == snapshot('value_onevalue_two')
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
async def test_run_repl_async_gather():
|
|
522
|
+
"""run_repl_async handles asyncio.gather with multiple futures."""
|
|
523
|
+
repl = pydantic_monty.MontyRepl()
|
|
524
|
+
|
|
525
|
+
async def fetch_a():
|
|
526
|
+
await asyncio.sleep(0.01)
|
|
527
|
+
return 'a'
|
|
528
|
+
|
|
529
|
+
async def fetch_b():
|
|
530
|
+
await asyncio.sleep(0.005)
|
|
531
|
+
return 'b'
|
|
532
|
+
|
|
533
|
+
code = """\
|
|
534
|
+
import asyncio
|
|
535
|
+
await asyncio.gather(fetch_a(), fetch_b())
|
|
536
|
+
"""
|
|
537
|
+
result = await run_repl_async(repl, code, external_functions={'fetch_a': fetch_a, 'fetch_b': fetch_b})
|
|
538
|
+
assert result == snapshot(['a', 'b'])
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
async def test_run_repl_async_function_not_found():
|
|
542
|
+
"""run_repl_async raises error for missing external function."""
|
|
543
|
+
repl = pydantic_monty.MontyRepl()
|
|
544
|
+
|
|
545
|
+
with pytest.raises(pydantic_monty.MontyRuntimeError) as exc_info:
|
|
546
|
+
await run_repl_async(repl, 'missing_func()', external_functions={})
|
|
547
|
+
inner = exc_info.value.exception()
|
|
548
|
+
assert isinstance(inner, LookupError)
|
|
549
|
+
assert inner.args[0] == snapshot("Unable to find 'missing_func' in external functions dict")
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
async def test_run_repl_async_error_preserves_state():
|
|
553
|
+
"""REPL state is preserved after an error in run_repl_async."""
|
|
554
|
+
repl = pydantic_monty.MontyRepl()
|
|
555
|
+
await run_repl_async(repl, 'x = 42')
|
|
556
|
+
|
|
557
|
+
def fail():
|
|
558
|
+
raise ValueError('oops')
|
|
559
|
+
|
|
560
|
+
with pytest.raises(pydantic_monty.MontyRuntimeError):
|
|
561
|
+
await run_repl_async(repl, 'fail()', external_functions={'fail': fail})
|
|
562
|
+
|
|
563
|
+
result = await run_repl_async(repl, 'x')
|
|
564
|
+
assert result == snapshot(42)
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
async def test_run_repl_async_with_inputs():
|
|
568
|
+
"""run_repl_async supports inputs parameter."""
|
|
569
|
+
repl = pydantic_monty.MontyRepl()
|
|
570
|
+
|
|
571
|
+
def add(a: int, b: int) -> int:
|
|
572
|
+
return a + b
|
|
573
|
+
|
|
574
|
+
result = await run_repl_async(repl, 'add(x, y)', inputs={'x': 3, 'y': 4}, external_functions={'add': add})
|
|
575
|
+
assert result == snapshot(7)
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
async def test_run_repl_async_with_print_callback():
|
|
579
|
+
"""run_repl_async supports print_callback parameter."""
|
|
580
|
+
repl = pydantic_monty.MontyRepl()
|
|
581
|
+
output: list[str] = []
|
|
582
|
+
|
|
583
|
+
def callback(stream: str, text: str) -> None:
|
|
584
|
+
output.append(text)
|
|
585
|
+
|
|
586
|
+
await run_repl_async(repl, 'print("hello from repl")', print_callback=callback)
|
|
587
|
+
assert output == snapshot(['hello from repl', '\n'])
|
|
588
|
+
|
|
589
|
+
|
|
590
|
+
async def test_run_repl_async_with_os():
|
|
591
|
+
"""run_repl_async supports OS access."""
|
|
592
|
+
from pydantic_monty import MemoryFile, OSAccess
|
|
593
|
+
|
|
594
|
+
repl = pydantic_monty.MontyRepl()
|
|
595
|
+
fs = OSAccess([MemoryFile('/test.txt', content='repl content')])
|
|
596
|
+
|
|
597
|
+
code = """\
|
|
598
|
+
from pathlib import Path
|
|
599
|
+
Path('/test.txt').read_text()
|
|
600
|
+
"""
|
|
601
|
+
result = await run_repl_async(repl, code, os=fs)
|
|
602
|
+
assert result == snapshot('repl content')
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
async def test_run_repl_async_mixed_sync_async():
|
|
606
|
+
"""run_repl_async handles mix of sync and async functions."""
|
|
607
|
+
repl = pydantic_monty.MontyRepl()
|
|
608
|
+
|
|
609
|
+
def sync_func():
|
|
610
|
+
return 10
|
|
611
|
+
|
|
612
|
+
async def async_func():
|
|
613
|
+
await asyncio.sleep(0.001)
|
|
614
|
+
return 5
|
|
615
|
+
|
|
616
|
+
code = """\
|
|
617
|
+
sync_val = sync_func()
|
|
618
|
+
async_val = await async_func()
|
|
619
|
+
sync_val + async_val
|
|
620
|
+
"""
|
|
621
|
+
result = await run_repl_async(repl, code, external_functions={'sync_func': sync_func, 'async_func': async_func})
|
|
622
|
+
assert result == snapshot(15)
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
async def test_run_repl_async_no_external_calls():
|
|
626
|
+
"""run_repl_async works when code has no external calls."""
|
|
627
|
+
repl = pydantic_monty.MontyRepl()
|
|
628
|
+
result = await run_repl_async(repl, '1 + 2 + 3')
|
|
629
|
+
assert result == snapshot(6)
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
# === LLM agent patterns: realistic run_repl_async scenarios ===
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
async def test_repl_llm_iterative_data_collection():
|
|
636
|
+
"""LLM defines a helper, collects data in batches, accumulates results across snippets."""
|
|
637
|
+
repl = pydantic_monty.MontyRepl()
|
|
638
|
+
|
|
639
|
+
responses: dict[int, list[dict[str, object]]] = {
|
|
640
|
+
0: [{'id': 1, 'name': 'Alice'}, {'id': 2, 'name': 'Bob'}],
|
|
641
|
+
2: [{'id': 3, 'name': 'Charlie'}],
|
|
642
|
+
3: [],
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
async def fetch_users(offset: int, limit: int) -> list[dict[str, object]]:
|
|
646
|
+
return responses.get(offset, [])
|
|
647
|
+
|
|
648
|
+
ext = {'fetch_users': fetch_users}
|
|
649
|
+
|
|
650
|
+
# Snippet 1: LLM sets up accumulator
|
|
651
|
+
await run_repl_async(repl, 'all_users = []', external_functions=ext)
|
|
652
|
+
|
|
653
|
+
# Snippet 2: LLM fetches first batch
|
|
654
|
+
await run_repl_async(
|
|
655
|
+
repl,
|
|
656
|
+
"""\
|
|
657
|
+
batch = await fetch_users(0, 2)
|
|
658
|
+
all_users = all_users + batch
|
|
659
|
+
len(batch)
|
|
660
|
+
""",
|
|
661
|
+
external_functions=ext,
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
# Snippet 3: LLM fetches next batch using state
|
|
665
|
+
await run_repl_async(
|
|
666
|
+
repl,
|
|
667
|
+
"""\
|
|
668
|
+
batch = await fetch_users(len(all_users), 2)
|
|
669
|
+
all_users = all_users + batch
|
|
670
|
+
len(batch)
|
|
671
|
+
""",
|
|
672
|
+
external_functions=ext,
|
|
673
|
+
)
|
|
674
|
+
|
|
675
|
+
# Snippet 4: LLM fetches again, gets empty — realizes done
|
|
676
|
+
await run_repl_async(
|
|
677
|
+
repl,
|
|
678
|
+
"""\
|
|
679
|
+
batch = await fetch_users(len(all_users), 2)
|
|
680
|
+
all_users = all_users + batch
|
|
681
|
+
len(batch)
|
|
682
|
+
""",
|
|
683
|
+
external_functions=ext,
|
|
684
|
+
)
|
|
685
|
+
|
|
686
|
+
# Snippet 5: LLM extracts final result
|
|
687
|
+
result = await run_repl_async(repl, '[u["name"] for u in all_users]', external_functions=ext)
|
|
688
|
+
assert result == snapshot(['Alice', 'Bob', 'Charlie'])
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
async def test_repl_llm_error_recovery_retry():
|
|
692
|
+
"""LLM catches an error, adjusts approach, retries successfully."""
|
|
693
|
+
repl = pydantic_monty.MontyRepl()
|
|
694
|
+
call_count = 0
|
|
695
|
+
|
|
696
|
+
async def flaky_api(query: str) -> str:
|
|
697
|
+
nonlocal call_count
|
|
698
|
+
call_count += 1
|
|
699
|
+
if call_count == 1:
|
|
700
|
+
raise ConnectionError('server unavailable')
|
|
701
|
+
return f'result for {query}'
|
|
702
|
+
|
|
703
|
+
ext = {'flaky_api': flaky_api}
|
|
704
|
+
|
|
705
|
+
# Snippet 1: LLM tries, gets error
|
|
706
|
+
with pytest.raises(pydantic_monty.MontyRuntimeError):
|
|
707
|
+
await run_repl_async(repl, "data = await flaky_api('test')", external_functions=ext)
|
|
708
|
+
|
|
709
|
+
# Snippet 2: LLM wraps in try/except and retries
|
|
710
|
+
result = await run_repl_async(
|
|
711
|
+
repl,
|
|
712
|
+
"""\
|
|
713
|
+
try:
|
|
714
|
+
data = await flaky_api('test')
|
|
715
|
+
except Exception as e:
|
|
716
|
+
data = 'fallback'
|
|
717
|
+
data
|
|
718
|
+
""",
|
|
719
|
+
external_functions=ext,
|
|
720
|
+
)
|
|
721
|
+
assert result == snapshot('result for test')
|
|
722
|
+
|
|
723
|
+
|
|
724
|
+
async def test_repl_llm_redefine_helper_function():
|
|
725
|
+
"""LLM defines a function, uses it, then redefines it with improvements."""
|
|
726
|
+
repl = pydantic_monty.MontyRepl()
|
|
727
|
+
|
|
728
|
+
async def fetch(url: str) -> str:
|
|
729
|
+
return f'<html>{url}</html>'
|
|
730
|
+
|
|
731
|
+
ext = {'fetch': fetch}
|
|
732
|
+
|
|
733
|
+
# Snippet 1: LLM defines initial parser
|
|
734
|
+
await run_repl_async(
|
|
735
|
+
repl,
|
|
736
|
+
"""\
|
|
737
|
+
def parse_title(html):
|
|
738
|
+
return html
|
|
739
|
+
""",
|
|
740
|
+
external_functions=ext,
|
|
741
|
+
)
|
|
742
|
+
|
|
743
|
+
# Snippet 2: LLM uses it, gets raw html back
|
|
744
|
+
result = await run_repl_async(
|
|
745
|
+
repl,
|
|
746
|
+
"""\
|
|
747
|
+
html = await fetch('example.com')
|
|
748
|
+
parse_title(html)
|
|
749
|
+
""",
|
|
750
|
+
external_functions=ext,
|
|
751
|
+
)
|
|
752
|
+
assert result == snapshot('<html>example.com</html>')
|
|
753
|
+
|
|
754
|
+
# Snippet 3: LLM redefines parser with better logic
|
|
755
|
+
await run_repl_async(
|
|
756
|
+
repl,
|
|
757
|
+
"""\
|
|
758
|
+
def parse_title(html):
|
|
759
|
+
start = html.find('>') + 1
|
|
760
|
+
end = html.rfind('<')
|
|
761
|
+
return html[start:end]
|
|
762
|
+
""",
|
|
763
|
+
external_functions=ext,
|
|
764
|
+
)
|
|
765
|
+
|
|
766
|
+
# Snippet 4: uses improved parser on previously fetched data
|
|
767
|
+
result = await run_repl_async(repl, 'parse_title(html)', external_functions=ext)
|
|
768
|
+
assert result == snapshot('example.com')
|
|
769
|
+
|
|
770
|
+
|
|
771
|
+
async def test_repl_llm_sequential_async_pipeline():
|
|
772
|
+
"""LLM builds a data pipeline: fetch -> transform -> store, each step depends on previous."""
|
|
773
|
+
repl = pydantic_monty.MontyRepl()
|
|
774
|
+
|
|
775
|
+
async def search(query: str) -> list[str]:
|
|
776
|
+
return [f'{query}_result_1', f'{query}_result_2']
|
|
777
|
+
|
|
778
|
+
async def summarize(text: str) -> str:
|
|
779
|
+
return f'summary({text})'
|
|
780
|
+
|
|
781
|
+
records: list[str] = []
|
|
782
|
+
|
|
783
|
+
def record(item: str) -> None:
|
|
784
|
+
records.append(item)
|
|
785
|
+
|
|
786
|
+
ext = {'search': search, 'summarize': summarize, 'record': record}
|
|
787
|
+
|
|
788
|
+
code = """\
|
|
789
|
+
results = await search('python async')
|
|
790
|
+
summaries = []
|
|
791
|
+
for r in results:
|
|
792
|
+
s = await summarize(r)
|
|
793
|
+
summaries.append(s)
|
|
794
|
+
record(s)
|
|
795
|
+
summaries
|
|
796
|
+
"""
|
|
797
|
+
result = await run_repl_async(repl, code, external_functions=ext)
|
|
798
|
+
assert result == snapshot(['summary(python async_result_1)', 'summary(python async_result_2)'])
|
|
799
|
+
assert records == snapshot(['summary(python async_result_1)', 'summary(python async_result_2)'])
|
|
800
|
+
|
|
801
|
+
|
|
802
|
+
async def test_repl_llm_gather_fan_out():
|
|
803
|
+
"""LLM uses asyncio.gather to fan out many concurrent requests."""
|
|
804
|
+
repl = pydantic_monty.MontyRepl()
|
|
805
|
+
|
|
806
|
+
async def fetch_price(item: str) -> float:
|
|
807
|
+
prices = {'apple': 1.5, 'banana': 0.75, 'cherry': 3.0, 'date': 5.0, 'elderberry': 8.0}
|
|
808
|
+
return prices[item]
|
|
809
|
+
|
|
810
|
+
ext = {'fetch_price': fetch_price}
|
|
811
|
+
|
|
812
|
+
code = """\
|
|
813
|
+
import asyncio
|
|
814
|
+
|
|
815
|
+
items = ['apple', 'banana', 'cherry', 'date', 'elderberry']
|
|
816
|
+
prices = await asyncio.gather(*(fetch_price(item) for item in items))
|
|
817
|
+
dict(zip(items, prices))
|
|
818
|
+
"""
|
|
819
|
+
result = await run_repl_async(repl, code, external_functions=ext)
|
|
820
|
+
assert result == snapshot({'apple': 1.5, 'banana': 0.75, 'cherry': 3.0, 'date': 5.0, 'elderberry': 8.0})
|
|
821
|
+
|
|
822
|
+
|
|
823
|
+
async def test_repl_llm_try_except_around_external():
|
|
824
|
+
"""LLM wraps individual external calls in try/except for graceful degradation."""
|
|
825
|
+
repl = pydantic_monty.MontyRepl()
|
|
826
|
+
|
|
827
|
+
def fetch_data(key: str) -> str:
|
|
828
|
+
if key == 'bad':
|
|
829
|
+
raise KeyError(f'no data for {key}')
|
|
830
|
+
return f'data_{key}'
|
|
831
|
+
|
|
832
|
+
ext = {'fetch_data': fetch_data}
|
|
833
|
+
|
|
834
|
+
code = """\
|
|
835
|
+
results = {}
|
|
836
|
+
for key in ['good', 'bad', 'also_good']:
|
|
837
|
+
try:
|
|
838
|
+
results[key] = fetch_data(key)
|
|
839
|
+
except KeyError:
|
|
840
|
+
results[key] = 'missing'
|
|
841
|
+
results
|
|
842
|
+
"""
|
|
843
|
+
result = await run_repl_async(repl, code, external_functions=ext)
|
|
844
|
+
assert result == snapshot({'good': 'data_good', 'bad': 'missing', 'also_good': 'data_also_good'})
|
|
845
|
+
|
|
846
|
+
|
|
847
|
+
async def test_repl_llm_conditional_external_call():
|
|
848
|
+
"""LLM only calls external function when a condition is met."""
|
|
849
|
+
repl = pydantic_monty.MontyRepl()
|
|
850
|
+
call_count = 0
|
|
851
|
+
|
|
852
|
+
async def expensive_lookup(key: str) -> str:
|
|
853
|
+
nonlocal call_count
|
|
854
|
+
call_count += 1
|
|
855
|
+
return f'looked up {key}'
|
|
856
|
+
|
|
857
|
+
ext = {'expensive_lookup': expensive_lookup}
|
|
858
|
+
|
|
859
|
+
# Snippet 1: set up a cache
|
|
860
|
+
await run_repl_async(repl, "cache = {'x': 'cached_x'}", external_functions=ext)
|
|
861
|
+
|
|
862
|
+
# Snippet 2: LLM checks cache before calling
|
|
863
|
+
code = """\
|
|
864
|
+
results = []
|
|
865
|
+
for key in ['x', 'y', 'x']:
|
|
866
|
+
if key in cache:
|
|
867
|
+
results.append(cache[key])
|
|
868
|
+
else:
|
|
869
|
+
val = await expensive_lookup(key)
|
|
870
|
+
cache[key] = val
|
|
871
|
+
results.append(val)
|
|
872
|
+
results
|
|
873
|
+
"""
|
|
874
|
+
result = await run_repl_async(repl, code, external_functions=ext)
|
|
875
|
+
assert result == snapshot(['cached_x', 'looked up y', 'cached_x'])
|
|
876
|
+
assert call_count == 1 # only 'y' triggered a call
|
|
877
|
+
|
|
878
|
+
|
|
879
|
+
async def test_repl_llm_side_effect_recording():
|
|
880
|
+
"""LLM uses a side-effect-only external function to record structured data."""
|
|
881
|
+
repl = pydantic_monty.MontyRepl()
|
|
882
|
+
recorded: list[dict[str, object]] = []
|
|
883
|
+
|
|
884
|
+
def record_model(name: str, params: str, price: float) -> None:
|
|
885
|
+
recorded.append({'name': name, 'params': params, 'price': price})
|
|
886
|
+
|
|
887
|
+
async def get_models() -> list[dict[str, str]]:
|
|
888
|
+
return [
|
|
889
|
+
{'name': 'gpt-4', 'params': '1.7T'},
|
|
890
|
+
{'name': 'claude-3', 'params': '???'},
|
|
891
|
+
]
|
|
892
|
+
|
|
893
|
+
ext = {'record_model': record_model, 'get_models': get_models}
|
|
894
|
+
|
|
895
|
+
code = """\
|
|
896
|
+
models = await get_models()
|
|
897
|
+
for m in models:
|
|
898
|
+
record_model(m['name'], m['params'], 0.01)
|
|
899
|
+
len(models)
|
|
900
|
+
"""
|
|
901
|
+
result = await run_repl_async(repl, code, external_functions=ext)
|
|
902
|
+
assert result == snapshot(2)
|
|
903
|
+
assert recorded == snapshot(
|
|
904
|
+
[{'name': 'gpt-4', 'params': '1.7T', 'price': 0.01}, {'name': 'claude-3', 'params': '???', 'price': 0.01}]
|
|
905
|
+
)
|
|
906
|
+
|
|
907
|
+
|
|
908
|
+
async def test_repl_llm_helper_wrapping_externals_with_retry():
|
|
909
|
+
"""LLM defines a helper function that wraps external calls with retry logic."""
|
|
910
|
+
repl = pydantic_monty.MontyRepl()
|
|
911
|
+
attempt_counts: dict[str, int] = {}
|
|
912
|
+
|
|
913
|
+
def unreliable_fetch(url: str) -> str:
|
|
914
|
+
attempt_counts.setdefault(url, 0)
|
|
915
|
+
attempt_counts[url] += 1
|
|
916
|
+
if attempt_counts[url] < 2:
|
|
917
|
+
raise ValueError('temporary failure')
|
|
918
|
+
return f'content of {url}'
|
|
919
|
+
|
|
920
|
+
ext = {'unreliable_fetch': unreliable_fetch}
|
|
921
|
+
|
|
922
|
+
# Snippet 1: LLM defines retry helper
|
|
923
|
+
await run_repl_async(
|
|
924
|
+
repl,
|
|
925
|
+
"""\
|
|
926
|
+
def fetch_with_retry(url, max_retries=3):
|
|
927
|
+
for i in range(max_retries):
|
|
928
|
+
try:
|
|
929
|
+
return unreliable_fetch(url)
|
|
930
|
+
except ValueError:
|
|
931
|
+
if i == max_retries - 1:
|
|
932
|
+
raise
|
|
933
|
+
raise ValueError('should not reach here')
|
|
934
|
+
""",
|
|
935
|
+
external_functions=ext,
|
|
936
|
+
)
|
|
937
|
+
|
|
938
|
+
# Snippet 2: LLM uses the retry helper
|
|
939
|
+
result = await run_repl_async(repl, "fetch_with_retry('example.com')", external_functions=ext)
|
|
940
|
+
assert result == snapshot('content of example.com')
|
|
941
|
+
assert attempt_counts == snapshot({'example.com': 2})
|
|
942
|
+
|
|
943
|
+
|
|
944
|
+
async def test_repl_llm_nested_gather_with_sequential_deps():
|
|
945
|
+
"""LLM does gather of tasks where each task has sequential async steps internally."""
|
|
946
|
+
repl = pydantic_monty.MontyRepl()
|
|
947
|
+
|
|
948
|
+
async def get_user(user_id: int) -> dict[str, object]:
|
|
949
|
+
return {'id': user_id, 'name': f'user_{user_id}'}
|
|
950
|
+
|
|
951
|
+
async def get_posts(user_id: int) -> list[str]:
|
|
952
|
+
return [f'post_{user_id}_1', f'post_{user_id}_2']
|
|
953
|
+
|
|
954
|
+
ext = {'get_user': get_user, 'get_posts': get_posts}
|
|
955
|
+
|
|
956
|
+
code = """\
|
|
957
|
+
import asyncio
|
|
958
|
+
|
|
959
|
+
async def get_user_with_posts(uid):
|
|
960
|
+
user = await get_user(uid)
|
|
961
|
+
posts = await get_posts(uid)
|
|
962
|
+
user['posts'] = posts
|
|
963
|
+
return user
|
|
964
|
+
|
|
965
|
+
results = await asyncio.gather(
|
|
966
|
+
get_user_with_posts(1),
|
|
967
|
+
get_user_with_posts(2),
|
|
968
|
+
get_user_with_posts(3),
|
|
969
|
+
)
|
|
970
|
+
results
|
|
971
|
+
"""
|
|
972
|
+
result = await run_repl_async(repl, code, external_functions=ext)
|
|
973
|
+
assert result == snapshot(
|
|
974
|
+
[
|
|
975
|
+
{'id': 1, 'name': 'user_1', 'posts': ['post_1_1', 'post_1_2']},
|
|
976
|
+
{'id': 2, 'name': 'user_2', 'posts': ['post_2_1', 'post_2_2']},
|
|
977
|
+
{'id': 3, 'name': 'user_3', 'posts': ['post_3_1', 'post_3_2']},
|
|
978
|
+
]
|
|
979
|
+
)
|
|
980
|
+
|
|
981
|
+
|
|
982
|
+
async def test_repl_llm_external_returns_complex_nested_structure():
|
|
983
|
+
"""LLM processes deeply nested API response from external function."""
|
|
984
|
+
repl = pydantic_monty.MontyRepl()
|
|
985
|
+
|
|
986
|
+
async def get_api_response() -> dict[str, object]:
|
|
987
|
+
return {
|
|
988
|
+
'status': 'ok',
|
|
989
|
+
'data': {
|
|
990
|
+
'users': [
|
|
991
|
+
{'name': 'Alice', 'scores': [95, 87, 92]},
|
|
992
|
+
{'name': 'Bob', 'scores': [78, 85, 90]},
|
|
993
|
+
],
|
|
994
|
+
'metadata': {'page': 1, 'total': 2},
|
|
995
|
+
},
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
ext = {'get_api_response': get_api_response}
|
|
999
|
+
|
|
1000
|
+
# Snippet 1: fetch and store
|
|
1001
|
+
await run_repl_async(repl, 'response = await get_api_response()', external_functions=ext)
|
|
1002
|
+
|
|
1003
|
+
# Snippet 2: LLM navigates nested structure
|
|
1004
|
+
result = await run_repl_async(
|
|
1005
|
+
repl,
|
|
1006
|
+
"""\
|
|
1007
|
+
users = response['data']['users']
|
|
1008
|
+
averages = {}
|
|
1009
|
+
for u in users:
|
|
1010
|
+
avg = sum(u['scores']) / len(u['scores'])
|
|
1011
|
+
averages[u['name']] = round(avg, 1)
|
|
1012
|
+
averages
|
|
1013
|
+
""",
|
|
1014
|
+
external_functions=ext,
|
|
1015
|
+
)
|
|
1016
|
+
assert result == snapshot({'Alice': 91.3, 'Bob': 84.3})
|
|
1017
|
+
|
|
1018
|
+
|
|
1019
|
+
async def test_repl_llm_external_with_kwargs():
|
|
1020
|
+
"""LLM calls external functions using keyword arguments."""
|
|
1021
|
+
repl = pydantic_monty.MontyRepl()
|
|
1022
|
+
|
|
1023
|
+
async def search(query: str, limit: int = 10, offset: int = 0) -> dict[str, object]:
|
|
1024
|
+
return {'query': query, 'limit': limit, 'offset': offset, 'results': [f'{query}_{i}' for i in range(limit)]}
|
|
1025
|
+
|
|
1026
|
+
ext = {'search': search}
|
|
1027
|
+
|
|
1028
|
+
code = """\
|
|
1029
|
+
page1 = await search('test', limit=2, offset=0)
|
|
1030
|
+
page2 = await search('test', limit=2, offset=2)
|
|
1031
|
+
page1['results'] + page2['results']
|
|
1032
|
+
"""
|
|
1033
|
+
result = await run_repl_async(repl, code, external_functions=ext)
|
|
1034
|
+
assert result == snapshot(['test_0', 'test_1', 'test_0', 'test_1'])
|
|
1035
|
+
|
|
1036
|
+
|
|
1037
|
+
async def test_repl_llm_os_read_then_process_with_external():
|
|
1038
|
+
"""LLM reads a file via OS, then processes content with an async external function."""
|
|
1039
|
+
from pydantic_monty import MemoryFile, OSAccess
|
|
1040
|
+
|
|
1041
|
+
repl = pydantic_monty.MontyRepl()
|
|
1042
|
+
fs = OSAccess([MemoryFile('/data.csv', content='alice,95\nbob,87\ncharlie,92')])
|
|
1043
|
+
|
|
1044
|
+
async def analyze(text: str) -> dict[str, int]:
|
|
1045
|
+
rows = text.strip().split('\n')
|
|
1046
|
+
return {name: int(score) for name, score in (r.split(',') for r in rows)}
|
|
1047
|
+
|
|
1048
|
+
ext = {'analyze': analyze}
|
|
1049
|
+
|
|
1050
|
+
# Snippet 1: read file
|
|
1051
|
+
await run_repl_async(
|
|
1052
|
+
repl,
|
|
1053
|
+
"""\
|
|
1054
|
+
from pathlib import Path
|
|
1055
|
+
raw = Path('/data.csv').read_text()
|
|
1056
|
+
""",
|
|
1057
|
+
external_functions=ext,
|
|
1058
|
+
os=fs,
|
|
1059
|
+
)
|
|
1060
|
+
|
|
1061
|
+
# Snippet 2: process with external
|
|
1062
|
+
result = await run_repl_async(repl, 'await analyze(raw)', external_functions=ext, os=fs)
|
|
1063
|
+
assert result == snapshot({'alice': 95, 'bob': 87, 'charlie': 92})
|
|
1064
|
+
|
|
1065
|
+
|
|
1066
|
+
async def test_repl_llm_long_multi_step_session():
|
|
1067
|
+
"""Simulates a multi-step LLM agent session: setup, explore, process, summarize."""
|
|
1068
|
+
repl = pydantic_monty.MontyRepl()
|
|
1069
|
+
|
|
1070
|
+
db: dict[str, list[dict[str, object]]] = {
|
|
1071
|
+
'products': [
|
|
1072
|
+
{'name': 'Widget', 'price': 9.99, 'category': 'tools'},
|
|
1073
|
+
{'name': 'Gadget', 'price': 24.99, 'category': 'electronics'},
|
|
1074
|
+
{'name': 'Doohickey', 'price': 4.99, 'category': 'tools'},
|
|
1075
|
+
{'name': 'Thingamajig', 'price': 49.99, 'category': 'electronics'},
|
|
1076
|
+
],
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
async def query_db(table: str, filters: dict[str, str] | None = None) -> list[dict[str, object]]:
|
|
1080
|
+
rows = db.get(table, [])
|
|
1081
|
+
if filters:
|
|
1082
|
+
for k, v in filters.items():
|
|
1083
|
+
rows = [r for r in rows if r.get(k) == v]
|
|
1084
|
+
return rows
|
|
1085
|
+
|
|
1086
|
+
ext = {'query_db': query_db}
|
|
1087
|
+
|
|
1088
|
+
# Step 1: LLM explores what's available
|
|
1089
|
+
result = await run_repl_async(repl, 'await query_db("products")', external_functions=ext)
|
|
1090
|
+
assert len(result) == 4
|
|
1091
|
+
|
|
1092
|
+
# Step 2: LLM filters by category
|
|
1093
|
+
await run_repl_async(
|
|
1094
|
+
repl,
|
|
1095
|
+
"tools = await query_db('products', filters={'category': 'tools'})",
|
|
1096
|
+
external_functions=ext,
|
|
1097
|
+
)
|
|
1098
|
+
|
|
1099
|
+
# Step 3: LLM computes stats
|
|
1100
|
+
result = await run_repl_async(
|
|
1101
|
+
repl,
|
|
1102
|
+
"""\
|
|
1103
|
+
total = sum(p['price'] for p in tools)
|
|
1104
|
+
avg = total / len(tools)
|
|
1105
|
+
{'count': len(tools), 'total': round(total, 2), 'average': round(avg, 2)}
|
|
1106
|
+
""",
|
|
1107
|
+
external_functions=ext,
|
|
1108
|
+
)
|
|
1109
|
+
assert result == snapshot({'count': 2, 'total': 14.98, 'average': 7.49})
|
|
1110
|
+
|
|
1111
|
+
# Step 4: LLM also checks electronics
|
|
1112
|
+
await run_repl_async(
|
|
1113
|
+
repl,
|
|
1114
|
+
"electronics = await query_db('products', filters={'category': 'electronics'})",
|
|
1115
|
+
external_functions=ext,
|
|
1116
|
+
)
|
|
1117
|
+
|
|
1118
|
+
# Step 5: LLM builds final summary from accumulated state
|
|
1119
|
+
result = await run_repl_async(
|
|
1120
|
+
repl,
|
|
1121
|
+
"""\
|
|
1122
|
+
summary = {}
|
|
1123
|
+
for cat, items in [('tools', tools), ('electronics', electronics)]:
|
|
1124
|
+
summary[cat] = {
|
|
1125
|
+
'count': len(items),
|
|
1126
|
+
'total': round(sum(i['price'] for i in items), 2),
|
|
1127
|
+
'items': [i['name'] for i in items],
|
|
1128
|
+
}
|
|
1129
|
+
summary
|
|
1130
|
+
""",
|
|
1131
|
+
external_functions=ext,
|
|
1132
|
+
)
|
|
1133
|
+
assert result == snapshot(
|
|
1134
|
+
{
|
|
1135
|
+
'tools': {'count': 2, 'total': 14.98, 'items': ['Widget', 'Doohickey']},
|
|
1136
|
+
'electronics': {'count': 2, 'total': 74.98, 'items': ['Gadget', 'Thingamajig']},
|
|
1137
|
+
}
|
|
1138
|
+
)
|
|
1139
|
+
|
|
1140
|
+
|
|
1141
|
+
async def test_repl_llm_string_manipulation_of_external_result():
|
|
1142
|
+
"""LLM fetches HTML-like content and does string processing across snippets."""
|
|
1143
|
+
repl = pydantic_monty.MontyRepl()
|
|
1144
|
+
|
|
1145
|
+
async def fetch_page(url: str) -> str:
|
|
1146
|
+
return '<title>Test Page</title><body><p>Hello</p><p>World</p></body>'
|
|
1147
|
+
|
|
1148
|
+
ext = {'fetch_page': fetch_page}
|
|
1149
|
+
|
|
1150
|
+
await run_repl_async(repl, "html = await fetch_page('example.com')", external_functions=ext)
|
|
1151
|
+
|
|
1152
|
+
# LLM extracts title
|
|
1153
|
+
result = await run_repl_async(
|
|
1154
|
+
repl,
|
|
1155
|
+
"""\
|
|
1156
|
+
start = html.find('<title>') + len('<title>')
|
|
1157
|
+
end = html.find('</title>')
|
|
1158
|
+
title = html[start:end]
|
|
1159
|
+
title
|
|
1160
|
+
""",
|
|
1161
|
+
external_functions=ext,
|
|
1162
|
+
)
|
|
1163
|
+
assert result == snapshot('Test Page')
|
|
1164
|
+
|
|
1165
|
+
# LLM extracts paragraphs
|
|
1166
|
+
result = await run_repl_async(
|
|
1167
|
+
repl,
|
|
1168
|
+
"""\
|
|
1169
|
+
paragraphs = []
|
|
1170
|
+
remaining = html
|
|
1171
|
+
while '<p>' in remaining:
|
|
1172
|
+
s = remaining.find('<p>') + 3
|
|
1173
|
+
e = remaining.find('</p>')
|
|
1174
|
+
paragraphs.append(remaining[s:e])
|
|
1175
|
+
remaining = remaining[e + 4:]
|
|
1176
|
+
paragraphs
|
|
1177
|
+
""",
|
|
1178
|
+
external_functions=ext,
|
|
1179
|
+
)
|
|
1180
|
+
assert result == snapshot(['Hello', 'World'])
|
|
1181
|
+
|
|
1182
|
+
|
|
1183
|
+
async def test_repl_llm_syntax_error_then_fix():
|
|
1184
|
+
"""LLM writes code with a syntax error, then fixes it in the next snippet."""
|
|
1185
|
+
repl = pydantic_monty.MontyRepl()
|
|
1186
|
+
|
|
1187
|
+
def add(a: int, b: int) -> int:
|
|
1188
|
+
return a + b
|
|
1189
|
+
|
|
1190
|
+
ext = {'add': add}
|
|
1191
|
+
|
|
1192
|
+
# Snippet 1: set up state
|
|
1193
|
+
await run_repl_async(repl, 'x = 10', external_functions=ext)
|
|
1194
|
+
|
|
1195
|
+
# Snippet 2: syntax error
|
|
1196
|
+
with pytest.raises(pydantic_monty.MontySyntaxError):
|
|
1197
|
+
await run_repl_async(repl, 'y = add(x,', external_functions=ext)
|
|
1198
|
+
|
|
1199
|
+
# Snippet 3: state preserved, LLM fixes the code
|
|
1200
|
+
result = await run_repl_async(repl, 'y = add(x, 5)\ny', external_functions=ext)
|
|
1201
|
+
assert result == snapshot(15)
|