superacli 1.1.6 → 1.1.8
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/CONTRIBUTING.md +270 -0
- package/README.md +141 -54
- package/__tests__/adapter-schema.test.js +251 -86
- 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__/resend-plugin.test.js +109 -82
- 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 +50 -7
- 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 +4 -3
- 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 +255 -63
- 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/index.html +183 -123
- 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/index.html +384 -0
- package/package.json +2 -2
- 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 +199 -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/docs.html +0 -224
- 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,2029 +0,0 @@
|
|
|
1
|
-
use std::{
|
|
2
|
-
cell::RefCell,
|
|
3
|
-
collections::{HashMap, HashSet},
|
|
4
|
-
error::Error,
|
|
5
|
-
ffi::CString,
|
|
6
|
-
fs,
|
|
7
|
-
panic::{self, AssertUnwindSafe},
|
|
8
|
-
path::Path,
|
|
9
|
-
sync::{
|
|
10
|
-
OnceLock,
|
|
11
|
-
mpsc::{self, RecvTimeoutError},
|
|
12
|
-
},
|
|
13
|
-
thread,
|
|
14
|
-
time::Duration,
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
use ahash::AHashMap;
|
|
18
|
-
use monty::{
|
|
19
|
-
ExcType, ExtFunctionResult, LimitedTracker, MontyException, MontyObject, MontyRun, NameLookupResult, OsFunction,
|
|
20
|
-
PrintWriter, ResourceLimits, RunProgress, dir_stat, file_stat,
|
|
21
|
-
};
|
|
22
|
-
use pyo3::{prelude::*, types::PyDict};
|
|
23
|
-
use similar::TextDiff;
|
|
24
|
-
|
|
25
|
-
/// Recursion limit for test execution.
|
|
26
|
-
///
|
|
27
|
-
/// Used for both Monty and CPython tests. CPython needs ~5 extra frames
|
|
28
|
-
/// for runpy overhead, which is added in run_file_and_get_traceback.
|
|
29
|
-
///
|
|
30
|
-
/// NOTE this value is chosen to avoid both:
|
|
31
|
-
/// * other recursion errors in python (if it's too low)
|
|
32
|
-
/// * and, stack overflows in debug rust (if it's too high)
|
|
33
|
-
const TEST_RECURSION_LIMIT: usize = 50;
|
|
34
|
-
|
|
35
|
-
/// Test configuration parsed from directive comments.
|
|
36
|
-
///
|
|
37
|
-
/// Parsed from an optional first-line comment like `# xfail=monty,cpython` or `# call-external`.
|
|
38
|
-
/// If not present, defaults to running on both interpreters in standard mode.
|
|
39
|
-
///
|
|
40
|
-
/// ## Xfail Semantics (Strict)
|
|
41
|
-
/// - `xfail=monty` - Test is expected to fail on Monty; if it passes, that's an error
|
|
42
|
-
/// - `xfail=cpython` - Test is expected to fail on CPython; if it passes, that's an error
|
|
43
|
-
/// - `xfail=monty,cpython` - Expected to fail on both interpreters
|
|
44
|
-
#[derive(Debug, Clone, Default)]
|
|
45
|
-
#[expect(clippy::struct_excessive_bools)]
|
|
46
|
-
struct TestConfig {
|
|
47
|
-
/// When true, test is expected to fail on Monty (strict xfail).
|
|
48
|
-
xfail_monty: bool,
|
|
49
|
-
/// When true, test is expected to fail on CPython (strict xfail).
|
|
50
|
-
xfail_cpython: bool,
|
|
51
|
-
/// When true, use MontyRun with external function support instead of MontyRun.
|
|
52
|
-
iter_mode: bool,
|
|
53
|
-
/// When true, wrap code in async context for CPython execution.
|
|
54
|
-
/// Used for tests with top-level await which Monty supports but CPython doesn't.
|
|
55
|
-
async_mode: bool,
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/// Represents the expected outcome of a test fixture
|
|
59
|
-
#[derive(Debug, Clone)]
|
|
60
|
-
enum Expectation {
|
|
61
|
-
/// Expect exception (parse-time or runtime) with specific message
|
|
62
|
-
Raise(String),
|
|
63
|
-
/// Expect successful execution, check py_str() output
|
|
64
|
-
ReturnStr(String),
|
|
65
|
-
/// Expect successful execution, check py_repr() output
|
|
66
|
-
Return(String),
|
|
67
|
-
/// Expect successful execution, check py_type() output
|
|
68
|
-
ReturnType(String),
|
|
69
|
-
/// Expect successful execution, check ref counts of named variables.
|
|
70
|
-
/// Only used when `ref-count-return` feature is enabled; skipped otherwise.
|
|
71
|
-
RefCounts(#[cfg_attr(not(feature = "ref-count-return"), expect(dead_code))] AHashMap<String, usize>),
|
|
72
|
-
/// Expect exception with full traceback comparison.
|
|
73
|
-
/// The expected traceback string should match exactly between Monty and CPython.
|
|
74
|
-
Traceback(String),
|
|
75
|
-
/// Expect successful execution without raising an exception (no return value check).
|
|
76
|
-
/// Used for tests that rely on asserts or just verify code runs.
|
|
77
|
-
NoException,
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
impl Expectation {
|
|
81
|
-
/// Returns the expected value string
|
|
82
|
-
fn expected_value(&self) -> &str {
|
|
83
|
-
match self {
|
|
84
|
-
Self::Raise(s) | Self::ReturnStr(s) | Self::Return(s) | Self::ReturnType(s) | Self::Traceback(s) => s,
|
|
85
|
-
Self::RefCounts(_) | Self::NoException => "",
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/// Parse a Python fixture file into code, expected outcome, and test configuration.
|
|
91
|
-
///
|
|
92
|
-
/// The file may optionally contain a `# xfail=monty,cpython` comment to specify
|
|
93
|
-
/// which interpreters the test is expected to fail on. If not present, defaults to
|
|
94
|
-
/// running on both and expecting success.
|
|
95
|
-
///
|
|
96
|
-
/// The file may have an expectation comment as the LAST line:
|
|
97
|
-
/// - `# Raise=ExceptionType('message')` - Exception (parse-time or runtime)
|
|
98
|
-
/// - `# Return.str=value` - Check py_str() output
|
|
99
|
-
/// - `# Return=value` - Check py_repr() output
|
|
100
|
-
/// - `# Return.type=typename` - Check py_type() output
|
|
101
|
-
/// - `# ref-counts={'var': count, ...}` - Check ref counts of named heap variables
|
|
102
|
-
///
|
|
103
|
-
/// Or a traceback expectation as a triple-quoted string at the end (uses actual test filename):
|
|
104
|
-
/// ```text
|
|
105
|
-
/// """TRACEBACK:
|
|
106
|
-
/// Traceback (most recent call last):
|
|
107
|
-
/// File "my_test.py", line 4, in <module>
|
|
108
|
-
/// foo()
|
|
109
|
-
/// ValueError: message
|
|
110
|
-
/// """
|
|
111
|
-
/// ```
|
|
112
|
-
///
|
|
113
|
-
/// If no expectation comment is present, the test just verifies the code runs without exception.
|
|
114
|
-
fn parse_fixture(content: &str) -> (String, Expectation, TestConfig) {
|
|
115
|
-
let lines: Vec<&str> = content.lines().collect();
|
|
116
|
-
|
|
117
|
-
assert!(!lines.is_empty(), "Empty fixture file");
|
|
118
|
-
|
|
119
|
-
// comment lines with leading # and spaces stripped
|
|
120
|
-
let comment_lines = lines
|
|
121
|
-
.iter()
|
|
122
|
-
.filter(|line| line.starts_with('#'))
|
|
123
|
-
.map(|line| line.trim_start_matches('#').trim())
|
|
124
|
-
.collect::<Vec<_>>();
|
|
125
|
-
|
|
126
|
-
let mut config = TestConfig {
|
|
127
|
-
iter_mode: comment_lines.iter().any(|line| line.starts_with("call-external")),
|
|
128
|
-
async_mode: comment_lines.iter().any(|line| line.starts_with("run-async")),
|
|
129
|
-
..Default::default()
|
|
130
|
-
};
|
|
131
|
-
// Check for "xfail=" directive
|
|
132
|
-
if let Some(&xfail_line) = comment_lines.iter().find(|line| line.starts_with("xfail=")) {
|
|
133
|
-
// Parse until whitespace or end of line
|
|
134
|
-
let xfail_end = xfail_line.find(|c: char| c.is_whitespace()).unwrap_or(xfail_line.len());
|
|
135
|
-
let xfail_str = &xfail_line[..xfail_end];
|
|
136
|
-
config.xfail_monty = xfail_str.contains("monty");
|
|
137
|
-
config.xfail_cpython = xfail_str.contains("cpython");
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Check for TRACEBACK expectation (triple-quoted string at end of file)
|
|
141
|
-
// Format: """TRACEBACK:\n...\n"""
|
|
142
|
-
if let Some((code, traceback)) = parse_traceback_expectation(content) {
|
|
143
|
-
return (code, Expectation::Traceback(traceback), config);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Get the last line and check if it's an expectation comment
|
|
147
|
-
let last_line = lines.last().unwrap();
|
|
148
|
-
|
|
149
|
-
// Parse expectation from comment line if present
|
|
150
|
-
// Note: Check more specific patterns first (Return.str, Return.type, ref-counts) before general Return
|
|
151
|
-
let (expectation, code_lines) = if let Some(expected) = last_line.strip_prefix("# ref-counts=") {
|
|
152
|
-
(
|
|
153
|
-
Expectation::RefCounts(parse_ref_counts(expected)),
|
|
154
|
-
&lines[..lines.len() - 1],
|
|
155
|
-
)
|
|
156
|
-
} else if let Some(expected) = last_line.strip_prefix("# Return.str=") {
|
|
157
|
-
(Expectation::ReturnStr(expected.to_string()), &lines[..lines.len() - 1])
|
|
158
|
-
} else if let Some(expected) = last_line.strip_prefix("# Return.type=") {
|
|
159
|
-
(Expectation::ReturnType(expected.to_string()), &lines[..lines.len() - 1])
|
|
160
|
-
} else if let Some(expected) = last_line.strip_prefix("# Return=") {
|
|
161
|
-
(Expectation::Return(expected.to_string()), &lines[..lines.len() - 1])
|
|
162
|
-
} else if let Some(expected) = last_line.strip_prefix("# Raise=") {
|
|
163
|
-
(Expectation::Raise(expected.to_string()), &lines[..lines.len() - 1])
|
|
164
|
-
} else {
|
|
165
|
-
// No expectation comment - just run and check it doesn't raise
|
|
166
|
-
(Expectation::NoException, &lines[..])
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
// Code is everything except the directive comment (and expectation comment if present)
|
|
170
|
-
let code = code_lines.join("\n");
|
|
171
|
-
|
|
172
|
-
(code, expectation, config)
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/// Parses a TRACEBACK expectation from the end of a fixture file.
|
|
176
|
-
///
|
|
177
|
-
/// Looks for a triple-quoted string starting with `"""TRACEBACK:` at the end of the file.
|
|
178
|
-
/// Returns `Some((code, expected_traceback))` if found, `None` otherwise.
|
|
179
|
-
///
|
|
180
|
-
/// The traceback string should contain the full expected output including the
|
|
181
|
-
/// "Traceback (most recent call last):" header and the exception line.
|
|
182
|
-
fn parse_traceback_expectation(content: &str) -> Option<(String, String)> {
|
|
183
|
-
// Format: """\nTRACEBACK:\n...\n"""
|
|
184
|
-
const MARKER: &str = "\"\"\"\nTRACEBACK:\n";
|
|
185
|
-
|
|
186
|
-
// Find the TRACEBACK marker
|
|
187
|
-
let marker_pos = content.find(MARKER)?;
|
|
188
|
-
|
|
189
|
-
// Extract the code before the marker
|
|
190
|
-
let code_part = &content[..marker_pos];
|
|
191
|
-
let lines: Vec<&str> = code_part.lines().collect();
|
|
192
|
-
let code = lines.join("\n").trim_end().to_string();
|
|
193
|
-
|
|
194
|
-
// Extract the traceback content between the markers
|
|
195
|
-
let after_marker = &content[marker_pos + MARKER.len()..];
|
|
196
|
-
|
|
197
|
-
// Find the closing triple quotes (preceded by newline)
|
|
198
|
-
let end_pos = after_marker.find("\n\"\"\"")?;
|
|
199
|
-
let traceback_content = &after_marker[..end_pos];
|
|
200
|
-
|
|
201
|
-
Some((code, traceback_content.to_string()))
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/// Parses the ref-counts format: {'var': count, 'var2': count2}
|
|
205
|
-
///
|
|
206
|
-
/// Supports both single and double quotes for variable names.
|
|
207
|
-
/// Example: {'x': 2, 'y': 1} or {"x": 2, "y": 1}
|
|
208
|
-
fn parse_ref_counts(s: &str) -> AHashMap<String, usize> {
|
|
209
|
-
let mut counts = AHashMap::new();
|
|
210
|
-
let trimmed = s.trim().trim_start_matches('{').trim_end_matches('}');
|
|
211
|
-
for pair in trimmed.split(',') {
|
|
212
|
-
let pair = pair.trim();
|
|
213
|
-
if pair.is_empty() {
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
let parts: Vec<&str> = pair.split(':').collect();
|
|
217
|
-
assert!(
|
|
218
|
-
parts.len() == 2,
|
|
219
|
-
"Invalid ref-counts pair format: {pair}. Expected 'name': count"
|
|
220
|
-
);
|
|
221
|
-
let name = parts[0].trim().trim_matches('\'').trim_matches('"');
|
|
222
|
-
let count: usize = parts[1]
|
|
223
|
-
.trim()
|
|
224
|
-
.parse()
|
|
225
|
-
.unwrap_or_else(|_| panic!("Invalid ref count value: {}", parts[1]));
|
|
226
|
-
counts.insert(name.to_string(), count);
|
|
227
|
-
}
|
|
228
|
-
counts
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/// Python implementations of external functions for running iter mode tests in CPython.
|
|
232
|
-
///
|
|
233
|
-
/// These implementations mirror the behavior of `dispatch_external_call` so that
|
|
234
|
-
/// iter mode tests produce identical results in both Monty and CPython.
|
|
235
|
-
///
|
|
236
|
-
/// This is loaded from `scripts/iter_test_methods.py` which is also imported by
|
|
237
|
-
/// `scripts/run_traceback.py` to ensure consistency.
|
|
238
|
-
const ITER_EXT_FUNCTIONS_PYTHON: &str = include_str!("../../../scripts/iter_test_methods.py");
|
|
239
|
-
|
|
240
|
-
/// Pre-imports Python modules that can cause race conditions during parallel test execution.
|
|
241
|
-
///
|
|
242
|
-
/// Python's import machinery isn't fully thread-safe during module initialization.
|
|
243
|
-
/// When multiple tests try to import modules like `typing` or `dataclasses` simultaneously,
|
|
244
|
-
/// one thread may see a partially initialized module, causing `AttributeError`.
|
|
245
|
-
///
|
|
246
|
-
/// This function must be called once before any parallel test execution to ensure
|
|
247
|
-
/// all relevant modules are fully initialized.
|
|
248
|
-
fn ensure_python_modules_imported() {
|
|
249
|
-
static INIT: OnceLock<()> = OnceLock::new();
|
|
250
|
-
INIT.get_or_init(|| {
|
|
251
|
-
Python::attach(|py| {
|
|
252
|
-
// Import modules that are used by iter_test_methods.py and can cause race conditions.
|
|
253
|
-
// The order matters: import dependencies first.
|
|
254
|
-
py.import("typing").expect("Failed to import typing");
|
|
255
|
-
py.import("dataclasses").expect("Failed to import dataclasses");
|
|
256
|
-
py.import("pathlib").expect("Failed to import pathlib");
|
|
257
|
-
py.import("stat").expect("Failed to import stat");
|
|
258
|
-
py.import("asyncio").expect("Failed to import asyncio");
|
|
259
|
-
py.import("traceback").expect("Failed to import traceback");
|
|
260
|
-
|
|
261
|
-
// Also pre-execute the iter_test_methods code once to ensure all its
|
|
262
|
-
// module-level code (dataclass definitions, monkey-patches) is initialized
|
|
263
|
-
let ext_funcs_cstr = CString::new(ITER_EXT_FUNCTIONS_PYTHON).expect("Invalid C string");
|
|
264
|
-
py.run(&ext_funcs_cstr, None, None)
|
|
265
|
-
.expect("Failed to pre-initialize iter_test_methods");
|
|
266
|
-
});
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/// Result from dispatching an external function call.
|
|
271
|
-
///
|
|
272
|
-
/// Distinguishes between synchronous calls (return immediately) and
|
|
273
|
-
/// asynchronous calls (return a future that needs later resolution).
|
|
274
|
-
enum DispatchResult {
|
|
275
|
-
/// Synchronous result - pass directly to `state.run()`.
|
|
276
|
-
Sync(ExtFunctionResult),
|
|
277
|
-
/// Asynchronous call - use `state.run_pending()` and resolve later.
|
|
278
|
-
/// Contains the value to resolve the future with.
|
|
279
|
-
Async(MontyObject),
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
/// Dispatches an external function call to the appropriate test implementation.
|
|
283
|
-
///
|
|
284
|
-
/// Returns `DispatchResult::Sync` for synchronous calls or `DispatchResult::Async`
|
|
285
|
-
/// for coroutine calls that should use `run_pending()`.
|
|
286
|
-
///
|
|
287
|
-
/// # Panics
|
|
288
|
-
/// Panics if the function name is unknown or arguments are invalid types.
|
|
289
|
-
fn dispatch_external_call(name: &str, args: Vec<MontyObject>) -> DispatchResult {
|
|
290
|
-
match name {
|
|
291
|
-
"add_ints" => {
|
|
292
|
-
assert!(args.len() == 2, "add_ints requires 2 arguments");
|
|
293
|
-
let a = i64::try_from(&args[0]).expect("add_ints: first arg must be int");
|
|
294
|
-
let b = i64::try_from(&args[1]).expect("add_ints: second arg must be int");
|
|
295
|
-
DispatchResult::Sync(MontyObject::Int(a + b).into())
|
|
296
|
-
}
|
|
297
|
-
"concat_strings" => {
|
|
298
|
-
assert!(args.len() == 2, "concat_strings requires 2 arguments");
|
|
299
|
-
let a = String::try_from(&args[0]).expect("concat_strings: first arg must be str");
|
|
300
|
-
let b = String::try_from(&args[1]).expect("concat_strings: second arg must be str");
|
|
301
|
-
DispatchResult::Sync(MontyObject::String(a + &b).into())
|
|
302
|
-
}
|
|
303
|
-
"return_value" => {
|
|
304
|
-
assert!(args.len() == 1, "return_value requires 1 argument");
|
|
305
|
-
DispatchResult::Sync(args.into_iter().next().unwrap().into())
|
|
306
|
-
}
|
|
307
|
-
"get_list" => {
|
|
308
|
-
assert!(args.is_empty(), "get_list requires no arguments");
|
|
309
|
-
DispatchResult::Sync(
|
|
310
|
-
MontyObject::List(vec![MontyObject::Int(1), MontyObject::Int(2), MontyObject::Int(3)]).into(),
|
|
311
|
-
)
|
|
312
|
-
}
|
|
313
|
-
"raise_error" => {
|
|
314
|
-
// raise_error(exc_type: str, message: str) -> raises exception
|
|
315
|
-
assert!(args.len() == 2, "raise_error requires 2 arguments");
|
|
316
|
-
let exc_type_str = String::try_from(&args[0]).expect("raise_error: first arg must be str");
|
|
317
|
-
let message = String::try_from(&args[1]).expect("raise_error: second arg must be str");
|
|
318
|
-
let exc_type = match exc_type_str.as_str() {
|
|
319
|
-
"ValueError" => ExcType::ValueError,
|
|
320
|
-
"TypeError" => ExcType::TypeError,
|
|
321
|
-
"KeyError" => ExcType::KeyError,
|
|
322
|
-
"RuntimeError" => ExcType::RuntimeError,
|
|
323
|
-
_ => panic!("raise_error: unsupported exception type: {exc_type_str}"),
|
|
324
|
-
};
|
|
325
|
-
DispatchResult::Sync(MontyException::new(exc_type, Some(message)).into())
|
|
326
|
-
}
|
|
327
|
-
"make_point" => {
|
|
328
|
-
assert!(args.is_empty(), "make_point requires no arguments");
|
|
329
|
-
// Return an immutable Point(x=1, y=2) dataclass
|
|
330
|
-
DispatchResult::Sync(
|
|
331
|
-
MontyObject::Dataclass {
|
|
332
|
-
name: "Point".to_string(),
|
|
333
|
-
type_id: 0, // Test fixture has no real Python type
|
|
334
|
-
field_names: vec!["x".to_string(), "y".to_string()],
|
|
335
|
-
attrs: vec![
|
|
336
|
-
(MontyObject::String("x".to_string()), MontyObject::Int(1)),
|
|
337
|
-
(MontyObject::String("y".to_string()), MontyObject::Int(2)),
|
|
338
|
-
]
|
|
339
|
-
.into(),
|
|
340
|
-
|
|
341
|
-
frozen: true,
|
|
342
|
-
}
|
|
343
|
-
.into(),
|
|
344
|
-
)
|
|
345
|
-
}
|
|
346
|
-
"make_mutable_point" => {
|
|
347
|
-
assert!(args.is_empty(), "make_mutable_point requires no arguments");
|
|
348
|
-
// Return a mutable Point(x=1, y=2) dataclass
|
|
349
|
-
DispatchResult::Sync(
|
|
350
|
-
MontyObject::Dataclass {
|
|
351
|
-
name: "MutablePoint".to_string(),
|
|
352
|
-
type_id: 0, // Test fixture has no real Python type
|
|
353
|
-
field_names: vec!["x".to_string(), "y".to_string()],
|
|
354
|
-
attrs: vec![
|
|
355
|
-
(MontyObject::String("x".to_string()), MontyObject::Int(1)),
|
|
356
|
-
(MontyObject::String("y".to_string()), MontyObject::Int(2)),
|
|
357
|
-
]
|
|
358
|
-
.into(),
|
|
359
|
-
|
|
360
|
-
frozen: false,
|
|
361
|
-
}
|
|
362
|
-
.into(),
|
|
363
|
-
)
|
|
364
|
-
}
|
|
365
|
-
"make_user" => {
|
|
366
|
-
assert!(args.len() == 1, "make_user requires 1 argument");
|
|
367
|
-
let name = String::try_from(&args[0]).expect("make_user: first arg must be str");
|
|
368
|
-
// Return an immutable User(name=name, active=True) dataclass
|
|
369
|
-
DispatchResult::Sync(
|
|
370
|
-
MontyObject::Dataclass {
|
|
371
|
-
name: "User".to_string(),
|
|
372
|
-
type_id: 0, // Test fixture has no real Python type
|
|
373
|
-
field_names: vec!["name".to_string(), "active".to_string()],
|
|
374
|
-
attrs: vec![
|
|
375
|
-
(MontyObject::String("name".to_string()), MontyObject::String(name)),
|
|
376
|
-
(MontyObject::String("active".to_string()), MontyObject::Bool(true)),
|
|
377
|
-
]
|
|
378
|
-
.into(),
|
|
379
|
-
|
|
380
|
-
frozen: true,
|
|
381
|
-
}
|
|
382
|
-
.into(),
|
|
383
|
-
)
|
|
384
|
-
}
|
|
385
|
-
"make_empty" => {
|
|
386
|
-
assert!(args.is_empty(), "make_empty requires no arguments");
|
|
387
|
-
// Return an immutable empty dataclass with no fields
|
|
388
|
-
DispatchResult::Sync(
|
|
389
|
-
MontyObject::Dataclass {
|
|
390
|
-
name: "Empty".to_string(),
|
|
391
|
-
type_id: 0, // Test fixture has no real Python type
|
|
392
|
-
field_names: vec![],
|
|
393
|
-
attrs: vec![].into(),
|
|
394
|
-
|
|
395
|
-
frozen: true,
|
|
396
|
-
}
|
|
397
|
-
.into(),
|
|
398
|
-
)
|
|
399
|
-
}
|
|
400
|
-
"async_call" => {
|
|
401
|
-
// async_call(x) -> coroutine that returns x
|
|
402
|
-
// This is an async function - use run_pending() and resolve later
|
|
403
|
-
assert!(args.len() == 1, "async_call requires 1 argument");
|
|
404
|
-
DispatchResult::Async(args.into_iter().next().unwrap())
|
|
405
|
-
}
|
|
406
|
-
_ => panic!("Unknown external function: {name}"),
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/// Dispatches a dataclass method call to the appropriate test implementation.
|
|
411
|
-
///
|
|
412
|
-
/// The first argument is always the dataclass instance (`self`). Known methods
|
|
413
|
-
/// are implemented to mirror the Python dataclass methods in `iter_test_methods.py`.
|
|
414
|
-
/// Unknown methods return `AttributeError`.
|
|
415
|
-
fn dispatch_method_call(
|
|
416
|
-
method_name: &str,
|
|
417
|
-
args: &[MontyObject],
|
|
418
|
-
kwargs: &[(MontyObject, MontyObject)],
|
|
419
|
-
) -> ExtFunctionResult {
|
|
420
|
-
let class_name = match args.first() {
|
|
421
|
-
Some(MontyObject::Dataclass { name, .. }) => name.as_str(),
|
|
422
|
-
_ => "<unknown>",
|
|
423
|
-
};
|
|
424
|
-
|
|
425
|
-
match (class_name, method_name) {
|
|
426
|
-
// Point.sum(self) -> int
|
|
427
|
-
("Point" | "MutablePoint", "sum") => {
|
|
428
|
-
let (x, y) = extract_point_fields(&args[0]);
|
|
429
|
-
MontyObject::Int(x + y).into()
|
|
430
|
-
}
|
|
431
|
-
// Point.add(self, dx, dy) -> Point
|
|
432
|
-
("Point", "add") => {
|
|
433
|
-
assert!(args.len() == 3, "Point.add requires self, dx, dy");
|
|
434
|
-
let (x, y) = extract_point_fields(&args[0]);
|
|
435
|
-
let dx = i64::try_from(&args[1]).expect("dx must be int");
|
|
436
|
-
let dy = i64::try_from(&args[2]).expect("dy must be int");
|
|
437
|
-
MontyObject::Dataclass {
|
|
438
|
-
name: "Point".to_string(),
|
|
439
|
-
type_id: 0,
|
|
440
|
-
field_names: vec!["x".to_string(), "y".to_string()],
|
|
441
|
-
attrs: vec![
|
|
442
|
-
(MontyObject::String("x".to_string()), MontyObject::Int(x + dx)),
|
|
443
|
-
(MontyObject::String("y".to_string()), MontyObject::Int(y + dy)),
|
|
444
|
-
]
|
|
445
|
-
.into(),
|
|
446
|
-
frozen: true,
|
|
447
|
-
}
|
|
448
|
-
.into()
|
|
449
|
-
}
|
|
450
|
-
// Point.scale(self, factor) -> Point
|
|
451
|
-
("Point", "scale") => {
|
|
452
|
-
assert!(args.len() == 2, "Point.scale requires self, factor");
|
|
453
|
-
let (x, y) = extract_point_fields(&args[0]);
|
|
454
|
-
let factor = i64::try_from(&args[1]).expect("factor must be int");
|
|
455
|
-
MontyObject::Dataclass {
|
|
456
|
-
name: "Point".to_string(),
|
|
457
|
-
type_id: 0,
|
|
458
|
-
field_names: vec!["x".to_string(), "y".to_string()],
|
|
459
|
-
attrs: vec![
|
|
460
|
-
(MontyObject::String("x".to_string()), MontyObject::Int(x * factor)),
|
|
461
|
-
(MontyObject::String("y".to_string()), MontyObject::Int(y * factor)),
|
|
462
|
-
]
|
|
463
|
-
.into(),
|
|
464
|
-
frozen: true,
|
|
465
|
-
}
|
|
466
|
-
.into()
|
|
467
|
-
}
|
|
468
|
-
// Point.describe(self, label='point') -> str
|
|
469
|
-
("Point", "describe") => {
|
|
470
|
-
let (x, y) = extract_point_fields(&args[0]);
|
|
471
|
-
// Check positional arg first, then kwargs, then default
|
|
472
|
-
let label = if args.len() > 1 {
|
|
473
|
-
String::try_from(&args[1]).expect("label must be str")
|
|
474
|
-
} else if let Some(kw_label) = get_kwarg_str(kwargs, "label") {
|
|
475
|
-
kw_label
|
|
476
|
-
} else {
|
|
477
|
-
"point".to_string()
|
|
478
|
-
};
|
|
479
|
-
MontyObject::String(format!("{label}({x}, {y})")).into()
|
|
480
|
-
}
|
|
481
|
-
// MutablePoint.shift(self, dx, dy) -> None (mutates in-place via host)
|
|
482
|
-
// Note: In the test runner, we can't actually mutate the dataclass in-place
|
|
483
|
-
// since the host doesn't have direct heap access. Return None as the method
|
|
484
|
-
// would in Python (the mutation happens inside Python's method body).
|
|
485
|
-
// For test coverage purposes, we just return None.
|
|
486
|
-
("MutablePoint", "shift") => MontyObject::None.into(),
|
|
487
|
-
// User.greeting(self) -> str
|
|
488
|
-
("User", "greeting") => {
|
|
489
|
-
let name = extract_user_name(&args[0]);
|
|
490
|
-
MontyObject::String(format!("Hello, {name}!")).into()
|
|
491
|
-
}
|
|
492
|
-
// Unknown method — return AttributeError
|
|
493
|
-
_ => {
|
|
494
|
-
let message = format!("'{class_name}' object has no attribute '{method_name}'");
|
|
495
|
-
MontyException::new(ExcType::AttributeError, Some(message)).into()
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
/// Extracts (x, y) fields from a Point or MutablePoint `MontyObject::Dataclass`.
|
|
501
|
-
fn extract_point_fields(obj: &MontyObject) -> (i64, i64) {
|
|
502
|
-
match obj {
|
|
503
|
-
MontyObject::Dataclass { attrs, .. } => {
|
|
504
|
-
let mut x = 0i64;
|
|
505
|
-
let mut y = 0i64;
|
|
506
|
-
for (key, value) in attrs {
|
|
507
|
-
if let MontyObject::String(k) = key {
|
|
508
|
-
match k.as_str() {
|
|
509
|
-
"x" => x = i64::try_from(value).expect("x must be int"),
|
|
510
|
-
"y" => y = i64::try_from(value).expect("y must be int"),
|
|
511
|
-
_ => {}
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
(x, y)
|
|
516
|
-
}
|
|
517
|
-
other => panic!("Expected Dataclass, got {other:?}"),
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
/// Extracts a string kwarg value by key name.
|
|
522
|
-
fn get_kwarg_str(kwargs: &[(MontyObject, MontyObject)], name: &str) -> Option<String> {
|
|
523
|
-
for (key, value) in kwargs {
|
|
524
|
-
if let MontyObject::String(key_str) = key
|
|
525
|
-
&& key_str == name
|
|
526
|
-
{
|
|
527
|
-
return Some(String::try_from(value).expect("kwarg value must be str"));
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
None
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
/// Extracts the `name` field from a User `MontyObject::Dataclass`.
|
|
534
|
-
fn extract_user_name(obj: &MontyObject) -> String {
|
|
535
|
-
match obj {
|
|
536
|
-
MontyObject::Dataclass { attrs, .. } => {
|
|
537
|
-
for (key, value) in attrs {
|
|
538
|
-
if let MontyObject::String(k) = key
|
|
539
|
-
&& k == "name"
|
|
540
|
-
{
|
|
541
|
-
return String::try_from(value).expect("name must be str");
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
panic!("User dataclass has no 'name' field");
|
|
545
|
-
}
|
|
546
|
-
other => panic!("Expected Dataclass, got {other:?}"),
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
// =============================================================================
|
|
551
|
-
// Virtual Filesystem for OS Call Tests
|
|
552
|
-
// =============================================================================
|
|
553
|
-
|
|
554
|
-
/// Virtual file entry for OS call tests (static VFS).
|
|
555
|
-
struct StaticVirtualFile {
|
|
556
|
-
content: &'static [u8],
|
|
557
|
-
mode: i64,
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
/// Virtual file entry (owned, for unified VFS lookups).
|
|
561
|
-
struct VirtualFile {
|
|
562
|
-
content: Vec<u8>,
|
|
563
|
-
mode: i64,
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
/// Virtual filesystem modification time (arbitrary fixed timestamp).
|
|
567
|
-
const VFS_MTIME: f64 = 1_700_000_000.0;
|
|
568
|
-
|
|
569
|
-
/// Virtual filesystem for testing Path methods.
|
|
570
|
-
///
|
|
571
|
-
/// Structure:
|
|
572
|
-
/// ```text
|
|
573
|
-
/// /virtual/
|
|
574
|
-
/// ├── file.txt (file, 644, "hello world\n")
|
|
575
|
-
/// ├── data.bin (file, 644, b"\x00\x01\x02\x03")
|
|
576
|
-
/// ├── empty.txt (file, 644, "")
|
|
577
|
-
/// ├── subdir/
|
|
578
|
-
/// │ ├── nested.txt (file, 644, "nested content")
|
|
579
|
-
/// │ └── deep/
|
|
580
|
-
/// │ └── file.txt (file, 644, "deep")
|
|
581
|
-
/// └── readonly.txt (file, 444, "readonly")
|
|
582
|
-
///
|
|
583
|
-
/// /nonexistent (does not exist)
|
|
584
|
-
/// ```
|
|
585
|
-
fn get_static_virtual_file(path: &str) -> Option<StaticVirtualFile> {
|
|
586
|
-
match path {
|
|
587
|
-
"/virtual/file.txt" => Some(StaticVirtualFile {
|
|
588
|
-
content: b"hello world\n",
|
|
589
|
-
mode: 0o644,
|
|
590
|
-
}),
|
|
591
|
-
"/virtual/data.bin" => Some(StaticVirtualFile {
|
|
592
|
-
content: b"\x00\x01\x02\x03",
|
|
593
|
-
mode: 0o644,
|
|
594
|
-
}),
|
|
595
|
-
"/virtual/empty.txt" => Some(StaticVirtualFile {
|
|
596
|
-
content: b"",
|
|
597
|
-
mode: 0o644,
|
|
598
|
-
}),
|
|
599
|
-
"/virtual/subdir/nested.txt" => Some(StaticVirtualFile {
|
|
600
|
-
content: b"nested content",
|
|
601
|
-
mode: 0o644,
|
|
602
|
-
}),
|
|
603
|
-
"/virtual/subdir/deep/file.txt" => Some(StaticVirtualFile {
|
|
604
|
-
content: b"deep",
|
|
605
|
-
mode: 0o644,
|
|
606
|
-
}),
|
|
607
|
-
"/virtual/readonly.txt" => Some(StaticVirtualFile {
|
|
608
|
-
content: b"readonly",
|
|
609
|
-
mode: 0o444,
|
|
610
|
-
}),
|
|
611
|
-
_ => None,
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
/// Gets a virtual file, checking the mutable layer first, then falling back to static.
|
|
616
|
-
fn get_virtual_file(path: &str) -> Option<VirtualFile> {
|
|
617
|
-
// Check mutable layer first
|
|
618
|
-
let mutable_result = MUTABLE_VFS.with(|vfs| {
|
|
619
|
-
let vfs = vfs.borrow();
|
|
620
|
-
// Check if deleted
|
|
621
|
-
if vfs.deleted_files.contains(path) {
|
|
622
|
-
return Some(None);
|
|
623
|
-
}
|
|
624
|
-
// Check if exists in mutable layer
|
|
625
|
-
if let Some((content, mode)) = vfs.files.get(path) {
|
|
626
|
-
return Some(Some(VirtualFile {
|
|
627
|
-
content: content.clone(),
|
|
628
|
-
mode: *mode,
|
|
629
|
-
}));
|
|
630
|
-
}
|
|
631
|
-
None
|
|
632
|
-
});
|
|
633
|
-
|
|
634
|
-
match mutable_result {
|
|
635
|
-
Some(Some(file)) => Some(file),
|
|
636
|
-
Some(None) => None, // File was deleted
|
|
637
|
-
None => {
|
|
638
|
-
// Fall back to static VFS
|
|
639
|
-
get_static_virtual_file(path).map(|f| VirtualFile {
|
|
640
|
-
content: f.content.to_vec(),
|
|
641
|
-
mode: f.mode,
|
|
642
|
-
})
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
// =============================================================================
|
|
648
|
-
// Mutable VFS Layer (Thread-Local Storage for Write Operations)
|
|
649
|
-
// =============================================================================
|
|
650
|
-
|
|
651
|
-
/// Mutable state for the virtual filesystem, supporting write operations.
|
|
652
|
-
///
|
|
653
|
-
/// This layer sits on top of the static VFS and allows tests to create, modify, and
|
|
654
|
-
/// delete files and directories. The state is thread-local so tests don't interfere
|
|
655
|
-
/// with each other.
|
|
656
|
-
#[derive(Default)]
|
|
657
|
-
struct MutableVfs {
|
|
658
|
-
/// Files created or modified during test execution.
|
|
659
|
-
files: HashMap<String, (Vec<u8>, i64)>, // path -> (content, mode)
|
|
660
|
-
/// Directories created during test execution.
|
|
661
|
-
dirs: HashSet<String>,
|
|
662
|
-
/// Files deleted during test execution (shadows static VFS entries).
|
|
663
|
-
deleted_files: HashSet<String>,
|
|
664
|
-
/// Directories deleted during test execution.
|
|
665
|
-
deleted_dirs: HashSet<String>,
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
thread_local! {
|
|
669
|
-
/// Thread-local mutable VFS state.
|
|
670
|
-
static MUTABLE_VFS: RefCell<MutableVfs> = RefCell::new(MutableVfs::default());
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
/// Resets the mutable VFS state for a new test.
|
|
674
|
-
fn reset_mutable_vfs() {
|
|
675
|
-
MUTABLE_VFS.with(|vfs| {
|
|
676
|
-
*vfs.borrow_mut() = MutableVfs::default();
|
|
677
|
-
});
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
/// Check if the given path is a directory in the virtual filesystem.
|
|
681
|
-
fn is_virtual_dir(path: &str) -> bool {
|
|
682
|
-
// Check mutable layer first
|
|
683
|
-
let result = MUTABLE_VFS.with(|vfs| {
|
|
684
|
-
let vfs = vfs.borrow();
|
|
685
|
-
if vfs.deleted_dirs.contains(path) {
|
|
686
|
-
return Some(false);
|
|
687
|
-
}
|
|
688
|
-
if vfs.dirs.contains(path) {
|
|
689
|
-
return Some(true);
|
|
690
|
-
}
|
|
691
|
-
None
|
|
692
|
-
});
|
|
693
|
-
if let Some(is_dir) = result {
|
|
694
|
-
return is_dir;
|
|
695
|
-
}
|
|
696
|
-
// Fall back to static VFS
|
|
697
|
-
matches!(path, "/virtual" | "/virtual/subdir" | "/virtual/subdir/deep")
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
/// Get directory entries for a virtual directory.
|
|
701
|
-
fn get_virtual_dir_entries(path: &str) -> Option<Vec<String>> {
|
|
702
|
-
// First check if the directory exists
|
|
703
|
-
if !is_virtual_dir(path) {
|
|
704
|
-
return None;
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
// Get static entries (if any)
|
|
708
|
-
let static_entries: Vec<&'static str> = match path {
|
|
709
|
-
"/virtual" => vec![
|
|
710
|
-
"/virtual/file.txt",
|
|
711
|
-
"/virtual/data.bin",
|
|
712
|
-
"/virtual/empty.txt",
|
|
713
|
-
"/virtual/subdir",
|
|
714
|
-
"/virtual/readonly.txt",
|
|
715
|
-
],
|
|
716
|
-
"/virtual/subdir" => vec!["/virtual/subdir/nested.txt", "/virtual/subdir/deep"],
|
|
717
|
-
"/virtual/subdir/deep" => vec!["/virtual/subdir/deep/file.txt"],
|
|
718
|
-
_ => vec![],
|
|
719
|
-
};
|
|
720
|
-
|
|
721
|
-
// Combine with mutable layer
|
|
722
|
-
MUTABLE_VFS.with(|vfs| {
|
|
723
|
-
let vfs = vfs.borrow();
|
|
724
|
-
let mut entries: HashSet<String> = static_entries
|
|
725
|
-
.iter()
|
|
726
|
-
.filter(|e| {
|
|
727
|
-
let s: &str = e;
|
|
728
|
-
!vfs.deleted_files.contains(s) && !vfs.deleted_dirs.contains(s)
|
|
729
|
-
})
|
|
730
|
-
.map(|e| (*e).to_owned())
|
|
731
|
-
.collect();
|
|
732
|
-
|
|
733
|
-
// Add mutable files and dirs in this directory
|
|
734
|
-
let prefix = if path.ends_with('/') {
|
|
735
|
-
path.to_owned()
|
|
736
|
-
} else {
|
|
737
|
-
format!("{path}/")
|
|
738
|
-
};
|
|
739
|
-
for file_path in vfs.files.keys() {
|
|
740
|
-
if file_path.starts_with(&prefix) {
|
|
741
|
-
// Only include direct children (not nested)
|
|
742
|
-
let rest = &file_path[prefix.len()..];
|
|
743
|
-
if !rest.contains('/') {
|
|
744
|
-
entries.insert(file_path.clone());
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
for dir_path in &vfs.dirs {
|
|
749
|
-
if dir_path.starts_with(&prefix) {
|
|
750
|
-
let rest = &dir_path[prefix.len()..];
|
|
751
|
-
if !rest.contains('/') {
|
|
752
|
-
entries.insert(dir_path.clone());
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
Some(entries.into_iter().collect())
|
|
758
|
-
})
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
/// Helper to get a boolean kwarg by name.
|
|
762
|
-
fn get_kwarg_bool(kwargs: &[(MontyObject, MontyObject)], name: &str) -> bool {
|
|
763
|
-
for (key, value) in kwargs {
|
|
764
|
-
if let MontyObject::String(key_str) = key
|
|
765
|
-
&& key_str == name
|
|
766
|
-
{
|
|
767
|
-
return matches!(value, MontyObject::Bool(true));
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
false
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
/// Dispatches an OS function call using the virtual filesystem.
|
|
774
|
-
///
|
|
775
|
-
/// Returns an `ExternalResult` to pass back to the Monty interpreter.
|
|
776
|
-
/// Raises `FileNotFoundError` for missing files/directories.
|
|
777
|
-
#[expect(clippy::cast_possible_wrap)] // Virtual file sizes are tiny, no wrap possible
|
|
778
|
-
fn dispatch_os_call(
|
|
779
|
-
function: OsFunction,
|
|
780
|
-
args: &[MontyObject],
|
|
781
|
-
kwargs: &[(MontyObject, MontyObject)],
|
|
782
|
-
) -> ExtFunctionResult {
|
|
783
|
-
// Handle GetEnviron first as it takes no path argument
|
|
784
|
-
if function == OsFunction::GetEnviron {
|
|
785
|
-
// Return the virtual environment as a dict
|
|
786
|
-
let env_dict = vec![
|
|
787
|
-
(
|
|
788
|
-
MontyObject::String("VIRTUAL_HOME".to_owned()),
|
|
789
|
-
MontyObject::String("/virtual/home".to_owned()),
|
|
790
|
-
),
|
|
791
|
-
(
|
|
792
|
-
MontyObject::String("VIRTUAL_USER".to_owned()),
|
|
793
|
-
MontyObject::String("testuser".to_owned()),
|
|
794
|
-
),
|
|
795
|
-
(
|
|
796
|
-
MontyObject::String("VIRTUAL_EMPTY".to_owned()),
|
|
797
|
-
MontyObject::String(String::new()),
|
|
798
|
-
),
|
|
799
|
-
];
|
|
800
|
-
return MontyObject::Dict(env_dict.into()).into();
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
// Extract path from MontyObject::Path (or String for backwards compatibility)
|
|
804
|
-
let path = match &args[0] {
|
|
805
|
-
MontyObject::Path(p) => p.clone(),
|
|
806
|
-
MontyObject::String(s) => s.clone(),
|
|
807
|
-
other => panic!("OS call: first arg must be path, got {other:?}"),
|
|
808
|
-
};
|
|
809
|
-
|
|
810
|
-
match function {
|
|
811
|
-
OsFunction::GetEnviron => unreachable!("handled above"),
|
|
812
|
-
OsFunction::Exists => {
|
|
813
|
-
let exists = get_virtual_file(&path).is_some() || is_virtual_dir(&path);
|
|
814
|
-
MontyObject::Bool(exists).into()
|
|
815
|
-
}
|
|
816
|
-
OsFunction::IsFile => {
|
|
817
|
-
let is_file = get_virtual_file(&path).is_some();
|
|
818
|
-
MontyObject::Bool(is_file).into()
|
|
819
|
-
}
|
|
820
|
-
OsFunction::IsDir => {
|
|
821
|
-
let is_dir = is_virtual_dir(&path);
|
|
822
|
-
MontyObject::Bool(is_dir).into()
|
|
823
|
-
}
|
|
824
|
-
OsFunction::IsSymlink => {
|
|
825
|
-
// Virtual filesystem doesn't have symlinks
|
|
826
|
-
MontyObject::Bool(false).into()
|
|
827
|
-
}
|
|
828
|
-
OsFunction::ReadText => {
|
|
829
|
-
if let Some(file) = get_virtual_file(&path) {
|
|
830
|
-
match std::str::from_utf8(&file.content) {
|
|
831
|
-
Ok(text) => MontyObject::String(text.to_owned()).into(),
|
|
832
|
-
Err(_) => MontyException::new(
|
|
833
|
-
ExcType::UnicodeDecodeError,
|
|
834
|
-
Some("'utf-8' codec can't decode bytes".to_owned()),
|
|
835
|
-
)
|
|
836
|
-
.into(),
|
|
837
|
-
}
|
|
838
|
-
} else {
|
|
839
|
-
MontyException::new(
|
|
840
|
-
ExcType::FileNotFoundError,
|
|
841
|
-
Some(format!("[Errno 2] No such file or directory: '{path}'")),
|
|
842
|
-
)
|
|
843
|
-
.into()
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
OsFunction::ReadBytes => {
|
|
847
|
-
if let Some(file) = get_virtual_file(&path) {
|
|
848
|
-
MontyObject::Bytes(file.content).into()
|
|
849
|
-
} else {
|
|
850
|
-
MontyException::new(
|
|
851
|
-
ExcType::FileNotFoundError,
|
|
852
|
-
Some(format!("[Errno 2] No such file or directory: '{path}'")),
|
|
853
|
-
)
|
|
854
|
-
.into()
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
OsFunction::Stat => {
|
|
858
|
-
if let Some(file) = get_virtual_file(&path) {
|
|
859
|
-
file_stat(file.mode, file.content.len() as i64, VFS_MTIME).into()
|
|
860
|
-
} else if is_virtual_dir(&path) {
|
|
861
|
-
dir_stat(0o755, VFS_MTIME).into()
|
|
862
|
-
} else {
|
|
863
|
-
MontyException::new(
|
|
864
|
-
ExcType::FileNotFoundError,
|
|
865
|
-
Some(format!("[Errno 2] No such file or directory: '{path}'")),
|
|
866
|
-
)
|
|
867
|
-
.into()
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
OsFunction::Iterdir => {
|
|
871
|
-
if let Some(entries) = get_virtual_dir_entries(&path) {
|
|
872
|
-
// Return Path objects, not strings
|
|
873
|
-
let list: Vec<MontyObject> = entries.into_iter().map(MontyObject::Path).collect();
|
|
874
|
-
MontyObject::List(list).into()
|
|
875
|
-
} else {
|
|
876
|
-
MontyException::new(
|
|
877
|
-
ExcType::FileNotFoundError,
|
|
878
|
-
Some(format!("[Errno 2] No such file or directory: '{path}'")),
|
|
879
|
-
)
|
|
880
|
-
.into()
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
OsFunction::Resolve | OsFunction::Absolute => {
|
|
884
|
-
// For virtual paths, return as-is (they're already absolute)
|
|
885
|
-
MontyObject::String(path).into()
|
|
886
|
-
}
|
|
887
|
-
OsFunction::Getenv => {
|
|
888
|
-
// Virtual environment for testing os.getenv()
|
|
889
|
-
// args[0] is key, args[1] is default (may be None)
|
|
890
|
-
let key = String::try_from(&args[0]).expect("getenv: first arg must be key string");
|
|
891
|
-
let default = &args[1];
|
|
892
|
-
|
|
893
|
-
// Provide a few test environment variables
|
|
894
|
-
let value = match key.as_str() {
|
|
895
|
-
"VIRTUAL_HOME" => Some("/virtual/home"),
|
|
896
|
-
"VIRTUAL_USER" => Some("testuser"),
|
|
897
|
-
"VIRTUAL_EMPTY" => Some(""),
|
|
898
|
-
_ => None,
|
|
899
|
-
};
|
|
900
|
-
|
|
901
|
-
if let Some(v) = value {
|
|
902
|
-
MontyObject::String(v.to_owned()).into()
|
|
903
|
-
} else if matches!(default, MontyObject::None) {
|
|
904
|
-
MontyObject::None.into()
|
|
905
|
-
} else {
|
|
906
|
-
// Return the default value
|
|
907
|
-
default.clone().into()
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
OsFunction::WriteText => {
|
|
911
|
-
// args[0] is path, args[1] is text content
|
|
912
|
-
let text = String::try_from(&args[1]).expect("write_text: second arg must be string");
|
|
913
|
-
MUTABLE_VFS.with(|vfs| {
|
|
914
|
-
let mut vfs = vfs.borrow_mut();
|
|
915
|
-
vfs.files.insert(path.clone(), (text.into_bytes(), 0o644));
|
|
916
|
-
vfs.deleted_files.remove(&path);
|
|
917
|
-
});
|
|
918
|
-
// write_text returns the number of bytes written
|
|
919
|
-
let byte_count = MUTABLE_VFS.with(|vfs| vfs.borrow().files.get(&path).map_or(0, |(c, _)| c.len()));
|
|
920
|
-
MontyObject::Int(byte_count as i64).into()
|
|
921
|
-
}
|
|
922
|
-
OsFunction::WriteBytes => {
|
|
923
|
-
// args[0] is path, args[1] is bytes content
|
|
924
|
-
let bytes = match &args[1] {
|
|
925
|
-
MontyObject::Bytes(b) => b.clone(),
|
|
926
|
-
other => panic!("write_bytes: second arg must be bytes, got {other:?}"),
|
|
927
|
-
};
|
|
928
|
-
let byte_count = bytes.len();
|
|
929
|
-
MUTABLE_VFS.with(|vfs| {
|
|
930
|
-
let mut vfs = vfs.borrow_mut();
|
|
931
|
-
vfs.files.insert(path.clone(), (bytes, 0o644));
|
|
932
|
-
vfs.deleted_files.remove(&path);
|
|
933
|
-
});
|
|
934
|
-
// write_bytes returns the number of bytes written
|
|
935
|
-
MontyObject::Int(byte_count as i64).into()
|
|
936
|
-
}
|
|
937
|
-
OsFunction::Mkdir => {
|
|
938
|
-
// Check for parents and exist_ok in kwargs (e.g., mkdir(parents=True, exist_ok=True))
|
|
939
|
-
let parents = get_kwarg_bool(kwargs, "parents");
|
|
940
|
-
let exist_ok = get_kwarg_bool(kwargs, "exist_ok");
|
|
941
|
-
|
|
942
|
-
// Check if already exists
|
|
943
|
-
if is_virtual_dir(&path) {
|
|
944
|
-
if exist_ok {
|
|
945
|
-
return MontyObject::None.into();
|
|
946
|
-
}
|
|
947
|
-
return MontyException::new(ExcType::OSError, Some(format!("[Errno 17] File exists: '{path}'"))).into();
|
|
948
|
-
}
|
|
949
|
-
|
|
950
|
-
// Check parent directory
|
|
951
|
-
let parent = std::path::Path::new(&path)
|
|
952
|
-
.parent()
|
|
953
|
-
.map(|p| p.to_string_lossy().to_string())
|
|
954
|
-
.unwrap_or_default();
|
|
955
|
-
if !parent.is_empty() && !is_virtual_dir(&parent) {
|
|
956
|
-
if parents {
|
|
957
|
-
// Create parent directories recursively
|
|
958
|
-
create_parent_dirs(&parent);
|
|
959
|
-
} else {
|
|
960
|
-
return MontyException::new(
|
|
961
|
-
ExcType::FileNotFoundError,
|
|
962
|
-
Some(format!("[Errno 2] No such file or directory: '{path}'")),
|
|
963
|
-
)
|
|
964
|
-
.into();
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
MUTABLE_VFS.with(|vfs| {
|
|
969
|
-
let mut vfs = vfs.borrow_mut();
|
|
970
|
-
vfs.deleted_dirs.remove(&path);
|
|
971
|
-
vfs.dirs.insert(path);
|
|
972
|
-
});
|
|
973
|
-
MontyObject::None.into()
|
|
974
|
-
}
|
|
975
|
-
OsFunction::Unlink => {
|
|
976
|
-
// args[0] is path
|
|
977
|
-
if get_virtual_file(&path).is_some() {
|
|
978
|
-
MUTABLE_VFS.with(|vfs| {
|
|
979
|
-
let mut vfs = vfs.borrow_mut();
|
|
980
|
-
vfs.files.remove(&path);
|
|
981
|
-
vfs.deleted_files.insert(path);
|
|
982
|
-
});
|
|
983
|
-
MontyObject::None.into()
|
|
984
|
-
} else {
|
|
985
|
-
MontyException::new(
|
|
986
|
-
ExcType::FileNotFoundError,
|
|
987
|
-
Some(format!("[Errno 2] No such file or directory: '{path}'")),
|
|
988
|
-
)
|
|
989
|
-
.into()
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
OsFunction::Rmdir => {
|
|
993
|
-
// args[0] is path
|
|
994
|
-
if is_virtual_dir(&path) {
|
|
995
|
-
MUTABLE_VFS.with(|vfs| {
|
|
996
|
-
let mut vfs = vfs.borrow_mut();
|
|
997
|
-
vfs.dirs.remove(&path);
|
|
998
|
-
vfs.deleted_dirs.insert(path);
|
|
999
|
-
});
|
|
1000
|
-
MontyObject::None.into()
|
|
1001
|
-
} else {
|
|
1002
|
-
MontyException::new(
|
|
1003
|
-
ExcType::FileNotFoundError,
|
|
1004
|
-
Some(format!("[Errno 2] No such file or directory: '{path}'")),
|
|
1005
|
-
)
|
|
1006
|
-
.into()
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
1009
|
-
OsFunction::Rename => {
|
|
1010
|
-
// args[0] is src path, args[1] is dest path
|
|
1011
|
-
let dest = match &args[1] {
|
|
1012
|
-
MontyObject::Path(p) => p.clone(),
|
|
1013
|
-
MontyObject::String(s) => s.clone(),
|
|
1014
|
-
other => panic!("rename: second arg must be path, got {other:?}"),
|
|
1015
|
-
};
|
|
1016
|
-
|
|
1017
|
-
if let Some(file) = get_virtual_file(&path) {
|
|
1018
|
-
MUTABLE_VFS.with(|vfs| {
|
|
1019
|
-
let mut vfs = vfs.borrow_mut();
|
|
1020
|
-
// Remove from old location
|
|
1021
|
-
vfs.files.remove(&path);
|
|
1022
|
-
vfs.deleted_files.insert(path);
|
|
1023
|
-
// Add to new location
|
|
1024
|
-
vfs.files.insert(dest, (file.content, file.mode));
|
|
1025
|
-
});
|
|
1026
|
-
MontyObject::None.into()
|
|
1027
|
-
} else if is_virtual_dir(&path) {
|
|
1028
|
-
MUTABLE_VFS.with(|vfs| {
|
|
1029
|
-
let mut vfs = vfs.borrow_mut();
|
|
1030
|
-
vfs.dirs.remove(&path);
|
|
1031
|
-
vfs.deleted_dirs.insert(path);
|
|
1032
|
-
vfs.dirs.insert(dest);
|
|
1033
|
-
});
|
|
1034
|
-
MontyObject::None.into()
|
|
1035
|
-
} else {
|
|
1036
|
-
MontyException::new(
|
|
1037
|
-
ExcType::FileNotFoundError,
|
|
1038
|
-
Some(format!("[Errno 2] No such file or directory: '{path}'")),
|
|
1039
|
-
)
|
|
1040
|
-
.into()
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
|
-
/// Helper to create parent directories recursively.
|
|
1047
|
-
fn create_parent_dirs(path: &str) {
|
|
1048
|
-
if is_virtual_dir(path) {
|
|
1049
|
-
return;
|
|
1050
|
-
}
|
|
1051
|
-
// Create parent first
|
|
1052
|
-
if let Some(parent) = std::path::Path::new(path).parent() {
|
|
1053
|
-
let parent_str = parent.to_string_lossy().to_string();
|
|
1054
|
-
if !parent_str.is_empty() {
|
|
1055
|
-
create_parent_dirs(&parent_str);
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
// Create this directory
|
|
1059
|
-
MUTABLE_VFS.with(|vfs| {
|
|
1060
|
-
let mut vfs = vfs.borrow_mut();
|
|
1061
|
-
vfs.dirs.insert(path.to_owned());
|
|
1062
|
-
});
|
|
1063
|
-
}
|
|
1064
|
-
|
|
1065
|
-
/// Represents a test failure with details about expected vs actual values.
|
|
1066
|
-
#[derive(Debug)]
|
|
1067
|
-
struct TestFailure {
|
|
1068
|
-
test_name: String,
|
|
1069
|
-
kind: String,
|
|
1070
|
-
expected: String,
|
|
1071
|
-
actual: String,
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
|
-
impl std::fmt::Display for TestFailure {
|
|
1075
|
-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
1076
|
-
writeln!(
|
|
1077
|
-
f,
|
|
1078
|
-
"[{}] {} mismatch\ngot {:?}\ndiff:",
|
|
1079
|
-
self.test_name, self.kind, self.actual
|
|
1080
|
-
)?;
|
|
1081
|
-
|
|
1082
|
-
for change in TextDiff::from_lines(&self.expected, &self.actual).iter_all_changes() {
|
|
1083
|
-
write!(f, "{}{}", change.tag(), change)?;
|
|
1084
|
-
}
|
|
1085
|
-
Ok(())
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
/// Try to run a test, returning Ok(()) on success or Err with failure details.
|
|
1090
|
-
///
|
|
1091
|
-
/// This function executes Python code via the MontyRun and validates the result
|
|
1092
|
-
/// against the expected outcome specified in the fixture.
|
|
1093
|
-
fn try_run_test(path: &Path, code: &str, expectation: &Expectation) -> Result<(), TestFailure> {
|
|
1094
|
-
let test_name = path.strip_prefix("test_cases/").unwrap_or(path).display().to_string();
|
|
1095
|
-
|
|
1096
|
-
// Reset the mutable VFS for each test
|
|
1097
|
-
reset_mutable_vfs();
|
|
1098
|
-
|
|
1099
|
-
// Handle ref-count-return tests separately since they need run_ref_counts()
|
|
1100
|
-
#[cfg(feature = "ref-count-return")]
|
|
1101
|
-
if let Expectation::RefCounts(expected) = expectation {
|
|
1102
|
-
match MontyRun::new(code.to_owned(), &test_name, vec![]) {
|
|
1103
|
-
Ok(ex) => {
|
|
1104
|
-
let result = ex.run_ref_counts(vec![]);
|
|
1105
|
-
match result {
|
|
1106
|
-
Ok(monty::RefCountOutput {
|
|
1107
|
-
counts,
|
|
1108
|
-
unique_refs,
|
|
1109
|
-
heap_count,
|
|
1110
|
-
..
|
|
1111
|
-
}) => {
|
|
1112
|
-
// Strict matching: verify all heap objects are accounted for by variables
|
|
1113
|
-
if unique_refs != heap_count {
|
|
1114
|
-
return Err(TestFailure {
|
|
1115
|
-
test_name,
|
|
1116
|
-
kind: "Strict matching".to_string(),
|
|
1117
|
-
expected: format!("{heap_count} heap objects"),
|
|
1118
|
-
actual: format!("{unique_refs} referenced by variables, counts: {counts:?}"),
|
|
1119
|
-
});
|
|
1120
|
-
}
|
|
1121
|
-
if &counts != expected {
|
|
1122
|
-
return Err(TestFailure {
|
|
1123
|
-
test_name,
|
|
1124
|
-
kind: "ref-counts".to_string(),
|
|
1125
|
-
expected: format!("{expected:?}"),
|
|
1126
|
-
actual: format!("{counts:?}"),
|
|
1127
|
-
});
|
|
1128
|
-
}
|
|
1129
|
-
return Ok(());
|
|
1130
|
-
}
|
|
1131
|
-
Err(e) => {
|
|
1132
|
-
return Err(TestFailure {
|
|
1133
|
-
test_name,
|
|
1134
|
-
kind: "Runtime".to_string(),
|
|
1135
|
-
expected: "success".to_string(),
|
|
1136
|
-
actual: e.to_string(),
|
|
1137
|
-
});
|
|
1138
|
-
}
|
|
1139
|
-
}
|
|
1140
|
-
}
|
|
1141
|
-
Err(parse_err) => {
|
|
1142
|
-
return Err(TestFailure {
|
|
1143
|
-
test_name,
|
|
1144
|
-
kind: "Parse".to_string(),
|
|
1145
|
-
expected: "success".to_string(),
|
|
1146
|
-
actual: parse_err.to_string(),
|
|
1147
|
-
});
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
|
|
1152
|
-
match MontyRun::new(code.to_owned(), &test_name, vec![]) {
|
|
1153
|
-
Ok(ex) => {
|
|
1154
|
-
let limits = ResourceLimits::new().max_recursion_depth(Some(TEST_RECURSION_LIMIT));
|
|
1155
|
-
let result = ex.run(vec![], LimitedTracker::new(limits), PrintWriter::Stdout);
|
|
1156
|
-
match result {
|
|
1157
|
-
Ok(obj) => match expectation {
|
|
1158
|
-
Expectation::ReturnStr(expected) => {
|
|
1159
|
-
let output = obj.to_string();
|
|
1160
|
-
if output != *expected {
|
|
1161
|
-
return Err(TestFailure {
|
|
1162
|
-
test_name,
|
|
1163
|
-
kind: "str()".to_string(),
|
|
1164
|
-
expected: expected.clone(),
|
|
1165
|
-
actual: output,
|
|
1166
|
-
});
|
|
1167
|
-
}
|
|
1168
|
-
}
|
|
1169
|
-
Expectation::Return(expected) => {
|
|
1170
|
-
let output = obj.py_repr();
|
|
1171
|
-
if output != *expected {
|
|
1172
|
-
return Err(TestFailure {
|
|
1173
|
-
test_name,
|
|
1174
|
-
kind: "py_repr()".to_string(),
|
|
1175
|
-
expected: expected.clone(),
|
|
1176
|
-
actual: output,
|
|
1177
|
-
});
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
|
-
Expectation::ReturnType(expected) => {
|
|
1181
|
-
let output = obj.type_name();
|
|
1182
|
-
if output != expected {
|
|
1183
|
-
return Err(TestFailure {
|
|
1184
|
-
test_name,
|
|
1185
|
-
kind: "type_name()".to_string(),
|
|
1186
|
-
expected: expected.clone(),
|
|
1187
|
-
actual: output.to_string(),
|
|
1188
|
-
});
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
#[cfg(not(feature = "ref-count-return"))]
|
|
1192
|
-
Expectation::RefCounts(_) => {
|
|
1193
|
-
// Skip ref-count tests when feature is disabled
|
|
1194
|
-
}
|
|
1195
|
-
Expectation::NoException => {
|
|
1196
|
-
// Success - code ran without exception as expected
|
|
1197
|
-
}
|
|
1198
|
-
Expectation::Raise(expected) | Expectation::Traceback(expected) => {
|
|
1199
|
-
return Err(TestFailure {
|
|
1200
|
-
test_name,
|
|
1201
|
-
kind: "Exception".to_string(),
|
|
1202
|
-
expected: expected.clone(),
|
|
1203
|
-
actual: "no exception raised".to_string(),
|
|
1204
|
-
});
|
|
1205
|
-
}
|
|
1206
|
-
#[cfg(feature = "ref-count-return")]
|
|
1207
|
-
Expectation::RefCounts(_) => unreachable!(),
|
|
1208
|
-
},
|
|
1209
|
-
Err(e) => {
|
|
1210
|
-
if let Expectation::Raise(expected) = expectation {
|
|
1211
|
-
let output = e.py_repr();
|
|
1212
|
-
if output != *expected {
|
|
1213
|
-
return Err(TestFailure {
|
|
1214
|
-
test_name,
|
|
1215
|
-
kind: "Exception".to_string(),
|
|
1216
|
-
expected: expected.clone(),
|
|
1217
|
-
actual: output,
|
|
1218
|
-
});
|
|
1219
|
-
}
|
|
1220
|
-
} else if let Expectation::Traceback(expected) = expectation {
|
|
1221
|
-
let output = e.to_string();
|
|
1222
|
-
if output != *expected {
|
|
1223
|
-
return Err(TestFailure {
|
|
1224
|
-
test_name,
|
|
1225
|
-
kind: "Traceback".to_string(),
|
|
1226
|
-
expected: expected.clone(),
|
|
1227
|
-
actual: output,
|
|
1228
|
-
});
|
|
1229
|
-
}
|
|
1230
|
-
} else {
|
|
1231
|
-
return Err(TestFailure {
|
|
1232
|
-
test_name,
|
|
1233
|
-
kind: "Unexpected error".to_string(),
|
|
1234
|
-
expected: "success".to_string(),
|
|
1235
|
-
actual: e.to_string(),
|
|
1236
|
-
});
|
|
1237
|
-
}
|
|
1238
|
-
}
|
|
1239
|
-
}
|
|
1240
|
-
}
|
|
1241
|
-
Err(parse_err) => {
|
|
1242
|
-
if let Expectation::Raise(expected) = expectation {
|
|
1243
|
-
let output = parse_err.py_repr();
|
|
1244
|
-
if output != *expected {
|
|
1245
|
-
return Err(TestFailure {
|
|
1246
|
-
test_name,
|
|
1247
|
-
kind: "Parse error".to_string(),
|
|
1248
|
-
expected: expected.clone(),
|
|
1249
|
-
actual: output,
|
|
1250
|
-
});
|
|
1251
|
-
}
|
|
1252
|
-
} else if let Expectation::Traceback(expected) = expectation {
|
|
1253
|
-
let output = parse_err.to_string();
|
|
1254
|
-
if output != *expected {
|
|
1255
|
-
return Err(TestFailure {
|
|
1256
|
-
test_name,
|
|
1257
|
-
kind: "Traceback".to_string(),
|
|
1258
|
-
expected: expected.clone(),
|
|
1259
|
-
actual: output,
|
|
1260
|
-
});
|
|
1261
|
-
}
|
|
1262
|
-
} else {
|
|
1263
|
-
return Err(TestFailure {
|
|
1264
|
-
test_name,
|
|
1265
|
-
kind: "Unexpected parse error".to_string(),
|
|
1266
|
-
expected: "success".to_string(),
|
|
1267
|
-
actual: parse_err.to_string(),
|
|
1268
|
-
});
|
|
1269
|
-
}
|
|
1270
|
-
}
|
|
1271
|
-
}
|
|
1272
|
-
Ok(())
|
|
1273
|
-
}
|
|
1274
|
-
|
|
1275
|
-
/// Try to run a test using MontyRun with external function support.
|
|
1276
|
-
///
|
|
1277
|
-
/// This function handles tests marked with `# call-external` directive by using the
|
|
1278
|
-
/// iterative executor API and providing implementations for predefined external functions.
|
|
1279
|
-
fn try_run_iter_test(path: &Path, code: &str, expectation: &Expectation) -> Result<(), TestFailure> {
|
|
1280
|
-
let test_name = path.strip_prefix("test_cases/").unwrap_or(path).display().to_string();
|
|
1281
|
-
|
|
1282
|
-
// Reset the mutable VFS for each test
|
|
1283
|
-
reset_mutable_vfs();
|
|
1284
|
-
|
|
1285
|
-
// Ref-counting tests not supported in iter mode
|
|
1286
|
-
#[cfg(feature = "ref-count-return")]
|
|
1287
|
-
if matches!(expectation, Expectation::RefCounts(_)) {
|
|
1288
|
-
return Err(TestFailure {
|
|
1289
|
-
test_name,
|
|
1290
|
-
kind: "Configuration".to_string(),
|
|
1291
|
-
expected: "non-refcount test".to_string(),
|
|
1292
|
-
actual: "ref-counts tests are not supported in iter mode".to_string(),
|
|
1293
|
-
});
|
|
1294
|
-
}
|
|
1295
|
-
|
|
1296
|
-
let exec = match MontyRun::new(code.to_owned(), &test_name, vec![]) {
|
|
1297
|
-
Ok(e) => e,
|
|
1298
|
-
Err(parse_err) => {
|
|
1299
|
-
if let Expectation::Raise(expected) = expectation {
|
|
1300
|
-
let output = parse_err.py_repr();
|
|
1301
|
-
if output != *expected {
|
|
1302
|
-
return Err(TestFailure {
|
|
1303
|
-
test_name,
|
|
1304
|
-
kind: "Parse error".to_string(),
|
|
1305
|
-
expected: expected.clone(),
|
|
1306
|
-
actual: output,
|
|
1307
|
-
});
|
|
1308
|
-
}
|
|
1309
|
-
return Ok(());
|
|
1310
|
-
} else if let Expectation::Traceback(expected) = expectation {
|
|
1311
|
-
let output = parse_err.to_string();
|
|
1312
|
-
if output != *expected {
|
|
1313
|
-
return Err(TestFailure {
|
|
1314
|
-
test_name,
|
|
1315
|
-
kind: "Traceback".to_string(),
|
|
1316
|
-
expected: expected.clone(),
|
|
1317
|
-
actual: output,
|
|
1318
|
-
});
|
|
1319
|
-
}
|
|
1320
|
-
return Ok(());
|
|
1321
|
-
}
|
|
1322
|
-
return Err(TestFailure {
|
|
1323
|
-
test_name,
|
|
1324
|
-
kind: "Unexpected parse error".to_string(),
|
|
1325
|
-
expected: "success".to_string(),
|
|
1326
|
-
actual: parse_err.to_string(),
|
|
1327
|
-
});
|
|
1328
|
-
}
|
|
1329
|
-
};
|
|
1330
|
-
|
|
1331
|
-
// Run execution loop, handling external function calls until complete
|
|
1332
|
-
let result = run_iter_loop(exec);
|
|
1333
|
-
|
|
1334
|
-
match result {
|
|
1335
|
-
Ok(obj) => match expectation {
|
|
1336
|
-
Expectation::ReturnStr(expected) => {
|
|
1337
|
-
let output = obj.to_string();
|
|
1338
|
-
if output != *expected {
|
|
1339
|
-
return Err(TestFailure {
|
|
1340
|
-
test_name,
|
|
1341
|
-
kind: "str()".to_string(),
|
|
1342
|
-
expected: expected.clone(),
|
|
1343
|
-
actual: output,
|
|
1344
|
-
});
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
Expectation::Return(expected) => {
|
|
1348
|
-
let output = obj.py_repr();
|
|
1349
|
-
if output != *expected {
|
|
1350
|
-
return Err(TestFailure {
|
|
1351
|
-
test_name,
|
|
1352
|
-
kind: "py_repr()".to_string(),
|
|
1353
|
-
expected: expected.clone(),
|
|
1354
|
-
actual: output,
|
|
1355
|
-
});
|
|
1356
|
-
}
|
|
1357
|
-
}
|
|
1358
|
-
Expectation::ReturnType(expected) => {
|
|
1359
|
-
let output = obj.type_name();
|
|
1360
|
-
if output != expected {
|
|
1361
|
-
return Err(TestFailure {
|
|
1362
|
-
test_name,
|
|
1363
|
-
kind: "type_name()".to_string(),
|
|
1364
|
-
expected: expected.clone(),
|
|
1365
|
-
actual: output.to_string(),
|
|
1366
|
-
});
|
|
1367
|
-
}
|
|
1368
|
-
}
|
|
1369
|
-
#[cfg(not(feature = "ref-count-return"))]
|
|
1370
|
-
Expectation::RefCounts(_) => {}
|
|
1371
|
-
Expectation::NoException => {}
|
|
1372
|
-
Expectation::Raise(expected) | Expectation::Traceback(expected) => {
|
|
1373
|
-
return Err(TestFailure {
|
|
1374
|
-
test_name,
|
|
1375
|
-
kind: "Exception".to_string(),
|
|
1376
|
-
expected: expected.clone(),
|
|
1377
|
-
actual: "no exception raised".to_string(),
|
|
1378
|
-
});
|
|
1379
|
-
}
|
|
1380
|
-
#[cfg(feature = "ref-count-return")]
|
|
1381
|
-
Expectation::RefCounts(_) => unreachable!(),
|
|
1382
|
-
},
|
|
1383
|
-
Err(e) => {
|
|
1384
|
-
if let Expectation::Raise(expected) = expectation {
|
|
1385
|
-
let output = e.py_repr();
|
|
1386
|
-
if output != *expected {
|
|
1387
|
-
return Err(TestFailure {
|
|
1388
|
-
test_name,
|
|
1389
|
-
kind: "Exception".to_string(),
|
|
1390
|
-
expected: expected.clone(),
|
|
1391
|
-
actual: output,
|
|
1392
|
-
});
|
|
1393
|
-
}
|
|
1394
|
-
} else if let Expectation::Traceback(expected) = expectation {
|
|
1395
|
-
let output = e.to_string();
|
|
1396
|
-
if output != *expected {
|
|
1397
|
-
return Err(TestFailure {
|
|
1398
|
-
test_name,
|
|
1399
|
-
kind: "Traceback".to_string(),
|
|
1400
|
-
expected: expected.clone(),
|
|
1401
|
-
actual: output,
|
|
1402
|
-
});
|
|
1403
|
-
}
|
|
1404
|
-
} else {
|
|
1405
|
-
return Err(TestFailure {
|
|
1406
|
-
test_name,
|
|
1407
|
-
kind: "Unexpected error".to_string(),
|
|
1408
|
-
expected: "success".to_string(),
|
|
1409
|
-
actual: e.to_string(),
|
|
1410
|
-
});
|
|
1411
|
-
}
|
|
1412
|
-
}
|
|
1413
|
-
}
|
|
1414
|
-
Ok(())
|
|
1415
|
-
}
|
|
1416
|
-
|
|
1417
|
-
/// Execute the iter loop, dispatching external function calls until complete.
|
|
1418
|
-
///
|
|
1419
|
-
/// When `ref-count-panic` feature is NOT enabled, this function also tests
|
|
1420
|
-
/// serialization round-trips by dumping and loading the execution state at
|
|
1421
|
-
/// each external function call boundary.
|
|
1422
|
-
///
|
|
1423
|
-
/// Supports both synchronous and asynchronous external functions:
|
|
1424
|
-
/// - Sync functions: result is passed immediately via `state.run()`
|
|
1425
|
-
/// - Async functions: `state.run_pending()` creates a future, resolved via `ResolveFutures`
|
|
1426
|
-
fn run_iter_loop(exec: MontyRun) -> Result<MontyObject, MontyException> {
|
|
1427
|
-
let limits = ResourceLimits::new().max_recursion_depth(Some(TEST_RECURSION_LIMIT));
|
|
1428
|
-
let mut progress = exec.start(vec![], LimitedTracker::new(limits), PrintWriter::Stdout)?;
|
|
1429
|
-
|
|
1430
|
-
// Track pending async calls: (call_id, result_value)
|
|
1431
|
-
let mut pending_results: Vec<(u32, MontyObject)> = Vec::new();
|
|
1432
|
-
|
|
1433
|
-
loop {
|
|
1434
|
-
// Test serialization round-trip at each step (skip when ref-count-panic is enabled
|
|
1435
|
-
// since the old RunProgress would panic on drop without proper cleanup)
|
|
1436
|
-
#[cfg(not(feature = "ref-count-panic"))]
|
|
1437
|
-
{
|
|
1438
|
-
let bytes = progress.dump().expect("failed to dump RunProgress");
|
|
1439
|
-
progress = RunProgress::load(&bytes).expect("failed to load RunProgress");
|
|
1440
|
-
}
|
|
1441
|
-
|
|
1442
|
-
match progress {
|
|
1443
|
-
RunProgress::Complete(result) => return Ok(result),
|
|
1444
|
-
RunProgress::FunctionCall(call) => {
|
|
1445
|
-
// Method calls on dataclasses are dispatched to the host.
|
|
1446
|
-
// Dispatch known methods; return AttributeError for unknown ones.
|
|
1447
|
-
if call.method_call {
|
|
1448
|
-
let result = dispatch_method_call(&call.function_name, &call.args, &call.kwargs);
|
|
1449
|
-
progress = call.resume(result, PrintWriter::Stdout)?;
|
|
1450
|
-
continue;
|
|
1451
|
-
}
|
|
1452
|
-
let dispatch_result = dispatch_external_call(&call.function_name, call.args.clone());
|
|
1453
|
-
match dispatch_result {
|
|
1454
|
-
DispatchResult::Sync(return_value) => {
|
|
1455
|
-
progress = call.resume(return_value, PrintWriter::Stdout)?;
|
|
1456
|
-
}
|
|
1457
|
-
DispatchResult::Async(result_value) => {
|
|
1458
|
-
// Store the result for later resolution
|
|
1459
|
-
pending_results.push((call.call_id, result_value));
|
|
1460
|
-
// Continue execution with a pending future
|
|
1461
|
-
progress = call.resume_pending(PrintWriter::Stdout)?;
|
|
1462
|
-
}
|
|
1463
|
-
}
|
|
1464
|
-
}
|
|
1465
|
-
RunProgress::ResolveFutures(state) => {
|
|
1466
|
-
// Resolve all pending futures that we have results for
|
|
1467
|
-
let results: Vec<(u32, ExtFunctionResult)> = state
|
|
1468
|
-
.pending_call_ids()
|
|
1469
|
-
.iter()
|
|
1470
|
-
.filter_map(|p| {
|
|
1471
|
-
pending_results.iter().position(|(id, _)| id == p).map(|idx| {
|
|
1472
|
-
let (call_id, value) = pending_results.remove(idx);
|
|
1473
|
-
(call_id, ExtFunctionResult::Return(value))
|
|
1474
|
-
})
|
|
1475
|
-
})
|
|
1476
|
-
.collect();
|
|
1477
|
-
|
|
1478
|
-
assert!(
|
|
1479
|
-
!results.is_empty(),
|
|
1480
|
-
"ResolveFutures: no results available for pending calls: {:?}",
|
|
1481
|
-
state.pending_call_ids().iter().collect::<Vec<_>>()
|
|
1482
|
-
);
|
|
1483
|
-
|
|
1484
|
-
progress = state.resume(results, PrintWriter::Stdout)?;
|
|
1485
|
-
}
|
|
1486
|
-
RunProgress::NameLookup(lookup) => {
|
|
1487
|
-
let result = match lookup.name.as_str() {
|
|
1488
|
-
// External functions — resolved as callable Function objects
|
|
1489
|
-
"add_ints" | "concat_strings" | "return_value" | "get_list" | "raise_error" | "make_point"
|
|
1490
|
-
| "make_mutable_point" | "make_user" | "make_empty" | "async_call" => {
|
|
1491
|
-
NameLookupResult::Value(MontyObject::Function {
|
|
1492
|
-
name: lookup.name.clone(),
|
|
1493
|
-
docstring: None,
|
|
1494
|
-
})
|
|
1495
|
-
}
|
|
1496
|
-
// Non-function constants — resolved as plain values
|
|
1497
|
-
"CONST_INT" => NameLookupResult::Value(MontyObject::Int(42)),
|
|
1498
|
-
"CONST_STR" => NameLookupResult::Value(MontyObject::String("hello".to_string())),
|
|
1499
|
-
#[expect(clippy::approx_constant, reason = "3.14 is the intended test value")]
|
|
1500
|
-
"CONST_FLOAT" => NameLookupResult::Value(MontyObject::Float(3.14)),
|
|
1501
|
-
"CONST_BOOL" => NameLookupResult::Value(MontyObject::Bool(true)),
|
|
1502
|
-
"CONST_LIST" => NameLookupResult::Value(MontyObject::List(vec![
|
|
1503
|
-
MontyObject::Int(1),
|
|
1504
|
-
MontyObject::Int(2),
|
|
1505
|
-
MontyObject::Int(3),
|
|
1506
|
-
])),
|
|
1507
|
-
"CONST_NONE" => NameLookupResult::Value(MontyObject::None),
|
|
1508
|
-
// Unknown names → NameError
|
|
1509
|
-
_ => NameLookupResult::Undefined,
|
|
1510
|
-
};
|
|
1511
|
-
progress = lookup.resume(result, PrintWriter::Stdout)?;
|
|
1512
|
-
}
|
|
1513
|
-
RunProgress::OsCall(call) => {
|
|
1514
|
-
let result = dispatch_os_call(call.function, &call.args, &call.kwargs);
|
|
1515
|
-
progress = call.resume(result, PrintWriter::Stdout)?;
|
|
1516
|
-
}
|
|
1517
|
-
}
|
|
1518
|
-
}
|
|
1519
|
-
}
|
|
1520
|
-
|
|
1521
|
-
/// Split Python code into statements and a final expression to evaluate.
|
|
1522
|
-
///
|
|
1523
|
-
/// For Return expectations, the last non-empty line is the expression to evaluate.
|
|
1524
|
-
/// For Raise/NoException, the entire code is statements (returns None for expression).
|
|
1525
|
-
///
|
|
1526
|
-
/// Returns (statements_code, optional_final_expression).
|
|
1527
|
-
fn split_code_for_module(code: &str, need_return_value: bool) -> (String, Option<String>) {
|
|
1528
|
-
let lines: Vec<&str> = code.lines().collect();
|
|
1529
|
-
|
|
1530
|
-
// Find the last non-empty line
|
|
1531
|
-
let last_idx = lines
|
|
1532
|
-
.iter()
|
|
1533
|
-
.rposition(|line| !line.trim().is_empty())
|
|
1534
|
-
.expect("Empty code");
|
|
1535
|
-
|
|
1536
|
-
if need_return_value {
|
|
1537
|
-
let last_line = lines[last_idx].trim();
|
|
1538
|
-
|
|
1539
|
-
// Check if the last line is a statement (can't be evaluated as an expression)
|
|
1540
|
-
// Matches both `assert expr` and `assert(expr)` forms
|
|
1541
|
-
if last_line.starts_with("assert ") || last_line.starts_with("assert(") {
|
|
1542
|
-
// All code is statements, no expression to evaluate
|
|
1543
|
-
(lines[..=last_idx].join("\n"), None)
|
|
1544
|
-
} else {
|
|
1545
|
-
// Everything except last line is statements, last line is the expression
|
|
1546
|
-
let statements = lines[..last_idx].join("\n");
|
|
1547
|
-
let expr = last_line.to_string();
|
|
1548
|
-
(statements, Some(expr))
|
|
1549
|
-
}
|
|
1550
|
-
} else {
|
|
1551
|
-
// All code is statements (for exception tests or NoException)
|
|
1552
|
-
(lines[..=last_idx].join("\n"), None)
|
|
1553
|
-
}
|
|
1554
|
-
}
|
|
1555
|
-
|
|
1556
|
-
/// Wraps code in an async context for CPython execution.
|
|
1557
|
-
///
|
|
1558
|
-
/// Monty supports top-level `await`, but CPython does not. This function transforms code
|
|
1559
|
-
/// like:
|
|
1560
|
-
///
|
|
1561
|
-
/// ```python
|
|
1562
|
-
/// async def foo():
|
|
1563
|
-
/// return 1
|
|
1564
|
-
/// result = await foo()
|
|
1565
|
-
/// ```
|
|
1566
|
-
///
|
|
1567
|
-
/// Into:
|
|
1568
|
-
///
|
|
1569
|
-
/// ```python
|
|
1570
|
-
/// import asyncio
|
|
1571
|
-
/// async def __test_main():
|
|
1572
|
-
/// async def foo():
|
|
1573
|
-
/// return 1
|
|
1574
|
-
/// result = await foo()
|
|
1575
|
-
/// return result # if need_return_value
|
|
1576
|
-
/// __test_result__ = asyncio.run(__test_main())
|
|
1577
|
-
/// ```
|
|
1578
|
-
fn wrap_code_for_async(code: &str, need_return_value: bool) -> (String, Option<String>) {
|
|
1579
|
-
let lines: Vec<&str> = code.lines().collect();
|
|
1580
|
-
|
|
1581
|
-
// Find the last non-empty, non-comment line
|
|
1582
|
-
let last_idx = lines
|
|
1583
|
-
.iter()
|
|
1584
|
-
.rposition(|line| {
|
|
1585
|
-
let trimmed = line.trim();
|
|
1586
|
-
!trimmed.is_empty() && !trimmed.starts_with('#')
|
|
1587
|
-
})
|
|
1588
|
-
.expect("Empty code");
|
|
1589
|
-
|
|
1590
|
-
// Indent all code by 4 spaces for the function body
|
|
1591
|
-
let indented: String = lines
|
|
1592
|
-
.iter()
|
|
1593
|
-
.map(|line| {
|
|
1594
|
-
if line.is_empty() {
|
|
1595
|
-
String::new()
|
|
1596
|
-
} else {
|
|
1597
|
-
format!(" {line}")
|
|
1598
|
-
}
|
|
1599
|
-
})
|
|
1600
|
-
.collect::<Vec<_>>()
|
|
1601
|
-
.join("\n");
|
|
1602
|
-
|
|
1603
|
-
let return_stmt = if need_return_value {
|
|
1604
|
-
// The last non-empty, non-comment line is the expression to return
|
|
1605
|
-
let last_line = lines[last_idx].trim();
|
|
1606
|
-
format!("\n return {last_line}")
|
|
1607
|
-
} else {
|
|
1608
|
-
String::new()
|
|
1609
|
-
};
|
|
1610
|
-
|
|
1611
|
-
let wrapped = format!(
|
|
1612
|
-
"import asyncio\nasync def __test_main():\n{indented}{return_stmt}\n__test_result__ = asyncio.run(__test_main())"
|
|
1613
|
-
);
|
|
1614
|
-
|
|
1615
|
-
if need_return_value {
|
|
1616
|
-
(wrapped, Some("__test_result__".to_string()))
|
|
1617
|
-
} else {
|
|
1618
|
-
(wrapped, None)
|
|
1619
|
-
}
|
|
1620
|
-
}
|
|
1621
|
-
|
|
1622
|
-
/// Run the traceback script to get CPython's traceback output for a test file.
|
|
1623
|
-
///
|
|
1624
|
-
/// This imports scripts/run_traceback.py via pyo3 and calls `run_file_and_get_traceback()`
|
|
1625
|
-
/// which executes the file via runpy.run_path() to ensure full traceback information
|
|
1626
|
-
/// (including caret lines) is preserved.
|
|
1627
|
-
///
|
|
1628
|
-
/// When `iter_mode` is true, external function implementations are injected into the
|
|
1629
|
-
/// file's globals before execution.
|
|
1630
|
-
///
|
|
1631
|
-
/// When `async_mode` is true, code is wrapped in an async context before execution.
|
|
1632
|
-
fn run_traceback_script(path: &Path, iter_mode: bool, async_mode: bool) -> String {
|
|
1633
|
-
Python::attach(|py| {
|
|
1634
|
-
let run_traceback = import_run_traceback(py);
|
|
1635
|
-
|
|
1636
|
-
// Get absolute path for the test file
|
|
1637
|
-
let abs_path = path.canonicalize().expect("Failed to get absolute path");
|
|
1638
|
-
let path_str = abs_path.to_str().expect("Invalid UTF-8 in path");
|
|
1639
|
-
|
|
1640
|
-
// Call run_file_and_get_traceback with the recursion limit, iter_mode, and async_mode flags
|
|
1641
|
-
let result = run_traceback
|
|
1642
|
-
.call_method1(
|
|
1643
|
-
"run_file_and_get_traceback",
|
|
1644
|
-
(path_str, TEST_RECURSION_LIMIT, iter_mode, async_mode),
|
|
1645
|
-
)
|
|
1646
|
-
.expect("Failed to call run_file_and_get_traceback");
|
|
1647
|
-
|
|
1648
|
-
// Handle None return (no exception raised)
|
|
1649
|
-
if result.is_none() {
|
|
1650
|
-
String::new()
|
|
1651
|
-
} else {
|
|
1652
|
-
result
|
|
1653
|
-
.extract()
|
|
1654
|
-
.expect("Failed to extract string from return value of run_file_and_get_traceback")
|
|
1655
|
-
}
|
|
1656
|
-
})
|
|
1657
|
-
}
|
|
1658
|
-
|
|
1659
|
-
fn format_traceback(py: Python<'_>, exc: &PyErr) -> String {
|
|
1660
|
-
let run_traceback = import_run_traceback(py);
|
|
1661
|
-
let exc_value = exc.value(py);
|
|
1662
|
-
let return_value = run_traceback
|
|
1663
|
-
.call_method1("format_full_traceback", (exc_value,))
|
|
1664
|
-
.expect("Failed to call format_full_traceback");
|
|
1665
|
-
return_value
|
|
1666
|
-
.extract()
|
|
1667
|
-
.expect("failed to extract string from return value of format_full_traceback")
|
|
1668
|
-
}
|
|
1669
|
-
|
|
1670
|
-
/// Import the run_traceback module
|
|
1671
|
-
fn import_run_traceback(py: Python<'_>) -> Bound<'_, PyModule> {
|
|
1672
|
-
// Add scripts directory to sys.path (tests run from crates/monty/)
|
|
1673
|
-
let sys = py.import("sys").expect("Failed to import sys");
|
|
1674
|
-
let sys_path = sys.getattr("path").expect("Failed to get sys.path");
|
|
1675
|
-
sys_path
|
|
1676
|
-
.call_method1("insert", (0, "../../scripts"))
|
|
1677
|
-
.expect("Failed to add scripts to sys.path");
|
|
1678
|
-
|
|
1679
|
-
// Import the run_traceback module
|
|
1680
|
-
py.import("run_traceback").expect("Failed to import run_traceback")
|
|
1681
|
-
}
|
|
1682
|
-
|
|
1683
|
-
/// Result from CPython execution - either a value to compare, or an early return.
|
|
1684
|
-
enum CpythonResult {
|
|
1685
|
-
/// Value to compare against expectation
|
|
1686
|
-
Value(String),
|
|
1687
|
-
/// No value to compare (NoException test succeeded)
|
|
1688
|
-
NoValue,
|
|
1689
|
-
/// Test failed with this error
|
|
1690
|
-
Failed(TestFailure),
|
|
1691
|
-
}
|
|
1692
|
-
|
|
1693
|
-
/// Try to run a test through CPython, returning Ok(()) on success or Err with failure details.
|
|
1694
|
-
///
|
|
1695
|
-
/// This function executes the same Python code via CPython (using pyo3) and
|
|
1696
|
-
/// compares the result with the expected value. This ensures Monty behaves
|
|
1697
|
-
/// identically to CPython.
|
|
1698
|
-
///
|
|
1699
|
-
/// Code is executed at module level (not wrapped in a function) so that
|
|
1700
|
-
/// `global` keyword semantics work correctly.
|
|
1701
|
-
///
|
|
1702
|
-
/// RefCounts tests are skipped as they're Monty-specific.
|
|
1703
|
-
/// Traceback tests use scripts/run_traceback.py for reliable caret line support.
|
|
1704
|
-
fn try_run_cpython_test(
|
|
1705
|
-
path: &Path,
|
|
1706
|
-
code: &str,
|
|
1707
|
-
expectation: &Expectation,
|
|
1708
|
-
iter_mode: bool,
|
|
1709
|
-
async_mode: bool,
|
|
1710
|
-
) -> Result<(), TestFailure> {
|
|
1711
|
-
// Ensure Python modules are imported before parallel tests access them.
|
|
1712
|
-
// This prevents race conditions during module initialization.
|
|
1713
|
-
ensure_python_modules_imported();
|
|
1714
|
-
|
|
1715
|
-
// Skip RefCounts tests - only relevant for Monty
|
|
1716
|
-
if matches!(expectation, Expectation::RefCounts(_)) {
|
|
1717
|
-
return Ok(());
|
|
1718
|
-
}
|
|
1719
|
-
|
|
1720
|
-
let test_name = path.strip_prefix("test_cases/").unwrap_or(path).display().to_string();
|
|
1721
|
-
|
|
1722
|
-
// Traceback tests use the external script for reliable caret line support
|
|
1723
|
-
if let Expectation::Traceback(expected) = expectation {
|
|
1724
|
-
let result = run_traceback_script(path, iter_mode, async_mode);
|
|
1725
|
-
if result != *expected {
|
|
1726
|
-
return Err(TestFailure {
|
|
1727
|
-
test_name,
|
|
1728
|
-
kind: "CPython traceback".to_string(),
|
|
1729
|
-
expected: expected.clone(),
|
|
1730
|
-
actual: result,
|
|
1731
|
-
});
|
|
1732
|
-
}
|
|
1733
|
-
return Ok(());
|
|
1734
|
-
}
|
|
1735
|
-
|
|
1736
|
-
let need_return_value = matches!(
|
|
1737
|
-
expectation,
|
|
1738
|
-
Expectation::Return(_) | Expectation::ReturnStr(_) | Expectation::ReturnType(_)
|
|
1739
|
-
);
|
|
1740
|
-
|
|
1741
|
-
// Use async wrapper for tests with top-level await
|
|
1742
|
-
let (statements, maybe_expr) = if async_mode {
|
|
1743
|
-
wrap_code_for_async(code, need_return_value)
|
|
1744
|
-
} else {
|
|
1745
|
-
split_code_for_module(code, need_return_value)
|
|
1746
|
-
};
|
|
1747
|
-
|
|
1748
|
-
let result: CpythonResult = Python::attach(|py| {
|
|
1749
|
-
// Execute statements at module level
|
|
1750
|
-
let globals = PyDict::new(py);
|
|
1751
|
-
|
|
1752
|
-
// For iter mode tests, inject external function implementations into globals
|
|
1753
|
-
if iter_mode {
|
|
1754
|
-
let ext_funcs_cstr = CString::new(ITER_EXT_FUNCTIONS_PYTHON).expect("Invalid C string in ext funcs");
|
|
1755
|
-
py.run(&ext_funcs_cstr, Some(&globals), None)
|
|
1756
|
-
.expect("Failed to define external functions for iter mode");
|
|
1757
|
-
}
|
|
1758
|
-
|
|
1759
|
-
// Run the statements
|
|
1760
|
-
let statements_cstr = CString::new(statements.as_str()).expect("Invalid C string in statements");
|
|
1761
|
-
let stmt_result = py.run(&statements_cstr, Some(&globals), None);
|
|
1762
|
-
|
|
1763
|
-
// Handle exception during statement execution
|
|
1764
|
-
if let Err(e) = stmt_result {
|
|
1765
|
-
if matches!(expectation, Expectation::NoException) {
|
|
1766
|
-
return CpythonResult::Failed(TestFailure {
|
|
1767
|
-
test_name: test_name.clone(),
|
|
1768
|
-
kind: "CPython unexpected exception".to_string(),
|
|
1769
|
-
expected: "no exception".to_string(),
|
|
1770
|
-
actual: format_traceback(py, &e),
|
|
1771
|
-
});
|
|
1772
|
-
}
|
|
1773
|
-
if matches!(expectation, Expectation::Raise(_)) {
|
|
1774
|
-
return CpythonResult::Value(format_cpython_exception(py, &e));
|
|
1775
|
-
}
|
|
1776
|
-
return CpythonResult::Failed(TestFailure {
|
|
1777
|
-
test_name: test_name.clone(),
|
|
1778
|
-
kind: "CPython unexpected exception".to_string(),
|
|
1779
|
-
expected: "success".to_string(),
|
|
1780
|
-
actual: format_traceback(py, &e),
|
|
1781
|
-
});
|
|
1782
|
-
}
|
|
1783
|
-
|
|
1784
|
-
// If we have an expression to evaluate, evaluate it
|
|
1785
|
-
if let Some(expr) = maybe_expr {
|
|
1786
|
-
let expr_cstr = CString::new(expr.as_str()).expect("Invalid C string in expr");
|
|
1787
|
-
match py.eval(&expr_cstr, Some(&globals), None) {
|
|
1788
|
-
Ok(result) => {
|
|
1789
|
-
// Code returned successfully - format based on expectation type
|
|
1790
|
-
match expectation {
|
|
1791
|
-
Expectation::Return(_) => CpythonResult::Value(result.repr().unwrap().to_string()),
|
|
1792
|
-
Expectation::ReturnStr(_) => CpythonResult::Value(result.str().unwrap().to_string()),
|
|
1793
|
-
Expectation::ReturnType(_) => {
|
|
1794
|
-
CpythonResult::Value(result.get_type().name().unwrap().to_string())
|
|
1795
|
-
}
|
|
1796
|
-
Expectation::Raise(expected) => CpythonResult::Failed(TestFailure {
|
|
1797
|
-
test_name: test_name.clone(),
|
|
1798
|
-
kind: "CPython exception".to_string(),
|
|
1799
|
-
expected: expected.clone(),
|
|
1800
|
-
actual: "no exception raised".to_string(),
|
|
1801
|
-
}),
|
|
1802
|
-
// Traceback tests are handled by run_traceback_script above
|
|
1803
|
-
Expectation::Traceback(_) | Expectation::NoException | Expectation::RefCounts(_) => {
|
|
1804
|
-
unreachable!()
|
|
1805
|
-
}
|
|
1806
|
-
}
|
|
1807
|
-
}
|
|
1808
|
-
Err(e) => {
|
|
1809
|
-
// Expression raised an exception
|
|
1810
|
-
if matches!(expectation, Expectation::NoException) {
|
|
1811
|
-
return CpythonResult::Failed(TestFailure {
|
|
1812
|
-
test_name: test_name.clone(),
|
|
1813
|
-
kind: "CPython unexpected exception".to_string(),
|
|
1814
|
-
expected: "no exception".to_string(),
|
|
1815
|
-
actual: format_traceback(py, &e),
|
|
1816
|
-
});
|
|
1817
|
-
}
|
|
1818
|
-
if matches!(expectation, Expectation::Raise(_)) {
|
|
1819
|
-
return CpythonResult::Value(format_cpython_exception(py, &e));
|
|
1820
|
-
}
|
|
1821
|
-
// Traceback tests are handled by run_traceback_script above
|
|
1822
|
-
CpythonResult::Failed(TestFailure {
|
|
1823
|
-
test_name: test_name.clone(),
|
|
1824
|
-
kind: "CPython unexpected exception".to_string(),
|
|
1825
|
-
expected: "success".to_string(),
|
|
1826
|
-
actual: format_traceback(py, &e),
|
|
1827
|
-
})
|
|
1828
|
-
}
|
|
1829
|
-
}
|
|
1830
|
-
} else {
|
|
1831
|
-
// No expression to evaluate
|
|
1832
|
-
// Traceback tests are handled by run_traceback_script above
|
|
1833
|
-
if let Expectation::Raise(expected) = expectation {
|
|
1834
|
-
return CpythonResult::Failed(TestFailure {
|
|
1835
|
-
test_name: test_name.clone(),
|
|
1836
|
-
kind: "CPython exception".to_string(),
|
|
1837
|
-
expected: expected.clone(),
|
|
1838
|
-
actual: "no exception raised".to_string(),
|
|
1839
|
-
});
|
|
1840
|
-
}
|
|
1841
|
-
CpythonResult::NoValue // NoException expectation - success
|
|
1842
|
-
}
|
|
1843
|
-
});
|
|
1844
|
-
|
|
1845
|
-
match result {
|
|
1846
|
-
CpythonResult::Value(actual) => {
|
|
1847
|
-
let expected = expectation.expected_value();
|
|
1848
|
-
if actual != expected {
|
|
1849
|
-
return Err(TestFailure {
|
|
1850
|
-
test_name,
|
|
1851
|
-
kind: "CPython result".to_string(),
|
|
1852
|
-
expected: expected.to_string(),
|
|
1853
|
-
actual,
|
|
1854
|
-
});
|
|
1855
|
-
}
|
|
1856
|
-
Ok(())
|
|
1857
|
-
}
|
|
1858
|
-
CpythonResult::NoValue => Ok(()),
|
|
1859
|
-
CpythonResult::Failed(failure) => Err(failure),
|
|
1860
|
-
}
|
|
1861
|
-
}
|
|
1862
|
-
|
|
1863
|
-
/// Format a CPython exception into the expected format.
|
|
1864
|
-
fn format_cpython_exception(py: Python<'_>, e: &PyErr) -> String {
|
|
1865
|
-
let exc_type = e.get_type(py).name().unwrap();
|
|
1866
|
-
let exc_message: String = e
|
|
1867
|
-
.value(py)
|
|
1868
|
-
.getattr("args")
|
|
1869
|
-
.and_then(|args| args.get_item(0))
|
|
1870
|
-
.and_then(|item| item.extract())
|
|
1871
|
-
.unwrap_or_default();
|
|
1872
|
-
|
|
1873
|
-
if exc_message.is_empty() {
|
|
1874
|
-
format!("{exc_type}()")
|
|
1875
|
-
} else if exc_message.contains('\'') {
|
|
1876
|
-
// Use double quotes when message contains single quotes (like Python's repr)
|
|
1877
|
-
format!("{exc_type}(\"{exc_message}\")")
|
|
1878
|
-
} else {
|
|
1879
|
-
// Use single quotes (default Python repr format)
|
|
1880
|
-
format!("{exc_type}('{exc_message}')")
|
|
1881
|
-
}
|
|
1882
|
-
}
|
|
1883
|
-
|
|
1884
|
-
/// Timeout duration for Monty tests.
|
|
1885
|
-
///
|
|
1886
|
-
/// Tests that exceed this duration are considered to be hanging (infinite loop)
|
|
1887
|
-
/// and will fail with a timeout error.
|
|
1888
|
-
const TEST_TIMEOUT: Duration = Duration::from_secs(2);
|
|
1889
|
-
|
|
1890
|
-
/// Result from running a test with a timeout.
|
|
1891
|
-
enum TimeoutResult<T> {
|
|
1892
|
-
/// The closure completed successfully.
|
|
1893
|
-
Ok(T),
|
|
1894
|
-
/// The closure panicked with the given message.
|
|
1895
|
-
Panicked(String),
|
|
1896
|
-
/// The timeout was exceeded.
|
|
1897
|
-
TimedOut,
|
|
1898
|
-
}
|
|
1899
|
-
|
|
1900
|
-
/// Runs a closure with a timeout, returning an error if it exceeds the duration or panics.
|
|
1901
|
-
///
|
|
1902
|
-
/// Spawns the closure in a separate thread and waits for the result with a timeout.
|
|
1903
|
-
/// Distinguishes between three cases:
|
|
1904
|
-
/// - Success: the closure returned normally
|
|
1905
|
-
/// - Panic: the closure panicked (detected via channel disconnect + catch_unwind)
|
|
1906
|
-
/// - Timeout: the timeout was exceeded (possible infinite loop)
|
|
1907
|
-
///
|
|
1908
|
-
/// Note that if a timeout occurs, the spawned thread will continue running in the
|
|
1909
|
-
/// background (Rust doesn't support killing threads), but the test will fail immediately.
|
|
1910
|
-
fn run_with_timeout<F, T>(timeout: Duration, f: F) -> TimeoutResult<T>
|
|
1911
|
-
where
|
|
1912
|
-
F: FnOnce() -> T + Send + 'static,
|
|
1913
|
-
T: Send + 'static,
|
|
1914
|
-
{
|
|
1915
|
-
let (tx, rx) = mpsc::channel();
|
|
1916
|
-
thread::spawn(move || {
|
|
1917
|
-
// Catch panics so we can report them properly instead of as timeouts
|
|
1918
|
-
let result = panic::catch_unwind(AssertUnwindSafe(f));
|
|
1919
|
-
match result {
|
|
1920
|
-
Ok(value) => {
|
|
1921
|
-
let _ = tx.send(Ok(value));
|
|
1922
|
-
}
|
|
1923
|
-
Err(panic_payload) => {
|
|
1924
|
-
// Extract panic message from the payload
|
|
1925
|
-
let msg = if let Some(s) = panic_payload.downcast_ref::<&str>() {
|
|
1926
|
-
(*s).to_string()
|
|
1927
|
-
} else if let Some(s) = panic_payload.downcast_ref::<String>() {
|
|
1928
|
-
s.clone()
|
|
1929
|
-
} else {
|
|
1930
|
-
"unknown panic".to_string()
|
|
1931
|
-
};
|
|
1932
|
-
let _ = tx.send(Err(msg));
|
|
1933
|
-
}
|
|
1934
|
-
}
|
|
1935
|
-
});
|
|
1936
|
-
|
|
1937
|
-
match rx.recv_timeout(timeout) {
|
|
1938
|
-
Ok(Ok(value)) => TimeoutResult::Ok(value),
|
|
1939
|
-
Ok(Err(panic_msg)) => TimeoutResult::Panicked(panic_msg),
|
|
1940
|
-
Err(RecvTimeoutError::Timeout) => TimeoutResult::TimedOut,
|
|
1941
|
-
// Disconnected without sending means something went very wrong
|
|
1942
|
-
Err(RecvTimeoutError::Disconnected) => {
|
|
1943
|
-
TimeoutResult::Panicked("thread terminated without sending result".to_string())
|
|
1944
|
-
}
|
|
1945
|
-
}
|
|
1946
|
-
}
|
|
1947
|
-
|
|
1948
|
-
/// Test function that runs each fixture through Monty.
|
|
1949
|
-
///
|
|
1950
|
-
/// Handles xfail with strict semantics: if a test is marked `xfail=monty`, it must fail.
|
|
1951
|
-
/// If an xfail test passes unexpectedly, that's an error.
|
|
1952
|
-
fn run_test_cases_monty(path: &Path) -> Result<(), Box<dyn Error>> {
|
|
1953
|
-
let content = fs::read_to_string(path)?;
|
|
1954
|
-
let (code, expectation, config) = parse_fixture(&content);
|
|
1955
|
-
let test_name = path.strip_prefix("test_cases/").unwrap_or(path).display().to_string();
|
|
1956
|
-
|
|
1957
|
-
// Move data into the closure since it needs 'static lifetime
|
|
1958
|
-
let path_owned = path.to_owned();
|
|
1959
|
-
let iter_mode = config.iter_mode;
|
|
1960
|
-
|
|
1961
|
-
let result = run_with_timeout(TEST_TIMEOUT, move || {
|
|
1962
|
-
if iter_mode {
|
|
1963
|
-
try_run_iter_test(&path_owned, &code, &expectation)
|
|
1964
|
-
} else {
|
|
1965
|
-
try_run_test(&path_owned, &code, &expectation)
|
|
1966
|
-
}
|
|
1967
|
-
});
|
|
1968
|
-
|
|
1969
|
-
// Handle timeout/panic errors from the test thread
|
|
1970
|
-
let result = match result {
|
|
1971
|
-
TimeoutResult::Ok(inner_result) => inner_result,
|
|
1972
|
-
TimeoutResult::Panicked(panic_msg) => Err(TestFailure {
|
|
1973
|
-
test_name: test_name.clone(),
|
|
1974
|
-
kind: "Panic".to_string(),
|
|
1975
|
-
expected: "no panic".to_string(),
|
|
1976
|
-
actual: format!("test panicked: {panic_msg}"),
|
|
1977
|
-
}),
|
|
1978
|
-
TimeoutResult::TimedOut => Err(TestFailure {
|
|
1979
|
-
test_name: test_name.clone(),
|
|
1980
|
-
kind: "Timeout".to_string(),
|
|
1981
|
-
expected: format!("completion within {TEST_TIMEOUT:?}"),
|
|
1982
|
-
actual: format!("test timed out after {TEST_TIMEOUT:?} (possible infinite loop)"),
|
|
1983
|
-
}),
|
|
1984
|
-
};
|
|
1985
|
-
|
|
1986
|
-
if config.xfail_monty {
|
|
1987
|
-
// Strict xfail: test must fail; if it passed, xfail should be removed
|
|
1988
|
-
assert!(
|
|
1989
|
-
result.is_err(),
|
|
1990
|
-
"[{test_name}] Test marked xfail=monty passed unexpectedly. Remove xfail if the test is now fixed."
|
|
1991
|
-
);
|
|
1992
|
-
} else if let Err(failure) = result {
|
|
1993
|
-
panic!("{failure}");
|
|
1994
|
-
}
|
|
1995
|
-
Ok(())
|
|
1996
|
-
}
|
|
1997
|
-
|
|
1998
|
-
/// Test function that runs each fixture through CPython.
|
|
1999
|
-
///
|
|
2000
|
-
/// Handles xfail with strict semantics: if a test is marked `xfail=cpython`, it must fail.
|
|
2001
|
-
/// If an xfail test passes unexpectedly, that's an error.
|
|
2002
|
-
fn run_test_cases_cpython(path: &Path) -> Result<(), Box<dyn Error>> {
|
|
2003
|
-
let content = fs::read_to_string(path)?;
|
|
2004
|
-
let (code, expectation, config) = parse_fixture(&content);
|
|
2005
|
-
let test_name = path.strip_prefix("test_cases/").unwrap_or(path).display().to_string();
|
|
2006
|
-
|
|
2007
|
-
let result = try_run_cpython_test(path, &code, &expectation, config.iter_mode, config.async_mode);
|
|
2008
|
-
|
|
2009
|
-
if config.xfail_cpython {
|
|
2010
|
-
// Strict xfail: test must fail; if it passed, xfail should be removed
|
|
2011
|
-
assert!(
|
|
2012
|
-
result.is_err(),
|
|
2013
|
-
"[{test_name}] Test marked xfail=cpython passed unexpectedly. Remove xfail if the test is now fixed."
|
|
2014
|
-
);
|
|
2015
|
-
} else if let Err(failure) = result {
|
|
2016
|
-
panic!("{failure}");
|
|
2017
|
-
}
|
|
2018
|
-
Ok(())
|
|
2019
|
-
}
|
|
2020
|
-
|
|
2021
|
-
// Generate tests for all fixture files using datatest-stable harness macro
|
|
2022
|
-
datatest_stable::harness!(
|
|
2023
|
-
run_test_cases_monty,
|
|
2024
|
-
"test_cases",
|
|
2025
|
-
r"^.*\.py$",
|
|
2026
|
-
run_test_cases_cpython,
|
|
2027
|
-
"test_cases",
|
|
2028
|
-
r"^.*\.py$",
|
|
2029
|
-
);
|