superacli 1.1.6 → 1.1.7
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/README.md +77 -53
- package/__tests__/azd-plugin.test.js +109 -0
- package/__tests__/config.test.js +4 -3
- package/__tests__/discover.test.js +59 -0
- package/__tests__/goose-plugin.test.js +149 -0
- package/__tests__/help-json.test.js +2 -0
- package/__tests__/openhands-plugin.test.js +106 -0
- package/__tests__/plugin-cocoindex-code-uninstall.test.js +19 -0
- package/__tests__/plugin-cocoindex-code.test.js +37 -0
- package/__tests__/plugin-install-guidance.test.js +81 -0
- package/__tests__/plugins-registry.test.js +44 -0
- package/__tests__/plugins-store.test.js +40 -5
- package/__tests__/process-adapter.test.js +50 -1
- package/__tests__/server-app.test.js +1 -0
- package/__tests__/server-routes-commands.test.js +20 -2
- package/__tests__/server-routes-plugins.test.js +130 -0
- package/__tests__/skills.test.js +26 -0
- package/__tests__/squirrelscan-plugin.test.js +129 -0
- package/__tests__/uipath-plugin.test.js +104 -0
- package/__tests__/uipathcli-plugin.test.js +95 -0
- package/cli/adapters/mcp.js +2 -0
- package/cli/adapters/process.js +49 -2
- package/cli/config.js +240 -3
- package/cli/discover.js +157 -0
- package/cli/help-json.js +16 -1
- package/cli/plugin-install-guidance.js +92 -37
- package/cli/plugins-manager.js +1 -0
- package/cli/plugins-registry.js +74 -8
- package/cli/plugins-store.js +78 -17
- package/cli/skills-mcp.js +1 -1
- package/cli/skills.js +39 -2
- package/cli/supercli.js +87 -11
- package/docs/feature-gaps.md +8 -8
- package/docs/features/azd-uipath-plugins.md +43 -0
- package/docs/features/server-plugins.md +62 -0
- package/docs/features/skills.md +9 -5
- package/docs/{supported-harnesses.md → plugins-available.md} +4 -3
- package/docs/{plugin-harness-guide.md → plugins-how-to.md} +1 -1
- package/docs/plugins.md +26 -20
- package/docs/server-plugins-usage-guide.md +182 -0
- package/docs/skills-catalog.md +12 -10
- package/package.json +1 -1
- package/plugins/agent-browser/README.md +69 -0
- package/plugins/agent-browser/plugin.json +111 -0
- package/plugins/agent-browser/skills/quickstart/SKILL.md +66 -0
- package/plugins/aider/README.md +53 -0
- package/plugins/aider/plugin.json +105 -0
- package/plugins/aider/scripts/aider-wrapper.js +243 -0
- package/plugins/aider/scripts/setup-aider.js +37 -0
- package/plugins/aider/skills/dry-run-review.md +24 -0
- package/plugins/aider/skills/model-and-provider.md +24 -0
- package/plugins/aider/skills/one-shot-edits.md +30 -0
- package/plugins/aider/skills/quickstart/SKILL.md +51 -0
- package/plugins/azd/README.md +28 -0
- package/plugins/azd/plugin.json +87 -0
- package/plugins/azd/skills/quickstart/SKILL.md +41 -0
- package/plugins/blogwatcher/README.md +3 -3
- package/plugins/boxlite/Dockerfile +9 -0
- package/plugins/boxlite/README.md +62 -0
- package/plugins/boxlite/plugin.json +201 -0
- package/plugins/boxlite/scripts/run-boxlite.js +106 -0
- package/plugins/boxlite/skills/quickstart/SKILL.md +40 -0
- package/plugins/cass/plugin.json +150 -0
- package/plugins/cass/scripts/setup-cass.js +47 -0
- package/plugins/cass/skills/quickstart/SKILL.md +46 -0
- package/plugins/clever/README.md +46 -0
- package/plugins/clever/plugin.json +119 -0
- package/plugins/clever/scripts/setup-clever.js +28 -0
- package/plugins/clever/skills/auth-and-profile.md +29 -0
- package/plugins/clever/skills/passthrough-safety.md +21 -0
- package/plugins/clever/skills/quickstart/SKILL.md +45 -0
- package/plugins/clever/skills/resource-inventory.md +24 -0
- package/plugins/clix/README.md +4 -4
- package/plugins/cocoindex-code/README.md +64 -0
- package/plugins/cocoindex-code/plugin.json +81 -0
- package/plugins/cocoindex-code/scripts/__pycache__/query.cpython-310.pyc +0 -0
- package/plugins/cocoindex-code/scripts/__pycache__/query.cpython-311.pyc +0 -0
- package/plugins/cocoindex-code/scripts/post-install.js +61 -0
- package/plugins/cocoindex-code/scripts/post-uninstall.js +25 -0
- package/plugins/cocoindex-code/scripts/query.py +88 -0
- package/plugins/cocoindex-code/scripts/run-query.js +50 -0
- package/plugins/cocoindex-code/skills/quickstart/SKILL.md +73 -0
- package/plugins/copilot/README.md +24 -0
- package/plugins/copilot/plugin.json +80 -0
- package/plugins/copilot/skills/quickstart/SKILL.md +44 -0
- package/plugins/gemini/README.md +24 -0
- package/plugins/gemini/plugin.json +98 -0
- package/plugins/gemini/skills/quickstart/SKILL.md +44 -0
- package/plugins/gifcap/plugin.json +119 -0
- package/plugins/gifcap/scripts/setup-gifcap.js +44 -0
- package/plugins/gifcap/skills/quickstart/SKILL.md +34 -0
- package/plugins/gifcap/test-record-quiet.gif +0 -0
- package/plugins/gifcap/test-record.gif +0 -0
- package/plugins/goose/README.md +36 -0
- package/plugins/goose/plugin.json +183 -0
- package/plugins/goose/skills/quickstart/SKILL.md +44 -0
- package/plugins/json-server/README.md +58 -0
- package/plugins/json-server/plugin.json +113 -0
- package/plugins/json-server/skills/quickstart/SKILL.md +57 -0
- package/plugins/lightpanda/README.md +145 -0
- package/plugins/lightpanda/package-lock.json +1375 -0
- package/plugins/lightpanda/package.json +12 -0
- package/plugins/lightpanda/plugin.json +116 -0
- package/plugins/lightpanda/scripts/lightpanda-contacts.js +494 -0
- package/plugins/lightpanda/scripts/lightpanda-generic-extract.js +403 -0
- package/plugins/lightpanda/scripts/lightpanda-wrapper.js +480 -0
- package/plugins/lightpanda/scripts/setup-lightpanda.js +39 -0
- package/plugins/lightpanda/skills/contact-discovery.md +51 -0
- package/plugins/lightpanda/skills/generic-extraction.md +66 -0
- package/plugins/lightpanda/skills/quickstart/SKILL.md +103 -0
- package/plugins/lightpanda/skills/resilient-navigation.md +42 -0
- package/plugins/monty/README.md +2 -2
- package/plugins/nullclaw/README.md +3 -3
- package/plugins/offline-ai/README.md +23 -0
- package/plugins/offline-ai/plugin.json +82 -0
- package/plugins/offline-ai/skills/quickstart/SKILL.md +43 -0
- package/plugins/openhands/README.md +25 -0
- package/plugins/openhands/plugin.json +116 -0
- package/plugins/openhands/skills/quickstart/SKILL.md +26 -0
- package/plugins/plandex/README.md +25 -0
- package/plugins/plandex/plugin.json +130 -0
- package/plugins/plandex/skills/quickstart/SKILL.md +50 -0
- package/plugins/plugins.json +188 -0
- package/plugins/squirrelscan/Dockerfile +5 -0
- package/plugins/squirrelscan/README.md +47 -0
- package/plugins/squirrelscan/plugin.json +493 -0
- package/plugins/squirrelscan/scripts/post-install.js +33 -0
- package/plugins/squirrelscan/scripts/post-uninstall.js +25 -0
- package/plugins/squirrelscan/scripts/run-squirrel.js +73 -0
- package/plugins/squirrelscan/skills/audit-workflow/SKILL.md +33 -0
- package/plugins/squirrelscan/skills/publish-report/SKILL.md +33 -0
- package/plugins/squirrelscan/skills/quickstart/SKILL.md +41 -0
- package/plugins/uipath/README.md +27 -0
- package/plugins/uipath/plugin.json +86 -0
- package/plugins/uipath/skills/quickstart/SKILL.md +47 -0
- package/plugins/uipathcli/README.md +28 -0
- package/plugins/uipathcli/plugin.json +120 -0
- package/plugins/uipathcli/scripts/run-uipath-cli.js +49 -0
- package/plugins/uipathcli/skills/quickstart/SKILL.md +22 -0
- package/plugins/xurl/README.md +4 -4
- package/server/app.js +5 -2
- package/server/public/app.js +3 -0
- package/server/routes/commands.js +95 -12
- package/server/routes/plugins.js +262 -0
- package/server/services/pluginsService.js +303 -0
- package/server/views/command-edit.ejs +196 -14
- package/server/views/partials/head.ejs +1 -0
- package/server/views/plugins.ejs +264 -0
- package/tests/test-plugins-registry.js +30 -0
- package/.beads/.br_history/issues.20260308_200823_636718328.jsonl +0 -20
- package/.beads/.br_history/issues.20260308_200823_636718328.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_200827_033159453.jsonl +0 -21
- package/.beads/.br_history/issues.20260308_200827_033159453.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_200829_595900053.jsonl +0 -22
- package/.beads/.br_history/issues.20260308_200829_595900053.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_200834_079930100.jsonl +0 -23
- package/.beads/.br_history/issues.20260308_200834_079930100.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_200858_370924996.jsonl +0 -24
- package/.beads/.br_history/issues.20260308_200858_370924996.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201031_019730855.jsonl +0 -24
- package/.beads/.br_history/issues.20260308_201031_019730855.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201031_578974884.jsonl +0 -24
- package/.beads/.br_history/issues.20260308_201031_578974884.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201054_780345548.jsonl +0 -24
- package/.beads/.br_history/issues.20260308_201054_780345548.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201054_896980019.jsonl +0 -24
- package/.beads/.br_history/issues.20260308_201054_896980019.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201128_599819688.jsonl +0 -24
- package/.beads/.br_history/issues.20260308_201128_599819688.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201128_710221699.jsonl +0 -24
- package/.beads/.br_history/issues.20260308_201128_710221699.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201204_745649213.jsonl +0 -24
- package/.beads/.br_history/issues.20260308_201204_745649213.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201338_908436144.jsonl +0 -24
- package/.beads/.br_history/issues.20260308_201338_908436144.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201344_734860714.jsonl +0 -25
- package/.beads/.br_history/issues.20260308_201344_734860714.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201630_819282295.jsonl +0 -25
- package/.beads/.br_history/issues.20260308_201630_819282295.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_203538_054279699.jsonl +0 -25
- package/.beads/.br_history/issues.20260308_203538_054279699.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_203547_597113070.jsonl +0 -26
- package/.beads/.br_history/issues.20260308_203547_597113070.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_203547_775139216.jsonl +0 -27
- package/.beads/.br_history/issues.20260308_203547_775139216.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_203547_950724773.jsonl +0 -28
- package/.beads/.br_history/issues.20260308_203547_950724773.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_203548_107684523.jsonl +0 -29
- package/.beads/.br_history/issues.20260308_203548_107684523.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_203548_310389993.jsonl +0 -30
- package/.beads/.br_history/issues.20260308_203548_310389993.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_203825_953337320.jsonl +0 -31
- package/.beads/.br_history/issues.20260308_203825_953337320.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_204056_071377736.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_204056_071377736.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205141_517616844.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205141_517616844.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205141_648994024.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205141_648994024.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205141_867598036.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205141_867598036.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205142_094157355.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205142_094157355.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205142_327315677.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205142_327315677.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205142_545563822.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205142_545563822.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205213_061989333.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205213_061989333.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205213_181103364.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205213_181103364.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205213_408872234.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205213_408872234.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205213_616681652.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205213_616681652.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205213_821507069.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205213_821507069.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205214_026661112.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205214_026661112.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205454_955250554.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205454_955250554.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205556_337800392.jsonl +0 -33
- package/.beads/.br_history/issues.20260308_205556_337800392.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205824_274686694.jsonl +0 -33
- package/.beads/.br_history/issues.20260308_205824_274686694.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_210240_583768328.jsonl +0 -34
- package/.beads/.br_history/issues.20260308_210240_583768328.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212223_641541494.jsonl +0 -34
- package/.beads/.br_history/issues.20260308_212223_641541494.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212227_735550996.jsonl +0 -35
- package/.beads/.br_history/issues.20260308_212227_735550996.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212232_547298548.jsonl +0 -36
- package/.beads/.br_history/issues.20260308_212232_547298548.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212528_843628125.jsonl +0 -37
- package/.beads/.br_history/issues.20260308_212528_843628125.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212529_094530502.jsonl +0 -38
- package/.beads/.br_history/issues.20260308_212529_094530502.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212529_331000853.jsonl +0 -39
- package/.beads/.br_history/issues.20260308_212529_331000853.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212529_587925652.jsonl +0 -40
- package/.beads/.br_history/issues.20260308_212529_587925652.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212804_927764103.jsonl +0 -41
- package/.beads/.br_history/issues.20260308_212804_927764103.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212805_153673453.jsonl +0 -42
- package/.beads/.br_history/issues.20260308_212805_153673453.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212805_415982363.jsonl +0 -43
- package/.beads/.br_history/issues.20260308_212805_415982363.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212805_657497741.jsonl +0 -44
- package/.beads/.br_history/issues.20260308_212805_657497741.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212805_952838724.jsonl +0 -45
- package/.beads/.br_history/issues.20260308_212805_952838724.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212806_325433779.jsonl +0 -46
- package/.beads/.br_history/issues.20260308_212806_325433779.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212806_584685598.jsonl +0 -47
- package/.beads/.br_history/issues.20260308_212806_584685598.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212806_827817208.jsonl +0 -48
- package/.beads/.br_history/issues.20260308_212806_827817208.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212807_111320451.jsonl +0 -49
- package/.beads/.br_history/issues.20260308_212807_111320451.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212807_409545536.jsonl +0 -50
- package/.beads/.br_history/issues.20260308_212807_409545536.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212807_625063294.jsonl +0 -51
- package/.beads/.br_history/issues.20260308_212807_625063294.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212807_843906551.jsonl +0 -52
- package/.beads/.br_history/issues.20260308_212807_843906551.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212808_100304073.jsonl +0 -53
- package/.beads/.br_history/issues.20260308_212808_100304073.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212808_324723976.jsonl +0 -54
- package/.beads/.br_history/issues.20260308_212808_324723976.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212808_557513104.jsonl +0 -55
- package/.beads/.br_history/issues.20260308_212808_557513104.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212808_788048322.jsonl +0 -56
- package/.beads/.br_history/issues.20260308_212808_788048322.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_213702_613249728.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_213702_613249728.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_213715_115792063.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_213715_115792063.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_213715_462220666.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_213715_462220666.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_213727_191258923.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_213727_191258923.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_213727_684383652.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_213727_684383652.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_213735_751882991.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_213735_751882991.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_222052_279844960.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_222052_279844960.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_222056_873282114.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_222056_873282114.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_222103_402410761.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_222103_402410761.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_235202_180577215.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_235202_180577215.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_235202_387414163.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_235202_387414163.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_235202_564422794.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_235202_564422794.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_235202_742600597.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_235202_742600597.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_235208_133360069.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_235208_133360069.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_235505_473406307.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_235505_473406307.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_235505_662360489.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_235505_662360489.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_235505_843935624.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_235505_843935624.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_235506_044530221.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_235506_044530221.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_002618_115728731.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_002618_115728731.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_003748_878174586.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_003748_878174586.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_004057_868755623.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_004057_868755623.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_004058_512842163.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_004058_512842163.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_004058_994445226.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_004058_994445226.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_004059_475988596.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_004059_475988596.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_161902_566857851.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_161902_566857851.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_170512_277017739.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_170512_277017739.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_170512_477876921.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_170512_477876921.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_170512_664382701.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_170512_664382701.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_170512_859400333.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_170512_859400333.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_212326_082771164.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_212326_082771164.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_212326_245619716.jsonl +0 -58
- package/.beads/.br_history/issues.20260309_212326_245619716.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_212326_403198317.jsonl +0 -59
- package/.beads/.br_history/issues.20260309_212326_403198317.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_212332_539197678.jsonl +0 -60
- package/.beads/.br_history/issues.20260309_212332_539197678.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_212332_731373599.jsonl +0 -60
- package/.beads/.br_history/issues.20260309_212332_731373599.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_212332_928710953.jsonl +0 -60
- package/.beads/.br_history/issues.20260309_212332_928710953.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_213021_341505240.jsonl +0 -60
- package/.beads/.br_history/issues.20260309_213021_341505240.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_213022_023136934.jsonl +0 -60
- package/.beads/.br_history/issues.20260309_213022_023136934.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_213022_400050719.jsonl +0 -60
- package/.beads/.br_history/issues.20260309_213022_400050719.jsonl.meta.json +0 -1
- package/.beads/config.yaml +0 -4
- package/.beads/issues.jsonl +0 -60
- package/.beads/metadata.json +0 -4
- package/docs/mcp-cheatsheet.md +0 -324
- package/docs/visual-overview.md +0 -21
- package/ref-monty/.cargo/config.toml +0 -3
- package/ref-monty/.claude/settings.json +0 -60
- package/ref-monty/.claude/skills/fastmod/SKILL.md +0 -22
- package/ref-monty/.claude/skills/python-playground/SKILL.md +0 -47
- package/ref-monty/.codecov.yml +0 -12
- package/ref-monty/.github/actions/build-pgo-wheel/action.yml +0 -72
- package/ref-monty/.github/workflows/ci.yml +0 -776
- package/ref-monty/.github/workflows/codspeed.yml +0 -45
- package/ref-monty/.github/workflows/init-npm-packages.yml +0 -82
- package/ref-monty/.pre-commit-config.yaml +0 -47
- package/ref-monty/.python-version +0 -1
- package/ref-monty/.rustfmt.toml +0 -4
- package/ref-monty/.zed/settings.json +0 -11
- package/ref-monty/CLAUDE.md +0 -535
- package/ref-monty/Cargo.lock +0 -3798
- package/ref-monty/Cargo.toml +0 -87
- package/ref-monty/LICENSE +0 -21
- package/ref-monty/Makefile +0 -216
- package/ref-monty/README.md +0 -430
- package/ref-monty/RELEASING.md +0 -47
- package/ref-monty/crates/fuzz/Cargo.toml +0 -30
- package/ref-monty/crates/fuzz/fuzz_targets/string_input_panic.rs +0 -37
- package/ref-monty/crates/fuzz/fuzz_targets/tokens_input_panic.rs +0 -552
- package/ref-monty/crates/monty/Cargo.toml +0 -68
- package/ref-monty/crates/monty/benches/main.rs +0 -247
- package/ref-monty/crates/monty/build.rs +0 -10
- package/ref-monty/crates/monty/src/args.rs +0 -733
- package/ref-monty/crates/monty/src/asyncio.rs +0 -179
- package/ref-monty/crates/monty/src/builtins/abs.rs +0 -55
- package/ref-monty/crates/monty/src/builtins/all.rs +0 -30
- package/ref-monty/crates/monty/src/builtins/any.rs +0 -30
- package/ref-monty/crates/monty/src/builtins/bin.rs +0 -59
- package/ref-monty/crates/monty/src/builtins/chr.rs +0 -46
- package/ref-monty/crates/monty/src/builtins/divmod.rs +0 -164
- package/ref-monty/crates/monty/src/builtins/enumerate.rs +0 -52
- package/ref-monty/crates/monty/src/builtins/filter.rs +0 -67
- package/ref-monty/crates/monty/src/builtins/getattr.rs +0 -65
- package/ref-monty/crates/monty/src/builtins/hash.rs +0 -28
- package/ref-monty/crates/monty/src/builtins/hex.rs +0 -58
- package/ref-monty/crates/monty/src/builtins/id.rs +0 -24
- package/ref-monty/crates/monty/src/builtins/isinstance.rs +0 -68
- package/ref-monty/crates/monty/src/builtins/len.rs +0 -25
- package/ref-monty/crates/monty/src/builtins/map.rs +0 -98
- package/ref-monty/crates/monty/src/builtins/min_max.rs +0 -113
- package/ref-monty/crates/monty/src/builtins/mod.rs +0 -246
- package/ref-monty/crates/monty/src/builtins/next.rs +0 -21
- package/ref-monty/crates/monty/src/builtins/oct.rs +0 -59
- package/ref-monty/crates/monty/src/builtins/ord.rs +0 -67
- package/ref-monty/crates/monty/src/builtins/pow.rs +0 -365
- package/ref-monty/crates/monty/src/builtins/print.rs +0 -141
- package/ref-monty/crates/monty/src/builtins/repr.rs +0 -16
- package/ref-monty/crates/monty/src/builtins/reversed.rs +0 -28
- package/ref-monty/crates/monty/src/builtins/round.rs +0 -174
- package/ref-monty/crates/monty/src/builtins/sorted.rs +0 -151
- package/ref-monty/crates/monty/src/builtins/sum.rs +0 -66
- package/ref-monty/crates/monty/src/builtins/type_.rs +0 -16
- package/ref-monty/crates/monty/src/builtins/zip.rs +0 -77
- package/ref-monty/crates/monty/src/bytecode/builder.rs +0 -699
- package/ref-monty/crates/monty/src/bytecode/code.rs +0 -310
- package/ref-monty/crates/monty/src/bytecode/compiler.rs +0 -3206
- package/ref-monty/crates/monty/src/bytecode/mod.rs +0 -24
- package/ref-monty/crates/monty/src/bytecode/op.rs +0 -617
- package/ref-monty/crates/monty/src/bytecode/vm/async_exec.rs +0 -1058
- package/ref-monty/crates/monty/src/bytecode/vm/attr.rs +0 -63
- package/ref-monty/crates/monty/src/bytecode/vm/binary.rs +0 -487
- package/ref-monty/crates/monty/src/bytecode/vm/call.rs +0 -767
- package/ref-monty/crates/monty/src/bytecode/vm/collections.rs +0 -741
- package/ref-monty/crates/monty/src/bytecode/vm/compare.rs +0 -147
- package/ref-monty/crates/monty/src/bytecode/vm/exceptions.rs +0 -297
- package/ref-monty/crates/monty/src/bytecode/vm/format.rs +0 -132
- package/ref-monty/crates/monty/src/bytecode/vm/mod.rs +0 -1958
- package/ref-monty/crates/monty/src/bytecode/vm/scheduler.rs +0 -620
- package/ref-monty/crates/monty/src/exception_private.rs +0 -1513
- package/ref-monty/crates/monty/src/exception_public.rs +0 -346
- package/ref-monty/crates/monty/src/expressions.rs +0 -694
- package/ref-monty/crates/monty/src/fstring.rs +0 -854
- package/ref-monty/crates/monty/src/function.rs +0 -119
- package/ref-monty/crates/monty/src/heap.rs +0 -1073
- package/ref-monty/crates/monty/src/heap_data.rs +0 -985
- package/ref-monty/crates/monty/src/heap_traits.rs +0 -312
- package/ref-monty/crates/monty/src/intern.rs +0 -837
- package/ref-monty/crates/monty/src/io.rs +0 -106
- package/ref-monty/crates/monty/src/lib.rs +0 -52
- package/ref-monty/crates/monty/src/modules/asyncio.rs +0 -144
- package/ref-monty/crates/monty/src/modules/math.rs +0 -1453
- package/ref-monty/crates/monty/src/modules/mod.rs +0 -120
- package/ref-monty/crates/monty/src/modules/os.rs +0 -116
- package/ref-monty/crates/monty/src/modules/pathlib.rs +0 -33
- package/ref-monty/crates/monty/src/modules/re.rs +0 -606
- package/ref-monty/crates/monty/src/modules/sys.rs +0 -60
- package/ref-monty/crates/monty/src/modules/typing.rs +0 -70
- package/ref-monty/crates/monty/src/namespace.rs +0 -21
- package/ref-monty/crates/monty/src/object.rs +0 -1040
- package/ref-monty/crates/monty/src/os.rs +0 -215
- package/ref-monty/crates/monty/src/parse.rs +0 -1730
- package/ref-monty/crates/monty/src/prepare.rs +0 -3015
- package/ref-monty/crates/monty/src/repl.rs +0 -1109
- package/ref-monty/crates/monty/src/resource.rs +0 -559
- package/ref-monty/crates/monty/src/run.rs +0 -457
- package/ref-monty/crates/monty/src/run_progress.rs +0 -821
- package/ref-monty/crates/monty/src/signature.rs +0 -651
- package/ref-monty/crates/monty/src/sorting.rs +0 -100
- package/ref-monty/crates/monty/src/types/bytes.rs +0 -2356
- package/ref-monty/crates/monty/src/types/dataclass.rs +0 -345
- package/ref-monty/crates/monty/src/types/dict.rs +0 -879
- package/ref-monty/crates/monty/src/types/dict_view.rs +0 -619
- package/ref-monty/crates/monty/src/types/iter.rs +0 -799
- package/ref-monty/crates/monty/src/types/list.rs +0 -929
- package/ref-monty/crates/monty/src/types/long_int.rs +0 -211
- package/ref-monty/crates/monty/src/types/mod.rs +0 -48
- package/ref-monty/crates/monty/src/types/module.rs +0 -146
- package/ref-monty/crates/monty/src/types/namedtuple.rs +0 -261
- package/ref-monty/crates/monty/src/types/path.rs +0 -596
- package/ref-monty/crates/monty/src/types/property.rs +0 -35
- package/ref-monty/crates/monty/src/types/py_trait.rs +0 -322
- package/ref-monty/crates/monty/src/types/range.rs +0 -285
- package/ref-monty/crates/monty/src/types/re_match.rs +0 -522
- package/ref-monty/crates/monty/src/types/re_pattern.rs +0 -726
- package/ref-monty/crates/monty/src/types/set.rs +0 -1373
- package/ref-monty/crates/monty/src/types/slice.rs +0 -257
- package/ref-monty/crates/monty/src/types/str.rs +0 -2051
- package/ref-monty/crates/monty/src/types/tuple.rs +0 -376
- package/ref-monty/crates/monty/src/types/type.rs +0 -407
- package/ref-monty/crates/monty/src/value.rs +0 -2558
- package/ref-monty/crates/monty/test_cases/args__dict_get_no_args.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__dict_get_too_many.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__dict_items_with_args.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__dict_keys_with_args.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__dict_pop_no_args.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__dict_pop_too_many.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__dict_values_with_args.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__id_too_many.py +0 -2
- package/ref-monty/crates/monty/test_cases/args__len_no_args.py +0 -2
- package/ref-monty/crates/monty/test_cases/args__len_too_many.py +0 -2
- package/ref-monty/crates/monty/test_cases/args__len_type_error_int.py +0 -9
- package/ref-monty/crates/monty/test_cases/args__len_type_error_none.py +0 -9
- package/ref-monty/crates/monty/test_cases/args__list_append_no_args.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__list_append_too_many.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__list_insert_too_few.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__list_insert_too_many.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__repr_no_args.py +0 -2
- package/ref-monty/crates/monty/test_cases/arith__div_zero_float.py +0 -2
- package/ref-monty/crates/monty/test_cases/arith__div_zero_int.py +0 -2
- package/ref-monty/crates/monty/test_cases/arith__floordiv_zero_float.py +0 -2
- package/ref-monty/crates/monty/test_cases/arith__floordiv_zero_int.py +0 -2
- package/ref-monty/crates/monty/test_cases/arith__pow_zero_neg.py +0 -2
- package/ref-monty/crates/monty/test_cases/arith__pow_zero_neg_builtin.py +0 -9
- package/ref-monty/crates/monty/test_cases/assert__expr_fail.py +0 -2
- package/ref-monty/crates/monty/test_cases/assert__fail.py +0 -2
- package/ref-monty/crates/monty/test_cases/assert__fail_msg.py +0 -2
- package/ref-monty/crates/monty/test_cases/assert__fn_fail.py +0 -3
- package/ref-monty/crates/monty/test_cases/assert__ops.py +0 -11
- package/ref-monty/crates/monty/test_cases/async__asyncio_run.py +0 -47
- package/ref-monty/crates/monty/test_cases/async__basic.py +0 -10
- package/ref-monty/crates/monty/test_cases/async__closure.py +0 -14
- package/ref-monty/crates/monty/test_cases/async__double_await_coroutine.py +0 -16
- package/ref-monty/crates/monty/test_cases/async__exception.py +0 -10
- package/ref-monty/crates/monty/test_cases/async__ext_call.py +0 -73
- package/ref-monty/crates/monty/test_cases/async__gather_all.py +0 -85
- package/ref-monty/crates/monty/test_cases/async__nested_await.py +0 -15
- package/ref-monty/crates/monty/test_cases/async__nested_gather_ext.py +0 -37
- package/ref-monty/crates/monty/test_cases/async__not_awaitable.py +0 -10
- package/ref-monty/crates/monty/test_cases/async__not_imported.py +0 -14
- package/ref-monty/crates/monty/test_cases/async__recursion_depth_isolation.py +0 -27
- package/ref-monty/crates/monty/test_cases/async__return_types.py +0 -31
- package/ref-monty/crates/monty/test_cases/async__sequential.py +0 -16
- package/ref-monty/crates/monty/test_cases/async__traceback.py +0 -19
- package/ref-monty/crates/monty/test_cases/async__with_args.py +0 -14
- package/ref-monty/crates/monty/test_cases/attr__get_int_error.py +0 -9
- package/ref-monty/crates/monty/test_cases/attr__get_list_error.py +0 -9
- package/ref-monty/crates/monty/test_cases/attr__set_frozen_nonfield.py +0 -12
- package/ref-monty/crates/monty/test_cases/attr__set_int_error.py +0 -10
- package/ref-monty/crates/monty/test_cases/attr__set_list_error.py +0 -10
- package/ref-monty/crates/monty/test_cases/bench__kitchen_sink.py +0 -68
- package/ref-monty/crates/monty/test_cases/bool__ops.py +0 -20
- package/ref-monty/crates/monty/test_cases/builtin__add_type_error.py +0 -2
- package/ref-monty/crates/monty/test_cases/builtin__filter.py +0 -62
- package/ref-monty/crates/monty/test_cases/builtin__filter_not_iterable.py +0 -11
- package/ref-monty/crates/monty/test_cases/builtin__getattr.py +0 -84
- package/ref-monty/crates/monty/test_cases/builtin__iter_funcs.py +0 -42
- package/ref-monty/crates/monty/test_cases/builtin__iter_next.py +0 -66
- package/ref-monty/crates/monty/test_cases/builtin__map.py +0 -74
- package/ref-monty/crates/monty/test_cases/builtin__map_not_iterable.py +0 -11
- package/ref-monty/crates/monty/test_cases/builtin__math_funcs.py +0 -154
- package/ref-monty/crates/monty/test_cases/builtin__more_iter_funcs.py +0 -148
- package/ref-monty/crates/monty/test_cases/builtin__next_stop_iteration.py +0 -10
- package/ref-monty/crates/monty/test_cases/builtin__print_invalid_kwarg.py +0 -9
- package/ref-monty/crates/monty/test_cases/builtin__print_kwargs.py +0 -12
- package/ref-monty/crates/monty/test_cases/builtin__repr.py +0 -3
- package/ref-monty/crates/monty/test_cases/builtin__string_funcs.py +0 -73
- package/ref-monty/crates/monty/test_cases/bytes__decode_invalid_utf8.py +0 -18
- package/ref-monty/crates/monty/test_cases/bytes__endswith_str_error.py +0 -10
- package/ref-monty/crates/monty/test_cases/bytes__getitem_index_error.py +0 -10
- package/ref-monty/crates/monty/test_cases/bytes__index_start_gt_end.py +0 -10
- package/ref-monty/crates/monty/test_cases/bytes__methods.py +0 -394
- package/ref-monty/crates/monty/test_cases/bytes__negative_count.py +0 -9
- package/ref-monty/crates/monty/test_cases/bytes__ops.py +0 -90
- package/ref-monty/crates/monty/test_cases/bytes__startswith_str_error.py +0 -10
- package/ref-monty/crates/monty/test_cases/call_object.py +0 -3
- package/ref-monty/crates/monty/test_cases/chain_comparison__all.py +0 -79
- package/ref-monty/crates/monty/test_cases/closure__param_shadows_outer.py +0 -81
- package/ref-monty/crates/monty/test_cases/closure__pep448.py +0 -203
- package/ref-monty/crates/monty/test_cases/closure__undefined_nonlocal.py +0 -13
- package/ref-monty/crates/monty/test_cases/compare__mixed_types.py +0 -120
- package/ref-monty/crates/monty/test_cases/comprehension__all.py +0 -208
- package/ref-monty/crates/monty/test_cases/comprehension__scope.py +0 -7
- package/ref-monty/crates/monty/test_cases/comprehension__unbound_local.py +0 -14
- package/ref-monty/crates/monty/test_cases/dataclass__basic.py +0 -238
- package/ref-monty/crates/monty/test_cases/dataclass__call_field_error.py +0 -12
- package/ref-monty/crates/monty/test_cases/dataclass__frozen_set_error.py +0 -12
- package/ref-monty/crates/monty/test_cases/dataclass__get_missing_attr_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/dict__get_unhashable_key.py +0 -3
- package/ref-monty/crates/monty/test_cases/dict__literal_unhashable_key.py +0 -2
- package/ref-monty/crates/monty/test_cases/dict__method_pop_missing_error.py +0 -3
- package/ref-monty/crates/monty/test_cases/dict__methods.py +0 -151
- package/ref-monty/crates/monty/test_cases/dict__ops.py +0 -133
- package/ref-monty/crates/monty/test_cases/dict__pop_unhashable_key.py +0 -4
- package/ref-monty/crates/monty/test_cases/dict__popitem_empty.py +0 -9
- package/ref-monty/crates/monty/test_cases/dict__subscript_missing_key.py +0 -3
- package/ref-monty/crates/monty/test_cases/dict__unhashable_dict_key.py +0 -2
- package/ref-monty/crates/monty/test_cases/dict__unhashable_list_key.py +0 -2
- package/ref-monty/crates/monty/test_cases/dict__unpack_type_error.py +0 -2
- package/ref-monty/crates/monty/test_cases/dict__views.py +0 -165
- package/ref-monty/crates/monty/test_cases/edge__all.py +0 -26
- package/ref-monty/crates/monty/test_cases/edge__float_int_mod.py +0 -2
- package/ref-monty/crates/monty/test_cases/edge__int_float_mod.py +0 -2
- package/ref-monty/crates/monty/test_cases/exc__args.py +0 -16
- package/ref-monty/crates/monty/test_cases/exc__str.py +0 -15
- package/ref-monty/crates/monty/test_cases/execute_ok__all.py +0 -54
- package/ref-monty/crates/monty/test_cases/execute_raise__error_instance_str.py +0 -2
- package/ref-monty/crates/monty/test_cases/execute_raise__error_no_args.py +0 -2
- package/ref-monty/crates/monty/test_cases/execute_raise__error_string_arg.py +0 -2
- package/ref-monty/crates/monty/test_cases/execute_raise__error_string_arg_quotes.py +0 -2
- package/ref-monty/crates/monty/test_cases/execute_raise__error_type.py +0 -2
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_instance_via_var.py +0 -4
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_list.py +0 -2
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_number.py +0 -2
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_type_call_via_var.py +0 -4
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_type_direct.py +0 -3
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_type_via_var.py +0 -4
- package/ref-monty/crates/monty/test_cases/ext_call__arg_side_effect_bug.py +0 -22
- package/ref-monty/crates/monty/test_cases/ext_call__augmented.py +0 -17
- package/ref-monty/crates/monty/test_cases/ext_call__augmented_refcount_bug.py +0 -7
- package/ref-monty/crates/monty/test_cases/ext_call__bare_raise_after_resume.py +0 -34
- package/ref-monty/crates/monty/test_cases/ext_call__basic.py +0 -99
- package/ref-monty/crates/monty/test_cases/ext_call__boolean.py +0 -37
- package/ref-monty/crates/monty/test_cases/ext_call__boolean_side_effect_hang.py +0 -17
- package/ref-monty/crates/monty/test_cases/ext_call__closure_bug.py +0 -16
- package/ref-monty/crates/monty/test_cases/ext_call__comparison.py +0 -26
- package/ref-monty/crates/monty/test_cases/ext_call__deep_call_stack.py +0 -18
- package/ref-monty/crates/monty/test_cases/ext_call__elif.py +0 -171
- package/ref-monty/crates/monty/test_cases/ext_call__exc.py +0 -4
- package/ref-monty/crates/monty/test_cases/ext_call__exc_deep_stack.py +0 -39
- package/ref-monty/crates/monty/test_cases/ext_call__exc_in_function.py +0 -17
- package/ref-monty/crates/monty/test_cases/ext_call__exc_nested_functions.py +0 -31
- package/ref-monty/crates/monty/test_cases/ext_call__ext_exc.py +0 -171
- package/ref-monty/crates/monty/test_cases/ext_call__for.py +0 -114
- package/ref-monty/crates/monty/test_cases/ext_call__fstring.py +0 -12
- package/ref-monty/crates/monty/test_cases/ext_call__if.py +0 -135
- package/ref-monty/crates/monty/test_cases/ext_call__if_condition.py +0 -37
- package/ref-monty/crates/monty/test_cases/ext_call__in_closure.py +0 -14
- package/ref-monty/crates/monty/test_cases/ext_call__in_function.py +0 -40
- package/ref-monty/crates/monty/test_cases/ext_call__in_function_simple.py +0 -7
- package/ref-monty/crates/monty/test_cases/ext_call__literals.py +0 -17
- package/ref-monty/crates/monty/test_cases/ext_call__multi_in_func.py +0 -32
- package/ref-monty/crates/monty/test_cases/ext_call__name_lookup.py +0 -69
- package/ref-monty/crates/monty/test_cases/ext_call__name_lookup_undefined.py +0 -4
- package/ref-monty/crates/monty/test_cases/ext_call__nested_calls.py +0 -14
- package/ref-monty/crates/monty/test_cases/ext_call__recursion_bug.py +0 -19
- package/ref-monty/crates/monty/test_cases/ext_call__return.py +0 -28
- package/ref-monty/crates/monty/test_cases/ext_call__side_effects.py +0 -25
- package/ref-monty/crates/monty/test_cases/ext_call__subscript.py +0 -7
- package/ref-monty/crates/monty/test_cases/ext_call__ternary.py +0 -28
- package/ref-monty/crates/monty/test_cases/ext_call__try.py +0 -280
- package/ref-monty/crates/monty/test_cases/ext_call__try_simple.py +0 -10
- package/ref-monty/crates/monty/test_cases/ext_call__unary.py +0 -13
- package/ref-monty/crates/monty/test_cases/frozenset__ops.py +0 -178
- package/ref-monty/crates/monty/test_cases/fstring__all.py +0 -236
- package/ref-monty/crates/monty/test_cases/fstring__error_eq_align_on_str.py +0 -3
- package/ref-monty/crates/monty/test_cases/fstring__error_float_f_on_str.py +0 -3
- package/ref-monty/crates/monty/test_cases/fstring__error_int_d_on_float.py +0 -3
- package/ref-monty/crates/monty/test_cases/fstring__error_int_d_on_str.py +0 -3
- package/ref-monty/crates/monty/test_cases/fstring__error_invalid_spec.py +0 -4
- package/ref-monty/crates/monty/test_cases/fstring__error_invalid_spec_dynamic.py +0 -4
- package/ref-monty/crates/monty/test_cases/fstring__error_invalid_spec_str.py +0 -4
- package/ref-monty/crates/monty/test_cases/fstring__error_str_s_on_int.py +0 -3
- package/ref-monty/crates/monty/test_cases/function__call_duplicate_kwargs.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__call_unpack.py +0 -42
- package/ref-monty/crates/monty/test_cases/function__defaults.py +0 -117
- package/ref-monty/crates/monty/test_cases/function__err_duplicate_arg.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_duplicate_first_arg.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_duplicate_kwarg_cleanup.py +0 -9
- package/ref-monty/crates/monty/test_cases/function__err_kwonly_as_positional.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_missing_all_posonly.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_missing_heap_cleanup.py +0 -9
- package/ref-monty/crates/monty/test_cases/function__err_missing_kwonly.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_missing_posonly_with_kwarg.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_missing_with_posonly.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_posonly_as_kwarg.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_posonly_first_as_kwarg.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_too_many_posonly.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_too_many_with_kwonly.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg_cleanup.py +0 -9
- package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg_quote.py +0 -13
- package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg_simple.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_unpack_duplicate_arg.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__err_unpack_duplicate_heap.py +0 -8
- package/ref-monty/crates/monty/test_cases/function__err_unpack_int.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__err_unpack_nonstring_key.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__err_unpack_not_mapping.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__kwargs_unpacking.py +0 -173
- package/ref-monty/crates/monty/test_cases/function__ops.py +0 -294
- package/ref-monty/crates/monty/test_cases/function__return_none.py +0 -42
- package/ref-monty/crates/monty/test_cases/function__signatures.py +0 -47
- package/ref-monty/crates/monty/test_cases/function__too_few_args_all.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__too_few_args_one.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__too_few_args_two.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__too_many_args_one.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__too_many_args_two.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__too_many_args_zero.py +0 -6
- package/ref-monty/crates/monty/test_cases/global__error_assigned_before.py +0 -7
- package/ref-monty/crates/monty/test_cases/global__ops.py +0 -163
- package/ref-monty/crates/monty/test_cases/hash__dict_unhashable.py +0 -2
- package/ref-monty/crates/monty/test_cases/hash__list_unhashable.py +0 -2
- package/ref-monty/crates/monty/test_cases/hash__ops.py +0 -153
- package/ref-monty/crates/monty/test_cases/id__bytes_literals_distinct.py +0 -3
- package/ref-monty/crates/monty/test_cases/id__int_copy_distinct.py +0 -5
- package/ref-monty/crates/monty/test_cases/id__is_number_is_number.py +0 -3
- package/ref-monty/crates/monty/test_cases/id__non_overlapping_lifetimes_distinct_types.py +0 -10
- package/ref-monty/crates/monty/test_cases/id__non_overlapping_lifetimes_same_types.py +0 -6
- package/ref-monty/crates/monty/test_cases/id__ops.py +0 -97
- package/ref-monty/crates/monty/test_cases/id__str_literals_same.py +0 -3
- package/ref-monty/crates/monty/test_cases/if__elif_else.py +0 -207
- package/ref-monty/crates/monty/test_cases/if__raise_elif.py +0 -11
- package/ref-monty/crates/monty/test_cases/if__raise_else.py +0 -13
- package/ref-monty/crates/monty/test_cases/if__raise_if.py +0 -9
- package/ref-monty/crates/monty/test_cases/if__raise_in_elif_condition.py +0 -18
- package/ref-monty/crates/monty/test_cases/if__raise_in_if_condition.py +0 -16
- package/ref-monty/crates/monty/test_cases/if_else_expr__all.py +0 -55
- package/ref-monty/crates/monty/test_cases/import__error_cannot_import.py +0 -9
- package/ref-monty/crates/monty/test_cases/import__error_module_not_found.py +0 -9
- package/ref-monty/crates/monty/test_cases/import__local_scope.py +0 -68
- package/ref-monty/crates/monty/test_cases/import__os.py +0 -25
- package/ref-monty/crates/monty/test_cases/import__relative_error.py +0 -9
- package/ref-monty/crates/monty/test_cases/import__relative_no_module_error.py +0 -9
- package/ref-monty/crates/monty/test_cases/import__runtime_error_when_executed.py +0 -14
- package/ref-monty/crates/monty/test_cases/import__star_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/import__sys.py +0 -47
- package/ref-monty/crates/monty/test_cases/import__sys_monty.py +0 -28
- package/ref-monty/crates/monty/test_cases/import__type_checking_guard.py +0 -37
- package/ref-monty/crates/monty/test_cases/import__typing.py +0 -25
- package/ref-monty/crates/monty/test_cases/import__typing_type_ignore.py +0 -4
- package/ref-monty/crates/monty/test_cases/int__bigint.py +0 -467
- package/ref-monty/crates/monty/test_cases/int__bigint_errors.py +0 -260
- package/ref-monty/crates/monty/test_cases/int__ops.py +0 -219
- package/ref-monty/crates/monty/test_cases/int__overflow_division.py +0 -84
- package/ref-monty/crates/monty/test_cases/is_variant__all.py +0 -36
- package/ref-monty/crates/monty/test_cases/isinstance__arg2_list_error.py +0 -2
- package/ref-monty/crates/monty/test_cases/isinstance__arg2_type_error.py +0 -2
- package/ref-monty/crates/monty/test_cases/iter__dict_mutation.py +0 -4
- package/ref-monty/crates/monty/test_cases/iter__for.py +0 -243
- package/ref-monty/crates/monty/test_cases/iter__for_loop_unpacking.py +0 -66
- package/ref-monty/crates/monty/test_cases/iter__generator_expr.py +0 -20
- package/ref-monty/crates/monty/test_cases/iter__generator_expr_type.py +0 -7
- package/ref-monty/crates/monty/test_cases/iter__not_iterable.py +0 -3
- package/ref-monty/crates/monty/test_cases/lambda__all.py +0 -145
- package/ref-monty/crates/monty/test_cases/list__extend_not_iterable.py +0 -7
- package/ref-monty/crates/monty/test_cases/list__getitem_out_of_bounds.py +0 -3
- package/ref-monty/crates/monty/test_cases/list__index_not_found.py +0 -9
- package/ref-monty/crates/monty/test_cases/list__index_start_gt_end.py +0 -10
- package/ref-monty/crates/monty/test_cases/list__ops.py +0 -473
- package/ref-monty/crates/monty/test_cases/list__pop_empty.py +0 -9
- package/ref-monty/crates/monty/test_cases/list__pop_out_of_range.py +0 -9
- package/ref-monty/crates/monty/test_cases/list__pop_type_error.py +0 -9
- package/ref-monty/crates/monty/test_cases/list__remove_not_found.py +0 -9
- package/ref-monty/crates/monty/test_cases/list__setitem_dict_index.py +0 -13
- package/ref-monty/crates/monty/test_cases/list__setitem_huge_int_index.py +0 -13
- package/ref-monty/crates/monty/test_cases/list__setitem_index_error.py +0 -10
- package/ref-monty/crates/monty/test_cases/list__setitem_type_error.py +0 -10
- package/ref-monty/crates/monty/test_cases/list__unpack_type_error.py +0 -2
- package/ref-monty/crates/monty/test_cases/longint__index_error.py +0 -3
- package/ref-monty/crates/monty/test_cases/longint__repeat_error.py +0 -3
- package/ref-monty/crates/monty/test_cases/loop__break_continue.py +0 -113
- package/ref-monty/crates/monty/test_cases/loop__break_finally.py +0 -69
- package/ref-monty/crates/monty/test_cases/loop__break_in_function_error.py +0 -13
- package/ref-monty/crates/monty/test_cases/loop__break_in_if_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/loop__break_nested_except_clears.py +0 -55
- package/ref-monty/crates/monty/test_cases/loop__break_outside_error.py +0 -9
- package/ref-monty/crates/monty/test_cases/loop__continue_finally.py +0 -81
- package/ref-monty/crates/monty/test_cases/loop__continue_in_function_error.py +0 -13
- package/ref-monty/crates/monty/test_cases/loop__continue_in_if_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/loop__continue_nested_except_clears.py +0 -60
- package/ref-monty/crates/monty/test_cases/loop__continue_outside_error.py +0 -9
- package/ref-monty/crates/monty/test_cases/math__acos_domain_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__acosh_domain_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__asin_domain_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__atanh_domain_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__cos_inf_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__cosh_overflow_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__exp_overflow_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__factorial_float_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__factorial_negative_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__floor_inf_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__floor_nan_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__floor_str_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__fmod_inf_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__gamma_neg_int_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__gcd_float_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__isqrt_negative_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__ldexp_overflow_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__log1p_domain_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__log_base1_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__log_zero_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__module.py +0 -1432
- package/ref-monty/crates/monty/test_cases/math__pow_domain_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__sin_inf_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__sqrt_negative_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__tan_inf_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__trunc_str_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/method__args_kwargs_unpacking.py +0 -259
- package/ref-monty/crates/monty/test_cases/name_error__unbound_local_func.py +0 -19
- package/ref-monty/crates/monty/test_cases/name_error__unbound_local_module.py +0 -12
- package/ref-monty/crates/monty/test_cases/name_error__undefined_call_chained.py +0 -9
- package/ref-monty/crates/monty/test_cases/name_error__undefined_call_in_expr.py +0 -9
- package/ref-monty/crates/monty/test_cases/name_error__undefined_call_in_function.py +0 -16
- package/ref-monty/crates/monty/test_cases/name_error__undefined_call_with_args.py +0 -9
- package/ref-monty/crates/monty/test_cases/name_error__undefined_global.py +0 -10
- package/ref-monty/crates/monty/test_cases/namedtuple__missing_attr.py +0 -11
- package/ref-monty/crates/monty/test_cases/namedtuple__ops.py +0 -34
- package/ref-monty/crates/monty/test_cases/nonlocal__error_module_level.py +0 -3
- package/ref-monty/crates/monty/test_cases/nonlocal__ops.py +0 -353
- package/ref-monty/crates/monty/test_cases/os__environ.py +0 -40
- package/ref-monty/crates/monty/test_cases/os__getenv_key_list_error.py +0 -5
- package/ref-monty/crates/monty/test_cases/os__getenv_key_type_error.py +0 -5
- package/ref-monty/crates/monty/test_cases/parse_error__complex.py +0 -3
- package/ref-monty/crates/monty/test_cases/pathlib__import.py +0 -11
- package/ref-monty/crates/monty/test_cases/pathlib__os.py +0 -136
- package/ref-monty/crates/monty/test_cases/pathlib__os_read_error.py +0 -12
- package/ref-monty/crates/monty/test_cases/pathlib__pure.py +0 -81
- package/ref-monty/crates/monty/test_cases/pyobject__cycle_dict_self.py +0 -5
- package/ref-monty/crates/monty/test_cases/pyobject__cycle_list_dict.py +0 -6
- package/ref-monty/crates/monty/test_cases/pyobject__cycle_list_self.py +0 -5
- package/ref-monty/crates/monty/test_cases/pyobject__cycle_multiple_refs.py +0 -6
- package/ref-monty/crates/monty/test_cases/range__error_no_args.py +0 -2
- package/ref-monty/crates/monty/test_cases/range__error_step_zero.py +0 -2
- package/ref-monty/crates/monty/test_cases/range__error_too_many_args.py +0 -2
- package/ref-monty/crates/monty/test_cases/range__getitem_index_error.py +0 -10
- package/ref-monty/crates/monty/test_cases/range__ops.py +0 -236
- package/ref-monty/crates/monty/test_cases/re__basic.py +0 -756
- package/ref-monty/crates/monty/test_cases/re__grouping.py +0 -241
- package/ref-monty/crates/monty/test_cases/re__match.py +0 -148
- package/ref-monty/crates/monty/test_cases/recursion__deep_drop.py +0 -26
- package/ref-monty/crates/monty/test_cases/recursion__deep_eq.py +0 -23
- package/ref-monty/crates/monty/test_cases/recursion__deep_hash.py +0 -46
- package/ref-monty/crates/monty/test_cases/recursion__deep_repr.py +0 -12
- package/ref-monty/crates/monty/test_cases/recursion__function_depth.py +0 -13
- package/ref-monty/crates/monty/test_cases/refcount__cycle_mutual_reference.py +0 -18
- package/ref-monty/crates/monty/test_cases/refcount__cycle_self_reference.py +0 -12
- package/ref-monty/crates/monty/test_cases/refcount__dict_basic.py +0 -5
- package/ref-monty/crates/monty/test_cases/refcount__dict_get.py +0 -5
- package/ref-monty/crates/monty/test_cases/refcount__dict_keys_and.py +0 -14
- package/ref-monty/crates/monty/test_cases/refcount__dict_overwrite.py +0 -6
- package/ref-monty/crates/monty/test_cases/refcount__gather_cleanup.py +0 -16
- package/ref-monty/crates/monty/test_cases/refcount__gather_exception.py +0 -18
- package/ref-monty/crates/monty/test_cases/refcount__gather_nested_cancel.py +0 -25
- package/ref-monty/crates/monty/test_cases/refcount__immediate_skipped.py +0 -4
- package/ref-monty/crates/monty/test_cases/refcount__kwargs_unpacking.py +0 -27
- package/ref-monty/crates/monty/test_cases/refcount__list_append_multiple.py +0 -6
- package/ref-monty/crates/monty/test_cases/refcount__list_append_ref.py +0 -5
- package/ref-monty/crates/monty/test_cases/refcount__list_concat.py +0 -5
- package/ref-monty/crates/monty/test_cases/refcount__list_getitem.py +0 -5
- package/ref-monty/crates/monty/test_cases/refcount__list_iadd.py +0 -5
- package/ref-monty/crates/monty/test_cases/refcount__nested_list.py +0 -4
- package/ref-monty/crates/monty/test_cases/refcount__re_pattern_sub_error_paths.py +0 -37
- package/ref-monty/crates/monty/test_cases/refcount__re_search_match.py +0 -34
- package/ref-monty/crates/monty/test_cases/refcount__re_sub_error_paths.py +0 -31
- package/ref-monty/crates/monty/test_cases/refcount__shared_reference.py +0 -4
- package/ref-monty/crates/monty/test_cases/refcount__single_list.py +0 -3
- package/ref-monty/crates/monty/test_cases/repr__cycle_detection.py +0 -24
- package/ref-monty/crates/monty/test_cases/set__ops.py +0 -191
- package/ref-monty/crates/monty/test_cases/set__review_bugs.py +0 -35
- package/ref-monty/crates/monty/test_cases/set__unpack_type_error.py +0 -2
- package/ref-monty/crates/monty/test_cases/slice__invalid_indices.py +0 -2
- package/ref-monty/crates/monty/test_cases/slice__kwargs.py +0 -9
- package/ref-monty/crates/monty/test_cases/slice__no_args.py +0 -9
- package/ref-monty/crates/monty/test_cases/slice__ops.py +0 -149
- package/ref-monty/crates/monty/test_cases/slice__step_zero.py +0 -9
- package/ref-monty/crates/monty/test_cases/slice__step_zero_bytes.py +0 -9
- package/ref-monty/crates/monty/test_cases/slice__step_zero_range.py +0 -9
- package/ref-monty/crates/monty/test_cases/slice__step_zero_str.py +0 -9
- package/ref-monty/crates/monty/test_cases/slice__step_zero_tuple.py +0 -9
- package/ref-monty/crates/monty/test_cases/slice__too_many_args.py +0 -9
- package/ref-monty/crates/monty/test_cases/str__getitem_index_error.py +0 -10
- package/ref-monty/crates/monty/test_cases/str__index_not_found.py +0 -9
- package/ref-monty/crates/monty/test_cases/str__join_no_args.py +0 -9
- package/ref-monty/crates/monty/test_cases/str__join_non_string.py +0 -9
- package/ref-monty/crates/monty/test_cases/str__join_not_iterable.py +0 -9
- package/ref-monty/crates/monty/test_cases/str__join_too_many_args.py +0 -9
- package/ref-monty/crates/monty/test_cases/str__methods.py +0 -327
- package/ref-monty/crates/monty/test_cases/str__ops.py +0 -162
- package/ref-monty/crates/monty/test_cases/str__partition_empty.py +0 -9
- package/ref-monty/crates/monty/test_cases/str__rsplit_empty_sep.py +0 -9
- package/ref-monty/crates/monty/test_cases/str__split_empty_sep.py +0 -9
- package/ref-monty/crates/monty/test_cases/sys__types.py +0 -7
- package/ref-monty/crates/monty/test_cases/traceback__division_error.py +0 -30
- package/ref-monty/crates/monty/test_cases/traceback__index_error.py +0 -17
- package/ref-monty/crates/monty/test_cases/traceback__insert_as_int.py +0 -10
- package/ref-monty/crates/monty/test_cases/traceback__nested_call.py +0 -29
- package/ref-monty/crates/monty/test_cases/traceback__nonlocal_module_scope.py +0 -10
- package/ref-monty/crates/monty/test_cases/traceback__nonlocal_unbound.py +0 -24
- package/ref-monty/crates/monty/test_cases/traceback__range_as_int.py +0 -9
- package/ref-monty/crates/monty/test_cases/traceback__recursion_error.py +0 -23
- package/ref-monty/crates/monty/test_cases/traceback__set_mutation.py +0 -11
- package/ref-monty/crates/monty/test_cases/traceback__undefined_attr_call.py +0 -16
- package/ref-monty/crates/monty/test_cases/traceback__undefined_call.py +0 -16
- package/ref-monty/crates/monty/test_cases/traceback__undefined_raise.py +0 -16
- package/ref-monty/crates/monty/test_cases/try_except__all.py +0 -472
- package/ref-monty/crates/monty/test_cases/try_except__bare_raise_no_context.py +0 -2
- package/ref-monty/crates/monty/test_cases/try_except__invalid_type.py +0 -5
- package/ref-monty/crates/monty/test_cases/tuple__getitem_out_of_bounds.py +0 -3
- package/ref-monty/crates/monty/test_cases/tuple__index_not_found.py +0 -9
- package/ref-monty/crates/monty/test_cases/tuple__index_start_gt_end.py +0 -10
- package/ref-monty/crates/monty/test_cases/tuple__methods.py +0 -19
- package/ref-monty/crates/monty/test_cases/tuple__ops.py +0 -133
- package/ref-monty/crates/monty/test_cases/tuple__unpack_type_error.py +0 -2
- package/ref-monty/crates/monty/test_cases/type__builtin_attr_error.py +0 -9
- package/ref-monty/crates/monty/test_cases/type__bytes_negative.py +0 -2
- package/ref-monty/crates/monty/test_cases/type__cell_not_builtin.py +0 -9
- package/ref-monty/crates/monty/test_cases/type__exception_attr_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/type__float_conversion_error.py +0 -2
- package/ref-monty/crates/monty/test_cases/type__float_repr_both_quotes.py +0 -9
- package/ref-monty/crates/monty/test_cases/type__float_repr_newline.py +0 -9
- package/ref-monty/crates/monty/test_cases/type__float_repr_single_quote.py +0 -9
- package/ref-monty/crates/monty/test_cases/type__int_conversion_error.py +0 -2
- package/ref-monty/crates/monty/test_cases/type__list_not_iterable.py +0 -2
- package/ref-monty/crates/monty/test_cases/type__non_builtin_name_error.py +0 -9
- package/ref-monty/crates/monty/test_cases/type__ops.py +0 -200
- package/ref-monty/crates/monty/test_cases/type__shadow_exc.py +0 -3
- package/ref-monty/crates/monty/test_cases/type__shadow_int.py +0 -9
- package/ref-monty/crates/monty/test_cases/type__shadow_len.py +0 -3
- package/ref-monty/crates/monty/test_cases/type__tuple_not_iterable.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__int_add_list.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__int_div_str.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__int_floordiv_str.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__int_iadd_str.py +0 -3
- package/ref-monty/crates/monty/test_cases/type_error__int_mod_str.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__int_pow_str.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__int_sub_str.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__list_add_int.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__list_add_str.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__list_iadd_int.py +0 -6
- package/ref-monty/crates/monty/test_cases/type_error__str_add_int.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__str_iadd_int.py +0 -3
- package/ref-monty/crates/monty/test_cases/type_error__unary_invert_str.py +0 -3
- package/ref-monty/crates/monty/test_cases/type_error__unary_minus_str.py +0 -4
- package/ref-monty/crates/monty/test_cases/type_error__unary_neg_str.py +0 -3
- package/ref-monty/crates/monty/test_cases/type_error__unary_plus_str.py +0 -4
- package/ref-monty/crates/monty/test_cases/typing__types.py +0 -24
- package/ref-monty/crates/monty/test_cases/unpack__nested.py +0 -48
- package/ref-monty/crates/monty/test_cases/unpack__non_sequence.py +0 -9
- package/ref-monty/crates/monty/test_cases/unpack__not_enough.py +0 -9
- package/ref-monty/crates/monty/test_cases/unpack__ops.py +0 -153
- package/ref-monty/crates/monty/test_cases/unpack__star_not_enough.py +0 -9
- package/ref-monty/crates/monty/test_cases/unpack__too_many.py +0 -9
- package/ref-monty/crates/monty/test_cases/version__cpython.py +0 -4
- package/ref-monty/crates/monty/test_cases/walrus__all.py +0 -178
- package/ref-monty/crates/monty/test_cases/while__all.py +0 -206
- package/ref-monty/crates/monty/tests/asyncio.rs +0 -764
- package/ref-monty/crates/monty/tests/binary_serde.rs +0 -185
- package/ref-monty/crates/monty/tests/bytecode_limits.rs +0 -248
- package/ref-monty/crates/monty/tests/datatest_runner.rs +0 -2029
- package/ref-monty/crates/monty/tests/inputs.rs +0 -420
- package/ref-monty/crates/monty/tests/json_serde.rs +0 -250
- package/ref-monty/crates/monty/tests/main.rs +0 -71
- package/ref-monty/crates/monty/tests/math_module.rs +0 -114
- package/ref-monty/crates/monty/tests/name_lookup.rs +0 -482
- package/ref-monty/crates/monty/tests/os_tests.rs +0 -459
- package/ref-monty/crates/monty/tests/parse_errors.rs +0 -441
- package/ref-monty/crates/monty/tests/print_writer.rs +0 -238
- package/ref-monty/crates/monty/tests/py_object.rs +0 -121
- package/ref-monty/crates/monty/tests/regex.rs +0 -90
- package/ref-monty/crates/monty/tests/repl.rs +0 -344
- package/ref-monty/crates/monty/tests/resource_limits.rs +0 -1826
- package/ref-monty/crates/monty/tests/try_from.rs +0 -167
- package/ref-monty/crates/monty-cli/Cargo.toml +0 -25
- package/ref-monty/crates/monty-cli/src/main.rs +0 -541
- package/ref-monty/crates/monty-js/.cargo/config.toml +0 -2
- package/ref-monty/crates/monty-js/.prettierignore +0 -8
- package/ref-monty/crates/monty-js/Cargo.toml +0 -32
- package/ref-monty/crates/monty-js/README.md +0 -207
- package/ref-monty/crates/monty-js/__test__/async.spec.ts +0 -350
- package/ref-monty/crates/monty-js/__test__/basic.spec.ts +0 -114
- package/ref-monty/crates/monty-js/__test__/exceptions.spec.ts +0 -427
- package/ref-monty/crates/monty-js/__test__/external.spec.ts +0 -354
- package/ref-monty/crates/monty-js/__test__/inputs.spec.ts +0 -143
- package/ref-monty/crates/monty-js/__test__/limits.spec.ts +0 -162
- package/ref-monty/crates/monty-js/__test__/package.json +0 -3
- package/ref-monty/crates/monty-js/__test__/print.spec.ts +0 -229
- package/ref-monty/crates/monty-js/__test__/repl.spec.ts +0 -34
- package/ref-monty/crates/monty-js/__test__/serialize.spec.ts +0 -205
- package/ref-monty/crates/monty-js/__test__/start.spec.ts +0 -443
- package/ref-monty/crates/monty-js/__test__/type_check.spec.ts +0 -147
- package/ref-monty/crates/monty-js/__test__/types.spec.ts +0 -319
- package/ref-monty/crates/monty-js/build.rs +0 -61
- package/ref-monty/crates/monty-js/index-header.d.ts +0 -3
- package/ref-monty/crates/monty-js/package-lock.json +0 -4694
- package/ref-monty/crates/monty-js/package.json +0 -100
- package/ref-monty/crates/monty-js/scripts/smoke-test.sh +0 -69
- package/ref-monty/crates/monty-js/smoke-test/package.json +0 -17
- package/ref-monty/crates/monty-js/smoke-test/test.ts +0 -171
- package/ref-monty/crates/monty-js/smoke-test/tsconfig.json +0 -11
- package/ref-monty/crates/monty-js/src/convert.rs +0 -648
- package/ref-monty/crates/monty-js/src/exceptions.rs +0 -293
- package/ref-monty/crates/monty-js/src/lib.rs +0 -41
- package/ref-monty/crates/monty-js/src/limits.rs +0 -53
- package/ref-monty/crates/monty-js/src/monty_cls.rs +0 -1407
- package/ref-monty/crates/monty-js/tsconfig.json +0 -17
- package/ref-monty/crates/monty-js/wrapper.ts +0 -701
- package/ref-monty/crates/monty-python/Cargo.toml +0 -38
- package/ref-monty/crates/monty-python/README.md +0 -134
- package/ref-monty/crates/monty-python/build.rs +0 -4
- package/ref-monty/crates/monty-python/example.py +0 -40
- package/ref-monty/crates/monty-python/exercise.py +0 -46
- package/ref-monty/crates/monty-python/pyproject.toml +0 -57
- package/ref-monty/crates/monty-python/python/pydantic_monty/__init__.py +0 -281
- package/ref-monty/crates/monty-python/python/pydantic_monty/_monty.pyi +0 -677
- package/ref-monty/crates/monty-python/python/pydantic_monty/os_access.py +0 -933
- package/ref-monty/crates/monty-python/python/pydantic_monty/py.typed +0 -0
- package/ref-monty/crates/monty-python/src/convert.rs +0 -273
- package/ref-monty/crates/monty-python/src/dataclass.rs +0 -461
- package/ref-monty/crates/monty-python/src/exceptions.rs +0 -557
- package/ref-monty/crates/monty-python/src/external.rs +0 -165
- package/ref-monty/crates/monty-python/src/lib.rs +0 -77
- package/ref-monty/crates/monty-python/src/limits.rs +0 -142
- package/ref-monty/crates/monty-python/src/monty_cls.rs +0 -1650
- package/ref-monty/crates/monty-python/src/repl.rs +0 -470
- package/ref-monty/crates/monty-python/src/serialization.rs +0 -761
- package/ref-monty/crates/monty-python/tests/test_async.py +0 -1201
- package/ref-monty/crates/monty-python/tests/test_basic.py +0 -66
- package/ref-monty/crates/monty-python/tests/test_dataclasses.py +0 -971
- package/ref-monty/crates/monty-python/tests/test_exceptions.py +0 -361
- package/ref-monty/crates/monty-python/tests/test_external.py +0 -367
- package/ref-monty/crates/monty-python/tests/test_inputs.py +0 -126
- package/ref-monty/crates/monty-python/tests/test_limits.py +0 -257
- package/ref-monty/crates/monty-python/tests/test_os_access.py +0 -1286
- package/ref-monty/crates/monty-python/tests/test_os_access_compat.py +0 -731
- package/ref-monty/crates/monty-python/tests/test_os_access_raw.py +0 -483
- package/ref-monty/crates/monty-python/tests/test_os_calls.py +0 -819
- package/ref-monty/crates/monty-python/tests/test_print.py +0 -208
- package/ref-monty/crates/monty-python/tests/test_re.py +0 -170
- package/ref-monty/crates/monty-python/tests/test_readme_examples.py +0 -20
- package/ref-monty/crates/monty-python/tests/test_repl.py +0 -749
- package/ref-monty/crates/monty-python/tests/test_serialize.py +0 -284
- package/ref-monty/crates/monty-python/tests/test_start.py +0 -346
- package/ref-monty/crates/monty-python/tests/test_threading.py +0 -163
- package/ref-monty/crates/monty-python/tests/test_type_check.py +0 -344
- package/ref-monty/crates/monty-python/tests/test_types.py +0 -553
- package/ref-monty/crates/monty-type-checking/Cargo.toml +0 -32
- package/ref-monty/crates/monty-type-checking/src/db.rs +0 -116
- package/ref-monty/crates/monty-type-checking/src/lib.rs +0 -4
- package/ref-monty/crates/monty-type-checking/src/type_check.rs +0 -280
- package/ref-monty/crates/monty-type-checking/tests/bad_types.py +0 -109
- package/ref-monty/crates/monty-type-checking/tests/bad_types_output.txt +0 -21
- package/ref-monty/crates/monty-type-checking/tests/good_types.py +0 -475
- package/ref-monty/crates/monty-type-checking/tests/main.rs +0 -205
- package/ref-monty/crates/monty-type-checking/tests/reveal_types.py +0 -56
- package/ref-monty/crates/monty-type-checking/tests/reveal_types_output.txt +0 -41
- package/ref-monty/crates/monty-typeshed/Cargo.toml +0 -29
- package/ref-monty/crates/monty-typeshed/README.md +0 -11
- package/ref-monty/crates/monty-typeshed/build.rs +0 -101
- package/ref-monty/crates/monty-typeshed/custom/README.md +0 -1
- package/ref-monty/crates/monty-typeshed/custom/asyncio.pyi +0 -138
- package/ref-monty/crates/monty-typeshed/custom/os.pyi +0 -87
- package/ref-monty/crates/monty-typeshed/custom/sys.pyi +0 -33
- package/ref-monty/crates/monty-typeshed/src/lib.rs +0 -56
- package/ref-monty/crates/monty-typeshed/update.py +0 -321
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/source_commit.txt +0 -1
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/VERSIONS +0 -20
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/_collections_abc.pyi +0 -105
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/_typeshed/__init__.pyi +0 -394
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/asyncio.pyi +0 -138
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/builtins.pyi +0 -1434
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/collections/__init__.pyi +0 -527
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/collections/abc.pyi +0 -2
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/dataclasses.pyi +0 -502
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/enum.pyi +0 -376
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/math.pyi +0 -149
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/os.pyi +0 -87
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/pathlib/__init__.pyi +0 -395
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/pathlib/types.pyi +0 -8
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/re.pyi +0 -337
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/sys.pyi +0 -33
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/types.pyi +0 -741
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/typing.pyi +0 -1217
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/typing_extensions.pyi +0 -716
- package/ref-monty/docs/usage-guide.md +0 -117
- package/ref-monty/examples/README.md +0 -3
- package/ref-monty/examples/expense_analysis/README.md +0 -3
- package/ref-monty/examples/expense_analysis/data.py +0 -124
- package/ref-monty/examples/expense_analysis/main.py +0 -115
- package/ref-monty/examples/sql_playground/README.md +0 -20
- package/ref-monty/examples/sql_playground/external_functions.py +0 -129
- package/ref-monty/examples/sql_playground/main.py +0 -81
- package/ref-monty/examples/sql_playground/sandbox_code.py +0 -82
- package/ref-monty/examples/sql_playground/type_stubs.pyi +0 -14
- package/ref-monty/examples/web_scraper/README.md +0 -15
- package/ref-monty/examples/web_scraper/browser.py +0 -56
- package/ref-monty/examples/web_scraper/example_code.py +0 -59
- package/ref-monty/examples/web_scraper/external_functions.py +0 -324
- package/ref-monty/examples/web_scraper/main.py +0 -193
- package/ref-monty/examples/web_scraper/sub_agent.py +0 -79
- package/ref-monty/monty-npm.md +0 -235
- package/ref-monty/pyproject.toml +0 -162
- package/ref-monty/scripts/check_imports.py +0 -91
- package/ref-monty/scripts/codecov_diff.py +0 -412
- package/ref-monty/scripts/complete_tests.py +0 -146
- package/ref-monty/scripts/flamegraph_to_text.py +0 -208
- package/ref-monty/scripts/iter_test_methods.py +0 -540
- package/ref-monty/scripts/run_traceback.py +0 -180
- package/ref-monty/scripts/startup_performance.py +0 -130
- package/ref-monty/uv.lock +0 -1779
- package/temp_resend_cli/repo/.github/scripts/pr-title-check.js +0 -34
- package/temp_resend_cli/repo/.github/workflows/ci.yml +0 -67
- package/temp_resend_cli/repo/.github/workflows/post-release.yml +0 -51
- package/temp_resend_cli/repo/.github/workflows/pr-title-check.yml +0 -13
- package/temp_resend_cli/repo/.github/workflows/release.yml +0 -175
- package/temp_resend_cli/repo/.github/workflows/test-install-unix.yml +0 -34
- package/temp_resend_cli/repo/.github/workflows/test-install-windows.yml +0 -48
- package/temp_resend_cli/repo/CHANGELOG.md +0 -31
- package/temp_resend_cli/repo/LICENSE +0 -21
- package/temp_resend_cli/repo/README.md +0 -450
- package/temp_resend_cli/repo/biome.json +0 -36
- package/temp_resend_cli/repo/install.ps1 +0 -141
- package/temp_resend_cli/repo/install.sh +0 -301
- package/temp_resend_cli/repo/package.json +0 -61
- package/temp_resend_cli/repo/pnpm-lock.yaml +0 -2439
- package/temp_resend_cli/repo/renovate.json +0 -4
- package/temp_resend_cli/repo/src/cli.ts +0 -98
- package/temp_resend_cli/repo/src/commands/api-keys/create.ts +0 -114
- package/temp_resend_cli/repo/src/commands/api-keys/delete.ts +0 -47
- package/temp_resend_cli/repo/src/commands/api-keys/index.ts +0 -26
- package/temp_resend_cli/repo/src/commands/api-keys/list.ts +0 -35
- package/temp_resend_cli/repo/src/commands/api-keys/utils.ts +0 -8
- package/temp_resend_cli/repo/src/commands/auth/index.ts +0 -20
- package/temp_resend_cli/repo/src/commands/auth/login.ts +0 -234
- package/temp_resend_cli/repo/src/commands/auth/logout.ts +0 -105
- package/temp_resend_cli/repo/src/commands/broadcasts/create.ts +0 -196
- package/temp_resend_cli/repo/src/commands/broadcasts/delete.ts +0 -46
- package/temp_resend_cli/repo/src/commands/broadcasts/get.ts +0 -59
- package/temp_resend_cli/repo/src/commands/broadcasts/index.ts +0 -43
- package/temp_resend_cli/repo/src/commands/broadcasts/list.ts +0 -60
- package/temp_resend_cli/repo/src/commands/broadcasts/send.ts +0 -56
- package/temp_resend_cli/repo/src/commands/broadcasts/update.ts +0 -95
- package/temp_resend_cli/repo/src/commands/broadcasts/utils.ts +0 -35
- package/temp_resend_cli/repo/src/commands/contact-properties/create.ts +0 -118
- package/temp_resend_cli/repo/src/commands/contact-properties/delete.ts +0 -48
- package/temp_resend_cli/repo/src/commands/contact-properties/get.ts +0 -46
- package/temp_resend_cli/repo/src/commands/contact-properties/index.ts +0 -48
- package/temp_resend_cli/repo/src/commands/contact-properties/list.ts +0 -68
- package/temp_resend_cli/repo/src/commands/contact-properties/update.ts +0 -88
- package/temp_resend_cli/repo/src/commands/contact-properties/utils.ts +0 -17
- package/temp_resend_cli/repo/src/commands/contacts/add-segment.ts +0 -78
- package/temp_resend_cli/repo/src/commands/contacts/create.ts +0 -122
- package/temp_resend_cli/repo/src/commands/contacts/delete.ts +0 -49
- package/temp_resend_cli/repo/src/commands/contacts/get.ts +0 -53
- package/temp_resend_cli/repo/src/commands/contacts/index.ts +0 -58
- package/temp_resend_cli/repo/src/commands/contacts/list.ts +0 -57
- package/temp_resend_cli/repo/src/commands/contacts/remove-segment.ts +0 -48
- package/temp_resend_cli/repo/src/commands/contacts/segments.ts +0 -39
- package/temp_resend_cli/repo/src/commands/contacts/topics.ts +0 -45
- package/temp_resend_cli/repo/src/commands/contacts/update-topics.ts +0 -90
- package/temp_resend_cli/repo/src/commands/contacts/update.ts +0 -77
- package/temp_resend_cli/repo/src/commands/contacts/utils.ts +0 -119
- package/temp_resend_cli/repo/src/commands/doctor.ts +0 -216
- package/temp_resend_cli/repo/src/commands/domains/create.ts +0 -83
- package/temp_resend_cli/repo/src/commands/domains/delete.ts +0 -42
- package/temp_resend_cli/repo/src/commands/domains/get.ts +0 -47
- package/temp_resend_cli/repo/src/commands/domains/index.ts +0 -35
- package/temp_resend_cli/repo/src/commands/domains/list.ts +0 -53
- package/temp_resend_cli/repo/src/commands/domains/update.ts +0 -75
- package/temp_resend_cli/repo/src/commands/domains/utils.ts +0 -44
- package/temp_resend_cli/repo/src/commands/domains/verify.ts +0 -38
- package/temp_resend_cli/repo/src/commands/emails/batch.ts +0 -140
- package/temp_resend_cli/repo/src/commands/emails/get.ts +0 -44
- package/temp_resend_cli/repo/src/commands/emails/index.ts +0 -30
- package/temp_resend_cli/repo/src/commands/emails/list.ts +0 -84
- package/temp_resend_cli/repo/src/commands/emails/receiving/attachment.ts +0 -55
- package/temp_resend_cli/repo/src/commands/emails/receiving/attachments.ts +0 -68
- package/temp_resend_cli/repo/src/commands/emails/receiving/get.ts +0 -58
- package/temp_resend_cli/repo/src/commands/emails/receiving/index.ts +0 -28
- package/temp_resend_cli/repo/src/commands/emails/receiving/list.ts +0 -59
- package/temp_resend_cli/repo/src/commands/emails/receiving/utils.ts +0 -38
- package/temp_resend_cli/repo/src/commands/emails/send.ts +0 -189
- package/temp_resend_cli/repo/src/commands/open.ts +0 -27
- package/temp_resend_cli/repo/src/commands/segments/create.ts +0 -50
- package/temp_resend_cli/repo/src/commands/segments/delete.ts +0 -47
- package/temp_resend_cli/repo/src/commands/segments/get.ts +0 -38
- package/temp_resend_cli/repo/src/commands/segments/index.ts +0 -36
- package/temp_resend_cli/repo/src/commands/segments/list.ts +0 -58
- package/temp_resend_cli/repo/src/commands/segments/utils.ts +0 -7
- package/temp_resend_cli/repo/src/commands/teams/index.ts +0 -10
- package/temp_resend_cli/repo/src/commands/teams/list.ts +0 -35
- package/temp_resend_cli/repo/src/commands/teams/remove.ts +0 -86
- package/temp_resend_cli/repo/src/commands/teams/switch.ts +0 -76
- package/temp_resend_cli/repo/src/commands/topics/create.ts +0 -73
- package/temp_resend_cli/repo/src/commands/topics/delete.ts +0 -47
- package/temp_resend_cli/repo/src/commands/topics/get.ts +0 -42
- package/temp_resend_cli/repo/src/commands/topics/index.ts +0 -42
- package/temp_resend_cli/repo/src/commands/topics/list.ts +0 -34
- package/temp_resend_cli/repo/src/commands/topics/update.ts +0 -59
- package/temp_resend_cli/repo/src/commands/topics/utils.ts +0 -16
- package/temp_resend_cli/repo/src/commands/webhooks/create.ts +0 -128
- package/temp_resend_cli/repo/src/commands/webhooks/delete.ts +0 -49
- package/temp_resend_cli/repo/src/commands/webhooks/get.ts +0 -42
- package/temp_resend_cli/repo/src/commands/webhooks/index.ts +0 -42
- package/temp_resend_cli/repo/src/commands/webhooks/list.ts +0 -55
- package/temp_resend_cli/repo/src/commands/webhooks/listen.ts +0 -379
- package/temp_resend_cli/repo/src/commands/webhooks/update.ts +0 -83
- package/temp_resend_cli/repo/src/commands/webhooks/utils.ts +0 -36
- package/temp_resend_cli/repo/src/commands/whoami.ts +0 -71
- package/temp_resend_cli/repo/src/lib/actions.ts +0 -157
- package/temp_resend_cli/repo/src/lib/client.ts +0 -37
- package/temp_resend_cli/repo/src/lib/config.ts +0 -217
- package/temp_resend_cli/repo/src/lib/files.ts +0 -15
- package/temp_resend_cli/repo/src/lib/help-text.ts +0 -38
- package/temp_resend_cli/repo/src/lib/output.ts +0 -56
- package/temp_resend_cli/repo/src/lib/pagination.ts +0 -36
- package/temp_resend_cli/repo/src/lib/prompts.ts +0 -149
- package/temp_resend_cli/repo/src/lib/spinner.ts +0 -100
- package/temp_resend_cli/repo/src/lib/table.ts +0 -57
- package/temp_resend_cli/repo/src/lib/tty.ts +0 -28
- package/temp_resend_cli/repo/src/lib/update-check.ts +0 -169
- package/temp_resend_cli/repo/src/lib/version.ts +0 -4
- package/temp_resend_cli/repo/tests/commands/api-keys/create.test.ts +0 -196
- package/temp_resend_cli/repo/tests/commands/api-keys/delete.test.ts +0 -157
- package/temp_resend_cli/repo/tests/commands/api-keys/list.test.ts +0 -134
- package/temp_resend_cli/repo/tests/commands/auth/login.test.ts +0 -153
- package/temp_resend_cli/repo/tests/commands/auth/logout.test.ts +0 -153
- package/temp_resend_cli/repo/tests/commands/broadcasts/create.test.ts +0 -454
- package/temp_resend_cli/repo/tests/commands/broadcasts/delete.test.ts +0 -183
- package/temp_resend_cli/repo/tests/commands/broadcasts/get.test.ts +0 -147
- package/temp_resend_cli/repo/tests/commands/broadcasts/list.test.ts +0 -199
- package/temp_resend_cli/repo/tests/commands/broadcasts/send.test.ts +0 -162
- package/temp_resend_cli/repo/tests/commands/broadcasts/update.test.ts +0 -288
- package/temp_resend_cli/repo/tests/commands/contact-properties/create.test.ts +0 -251
- package/temp_resend_cli/repo/tests/commands/contact-properties/delete.test.ts +0 -184
- package/temp_resend_cli/repo/tests/commands/contact-properties/get.test.ts +0 -145
- package/temp_resend_cli/repo/tests/commands/contact-properties/list.test.ts +0 -181
- package/temp_resend_cli/repo/tests/commands/contact-properties/update.test.ts +0 -217
- package/temp_resend_cli/repo/tests/commands/contacts/add-segment.test.ts +0 -189
- package/temp_resend_cli/repo/tests/commands/contacts/create.test.ts +0 -271
- package/temp_resend_cli/repo/tests/commands/contacts/delete.test.ts +0 -193
- package/temp_resend_cli/repo/tests/commands/contacts/get.test.ts +0 -149
- package/temp_resend_cli/repo/tests/commands/contacts/list.test.ts +0 -176
- package/temp_resend_cli/repo/tests/commands/contacts/remove-segment.test.ts +0 -167
- package/temp_resend_cli/repo/tests/commands/contacts/segments.test.ts +0 -168
- package/temp_resend_cli/repo/tests/commands/contacts/topics.test.ts +0 -164
- package/temp_resend_cli/repo/tests/commands/contacts/update-topics.test.ts +0 -248
- package/temp_resend_cli/repo/tests/commands/contacts/update.test.ts +0 -206
- package/temp_resend_cli/repo/tests/commands/doctor.test.ts +0 -164
- package/temp_resend_cli/repo/tests/commands/domains/create.test.ts +0 -193
- package/temp_resend_cli/repo/tests/commands/domains/delete.test.ts +0 -157
- package/temp_resend_cli/repo/tests/commands/domains/get.test.ts +0 -138
- package/temp_resend_cli/repo/tests/commands/domains/list.test.ts +0 -165
- package/temp_resend_cli/repo/tests/commands/domains/update.test.ts +0 -224
- package/temp_resend_cli/repo/tests/commands/domains/verify.test.ts +0 -118
- package/temp_resend_cli/repo/tests/commands/emails/batch.test.ts +0 -324
- package/temp_resend_cli/repo/tests/commands/emails/get.test.ts +0 -132
- package/temp_resend_cli/repo/tests/commands/emails/receiving/attachment.test.ts +0 -141
- package/temp_resend_cli/repo/tests/commands/emails/receiving/attachments.test.ts +0 -169
- package/temp_resend_cli/repo/tests/commands/emails/receiving/get.test.ts +0 -141
- package/temp_resend_cli/repo/tests/commands/emails/receiving/list.test.ts +0 -182
- package/temp_resend_cli/repo/tests/commands/emails/send.test.ts +0 -312
- package/temp_resend_cli/repo/tests/commands/segments/create.test.ts +0 -164
- package/temp_resend_cli/repo/tests/commands/segments/delete.test.ts +0 -183
- package/temp_resend_cli/repo/tests/commands/segments/get.test.ts +0 -138
- package/temp_resend_cli/repo/tests/commands/segments/list.test.ts +0 -174
- package/temp_resend_cli/repo/tests/commands/teams/list.test.ts +0 -62
- package/temp_resend_cli/repo/tests/commands/teams/remove.test.ts +0 -110
- package/temp_resend_cli/repo/tests/commands/teams/switch.test.ts +0 -103
- package/temp_resend_cli/repo/tests/commands/topics/create.test.ts +0 -192
- package/temp_resend_cli/repo/tests/commands/topics/delete.test.ts +0 -157
- package/temp_resend_cli/repo/tests/commands/topics/get.test.ts +0 -126
- package/temp_resend_cli/repo/tests/commands/topics/list.test.ts +0 -125
- package/temp_resend_cli/repo/tests/commands/topics/update.test.ts +0 -178
- package/temp_resend_cli/repo/tests/commands/webhooks/create.test.ts +0 -225
- package/temp_resend_cli/repo/tests/commands/webhooks/delete.test.ts +0 -157
- package/temp_resend_cli/repo/tests/commands/webhooks/get.test.ts +0 -126
- package/temp_resend_cli/repo/tests/commands/webhooks/list.test.ts +0 -178
- package/temp_resend_cli/repo/tests/commands/webhooks/update.test.ts +0 -207
- package/temp_resend_cli/repo/tests/commands/whoami.test.ts +0 -98
- package/temp_resend_cli/repo/tests/e2e/smoke.test.ts +0 -93
- package/temp_resend_cli/repo/tests/helpers.ts +0 -86
- package/temp_resend_cli/repo/tests/lib/client.test.ts +0 -71
- package/temp_resend_cli/repo/tests/lib/config.test.ts +0 -451
- package/temp_resend_cli/repo/tests/lib/files.test.ts +0 -73
- package/temp_resend_cli/repo/tests/lib/help-text.test.ts +0 -97
- package/temp_resend_cli/repo/tests/lib/output.test.ts +0 -136
- package/temp_resend_cli/repo/tests/lib/prompts.test.ts +0 -185
- package/temp_resend_cli/repo/tests/lib/spinner.test.ts +0 -166
- package/temp_resend_cli/repo/tests/lib/table.test.ts +0 -63
- package/temp_resend_cli/repo/tests/lib/tty.test.ts +0 -89
- package/temp_resend_cli/repo/tests/lib/update-check.test.ts +0 -179
- package/temp_resend_cli/repo/tsconfig.json +0 -14
- package/temp_resend_cli/repo/vitest.config.e2e.ts +0 -8
- package/temp_resend_cli/repo/vitest.config.ts +0 -10
- /package/docs/{plugin-examples.md → plugins-examples.md} +0 -0
|
@@ -1,1826 +0,0 @@
|
|
|
1
|
-
/// Tests for resource limits and garbage collection.
|
|
2
|
-
///
|
|
3
|
-
/// These tests verify that the `ResourceTracker` system correctly enforces
|
|
4
|
-
/// allocation limits, time limits, and triggers garbage collection.
|
|
5
|
-
use std::time::{Duration, Instant};
|
|
6
|
-
|
|
7
|
-
use monty::{
|
|
8
|
-
ExcType, LimitedTracker, MontyObject, MontyRun, NameLookupResult, PrintWriter, ResourceLimits, RunProgress,
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
/// Resolves consecutive `NameLookup` yields by providing a `Function` object for each name.
|
|
12
|
-
///
|
|
13
|
-
/// External functions are no longer declared upfront. Instead, the VM yields `NameLookup`
|
|
14
|
-
/// when it encounters an unresolved name. This helper resolves all such lookups until
|
|
15
|
-
/// a different progress variant is reached.
|
|
16
|
-
fn resolve_name_lookups<T: monty::ResourceTracker>(
|
|
17
|
-
mut progress: RunProgress<T>,
|
|
18
|
-
) -> Result<RunProgress<T>, monty::MontyException> {
|
|
19
|
-
while let RunProgress::NameLookup(lookup) = progress {
|
|
20
|
-
let name = lookup.name.clone();
|
|
21
|
-
progress = lookup.resume(
|
|
22
|
-
NameLookupResult::Value(MontyObject::Function { name, docstring: None }),
|
|
23
|
-
PrintWriter::Stdout,
|
|
24
|
-
)?;
|
|
25
|
-
}
|
|
26
|
-
Ok(progress)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/// Test that GC properly collects dict cycles via the has_refs() check in allocate().
|
|
30
|
-
///
|
|
31
|
-
/// This test creates cycles using dict literals and dict setitem. Dict setitem
|
|
32
|
-
/// does NOT call mark_potential_cycle(), so the ONLY way may_have_cycles gets
|
|
33
|
-
/// set is through the has_refs() check when allocating a dict with refs.
|
|
34
|
-
///
|
|
35
|
-
/// If has_refs() is disabled, this test will FAIL because GC never runs.
|
|
36
|
-
#[test]
|
|
37
|
-
#[cfg(feature = "ref-count-return")]
|
|
38
|
-
fn gc_collects_dict_cycles_via_has_refs() {
|
|
39
|
-
// Create 200,001 dict cycles. Each iteration:
|
|
40
|
-
// - Creates empty dict d1
|
|
41
|
-
// - Creates dict d2 = {'ref': d1} - d2 is allocated WITH a ref to d1
|
|
42
|
-
// This triggers has_refs() which sets may_have_cycles = true
|
|
43
|
-
// - Sets d1['ref'] = d2 - creates cycle d1 <-> d2
|
|
44
|
-
// Dict setitem does NOT call mark_potential_cycle()
|
|
45
|
-
// - On next iteration, both dicts are reassigned, making the cycle unreachable
|
|
46
|
-
//
|
|
47
|
-
// GC runs every 100,000 allocations. With 200,001 iterations:
|
|
48
|
-
// - GC runs at 100k (collects cycles 0-49,999 approximately)
|
|
49
|
-
// - GC runs at 200k (collects more cycles)
|
|
50
|
-
// After GC runs, only the final cycle should remain.
|
|
51
|
-
let code = r"
|
|
52
|
-
# Create many dict cycles
|
|
53
|
-
for i in range(200001):
|
|
54
|
-
d1 = {}
|
|
55
|
-
d2 = {'ref': d1} # d2 allocated WITH ref - has_refs() must trigger here
|
|
56
|
-
d1['ref'] = d2 # Cycle formed - dict setitem does NOT call mark_potential_cycle
|
|
57
|
-
|
|
58
|
-
# Create final result (not a cycle)
|
|
59
|
-
result = 'done'
|
|
60
|
-
result
|
|
61
|
-
";
|
|
62
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
63
|
-
|
|
64
|
-
let output = ex.run_ref_counts(vec![]).expect("should succeed");
|
|
65
|
-
|
|
66
|
-
// GC_INTERVAL is 100,000. With 200,001 iterations creating dict cycles,
|
|
67
|
-
// GC must have run at least once, resetting allocations_since_gc.
|
|
68
|
-
// If may_have_cycles was never set (has_refs() disabled), GC never runs
|
|
69
|
-
// and allocations_since_gc would be ~400k (2 dicts per iteration).
|
|
70
|
-
assert!(
|
|
71
|
-
output.allocations_since_gc < 100_000,
|
|
72
|
-
"GC should have run (has_refs() must set may_have_cycles): allocations_since_gc = {}",
|
|
73
|
-
output.allocations_since_gc
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
// Verify that GC collected most cycles.
|
|
77
|
-
// If GC failed to collect cycles, heap_count would be >> 400k.
|
|
78
|
-
// We allow a small number of extra objects for implementation details.
|
|
79
|
-
assert!(
|
|
80
|
-
output.heap_count < 20,
|
|
81
|
-
"GC should collect most unreachable dict cycles: {} heap objects (expected < 20)",
|
|
82
|
-
output.heap_count
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/// Test that GC properly collects self-referencing list cycles.
|
|
87
|
-
///
|
|
88
|
-
/// This test creates cycles using list.append(), which calls mark_potential_cycle().
|
|
89
|
-
/// This tests the mutation-based cycle detection path.
|
|
90
|
-
#[test]
|
|
91
|
-
#[cfg(feature = "ref-count-return")]
|
|
92
|
-
fn gc_collects_list_cycles() {
|
|
93
|
-
// Create 200,001 self-referencing list cycles. Each iteration:
|
|
94
|
-
// - Creates empty list `a`
|
|
95
|
-
// - Appends `a` to itself (creating a self-reference cycle)
|
|
96
|
-
// This calls mark_potential_cycle() and sets may_have_cycles = true
|
|
97
|
-
// - On next iteration, `a` is reassigned, making the cycle unreachable
|
|
98
|
-
//
|
|
99
|
-
// GC runs every 100,000 allocations. With 200,001 iterations:
|
|
100
|
-
// - GC runs at 100k (collects cycles 0-99,999)
|
|
101
|
-
// - GC runs at 200k (collects cycles 100k-199,999)
|
|
102
|
-
// After GC runs, only the final cycle should remain.
|
|
103
|
-
let code = r"
|
|
104
|
-
# Create many self-referencing list cycles
|
|
105
|
-
for i in range(200001):
|
|
106
|
-
a = []
|
|
107
|
-
a.append(a) # Creates cycle via list.append() which calls mark_potential_cycle()
|
|
108
|
-
|
|
109
|
-
# Create final result (not a cycle)
|
|
110
|
-
result = [1, 2, 3]
|
|
111
|
-
len(result)
|
|
112
|
-
";
|
|
113
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
114
|
-
|
|
115
|
-
let output = ex.run_ref_counts(vec![]).expect("should succeed");
|
|
116
|
-
|
|
117
|
-
// GC_INTERVAL is 100,000. With 200,001 iterations creating list cycles,
|
|
118
|
-
// GC must have run at least twice, resetting allocations_since_gc.
|
|
119
|
-
assert!(
|
|
120
|
-
output.allocations_since_gc < 100_000,
|
|
121
|
-
"GC should have run: allocations_since_gc = {}",
|
|
122
|
-
output.allocations_since_gc
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
// Verify that GC collected most cycles.
|
|
126
|
-
// If GC failed to collect cycles, heap_count would be >> 200k.
|
|
127
|
-
assert!(
|
|
128
|
-
output.heap_count < 20,
|
|
129
|
-
"GC should collect most unreachable list cycles: {} heap objects (expected < 20)",
|
|
130
|
-
output.heap_count
|
|
131
|
-
);
|
|
132
|
-
|
|
133
|
-
// Verify expected ref counts
|
|
134
|
-
// `a` is the last self-referencing list (refcount 2: variable + self-reference)
|
|
135
|
-
// `result` is a simple list (refcount 1: just the variable)
|
|
136
|
-
assert_eq!(
|
|
137
|
-
output.counts.get("a"),
|
|
138
|
-
Some(&2),
|
|
139
|
-
"self-referencing list should have refcount 2"
|
|
140
|
-
);
|
|
141
|
-
assert_eq!(
|
|
142
|
-
output.counts.get("result"),
|
|
143
|
-
Some(&1),
|
|
144
|
-
"result list should have refcount 1"
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/// Test that allocation limits return an error.
|
|
149
|
-
#[test]
|
|
150
|
-
fn allocation_limit_exceeded() {
|
|
151
|
-
// Use multi-character strings to ensure heap allocation (single ASCII chars are interned)
|
|
152
|
-
let code = r"
|
|
153
|
-
result = []
|
|
154
|
-
for i in range(100, 115):
|
|
155
|
-
result.append(str(i))
|
|
156
|
-
result
|
|
157
|
-
";
|
|
158
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
159
|
-
|
|
160
|
-
let limits = ResourceLimits::new().max_allocations(4);
|
|
161
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
162
|
-
|
|
163
|
-
// Should fail due to allocation limit
|
|
164
|
-
assert!(result.is_err(), "should exceed allocation limit");
|
|
165
|
-
let exc = result.unwrap_err();
|
|
166
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
167
|
-
assert!(
|
|
168
|
-
exc.message().is_some_and(|m| m.contains("allocation limit exceeded")),
|
|
169
|
-
"expected allocation limit error, got: {exc}"
|
|
170
|
-
);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
#[test]
|
|
174
|
-
fn allocation_limit_not_exceeded() {
|
|
175
|
-
// Single-digit strings are interned (no allocation), so this uses minimal heap
|
|
176
|
-
let code = r"
|
|
177
|
-
result = []
|
|
178
|
-
for i in range(9):
|
|
179
|
-
result.append(str(i))
|
|
180
|
-
result
|
|
181
|
-
";
|
|
182
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
183
|
-
|
|
184
|
-
// Allocations: list (1) + range (1) + iterator (1) = 3
|
|
185
|
-
// Note: str(0)...str(8) are single ASCII chars, so they use pre-interned strings
|
|
186
|
-
let limits = ResourceLimits::new().max_allocations(5);
|
|
187
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
188
|
-
|
|
189
|
-
// Should succeed
|
|
190
|
-
assert!(result.is_ok(), "should not exceed allocation limit");
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
#[test]
|
|
194
|
-
fn time_limit_exceeded() {
|
|
195
|
-
// Create a long-running loop using for + range (while isn't implemented yet)
|
|
196
|
-
// Use a very large range to ensure it runs long enough to hit the time limit
|
|
197
|
-
let code = r"
|
|
198
|
-
x = 0
|
|
199
|
-
for i in range(100000000):
|
|
200
|
-
x = x + 1
|
|
201
|
-
x
|
|
202
|
-
";
|
|
203
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
204
|
-
|
|
205
|
-
// Set a short time limit
|
|
206
|
-
let limits = ResourceLimits::new().max_duration(Duration::from_millis(50));
|
|
207
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
208
|
-
|
|
209
|
-
// Should fail due to time limit
|
|
210
|
-
assert!(result.is_err(), "should exceed time limit");
|
|
211
|
-
let exc = result.unwrap_err();
|
|
212
|
-
assert_eq!(exc.exc_type(), ExcType::TimeoutError);
|
|
213
|
-
assert!(
|
|
214
|
-
exc.message().is_some_and(|m| m.contains("time limit exceeded")),
|
|
215
|
-
"expected time limit error, got: {exc}"
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
#[test]
|
|
220
|
-
fn time_limit_not_exceeded() {
|
|
221
|
-
// Simple code that runs quickly
|
|
222
|
-
let code = "x = 1 + 2\nx";
|
|
223
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
224
|
-
|
|
225
|
-
// Set a generous time limit
|
|
226
|
-
let limits = ResourceLimits::new().max_duration(Duration::from_secs(5));
|
|
227
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
228
|
-
|
|
229
|
-
// Should succeed
|
|
230
|
-
assert!(result.is_ok(), "should not exceed time limit");
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/// Test that memory limits return an error.
|
|
234
|
-
#[test]
|
|
235
|
-
fn memory_limit_exceeded() {
|
|
236
|
-
// Create code that builds up memory using lists
|
|
237
|
-
// Each iteration creates a new list that gets appended
|
|
238
|
-
let code = r"
|
|
239
|
-
result = []
|
|
240
|
-
for i in range(100):
|
|
241
|
-
result.append([1, 2, 3, 4, 5])
|
|
242
|
-
result
|
|
243
|
-
";
|
|
244
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
245
|
-
|
|
246
|
-
// Set a very low memory limit (100 bytes) to trigger on nested list allocation
|
|
247
|
-
let limits = ResourceLimits::new().max_memory(100);
|
|
248
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
249
|
-
|
|
250
|
-
// Should fail due to memory limit
|
|
251
|
-
assert!(result.is_err(), "should exceed memory limit");
|
|
252
|
-
let exc = result.unwrap_err();
|
|
253
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
254
|
-
assert!(
|
|
255
|
-
exc.message().is_some_and(|m| m.contains("memory limit exceeded")),
|
|
256
|
-
"expected memory limit error, got: {exc}"
|
|
257
|
-
);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
#[test]
|
|
261
|
-
fn combined_limits() {
|
|
262
|
-
// Test multiple limits together
|
|
263
|
-
let code = "x = 1 + 2\nx";
|
|
264
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
265
|
-
|
|
266
|
-
let limits = ResourceLimits::new()
|
|
267
|
-
.max_allocations(1000)
|
|
268
|
-
.max_duration(Duration::from_secs(5))
|
|
269
|
-
.max_memory(1024 * 1024);
|
|
270
|
-
|
|
271
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
272
|
-
assert!(result.is_ok(), "should succeed with generous limits");
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
#[test]
|
|
276
|
-
fn run_without_limits_succeeds() {
|
|
277
|
-
// Verify that run() still works (no limits)
|
|
278
|
-
let code = r"
|
|
279
|
-
result = []
|
|
280
|
-
for i in range(100):
|
|
281
|
-
result.append(str(i))
|
|
282
|
-
len(result)
|
|
283
|
-
";
|
|
284
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
285
|
-
|
|
286
|
-
// Standard run should succeed
|
|
287
|
-
let result = ex.run_no_limits(vec![]);
|
|
288
|
-
assert!(result.is_ok(), "standard run should succeed");
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
#[test]
|
|
292
|
-
fn gc_interval_triggers_collection() {
|
|
293
|
-
// This test verifies that GC can run without crashing
|
|
294
|
-
// We can't easily verify that GC actually collected anything without
|
|
295
|
-
// adding more introspection, but we can verify it runs
|
|
296
|
-
let code = r"
|
|
297
|
-
result = []
|
|
298
|
-
for i in range(100):
|
|
299
|
-
temp = [1, 2, 3]
|
|
300
|
-
result.append(i)
|
|
301
|
-
len(result)
|
|
302
|
-
";
|
|
303
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
304
|
-
|
|
305
|
-
// Set GC to run every 10 allocations
|
|
306
|
-
let limits = ResourceLimits::new().gc_interval(10);
|
|
307
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
308
|
-
|
|
309
|
-
assert!(result.is_ok(), "should succeed with GC enabled");
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
#[test]
|
|
313
|
-
#[cfg_attr(
|
|
314
|
-
feature = "ref-count-panic",
|
|
315
|
-
ignore = "resource exhaustion doesn't guarantee heap state consistency"
|
|
316
|
-
)]
|
|
317
|
-
fn executor_iter_resource_limit_on_resume() {
|
|
318
|
-
// Test that resource limits are enforced across function calls
|
|
319
|
-
// First function call succeeds, but resumed execution exceeds limit
|
|
320
|
-
|
|
321
|
-
// f-string to create multi-char strings (not interned)
|
|
322
|
-
let code = "foo(1)\nx = []\nfor i in range(10):\n x.append(f'x{i}')\nlen(x)";
|
|
323
|
-
let run = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
324
|
-
|
|
325
|
-
// First function call should succeed with generous limit
|
|
326
|
-
let limits = ResourceLimits::new().max_allocations(5);
|
|
327
|
-
let progress = run
|
|
328
|
-
.start(vec![], LimitedTracker::new(limits), PrintWriter::Stdout)
|
|
329
|
-
.unwrap();
|
|
330
|
-
let call = resolve_name_lookups(progress)
|
|
331
|
-
.unwrap()
|
|
332
|
-
.into_function_call()
|
|
333
|
-
.expect("function call");
|
|
334
|
-
assert_eq!(call.function_name, "foo");
|
|
335
|
-
assert_eq!(call.args, vec![MontyObject::Int(1)]);
|
|
336
|
-
|
|
337
|
-
// Resume - should fail due to allocation limit during the for loop
|
|
338
|
-
let result = call.resume(MontyObject::None, PrintWriter::Stdout);
|
|
339
|
-
assert!(result.is_err(), "should exceed allocation limit on resume");
|
|
340
|
-
let exc = result.unwrap_err();
|
|
341
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
342
|
-
assert!(
|
|
343
|
-
exc.message().is_some_and(|m| m.contains("allocation limit exceeded")),
|
|
344
|
-
"expected allocation limit error, got: {exc}"
|
|
345
|
-
);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
#[test]
|
|
349
|
-
#[cfg_attr(
|
|
350
|
-
feature = "ref-count-panic",
|
|
351
|
-
ignore = "resource exhaustion doesn't guarantee heap state consistency"
|
|
352
|
-
)]
|
|
353
|
-
fn executor_iter_resource_limit_before_function_call() {
|
|
354
|
-
// Test that resource limits are enforced before first function call
|
|
355
|
-
|
|
356
|
-
// f-string to create multi-char strings (not interned)
|
|
357
|
-
let code = "x = []\nfor i in range(10):\n x.append(f'x{i}')\nfoo(len(x))\n42";
|
|
358
|
-
let run = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
359
|
-
|
|
360
|
-
// Should fail before reaching the function call
|
|
361
|
-
let limits = ResourceLimits::new().max_allocations(3);
|
|
362
|
-
let result = run.start(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
363
|
-
|
|
364
|
-
assert!(result.is_err(), "should exceed allocation limit before function call");
|
|
365
|
-
let exc = result.unwrap_err();
|
|
366
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
367
|
-
assert!(
|
|
368
|
-
exc.message().is_some_and(|m| m.contains("allocation limit exceeded")),
|
|
369
|
-
"expected allocation limit error, got: {exc}"
|
|
370
|
-
);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
#[test]
|
|
374
|
-
#[cfg_attr(
|
|
375
|
-
feature = "ref-count-panic",
|
|
376
|
-
ignore = "resource exhaustion doesn't guarantee heap state consistency"
|
|
377
|
-
)]
|
|
378
|
-
fn char_f_string_not_allocated() {
|
|
379
|
-
// Single character f-string interned not not allocated
|
|
380
|
-
|
|
381
|
-
let code = "x = []\nfor i in range(10):\n x.append(f'{i}')";
|
|
382
|
-
let run = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
383
|
-
|
|
384
|
-
let limits = ResourceLimits::new().max_allocations(4);
|
|
385
|
-
run.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout)
|
|
386
|
-
.unwrap();
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
#[test]
|
|
390
|
-
fn executor_iter_resource_limit_multiple_function_calls() {
|
|
391
|
-
// Test resource limits across multiple function calls
|
|
392
|
-
let code = "foo(1)\nbar(2)\nbaz(3)\n4";
|
|
393
|
-
let run = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
394
|
-
|
|
395
|
-
// Very tight allocation limit - should still work for simple function calls
|
|
396
|
-
let limits = ResourceLimits::new().max_allocations(100);
|
|
397
|
-
|
|
398
|
-
let progress = run
|
|
399
|
-
.start(vec![], LimitedTracker::new(limits), PrintWriter::Stdout)
|
|
400
|
-
.unwrap();
|
|
401
|
-
let call = resolve_name_lookups(progress)
|
|
402
|
-
.unwrap()
|
|
403
|
-
.into_function_call()
|
|
404
|
-
.expect("first call");
|
|
405
|
-
assert_eq!(call.function_name, "foo");
|
|
406
|
-
assert_eq!(call.args, vec![MontyObject::Int(1)]);
|
|
407
|
-
|
|
408
|
-
let progress = call.resume(MontyObject::None, PrintWriter::Stdout).unwrap();
|
|
409
|
-
let call = resolve_name_lookups(progress)
|
|
410
|
-
.unwrap()
|
|
411
|
-
.into_function_call()
|
|
412
|
-
.expect("second call");
|
|
413
|
-
assert_eq!(call.function_name, "bar");
|
|
414
|
-
assert_eq!(call.args, vec![MontyObject::Int(2)]);
|
|
415
|
-
|
|
416
|
-
let progress = call.resume(MontyObject::None, PrintWriter::Stdout).unwrap();
|
|
417
|
-
let call = resolve_name_lookups(progress)
|
|
418
|
-
.unwrap()
|
|
419
|
-
.into_function_call()
|
|
420
|
-
.expect("third call");
|
|
421
|
-
assert_eq!(call.function_name, "baz");
|
|
422
|
-
assert_eq!(call.args, vec![MontyObject::Int(3)]);
|
|
423
|
-
|
|
424
|
-
let result = call
|
|
425
|
-
.resume(MontyObject::None, PrintWriter::Stdout)
|
|
426
|
-
.unwrap()
|
|
427
|
-
.into_complete()
|
|
428
|
-
.expect("complete");
|
|
429
|
-
assert_eq!(result, MontyObject::Int(4));
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
/// Test that deep recursion triggers memory limit due to namespace tracking.
|
|
433
|
-
///
|
|
434
|
-
/// Function call namespaces (local variables) are tracked by ResourceTracker.
|
|
435
|
-
/// Each recursive call creates a new namespace, which should count against
|
|
436
|
-
/// the memory limit.
|
|
437
|
-
#[test]
|
|
438
|
-
#[cfg_attr(
|
|
439
|
-
feature = "ref-count-panic",
|
|
440
|
-
ignore = "resource exhaustion doesn't guarantee heap state consistency"
|
|
441
|
-
)]
|
|
442
|
-
fn recursion_respects_memory_limit() {
|
|
443
|
-
// Recursive function that creates stack frames with local variables
|
|
444
|
-
let code = r"
|
|
445
|
-
def recurse(n):
|
|
446
|
-
x = 1
|
|
447
|
-
if n > 0:
|
|
448
|
-
return recurse(n - 1)
|
|
449
|
-
return 0
|
|
450
|
-
recurse(1000)
|
|
451
|
-
";
|
|
452
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
453
|
-
|
|
454
|
-
// Very tight memory limit - should fail due to namespace memory
|
|
455
|
-
// Each frame needs at least namespace_size * size_of::<Value>() bytes
|
|
456
|
-
let limits = ResourceLimits::new().max_memory(1000);
|
|
457
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
458
|
-
|
|
459
|
-
assert!(result.is_err(), "should exceed memory limit from recursion");
|
|
460
|
-
let exc = result.unwrap_err();
|
|
461
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
462
|
-
assert!(
|
|
463
|
-
exc.message().is_some_and(|m| m.contains("memory limit exceeded")),
|
|
464
|
-
"expected memory limit error, got: {exc}"
|
|
465
|
-
);
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
/// Test that recursion depth limit returns an error.
|
|
469
|
-
#[test]
|
|
470
|
-
#[cfg_attr(
|
|
471
|
-
feature = "ref-count-panic",
|
|
472
|
-
ignore = "resource exhaustion doesn't guarantee heap state consistency"
|
|
473
|
-
)]
|
|
474
|
-
fn recursion_depth_limit_exceeded() {
|
|
475
|
-
let code = r"
|
|
476
|
-
def recurse(n):
|
|
477
|
-
if n > 0:
|
|
478
|
-
return recurse(n - 1)
|
|
479
|
-
return 0
|
|
480
|
-
recurse(100)
|
|
481
|
-
";
|
|
482
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
483
|
-
|
|
484
|
-
// Set recursion limit to 10
|
|
485
|
-
let limits = ResourceLimits::new().max_recursion_depth(Some(10));
|
|
486
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
487
|
-
|
|
488
|
-
assert!(result.is_err(), "should exceed recursion depth limit");
|
|
489
|
-
let exc = result.unwrap_err();
|
|
490
|
-
assert_eq!(exc.exc_type(), ExcType::RecursionError);
|
|
491
|
-
assert!(
|
|
492
|
-
exc.message()
|
|
493
|
-
.is_some_and(|m| m.contains("maximum recursion depth exceeded")),
|
|
494
|
-
"expected recursion depth error, got: {exc}"
|
|
495
|
-
);
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
#[test]
|
|
499
|
-
fn recursion_depth_limit_not_exceeded() {
|
|
500
|
-
let code = r"
|
|
501
|
-
def recurse(n):
|
|
502
|
-
if n > 0:
|
|
503
|
-
return recurse(n - 1)
|
|
504
|
-
return 0
|
|
505
|
-
recurse(5)
|
|
506
|
-
";
|
|
507
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
508
|
-
|
|
509
|
-
// Set recursion limit to 10 - should succeed with 5 levels
|
|
510
|
-
let limits = ResourceLimits::new().max_recursion_depth(Some(10));
|
|
511
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
512
|
-
|
|
513
|
-
assert!(result.is_ok(), "should not exceed recursion depth limit");
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
// === BigInt large result pre-check tests ===
|
|
517
|
-
// These tests verify that operations that would produce very large BigInt results
|
|
518
|
-
// are rejected before the computation begins, preventing DoS attacks.
|
|
519
|
-
|
|
520
|
-
/// Test that large pow operations are rejected by memory limits.
|
|
521
|
-
#[test]
|
|
522
|
-
fn bigint_pow_memory_limit() {
|
|
523
|
-
// 2 ** 10_000_000 would produce ~1.25MB result
|
|
524
|
-
let code = "2 ** 10000000";
|
|
525
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
526
|
-
|
|
527
|
-
// Set a 1MB memory limit - should fail before computing
|
|
528
|
-
let limits = ResourceLimits::new().max_memory(1_000_000);
|
|
529
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
530
|
-
|
|
531
|
-
assert!(result.is_err(), "large pow should exceed memory limit");
|
|
532
|
-
let exc = result.unwrap_err();
|
|
533
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
534
|
-
assert!(
|
|
535
|
-
exc.message().is_some_and(|m| m.contains("memory limit exceeded")),
|
|
536
|
-
"expected memory limit error, got: {exc}"
|
|
537
|
-
);
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
/// Test that pow with huge exponents is rejected even when the size estimate overflows u64.
|
|
541
|
-
///
|
|
542
|
-
/// This catches a bug where `estimate_pow_bytes` returned `None` on u64 overflow,
|
|
543
|
-
/// and the `if let Some(estimated)` pattern silently skipped the check.
|
|
544
|
-
#[test]
|
|
545
|
-
fn pow_overflowing_estimate_rejected() {
|
|
546
|
-
// base ~63 bits, exp ~62 bits: estimated result bits = 63 * 3962939411543162624 overflows u64
|
|
547
|
-
let code = "-7234189268083315611 ** 3962939411543162624";
|
|
548
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
549
|
-
|
|
550
|
-
let limits = ResourceLimits::new().max_memory(1_000_000);
|
|
551
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
552
|
-
|
|
553
|
-
assert!(result.is_err(), "pow with overflowing estimate should be rejected");
|
|
554
|
-
let exc = result.unwrap_err();
|
|
555
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
556
|
-
assert!(
|
|
557
|
-
exc.message().is_some_and(|m| m.contains("memory limit exceeded")),
|
|
558
|
-
"expected memory limit error, got: {exc}"
|
|
559
|
-
);
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
/// Test that pow with a large base and moderate exponent is rejected by memory limits.
|
|
563
|
-
///
|
|
564
|
-
/// `-7234408281351689115 ** 65327` has a 63-bit base, so the result is ~63*65327 ≈ 4M bits ≈ 514KB.
|
|
565
|
-
/// With a 100KB memory limit the pre-check should reject this before computing.
|
|
566
|
-
#[test]
|
|
567
|
-
fn pow_large_base_moderate_exp_rejected() {
|
|
568
|
-
let code = "-7234408281351689115 ** 65327";
|
|
569
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
570
|
-
|
|
571
|
-
let limits = ResourceLimits::new().max_memory(100_000);
|
|
572
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
573
|
-
|
|
574
|
-
assert!(result.is_err(), "large pow should exceed memory limit");
|
|
575
|
-
let exc = result.unwrap_err();
|
|
576
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
577
|
-
assert!(
|
|
578
|
-
exc.message().is_some_and(|m| m.contains("memory limit exceeded")),
|
|
579
|
-
"expected memory limit error, got: {exc}"
|
|
580
|
-
);
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
/// Test that the 4× safety multiplier for pow intermediate allocations catches
|
|
584
|
-
/// cases where the final result fits but repeated-squaring intermediates don't.
|
|
585
|
-
///
|
|
586
|
-
/// `2 ** 500000`: final result = 2 * 500000 bits = 125KB. Without multiplier this
|
|
587
|
-
/// passes a 200KB limit. With 4× multiplier: 500KB > 200KB → rejected.
|
|
588
|
-
#[test]
|
|
589
|
-
fn pow_intermediate_allocation_multiplier() {
|
|
590
|
-
let code = "2 ** 500000";
|
|
591
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
592
|
-
|
|
593
|
-
// 200KB limit: final result (125KB) fits, but 4× estimate (500KB) exceeds it
|
|
594
|
-
let limits = ResourceLimits::new().max_memory(200_000);
|
|
595
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
596
|
-
|
|
597
|
-
assert!(
|
|
598
|
-
result.is_err(),
|
|
599
|
-
"pow should be rejected due to intermediate allocation overhead"
|
|
600
|
-
);
|
|
601
|
-
let exc = result.unwrap_err();
|
|
602
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
603
|
-
// 2 bits * 500000 = 125KB final, × 4 = 500072 bytes (includes base memory offset)
|
|
604
|
-
assert_eq!(
|
|
605
|
-
exc.message(),
|
|
606
|
-
Some("memory limit exceeded: 500072 bytes > 200000 bytes")
|
|
607
|
-
);
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
/// Test that pow still succeeds when the 4× estimate is within the limit.
|
|
611
|
-
///
|
|
612
|
-
/// `2 ** 100000`: final result = 2 * 100000 bits ≈ 25KB. With 4× multiplier: ~100KB.
|
|
613
|
-
/// A 1MB limit should comfortably allow this.
|
|
614
|
-
#[test]
|
|
615
|
-
fn pow_within_limit_with_multiplier() {
|
|
616
|
-
let code = "x = 2 ** 100000\nx > 0";
|
|
617
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
618
|
-
|
|
619
|
-
let limits = ResourceLimits::new().max_memory(1_000_000);
|
|
620
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
621
|
-
|
|
622
|
-
assert!(result.is_ok(), "pow with 4× estimate under limit should succeed");
|
|
623
|
-
assert_eq!(result.unwrap(), MontyObject::Bool(true));
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
/// Test the exact fuzzer OOM pattern: right-associative chained exponentiation.
|
|
627
|
-
///
|
|
628
|
-
/// `3 ** 3661666` is the first sub-expression of the fuzzer input
|
|
629
|
-
/// `1666**3**366**3**3661666`. Since `**` is right-associative, `3**3661666`
|
|
630
|
-
/// is computed first. Base 3 has 2 bits, so: 2 * 3661666 = 7323332 bits ≈ 915KB.
|
|
631
|
-
/// With 4× multiplier: 3660KB > 1MB fuzz limit → rejected.
|
|
632
|
-
#[test]
|
|
633
|
-
fn pow_fuzzer_oom_chained_exponentiation() {
|
|
634
|
-
// This is the subexpression that caused the fuzzer OOM
|
|
635
|
-
let code = "3 ** 3661666";
|
|
636
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
637
|
-
|
|
638
|
-
// 1MB limit (matching the fuzzer's resource limit)
|
|
639
|
-
let limits = ResourceLimits::new().max_memory(1_024 * 1_024);
|
|
640
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
641
|
-
|
|
642
|
-
assert!(
|
|
643
|
-
result.is_err(),
|
|
644
|
-
"fuzzer OOM pattern should be rejected by 4× multiplier"
|
|
645
|
-
);
|
|
646
|
-
let exc = result.unwrap_err();
|
|
647
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
648
|
-
// 2 bits * 3661666 = 915KB final, × 4 = 3661740 bytes
|
|
649
|
-
assert_eq!(
|
|
650
|
-
exc.message(),
|
|
651
|
-
Some("memory limit exceeded: 3661740 bytes > 1048576 bytes")
|
|
652
|
-
);
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
/// Test the full fuzzer input that originally caused OOM.
|
|
656
|
-
///
|
|
657
|
-
/// The input `1666**3**366**3**3661666` should be rejected before any large
|
|
658
|
-
/// intermediate allocation occurs.
|
|
659
|
-
#[test]
|
|
660
|
-
fn pow_fuzzer_oom_full_input() {
|
|
661
|
-
let code = "1666**3**366**3**3661666";
|
|
662
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
663
|
-
|
|
664
|
-
let limits = ResourceLimits::new().max_memory(1_024 * 1_024);
|
|
665
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
666
|
-
|
|
667
|
-
assert!(result.is_err(), "full fuzzer OOM input should be rejected");
|
|
668
|
-
let exc = result.unwrap_err();
|
|
669
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
670
|
-
// 3**3661666 is evaluated first (right-associative). Base 3 = 2 bits,
|
|
671
|
-
// so estimate = 2 * 3661666 bits = 915KB. With 4× multiplier: 3661740 bytes > 1MB.
|
|
672
|
-
assert_eq!(
|
|
673
|
-
exc.message(),
|
|
674
|
-
Some("memory limit exceeded: 3661740 bytes > 1048576 bytes")
|
|
675
|
-
);
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
/// Test that large left shift operations are rejected by memory limits.
|
|
679
|
-
#[test]
|
|
680
|
-
fn bigint_lshift_memory_limit() {
|
|
681
|
-
// 1 << 10_000_000 would produce ~1.25MB result
|
|
682
|
-
let code = "1 << 10000000";
|
|
683
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
684
|
-
|
|
685
|
-
// Set a 1MB memory limit - should fail before computing
|
|
686
|
-
let limits = ResourceLimits::new().max_memory(1_000_000);
|
|
687
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
688
|
-
|
|
689
|
-
assert!(result.is_err(), "large lshift should exceed memory limit");
|
|
690
|
-
let exc = result.unwrap_err();
|
|
691
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
692
|
-
assert!(
|
|
693
|
-
exc.message().is_some_and(|m| m.contains("memory limit exceeded")),
|
|
694
|
-
"expected memory limit error, got: {exc}"
|
|
695
|
-
);
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
/// Test that large multiplication operations are rejected by memory limits.
|
|
699
|
-
#[test]
|
|
700
|
-
fn bigint_mult_memory_limit() {
|
|
701
|
-
// (2**4_000_000) * (2**4_000_000) would produce ~1MB result
|
|
702
|
-
let code = "big = 2 ** 4000000\nbig * big";
|
|
703
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
704
|
-
|
|
705
|
-
// Set a 1MB memory limit - should fail before computing the multiplication
|
|
706
|
-
let limits = ResourceLimits::new().max_memory(1_000_000);
|
|
707
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
708
|
-
|
|
709
|
-
assert!(result.is_err(), "large mult should exceed memory limit");
|
|
710
|
-
let exc = result.unwrap_err();
|
|
711
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
712
|
-
assert!(
|
|
713
|
-
exc.message().is_some_and(|m| m.contains("memory limit exceeded")),
|
|
714
|
-
"expected memory limit error, got: {exc}"
|
|
715
|
-
);
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
/// Test that small BigInt operations succeed within memory limits.
|
|
719
|
-
#[test]
|
|
720
|
-
fn bigint_small_operations_within_limit() {
|
|
721
|
-
// 2 ** 1000 produces ~125 bytes - well under limit
|
|
722
|
-
let code = "x = 2 ** 1000\ny = 1 << 1000\nz = x * 2\nx > 0 and y > 0 and z > 0";
|
|
723
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
724
|
-
|
|
725
|
-
// Set a 1MB memory limit - should succeed
|
|
726
|
-
let limits = ResourceLimits::new().max_memory(1_000_000);
|
|
727
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
728
|
-
|
|
729
|
-
assert!(result.is_ok(), "small BigInt operations should succeed within limit");
|
|
730
|
-
let val = result.unwrap();
|
|
731
|
-
assert_eq!(val, MontyObject::Bool(true));
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
/// Test that edge cases (0, 1, -1) with huge exponents succeed even with limits.
|
|
735
|
-
/// These produce constant-size results regardless of exponent.
|
|
736
|
-
#[test]
|
|
737
|
-
fn bigint_edge_cases_always_succeed() {
|
|
738
|
-
// Test each edge case individually to minimize other allocations
|
|
739
|
-
// These edge cases produce constant-size results regardless of exponent:
|
|
740
|
-
// - 0 ** huge = 0
|
|
741
|
-
// - 1 ** huge = 1
|
|
742
|
-
// - (-1) ** huge = 1 or -1
|
|
743
|
-
// - 0 << huge = 0
|
|
744
|
-
|
|
745
|
-
// 1MB limit would reject 2**10000000 (~1.25MB) but allows edge cases
|
|
746
|
-
let limits = ResourceLimits::new().max_memory(1_000_000);
|
|
747
|
-
|
|
748
|
-
// 0 ** huge = 0
|
|
749
|
-
let code = "0 ** 10000000";
|
|
750
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
751
|
-
let result = ex.run(vec![], LimitedTracker::new(limits.clone()), PrintWriter::Stdout);
|
|
752
|
-
assert!(result.is_ok(), "0 ** huge should succeed");
|
|
753
|
-
assert_eq!(result.unwrap(), MontyObject::Int(0));
|
|
754
|
-
|
|
755
|
-
// 1 ** huge = 1
|
|
756
|
-
let code = "1 ** 10000000";
|
|
757
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
758
|
-
let result = ex.run(vec![], LimitedTracker::new(limits.clone()), PrintWriter::Stdout);
|
|
759
|
-
assert!(result.is_ok(), "1 ** huge should succeed");
|
|
760
|
-
assert_eq!(result.unwrap(), MontyObject::Int(1));
|
|
761
|
-
|
|
762
|
-
// (-1) ** huge_even = 1
|
|
763
|
-
let code = "(-1) ** 10000000";
|
|
764
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
765
|
-
let result = ex.run(vec![], LimitedTracker::new(limits.clone()), PrintWriter::Stdout);
|
|
766
|
-
assert!(result.is_ok(), "(-1) ** huge_even should succeed");
|
|
767
|
-
assert_eq!(result.unwrap(), MontyObject::Int(1));
|
|
768
|
-
|
|
769
|
-
// (-1) ** huge_odd = -1
|
|
770
|
-
let code = "(-1) ** 10000001";
|
|
771
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
772
|
-
let result = ex.run(vec![], LimitedTracker::new(limits.clone()), PrintWriter::Stdout);
|
|
773
|
-
assert!(result.is_ok(), "(-1) ** huge_odd should succeed");
|
|
774
|
-
assert_eq!(result.unwrap(), MontyObject::Int(-1));
|
|
775
|
-
|
|
776
|
-
// 0 << huge = 0
|
|
777
|
-
let code = "0 << 10000000";
|
|
778
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
779
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
780
|
-
assert!(result.is_ok(), "0 << huge should succeed");
|
|
781
|
-
assert_eq!(result.unwrap(), MontyObject::Int(0));
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
/// Test that pow() builtin also respects memory limits.
|
|
785
|
-
#[test]
|
|
786
|
-
fn bigint_builtin_pow_memory_limit() {
|
|
787
|
-
let code = "pow(2, 10000000)";
|
|
788
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
789
|
-
|
|
790
|
-
let limits = ResourceLimits::new().max_memory(1_000_000);
|
|
791
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
792
|
-
|
|
793
|
-
assert!(result.is_err(), "builtin pow should respect memory limit");
|
|
794
|
-
let exc = result.unwrap_err();
|
|
795
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
/// Test that large BigInt operations are rejected BEFORE allocation via check_large_result.
|
|
799
|
-
///
|
|
800
|
-
/// The pre-allocation size check estimates result size and rejects operations that would
|
|
801
|
-
/// exceed the memory limit before any memory is actually consumed.
|
|
802
|
-
#[test]
|
|
803
|
-
fn bigint_rejected_before_allocation() {
|
|
804
|
-
// 2**1000000: base 2 has 2 bits, so estimate = 2 * 1000000 bits = 250KB
|
|
805
|
-
// With 4× safety multiplier for intermediate allocations = 1000KB
|
|
806
|
-
// Set limit to 100KB - the pre-check should reject before allocating
|
|
807
|
-
let code = "2 ** 1000000";
|
|
808
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
809
|
-
|
|
810
|
-
let limits = ResourceLimits::new().max_memory(100_000); // 100KB limit
|
|
811
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
812
|
-
|
|
813
|
-
assert!(result.is_err(), "should be rejected before allocation");
|
|
814
|
-
let exc = result.unwrap_err();
|
|
815
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
816
|
-
assert_eq!(
|
|
817
|
-
exc.message(),
|
|
818
|
-
Some("memory limit exceeded: 1000072 bytes > 100000 bytes")
|
|
819
|
-
);
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
// === String/Bytes large result pre-check tests ===
|
|
823
|
-
// These tests verify that string/bytes multiplication operations that would produce
|
|
824
|
-
// very large results are rejected before the computation begins.
|
|
825
|
-
|
|
826
|
-
/// Test that large string multiplication is rejected before allocation.
|
|
827
|
-
#[test]
|
|
828
|
-
fn string_mult_memory_limit() {
|
|
829
|
-
// 'x' * 1000000 = 1MB string
|
|
830
|
-
let code = "'x' * 1000000";
|
|
831
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
832
|
-
|
|
833
|
-
let limits = ResourceLimits::new().max_memory(100_000); // 100KB limit
|
|
834
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
835
|
-
|
|
836
|
-
assert!(result.is_err(), "large string mult should be rejected");
|
|
837
|
-
let exc = result.unwrap_err();
|
|
838
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
839
|
-
assert!(
|
|
840
|
-
exc.message().is_some_and(|m| m.contains("memory limit exceeded")),
|
|
841
|
-
"expected memory limit error, got: {exc}"
|
|
842
|
-
);
|
|
843
|
-
}
|
|
844
|
-
|
|
845
|
-
/// Test that large bytes multiplication is rejected before allocation.
|
|
846
|
-
#[test]
|
|
847
|
-
fn bytes_mult_memory_limit() {
|
|
848
|
-
// b'x' * 1000000 = 1MB bytes
|
|
849
|
-
let code = "b'x' * 1000000";
|
|
850
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
851
|
-
|
|
852
|
-
let limits = ResourceLimits::new().max_memory(100_000); // 100KB limit
|
|
853
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
854
|
-
|
|
855
|
-
assert!(result.is_err(), "large bytes mult should be rejected");
|
|
856
|
-
let exc = result.unwrap_err();
|
|
857
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
858
|
-
assert!(
|
|
859
|
-
exc.message().is_some_and(|m| m.contains("memory limit exceeded")),
|
|
860
|
-
"expected memory limit error, got: {exc}"
|
|
861
|
-
);
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
/// Test that small string multiplication works within limits.
|
|
865
|
-
#[test]
|
|
866
|
-
fn string_mult_within_limit() {
|
|
867
|
-
// 'abc' * 100 = 300 bytes, well within 100KB limit
|
|
868
|
-
let code = "'abc' * 100 == 'abc' * 100";
|
|
869
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
870
|
-
|
|
871
|
-
let limits = ResourceLimits::new().max_memory(100_000);
|
|
872
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
873
|
-
|
|
874
|
-
assert!(result.is_ok(), "small string mult should succeed");
|
|
875
|
-
assert_eq!(result.unwrap(), MontyObject::Bool(true));
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
/// Test that small bytes multiplication works within limits.
|
|
879
|
-
#[test]
|
|
880
|
-
fn bytes_mult_within_limit() {
|
|
881
|
-
// b'abc' * 100 = 300 bytes, well within 100KB limit
|
|
882
|
-
let code = "b'abc' * 100 == b'abc' * 100";
|
|
883
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
884
|
-
|
|
885
|
-
let limits = ResourceLimits::new().max_memory(100_000);
|
|
886
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
887
|
-
|
|
888
|
-
assert!(result.is_ok(), "small bytes mult should succeed");
|
|
889
|
-
assert_eq!(result.unwrap(), MontyObject::Bool(true));
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
/// Test that string multiplication is rejected before allocation via check_large_result.
|
|
893
|
-
#[test]
|
|
894
|
-
fn string_mult_rejected_before_allocation() {
|
|
895
|
-
// 'x' * 200000 = 200KB string
|
|
896
|
-
// Set limit to 100KB - the pre-check should reject before allocating
|
|
897
|
-
let code = "'x' * 200000";
|
|
898
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
899
|
-
|
|
900
|
-
let limits = ResourceLimits::new().max_memory(100_000); // 100KB limit
|
|
901
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
902
|
-
|
|
903
|
-
assert!(result.is_err(), "should be rejected before allocation");
|
|
904
|
-
let exc = result.unwrap_err();
|
|
905
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
906
|
-
// The exact size may include some overhead, but should be around 200KB
|
|
907
|
-
assert!(
|
|
908
|
-
exc.message()
|
|
909
|
-
.is_some_and(|m| m.contains("memory limit exceeded") && m.contains("> 100000 bytes")),
|
|
910
|
-
"expected memory limit error with ~200KB size, got: {:?}",
|
|
911
|
-
exc.message()
|
|
912
|
-
);
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
/// Test that large list multiplication is rejected before allocation.
|
|
916
|
-
#[test]
|
|
917
|
-
fn list_mult_memory_limit() {
|
|
918
|
-
// [1] * 10000 = 10,000 Values = ~160KB (at 16 bytes per Value)
|
|
919
|
-
let code = "[1] * 10000";
|
|
920
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
921
|
-
|
|
922
|
-
let limits = ResourceLimits::new().max_memory(100_000); // 100KB limit
|
|
923
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
924
|
-
|
|
925
|
-
assert!(result.is_err(), "large list mult should be rejected");
|
|
926
|
-
let exc = result.unwrap_err();
|
|
927
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
928
|
-
assert!(
|
|
929
|
-
exc.message().is_some_and(|m| m.contains("memory limit exceeded")),
|
|
930
|
-
"expected memory limit error, got: {exc}"
|
|
931
|
-
);
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
/// Test that large tuple multiplication is rejected before allocation.
|
|
935
|
-
#[test]
|
|
936
|
-
fn tuple_mult_memory_limit() {
|
|
937
|
-
// (1,) * 10000 = 10,000 Values = ~160KB (at 16 bytes per Value)
|
|
938
|
-
let code = "(1,) * 10000";
|
|
939
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
940
|
-
|
|
941
|
-
let limits = ResourceLimits::new().max_memory(100_000); // 100KB limit
|
|
942
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
943
|
-
|
|
944
|
-
assert!(result.is_err(), "large tuple mult should be rejected");
|
|
945
|
-
let exc = result.unwrap_err();
|
|
946
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
947
|
-
assert!(
|
|
948
|
-
exc.message().is_some_and(|m| m.contains("memory limit exceeded")),
|
|
949
|
-
"expected memory limit error, got: {exc}"
|
|
950
|
-
);
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
/// Test that small list multiplication works within limits.
|
|
954
|
-
#[test]
|
|
955
|
-
fn list_mult_within_limit() {
|
|
956
|
-
// [1, 2, 3] * 20 = 60 Values, well within 100KB limit
|
|
957
|
-
let code = "[1, 2, 3] * 20 == [1, 2, 3] * 20";
|
|
958
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
959
|
-
|
|
960
|
-
let limits = ResourceLimits::new().max_memory(100_000);
|
|
961
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
962
|
-
|
|
963
|
-
assert!(result.is_ok(), "small list mult should succeed");
|
|
964
|
-
assert_eq!(result.unwrap(), MontyObject::Bool(true));
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
/// Test that `int * bytes` (int on left) is also rejected by the pre-check.
|
|
968
|
-
///
|
|
969
|
-
/// This catches a bug where interned bytes/strings bypassed the `mult_sequence`
|
|
970
|
-
/// pre-check because `py_mult` handled `InternBytes * Int` inline without
|
|
971
|
-
/// checking resource limits.
|
|
972
|
-
#[test]
|
|
973
|
-
fn int_times_bytes_memory_limit() {
|
|
974
|
-
// int on left side: 1000000 * b'x' = 1MB
|
|
975
|
-
let code = "1000000 * b'x'";
|
|
976
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
977
|
-
|
|
978
|
-
let limits = ResourceLimits::new().max_memory(100_000); // 100KB limit
|
|
979
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
980
|
-
|
|
981
|
-
assert!(result.is_err(), "int * bytes should be rejected");
|
|
982
|
-
let exc = result.unwrap_err();
|
|
983
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
984
|
-
assert!(
|
|
985
|
-
exc.message().is_some_and(|m| m.contains("memory limit exceeded")),
|
|
986
|
-
"expected memory limit error, got: {exc}"
|
|
987
|
-
);
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
/// Test that `int * str` (int on left) is also rejected by the pre-check.
|
|
991
|
-
#[test]
|
|
992
|
-
fn int_times_string_memory_limit() {
|
|
993
|
-
// int on left side: 1000000 * 'x' = 1MB
|
|
994
|
-
let code = "1000000 * 'x'";
|
|
995
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
996
|
-
|
|
997
|
-
let limits = ResourceLimits::new().max_memory(100_000); // 100KB limit
|
|
998
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
999
|
-
|
|
1000
|
-
assert!(result.is_err(), "int * str should be rejected");
|
|
1001
|
-
let exc = result.unwrap_err();
|
|
1002
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
1003
|
-
assert!(
|
|
1004
|
-
exc.message().is_some_and(|m| m.contains("memory limit exceeded")),
|
|
1005
|
-
"expected memory limit error, got: {exc}"
|
|
1006
|
-
);
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
/// Test that `bigint * bytes` (LongInt on left) is rejected by the pre-check.
|
|
1010
|
-
#[test]
|
|
1011
|
-
fn longint_times_bytes_memory_limit() {
|
|
1012
|
-
// i64::MAX + 1 = 9223372036854775808, which is a LongInt but fits in usize on 64-bit.
|
|
1013
|
-
// Multiplied by 1-byte bytes literal, this would be ~9.2 exabytes.
|
|
1014
|
-
let code = "9223372036854775808 * b'x'";
|
|
1015
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
1016
|
-
|
|
1017
|
-
let limits = ResourceLimits::new().max_memory(100_000);
|
|
1018
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
1019
|
-
|
|
1020
|
-
assert!(result.is_err(), "bigint * bytes should be rejected");
|
|
1021
|
-
let exc = result.unwrap_err();
|
|
1022
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
1023
|
-
assert!(
|
|
1024
|
-
exc.message().is_some_and(|m| m.contains("memory limit exceeded")),
|
|
1025
|
-
"expected memory limit error, got: {exc}"
|
|
1026
|
-
);
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
/// Test that `bigint * str` (LongInt on left) is rejected by the pre-check.
|
|
1030
|
-
#[test]
|
|
1031
|
-
fn longint_times_string_memory_limit() {
|
|
1032
|
-
// i64::MAX + 1 = 9223372036854775808, which is a LongInt but fits in usize on 64-bit.
|
|
1033
|
-
let code = "9223372036854775808 * 'x'";
|
|
1034
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
1035
|
-
|
|
1036
|
-
let limits = ResourceLimits::new().max_memory(100_000);
|
|
1037
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
1038
|
-
|
|
1039
|
-
assert!(result.is_err(), "bigint * str should be rejected");
|
|
1040
|
-
let exc = result.unwrap_err();
|
|
1041
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
1042
|
-
assert!(
|
|
1043
|
-
exc.message().is_some_and(|m| m.contains("memory limit exceeded")),
|
|
1044
|
-
"expected memory limit error, got: {exc}"
|
|
1045
|
-
);
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
|
-
/// Test that small tuple multiplication works within limits.
|
|
1049
|
-
#[test]
|
|
1050
|
-
fn tuple_mult_within_limit() {
|
|
1051
|
-
// (1, 2, 3) * 20 = 60 Values, well within 100KB limit
|
|
1052
|
-
let code = "(1, 2, 3) * 20 == (1, 2, 3) * 20";
|
|
1053
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
1054
|
-
|
|
1055
|
-
let limits = ResourceLimits::new().max_memory(100_000);
|
|
1056
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
1057
|
-
|
|
1058
|
-
assert!(result.is_ok(), "small tuple mult should succeed");
|
|
1059
|
-
assert_eq!(result.unwrap(), MontyObject::Bool(true));
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
// === Timeout enforcement in builtin iteration loops ===
|
|
1063
|
-
// These tests verify that `max_duration_secs` is enforced inside Rust-side loops
|
|
1064
|
-
// within builtin functions. Previously, builtins like sum(), sorted(), min(), max()
|
|
1065
|
-
// ran Rust loops entirely within a single bytecode instruction, bypassing the VM's
|
|
1066
|
-
// per-instruction timeout check. The fix adds `heap.check_time()` calls inside
|
|
1067
|
-
// `MontyIter::for_next()` and other non-iterator loops.
|
|
1068
|
-
|
|
1069
|
-
/// Helper: runs code with a short time limit and asserts it produces a TimeoutError promptly.
|
|
1070
|
-
fn assert_timeout_in_builtin(code: &str, label: &str) {
|
|
1071
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
1072
|
-
|
|
1073
|
-
let limits = ResourceLimits::new().max_duration(Duration::from_millis(100));
|
|
1074
|
-
let start = std::time::Instant::now();
|
|
1075
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
1076
|
-
let elapsed = start.elapsed();
|
|
1077
|
-
|
|
1078
|
-
assert!(result.is_err(), "{label}: should exceed time limit");
|
|
1079
|
-
let exc = result.unwrap_err();
|
|
1080
|
-
assert_eq!(
|
|
1081
|
-
exc.exc_type(),
|
|
1082
|
-
ExcType::TimeoutError,
|
|
1083
|
-
"{label}: expected TimeoutError, got: {exc}"
|
|
1084
|
-
);
|
|
1085
|
-
assert!(
|
|
1086
|
-
elapsed < Duration::from_secs(2),
|
|
1087
|
-
"{label}: should terminate promptly, took {elapsed:?}"
|
|
1088
|
-
);
|
|
1089
|
-
}
|
|
1090
|
-
|
|
1091
|
-
/// Test that `sum(range(huge))` respects the time limit.
|
|
1092
|
-
///
|
|
1093
|
-
/// `sum()` iterates via `for_next()` which now calls `heap.check_time()`.
|
|
1094
|
-
#[test]
|
|
1095
|
-
fn timeout_in_sum_builtin() {
|
|
1096
|
-
assert_timeout_in_builtin("sum(range(10**18))", "sum(range(10**18))");
|
|
1097
|
-
}
|
|
1098
|
-
|
|
1099
|
-
/// Test that `list(range(huge))` respects the time limit.
|
|
1100
|
-
///
|
|
1101
|
-
/// The `list()` constructor collects via `MontyIter::collect()` -> `for_next()`.
|
|
1102
|
-
#[test]
|
|
1103
|
-
fn timeout_in_list_constructor() {
|
|
1104
|
-
assert_timeout_in_builtin("list(range(10**18))", "list(range(10**18))");
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
/// Test that `sorted(range(huge))` respects the time limit.
|
|
1108
|
-
///
|
|
1109
|
-
/// `sorted()` first collects items via `for_next()`, then sorts. The collection
|
|
1110
|
-
/// phase alone should trigger the timeout for very large ranges.
|
|
1111
|
-
#[test]
|
|
1112
|
-
fn timeout_in_sorted_builtin() {
|
|
1113
|
-
assert_timeout_in_builtin("sorted(range(10**18))", "sorted(range(10**18))");
|
|
1114
|
-
}
|
|
1115
|
-
|
|
1116
|
-
/// Test that `min(range(huge))` respects the time limit.
|
|
1117
|
-
///
|
|
1118
|
-
/// `min()` with a single iterable argument iterates via `for_next()`.
|
|
1119
|
-
#[test]
|
|
1120
|
-
fn timeout_in_min_builtin() {
|
|
1121
|
-
assert_timeout_in_builtin("min(range(10**18))", "min(range(10**18))");
|
|
1122
|
-
}
|
|
1123
|
-
|
|
1124
|
-
/// Test that `max(range(huge))` respects the time limit.
|
|
1125
|
-
///
|
|
1126
|
-
/// `max()` with a single iterable argument iterates via `for_next()`.
|
|
1127
|
-
#[test]
|
|
1128
|
-
fn timeout_in_max_builtin() {
|
|
1129
|
-
assert_timeout_in_builtin("max(range(10**18))", "max(range(10**18))");
|
|
1130
|
-
}
|
|
1131
|
-
|
|
1132
|
-
/// Test that `all(range(huge))` respects the time limit.
|
|
1133
|
-
///
|
|
1134
|
-
/// `all()` iterates via `for_next()` and only short-circuits on falsy values.
|
|
1135
|
-
/// `range(1, 10**18)` produces only truthy values so it keeps iterating.
|
|
1136
|
-
#[test]
|
|
1137
|
-
fn timeout_in_all_builtin() {
|
|
1138
|
-
assert_timeout_in_builtin("all(range(1, 10**18))", "all(range(1, 10**18))");
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
/// Test that `enumerate(range(huge))` iteration respects the time limit.
|
|
1142
|
-
///
|
|
1143
|
-
/// `enumerate()` creates tuples on each iteration via `for_next()`.
|
|
1144
|
-
#[test]
|
|
1145
|
-
#[cfg_attr(
|
|
1146
|
-
feature = "ref-count-panic",
|
|
1147
|
-
ignore = "resource exhaustion doesn't guarantee heap state consistency"
|
|
1148
|
-
)]
|
|
1149
|
-
fn timeout_in_any_builtin() {
|
|
1150
|
-
// range(0, 1) repeated via a for loop calling any on each chunk isn't ideal,
|
|
1151
|
-
// but we can test with a large range starting from 0 where only first element is falsy
|
|
1152
|
-
// Actually, any(range(10**18)) will return True immediately because range starts at 0
|
|
1153
|
-
// which is falsy, but 1 is truthy. So any() returns True after checking 0, 1.
|
|
1154
|
-
// Instead, we need a different approach - just use the for_next timeout via enumerate.
|
|
1155
|
-
assert_timeout_in_builtin("list(enumerate(range(10**18)))", "enumerate(range(10**18))");
|
|
1156
|
-
}
|
|
1157
|
-
|
|
1158
|
-
/// Test that `tuple(range(huge))` respects the time limit.
|
|
1159
|
-
///
|
|
1160
|
-
/// The `tuple()` constructor collects via `MontyIter::collect()` -> `for_next()`.
|
|
1161
|
-
#[test]
|
|
1162
|
-
fn timeout_in_tuple_constructor() {
|
|
1163
|
-
assert_timeout_in_builtin("tuple(range(10**18))", "tuple(range(10**18))");
|
|
1164
|
-
}
|
|
1165
|
-
|
|
1166
|
-
/// Test that `' '.join(...)` iteration respects the time limit.
|
|
1167
|
-
///
|
|
1168
|
-
/// `str.join()` collects items from the iterable via `for_next()`.
|
|
1169
|
-
#[test]
|
|
1170
|
-
fn timeout_in_str_join() {
|
|
1171
|
-
assert_timeout_in_builtin("' '.join(str(i) for i in range(10**18))", "str.join with generator");
|
|
1172
|
-
}
|
|
1173
|
-
|
|
1174
|
-
/// Test that the insertion sort inner loop in `sorted()` respects the time limit.
|
|
1175
|
-
///
|
|
1176
|
-
/// Uses reverse-sorted data to trigger worst-case O(n^2) insertion sort behavior.
|
|
1177
|
-
/// The sort comparison loop has an explicit `heap.check_time()` call.
|
|
1178
|
-
#[test]
|
|
1179
|
-
#[cfg_attr(
|
|
1180
|
-
feature = "ref-count-panic",
|
|
1181
|
-
ignore = "resource exhaustion doesn't guarantee heap state consistency"
|
|
1182
|
-
)]
|
|
1183
|
-
fn timeout_in_sorted_comparison_loop() {
|
|
1184
|
-
// Build a reverse-sorted list, then sort it. Insertion sort on reverse-sorted
|
|
1185
|
-
// data is O(n^2).
|
|
1186
|
-
let code = r"
|
|
1187
|
-
x = list(range(10**6, 0, -1))
|
|
1188
|
-
sorted(x)
|
|
1189
|
-
";
|
|
1190
|
-
assert_timeout_in_builtin(code, "sorted(reversed list)");
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
/// Test that `[1] * 10_000_000` (list repetition) respects the time limit.
|
|
1194
|
-
///
|
|
1195
|
-
/// The `mult_sequence()` copy loop now calls `heap.check_time()` on each
|
|
1196
|
-
/// repetition to prevent large sequence multiplications from bypassing timeout.
|
|
1197
|
-
#[test]
|
|
1198
|
-
#[cfg_attr(
|
|
1199
|
-
feature = "ref-count-panic",
|
|
1200
|
-
ignore = "resource exhaustion doesn't guarantee heap state consistency"
|
|
1201
|
-
)]
|
|
1202
|
-
fn timeout_in_list_repetition() {
|
|
1203
|
-
assert_timeout_in_builtin("[1, 2, 3] * 10_000_000", "list repetition");
|
|
1204
|
-
}
|
|
1205
|
-
|
|
1206
|
-
/// Test that `(1,) * 10_000_000` (tuple repetition) respects the time limit.
|
|
1207
|
-
///
|
|
1208
|
-
/// Same as list repetition but for tuples — both paths in `mult_sequence()`
|
|
1209
|
-
/// now check the time limit.
|
|
1210
|
-
#[test]
|
|
1211
|
-
#[cfg_attr(
|
|
1212
|
-
feature = "ref-count-panic",
|
|
1213
|
-
ignore = "resource exhaustion doesn't guarantee heap state consistency"
|
|
1214
|
-
)]
|
|
1215
|
-
fn timeout_in_tuple_repetition() {
|
|
1216
|
-
assert_timeout_in_builtin("(1, 2, 3) * 10_000_000", "tuple repetition");
|
|
1217
|
-
}
|
|
1218
|
-
|
|
1219
|
-
/// Test that comparing two large equal lists respects the time limit.
|
|
1220
|
-
///
|
|
1221
|
-
/// `List::py_eq()` iterates element-wise comparing pairs. With large equal lists,
|
|
1222
|
-
/// it must compare every element before returning True.
|
|
1223
|
-
#[test]
|
|
1224
|
-
#[cfg_attr(
|
|
1225
|
-
feature = "ref-count-panic",
|
|
1226
|
-
ignore = "resource exhaustion doesn't guarantee heap state consistency"
|
|
1227
|
-
)]
|
|
1228
|
-
fn timeout_in_list_equality() {
|
|
1229
|
-
let code = r"
|
|
1230
|
-
a = list(range(10_000_000))
|
|
1231
|
-
b = list(range(10_000_000))
|
|
1232
|
-
a == b
|
|
1233
|
-
";
|
|
1234
|
-
assert_timeout_in_builtin(code, "list equality");
|
|
1235
|
-
}
|
|
1236
|
-
|
|
1237
|
-
/// Test that comparing two large equal dicts respects the time limit.
|
|
1238
|
-
///
|
|
1239
|
-
/// `Dict::py_eq()` iterates all entries checking keys and values. With large equal
|
|
1240
|
-
/// dicts, it must check every entry before returning True.
|
|
1241
|
-
#[test]
|
|
1242
|
-
#[cfg_attr(
|
|
1243
|
-
feature = "ref-count-panic",
|
|
1244
|
-
ignore = "resource exhaustion doesn't guarantee heap state consistency"
|
|
1245
|
-
)]
|
|
1246
|
-
fn timeout_in_dict_equality() {
|
|
1247
|
-
let code = r"
|
|
1248
|
-
a = {i: i for i in range(10_000_000)}
|
|
1249
|
-
b = {i: i for i in range(10_000_000)}
|
|
1250
|
-
a == b
|
|
1251
|
-
";
|
|
1252
|
-
assert_timeout_in_builtin(code, "dict equality");
|
|
1253
|
-
}
|
|
1254
|
-
|
|
1255
|
-
/// Test that `str.splitlines()` on a large string respects the time limit.
|
|
1256
|
-
///
|
|
1257
|
-
/// `str_splitlines()` scans the entire string for line endings in a while loop
|
|
1258
|
-
/// that now calls `heap.check_time()` on each iteration.
|
|
1259
|
-
#[test]
|
|
1260
|
-
#[cfg_attr(
|
|
1261
|
-
feature = "ref-count-panic",
|
|
1262
|
-
ignore = "resource exhaustion doesn't guarantee heap state consistency"
|
|
1263
|
-
)]
|
|
1264
|
-
fn timeout_in_str_splitlines() {
|
|
1265
|
-
let code = r"
|
|
1266
|
-
s = 'a\n' * 5_000_000
|
|
1267
|
-
s.splitlines()
|
|
1268
|
-
";
|
|
1269
|
-
assert_timeout_in_builtin(code, "str.splitlines()");
|
|
1270
|
-
}
|
|
1271
|
-
|
|
1272
|
-
/// Test that `bytes.splitlines()` on large bytes respects the time limit.
|
|
1273
|
-
///
|
|
1274
|
-
/// `bytes_splitlines()` scans bytes for line endings and now checks the time limit.
|
|
1275
|
-
#[test]
|
|
1276
|
-
#[cfg_attr(
|
|
1277
|
-
feature = "ref-count-panic",
|
|
1278
|
-
ignore = "resource exhaustion doesn't guarantee heap state consistency"
|
|
1279
|
-
)]
|
|
1280
|
-
fn timeout_in_bytes_splitlines() {
|
|
1281
|
-
let code = r"
|
|
1282
|
-
s = b'a\n' * 5_000_000
|
|
1283
|
-
s.splitlines()
|
|
1284
|
-
";
|
|
1285
|
-
assert_timeout_in_builtin(code, "bytes.splitlines()");
|
|
1286
|
-
}
|
|
1287
|
-
|
|
1288
|
-
// === Timeout truncation in repr ===
|
|
1289
|
-
// These tests verify that `repr()` on large containers respects the time limit
|
|
1290
|
-
// and terminates promptly instead of hanging indefinitely. The repr methods
|
|
1291
|
-
// (`repr_sequence_fmt`, `Dict::py_repr_fmt`, `SetInner::repr_fmt`) call
|
|
1292
|
-
// `heap.check_time()` on each iteration and write `...[timeout]` when the
|
|
1293
|
-
// time limit is exceeded, returning normally instead of propagating an error.
|
|
1294
|
-
//
|
|
1295
|
-
// Each test uses the external function "interrupt" pattern: the large object is
|
|
1296
|
-
// built with NO time limit, then execution pauses at `interrupt()`. A short time
|
|
1297
|
-
// limit is set before resuming, so only the `repr()` call is timed.
|
|
1298
|
-
|
|
1299
|
-
/// Helper: builds a large object without time limit, then runs `repr()` on it
|
|
1300
|
-
/// with a short time limit and asserts it produces a TimeoutError promptly.
|
|
1301
|
-
///
|
|
1302
|
-
/// The code must call `interrupt()` between object construction and `repr()`.
|
|
1303
|
-
fn assert_repr_timeout(code: &str, label: &str) {
|
|
1304
|
-
let run = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
1305
|
-
|
|
1306
|
-
// Phase 1: build the large object with no time limit
|
|
1307
|
-
let limits = ResourceLimits::new();
|
|
1308
|
-
let progress = run
|
|
1309
|
-
.start(vec![], LimitedTracker::new(limits), PrintWriter::Stdout)
|
|
1310
|
-
.unwrap();
|
|
1311
|
-
let mut call = resolve_name_lookups(progress)
|
|
1312
|
-
.unwrap()
|
|
1313
|
-
.into_function_call()
|
|
1314
|
-
.expect("interrupt call");
|
|
1315
|
-
assert_eq!(call.function_name, "interrupt");
|
|
1316
|
-
|
|
1317
|
-
// Phase 2: set a short time limit and resume — repr() should timeout
|
|
1318
|
-
call.tracker_mut().set_max_duration(Duration::from_millis(10));
|
|
1319
|
-
|
|
1320
|
-
let start = Instant::now();
|
|
1321
|
-
let result = call.resume(MontyObject::None, PrintWriter::Stdout);
|
|
1322
|
-
let elapsed = start.elapsed();
|
|
1323
|
-
|
|
1324
|
-
let exc = result.unwrap_err();
|
|
1325
|
-
assert_eq!(
|
|
1326
|
-
exc.exc_type(),
|
|
1327
|
-
ExcType::TimeoutError,
|
|
1328
|
-
"{label}: expected TimeoutError, got: {exc}"
|
|
1329
|
-
);
|
|
1330
|
-
let msg = exc.message().unwrap();
|
|
1331
|
-
assert!(msg.starts_with("time limit exceeded:"));
|
|
1332
|
-
assert!(msg.ends_with("ms > 10ms"));
|
|
1333
|
-
assert!(
|
|
1334
|
-
elapsed < Duration::from_millis(200),
|
|
1335
|
-
"{label}: should terminate promptly, took {elapsed:?}"
|
|
1336
|
-
);
|
|
1337
|
-
}
|
|
1338
|
-
|
|
1339
|
-
/// Test that `repr(large_list)` respects the time limit.
|
|
1340
|
-
///
|
|
1341
|
-
/// Uses a list of 100K short strings so that repr formatting is slow enough
|
|
1342
|
-
/// to trigger the timeout.
|
|
1343
|
-
#[test]
|
|
1344
|
-
#[cfg_attr(
|
|
1345
|
-
feature = "ref-count-panic",
|
|
1346
|
-
ignore = "resource exhaustion doesn't guarantee heap state consistency"
|
|
1347
|
-
)]
|
|
1348
|
-
fn timeout_truncation_in_list_repr() {
|
|
1349
|
-
let code = r"
|
|
1350
|
-
x = ['abcdefghij'] * 100_000
|
|
1351
|
-
interrupt()
|
|
1352
|
-
repr(x)
|
|
1353
|
-
";
|
|
1354
|
-
assert_repr_timeout(code, "list repr");
|
|
1355
|
-
}
|
|
1356
|
-
|
|
1357
|
-
/// Test that `repr(large_dict)` respects the time limit.
|
|
1358
|
-
///
|
|
1359
|
-
/// Uses a dict with 100K entries where values are short strings,
|
|
1360
|
-
/// making repr formatting slow enough to trigger the timeout.
|
|
1361
|
-
#[test]
|
|
1362
|
-
#[cfg_attr(
|
|
1363
|
-
feature = "ref-count-panic",
|
|
1364
|
-
ignore = "resource exhaustion doesn't guarantee heap state consistency"
|
|
1365
|
-
)]
|
|
1366
|
-
fn timeout_truncation_in_dict_repr() {
|
|
1367
|
-
let code = r"
|
|
1368
|
-
x = {i: 'abcdefghij' for i in range(100_000)}
|
|
1369
|
-
interrupt()
|
|
1370
|
-
repr(x)
|
|
1371
|
-
";
|
|
1372
|
-
assert_repr_timeout(code, "dict repr");
|
|
1373
|
-
}
|
|
1374
|
-
|
|
1375
|
-
/// Test that `repr(large_set)` respects the time limit.
|
|
1376
|
-
///
|
|
1377
|
-
/// Uses a set of 100K unique strings so that repr formatting is slow enough
|
|
1378
|
-
/// to trigger the timeout.
|
|
1379
|
-
#[test]
|
|
1380
|
-
#[cfg_attr(
|
|
1381
|
-
feature = "ref-count-panic",
|
|
1382
|
-
ignore = "resource exhaustion doesn't guarantee heap state consistency"
|
|
1383
|
-
)]
|
|
1384
|
-
fn timeout_truncation_in_set_repr() {
|
|
1385
|
-
let code = r"
|
|
1386
|
-
x = {str(i) for i in range(100_000)}
|
|
1387
|
-
interrupt()
|
|
1388
|
-
repr(x)
|
|
1389
|
-
";
|
|
1390
|
-
assert_repr_timeout(code, "set repr");
|
|
1391
|
-
}
|
|
1392
|
-
|
|
1393
|
-
/// Test that `str.replace` with amplification is rejected before allocation.
|
|
1394
|
-
///
|
|
1395
|
-
/// `'a' * 1000` is 1KB (within limit), but replacing each 'a' with a 1KB string
|
|
1396
|
-
/// produces a 1MB result. The pre-check should reject this before `String::replace()`
|
|
1397
|
-
/// allocates the result on the Rust heap.
|
|
1398
|
-
#[test]
|
|
1399
|
-
fn str_replace_amplification_memory_limit() {
|
|
1400
|
-
let code = r"
|
|
1401
|
-
s = 'a' * 1000
|
|
1402
|
-
s.replace('a', 'b' * 1000)
|
|
1403
|
-
";
|
|
1404
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
1405
|
-
|
|
1406
|
-
let limits = ResourceLimits::new().max_memory(500_000); // 500KB limit
|
|
1407
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
1408
|
-
|
|
1409
|
-
assert!(result.is_err(), "str.replace amplification should be rejected");
|
|
1410
|
-
let exc = result.unwrap_err();
|
|
1411
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
1412
|
-
assert!(
|
|
1413
|
-
exc.message().is_some_and(|m| m.contains("memory limit exceeded")),
|
|
1414
|
-
"expected memory limit error, got: {exc}"
|
|
1415
|
-
);
|
|
1416
|
-
}
|
|
1417
|
-
|
|
1418
|
-
/// Test that small `str.replace` works within limits.
|
|
1419
|
-
#[test]
|
|
1420
|
-
fn str_replace_within_limit() {
|
|
1421
|
-
let code = "'hello world'.replace('world', 'rust') == 'hello rust'";
|
|
1422
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
1423
|
-
|
|
1424
|
-
let limits = ResourceLimits::new().max_memory(100_000);
|
|
1425
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
1426
|
-
|
|
1427
|
-
assert!(result.is_ok(), "small str.replace should succeed");
|
|
1428
|
-
assert_eq!(result.unwrap(), MontyObject::Bool(true));
|
|
1429
|
-
}
|
|
1430
|
-
|
|
1431
|
-
/// Test that `bytes.replace` with amplification is rejected before allocation.
|
|
1432
|
-
#[test]
|
|
1433
|
-
fn bytes_replace_amplification_memory_limit() {
|
|
1434
|
-
let code = r"
|
|
1435
|
-
s = b'a' * 1000
|
|
1436
|
-
s.replace(b'a', b'b' * 1000)
|
|
1437
|
-
";
|
|
1438
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
1439
|
-
|
|
1440
|
-
let limits = ResourceLimits::new().max_memory(500_000);
|
|
1441
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
1442
|
-
|
|
1443
|
-
assert!(result.is_err(), "bytes.replace amplification should be rejected");
|
|
1444
|
-
let exc = result.unwrap_err();
|
|
1445
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
1446
|
-
assert!(
|
|
1447
|
-
exc.message().is_some_and(|m| m.contains("memory limit exceeded")),
|
|
1448
|
-
"expected memory limit error, got: {exc}"
|
|
1449
|
-
);
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
|
-
/// Test that `str.replace` with empty pattern amplification is rejected.
|
|
1453
|
-
///
|
|
1454
|
-
/// Empty pattern inserts `new` before each char and after the last, so
|
|
1455
|
-
/// result size = input_len * (new_len + 1).
|
|
1456
|
-
#[test]
|
|
1457
|
-
fn str_replace_empty_pattern_memory_limit() {
|
|
1458
|
-
let code = r"
|
|
1459
|
-
s = 'a' * 500
|
|
1460
|
-
s.replace('', 'x' * 1000)
|
|
1461
|
-
";
|
|
1462
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
1463
|
-
|
|
1464
|
-
let limits = ResourceLimits::new().max_memory(200_000);
|
|
1465
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
1466
|
-
|
|
1467
|
-
assert!(
|
|
1468
|
-
result.is_err(),
|
|
1469
|
-
"str.replace with empty pattern amplification should be rejected"
|
|
1470
|
-
);
|
|
1471
|
-
let exc = result.unwrap_err();
|
|
1472
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
1473
|
-
}
|
|
1474
|
-
|
|
1475
|
-
/// Test that `str.ljust` with huge width is rejected before allocation.
|
|
1476
|
-
///
|
|
1477
|
-
/// Without the pre-check, `String::with_capacity(width)` would allocate
|
|
1478
|
-
/// directly on the Rust heap, bypassing the memory tracker entirely.
|
|
1479
|
-
#[test]
|
|
1480
|
-
fn str_ljust_memory_limit() {
|
|
1481
|
-
let code = "'x'.ljust(2000000)";
|
|
1482
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
1483
|
-
|
|
1484
|
-
let limits = ResourceLimits::new().max_memory(100_000);
|
|
1485
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
1486
|
-
|
|
1487
|
-
assert!(result.is_err(), "str.ljust with huge width should be rejected");
|
|
1488
|
-
let exc = result.unwrap_err();
|
|
1489
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
1490
|
-
assert!(
|
|
1491
|
-
exc.message().is_some_and(|m| m.contains("memory limit exceeded")),
|
|
1492
|
-
"expected memory limit error, got: {exc}"
|
|
1493
|
-
);
|
|
1494
|
-
}
|
|
1495
|
-
|
|
1496
|
-
/// Test that `str.rjust` with huge width is rejected before allocation.
|
|
1497
|
-
#[test]
|
|
1498
|
-
fn str_rjust_memory_limit() {
|
|
1499
|
-
let code = "'x'.rjust(2000000)";
|
|
1500
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
1501
|
-
|
|
1502
|
-
let limits = ResourceLimits::new().max_memory(100_000);
|
|
1503
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
1504
|
-
|
|
1505
|
-
assert!(result.is_err(), "str.rjust with huge width should be rejected");
|
|
1506
|
-
let exc = result.unwrap_err();
|
|
1507
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
1508
|
-
}
|
|
1509
|
-
|
|
1510
|
-
/// Test that `str.center` with huge width is rejected before allocation.
|
|
1511
|
-
#[test]
|
|
1512
|
-
fn str_center_memory_limit() {
|
|
1513
|
-
let code = "'x'.center(2000000)";
|
|
1514
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
1515
|
-
|
|
1516
|
-
let limits = ResourceLimits::new().max_memory(100_000);
|
|
1517
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
1518
|
-
|
|
1519
|
-
assert!(result.is_err(), "str.center with huge width should be rejected");
|
|
1520
|
-
let exc = result.unwrap_err();
|
|
1521
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
1522
|
-
}
|
|
1523
|
-
|
|
1524
|
-
/// Test that `str.zfill` with huge width is rejected before allocation.
|
|
1525
|
-
#[test]
|
|
1526
|
-
fn str_zfill_memory_limit() {
|
|
1527
|
-
let code = "'42'.zfill(2000000)";
|
|
1528
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
1529
|
-
|
|
1530
|
-
let limits = ResourceLimits::new().max_memory(100_000);
|
|
1531
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
1532
|
-
|
|
1533
|
-
assert!(result.is_err(), "str.zfill with huge width should be rejected");
|
|
1534
|
-
let exc = result.unwrap_err();
|
|
1535
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
1536
|
-
}
|
|
1537
|
-
|
|
1538
|
-
/// Test that small padding operations work within limits.
|
|
1539
|
-
#[test]
|
|
1540
|
-
fn str_padding_within_limit() {
|
|
1541
|
-
let code = "'hi'.ljust(10) == 'hi '";
|
|
1542
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
1543
|
-
|
|
1544
|
-
let limits = ResourceLimits::new().max_memory(100_000);
|
|
1545
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
1546
|
-
|
|
1547
|
-
assert!(result.is_ok(), "small padding should succeed");
|
|
1548
|
-
assert_eq!(result.unwrap(), MontyObject::Bool(true));
|
|
1549
|
-
}
|
|
1550
|
-
|
|
1551
|
-
/// Test that `bytes.ljust` with huge width is rejected before allocation.
|
|
1552
|
-
#[test]
|
|
1553
|
-
fn bytes_ljust_memory_limit() {
|
|
1554
|
-
let code = "b'x'.ljust(2000000)";
|
|
1555
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
1556
|
-
|
|
1557
|
-
let limits = ResourceLimits::new().max_memory(100_000);
|
|
1558
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
1559
|
-
|
|
1560
|
-
assert!(result.is_err(), "bytes.ljust with huge width should be rejected");
|
|
1561
|
-
let exc = result.unwrap_err();
|
|
1562
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
1563
|
-
}
|
|
1564
|
-
|
|
1565
|
-
/// Test that `bytes.rjust` with huge width is rejected before allocation.
|
|
1566
|
-
#[test]
|
|
1567
|
-
fn bytes_rjust_memory_limit() {
|
|
1568
|
-
let code = "b'x'.rjust(2000000)";
|
|
1569
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
1570
|
-
|
|
1571
|
-
let limits = ResourceLimits::new().max_memory(100_000);
|
|
1572
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
1573
|
-
|
|
1574
|
-
assert!(result.is_err(), "bytes.rjust with huge width should be rejected");
|
|
1575
|
-
let exc = result.unwrap_err();
|
|
1576
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
1577
|
-
}
|
|
1578
|
-
|
|
1579
|
-
/// Test that `bytes.center` with huge width is rejected before allocation.
|
|
1580
|
-
#[test]
|
|
1581
|
-
fn bytes_center_memory_limit() {
|
|
1582
|
-
let code = "b'x'.center(2000000)";
|
|
1583
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
1584
|
-
|
|
1585
|
-
let limits = ResourceLimits::new().max_memory(100_000);
|
|
1586
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
1587
|
-
|
|
1588
|
-
assert!(result.is_err(), "bytes.center with huge width should be rejected");
|
|
1589
|
-
let exc = result.unwrap_err();
|
|
1590
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
1591
|
-
}
|
|
1592
|
-
|
|
1593
|
-
/// Test that `bytes.zfill` with huge width is rejected before allocation.
|
|
1594
|
-
#[test]
|
|
1595
|
-
fn bytes_zfill_memory_limit() {
|
|
1596
|
-
let code = "b'42'.zfill(2000000)";
|
|
1597
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
1598
|
-
|
|
1599
|
-
let limits = ResourceLimits::new().max_memory(100_000);
|
|
1600
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
1601
|
-
|
|
1602
|
-
assert!(result.is_err(), "bytes.zfill with huge width should be rejected");
|
|
1603
|
-
let exc = result.unwrap_err();
|
|
1604
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
1605
|
-
}
|
|
1606
|
-
|
|
1607
|
-
/// Test that f-string formatting with huge width is rejected before allocation.
|
|
1608
|
-
#[test]
|
|
1609
|
-
fn fstring_dynamic_width_memory_limit() {
|
|
1610
|
-
// Dynamic format spec via f-string nesting: {w} produces a runtime-parsed spec
|
|
1611
|
-
let code = "w = 2000000\nf\"{'x':>{w}}\"";
|
|
1612
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
1613
|
-
|
|
1614
|
-
let limits = ResourceLimits::new().max_memory(100_000);
|
|
1615
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
1616
|
-
|
|
1617
|
-
assert!(result.is_err(), "f-string with huge dynamic width should be rejected");
|
|
1618
|
-
let exc = result.unwrap_err();
|
|
1619
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
1620
|
-
}
|
|
1621
|
-
|
|
1622
|
-
// === re.sub() memory tracking tests ===
|
|
1623
|
-
// These tests verify that the single-pass replacement loop in `re.sub()` tracks
|
|
1624
|
-
// the running output size and bails out when the resource limit is exceeded.
|
|
1625
|
-
|
|
1626
|
-
/// Test that `re.sub` with every-char pattern amplification is rejected.
|
|
1627
|
-
///
|
|
1628
|
-
/// Pattern 'a' matches every character in 'aaa...'. Each replacement expands
|
|
1629
|
-
/// 1 byte → 1000 bytes, so the output grows to ~1MB which exceeds the 500KB limit.
|
|
1630
|
-
/// The inline loop catches this after a few hundred matches.
|
|
1631
|
-
#[test]
|
|
1632
|
-
fn re_sub_amplification_memory_limit() {
|
|
1633
|
-
let code = r"
|
|
1634
|
-
import re
|
|
1635
|
-
s = 'a' * 1000
|
|
1636
|
-
re.sub('a', 'b' * 1000, s)
|
|
1637
|
-
";
|
|
1638
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
1639
|
-
|
|
1640
|
-
let limits = ResourceLimits::new().max_memory(500_000);
|
|
1641
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
1642
|
-
|
|
1643
|
-
assert!(result.is_err(), "re.sub amplification should be rejected");
|
|
1644
|
-
let exc = result.unwrap_err();
|
|
1645
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
1646
|
-
assert!(
|
|
1647
|
-
exc.message().is_some_and(|m| m.contains("memory limit exceeded")),
|
|
1648
|
-
"expected memory limit error, got: {exc}"
|
|
1649
|
-
);
|
|
1650
|
-
}
|
|
1651
|
-
|
|
1652
|
-
/// Test that `re.sub` with empty pattern amplification is rejected.
|
|
1653
|
-
///
|
|
1654
|
-
/// Empty pattern matches N+1 times for N-char input (between and around every
|
|
1655
|
-
/// character). Each match inserts 1000 bytes, so 501 matches × 1000 ≈ 500KB
|
|
1656
|
-
/// which exceeds the 200KB limit.
|
|
1657
|
-
#[test]
|
|
1658
|
-
fn re_sub_empty_pattern_amplification_memory_limit() {
|
|
1659
|
-
let code = r"
|
|
1660
|
-
import re
|
|
1661
|
-
s = 'a' * 500
|
|
1662
|
-
re.sub('', 'x' * 1000, s)
|
|
1663
|
-
";
|
|
1664
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
1665
|
-
|
|
1666
|
-
let limits = ResourceLimits::new().max_memory(200_000);
|
|
1667
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
1668
|
-
|
|
1669
|
-
assert!(
|
|
1670
|
-
result.is_err(),
|
|
1671
|
-
"re.sub with empty pattern amplification should be rejected"
|
|
1672
|
-
);
|
|
1673
|
-
let exc = result.unwrap_err();
|
|
1674
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
1675
|
-
}
|
|
1676
|
-
|
|
1677
|
-
/// Test that `pattern.sub` (compiled pattern method) is also rejected.
|
|
1678
|
-
#[test]
|
|
1679
|
-
fn re_pattern_sub_amplification_memory_limit() {
|
|
1680
|
-
let code = r"
|
|
1681
|
-
import re
|
|
1682
|
-
p = re.compile('a')
|
|
1683
|
-
s = 'a' * 1000
|
|
1684
|
-
p.sub('b' * 1000, s)
|
|
1685
|
-
";
|
|
1686
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
1687
|
-
|
|
1688
|
-
let limits = ResourceLimits::new().max_memory(500_000);
|
|
1689
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
1690
|
-
|
|
1691
|
-
assert!(result.is_err(), "pattern.sub amplification should be rejected");
|
|
1692
|
-
let exc = result.unwrap_err();
|
|
1693
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
1694
|
-
}
|
|
1695
|
-
|
|
1696
|
-
/// Test that `re.sub` raises `re.PatternError` when the regex engine hits its backtracking limit.
|
|
1697
|
-
///
|
|
1698
|
-
/// The pattern `(a+)+\1b` forces `fancy_regex` into its backtracking VM (due to the
|
|
1699
|
-
/// backreference `\1`). With enough `a`s followed by a non-matching character, the
|
|
1700
|
-
/// exponential blowup exceeds the engine's backtracking step limit (~1M steps).
|
|
1701
|
-
#[test]
|
|
1702
|
-
fn re_sub_backtracking_limit_raises_pattern_error() {
|
|
1703
|
-
let code = r"
|
|
1704
|
-
import re
|
|
1705
|
-
re.sub('(a+)+\\1b', 'X', 'a' * 30 + 'c')
|
|
1706
|
-
";
|
|
1707
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
1708
|
-
|
|
1709
|
-
let limits = ResourceLimits::new().max_memory(500_000);
|
|
1710
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
1711
|
-
|
|
1712
|
-
assert!(result.is_err(), "backtracking limit should raise an error");
|
|
1713
|
-
let exc = result.unwrap_err();
|
|
1714
|
-
assert_eq!(exc.exc_type(), ExcType::RePatternError);
|
|
1715
|
-
assert!(
|
|
1716
|
-
exc.message().is_some_and(|m| m.contains("backtrack")),
|
|
1717
|
-
"expected backtracking error, got: {exc}"
|
|
1718
|
-
);
|
|
1719
|
-
}
|
|
1720
|
-
|
|
1721
|
-
// --- Selective patterns: few matches in large text stay within limits ---
|
|
1722
|
-
|
|
1723
|
-
/// Test that a selective pattern on large text passes.
|
|
1724
|
-
///
|
|
1725
|
-
/// The pattern `xxx` only matches 3 times (at positions 0, 3, 6 in the 9-char prefix),
|
|
1726
|
-
/// so the result is ~10000 - 9 + 300 = 10291 bytes — well within the 500KB limit.
|
|
1727
|
-
#[test]
|
|
1728
|
-
fn re_sub_selective_pattern_passes() {
|
|
1729
|
-
// 'xxx' repeated 3 times at the start, rest is 'a's
|
|
1730
|
-
let code = r"
|
|
1731
|
-
import re
|
|
1732
|
-
s = 'xxx' * 3 + 'a' * 9991
|
|
1733
|
-
result = re.sub('xxx', 'y' * 100, s)
|
|
1734
|
-
len(result) == 9991 + 3 * 100
|
|
1735
|
-
";
|
|
1736
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
1737
|
-
|
|
1738
|
-
let limits = ResourceLimits::new().max_memory(500_000);
|
|
1739
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
1740
|
-
|
|
1741
|
-
assert!(
|
|
1742
|
-
result.is_ok(),
|
|
1743
|
-
"selective pattern with few matches should pass: {result:?}"
|
|
1744
|
-
);
|
|
1745
|
-
assert_eq!(result.unwrap(), MontyObject::Bool(true));
|
|
1746
|
-
}
|
|
1747
|
-
|
|
1748
|
-
/// Test that a digit-matching pattern on mostly-text input passes.
|
|
1749
|
-
///
|
|
1750
|
-
/// Pattern `\d+` matches only the 10-digit number, so the result is
|
|
1751
|
-
/// 990 + 200 = 1190 bytes — well within the 150KB limit.
|
|
1752
|
-
#[test]
|
|
1753
|
-
fn re_sub_digit_pattern_passes() {
|
|
1754
|
-
let code = r"
|
|
1755
|
-
import re
|
|
1756
|
-
s = 'a' * 990 + '1234567890'
|
|
1757
|
-
result = re.sub('\d+', 'X' * 200, s)
|
|
1758
|
-
len(result) == 990 + 200
|
|
1759
|
-
";
|
|
1760
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
1761
|
-
|
|
1762
|
-
let limits = ResourceLimits::new().max_memory(150_000);
|
|
1763
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
1764
|
-
|
|
1765
|
-
assert!(result.is_ok(), "digit pattern on mostly-text should pass: {result:?}");
|
|
1766
|
-
assert_eq!(result.unwrap(), MontyObject::Bool(true));
|
|
1767
|
-
}
|
|
1768
|
-
|
|
1769
|
-
/// Test that every-char amplification is still rejected even with a generic pattern.
|
|
1770
|
-
///
|
|
1771
|
-
/// Pattern `.` matches every character (10000 matches), each expanding 1 → 1000 bytes.
|
|
1772
|
-
/// The inline loop catches this after a few hundred matches once the running output
|
|
1773
|
-
/// size exceeds the 500KB limit.
|
|
1774
|
-
#[test]
|
|
1775
|
-
fn re_sub_every_char_amplification_rejected() {
|
|
1776
|
-
let code = r"
|
|
1777
|
-
import re
|
|
1778
|
-
s = 'a' * 10000
|
|
1779
|
-
re.sub('.', 'b' * 1000, s)
|
|
1780
|
-
";
|
|
1781
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
1782
|
-
|
|
1783
|
-
let limits = ResourceLimits::new().max_memory(500_000);
|
|
1784
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
1785
|
-
|
|
1786
|
-
assert!(result.is_err(), "every-char pattern amplification should be rejected");
|
|
1787
|
-
let exc = result.unwrap_err();
|
|
1788
|
-
assert_eq!(exc.exc_type(), ExcType::MemoryError);
|
|
1789
|
-
}
|
|
1790
|
-
|
|
1791
|
-
// --- General re.sub tests ---
|
|
1792
|
-
|
|
1793
|
-
/// Test that small `re.sub` works within limits.
|
|
1794
|
-
#[test]
|
|
1795
|
-
fn re_sub_within_limit() {
|
|
1796
|
-
let code = r"
|
|
1797
|
-
import re
|
|
1798
|
-
re.sub('world', 'rust', 'hello world') == 'hello rust'
|
|
1799
|
-
";
|
|
1800
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
1801
|
-
|
|
1802
|
-
let limits = ResourceLimits::new().max_memory(100_000);
|
|
1803
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
1804
|
-
|
|
1805
|
-
assert!(result.is_ok(), "small re.sub should succeed");
|
|
1806
|
-
assert_eq!(result.unwrap(), MontyObject::Bool(true));
|
|
1807
|
-
}
|
|
1808
|
-
|
|
1809
|
-
/// Test that `re.sub` with count parameter limits replacements correctly.
|
|
1810
|
-
///
|
|
1811
|
-
/// `count=5` caps replacements to 5, so the result is
|
|
1812
|
-
/// 995 unchanged bytes + 5 × 100 replacement bytes = 1495 bytes.
|
|
1813
|
-
#[test]
|
|
1814
|
-
fn re_sub_with_count_within_limit() {
|
|
1815
|
-
let code = r"
|
|
1816
|
-
import re
|
|
1817
|
-
re.sub('a', 'b' * 100, 'a' * 1000, count=5) == 'b' * 500 + 'a' * 995
|
|
1818
|
-
";
|
|
1819
|
-
let ex = MontyRun::new(code.to_owned(), "test.py", vec![]).unwrap();
|
|
1820
|
-
|
|
1821
|
-
let limits = ResourceLimits::new().max_memory(500_000);
|
|
1822
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
1823
|
-
|
|
1824
|
-
assert!(result.is_ok(), "re.sub with small count should succeed");
|
|
1825
|
-
assert_eq!(result.unwrap(), MontyObject::Bool(true));
|
|
1826
|
-
}
|