superacli 1.1.5 → 1.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1081) hide show
  1. package/README.md +77 -53
  2. package/__tests__/azd-plugin.test.js +109 -0
  3. package/__tests__/config.test.js +4 -3
  4. package/__tests__/discover.test.js +59 -0
  5. package/__tests__/goose-plugin.test.js +149 -0
  6. package/__tests__/help-json.test.js +2 -0
  7. package/__tests__/openhands-plugin.test.js +106 -0
  8. package/__tests__/plugin-cocoindex-code-uninstall.test.js +19 -0
  9. package/__tests__/plugin-cocoindex-code.test.js +37 -0
  10. package/__tests__/plugin-install-guidance.test.js +81 -0
  11. package/__tests__/plugins-registry.test.js +44 -0
  12. package/__tests__/plugins-store.test.js +40 -5
  13. package/__tests__/process-adapter.test.js +50 -1
  14. package/__tests__/resend-plugin.test.js +22 -1
  15. package/__tests__/server-app.test.js +1 -0
  16. package/__tests__/server-routes-commands.test.js +20 -2
  17. package/__tests__/server-routes-plugins.test.js +130 -0
  18. package/__tests__/skills.test.js +26 -0
  19. package/__tests__/squirrelscan-plugin.test.js +129 -0
  20. package/__tests__/uipath-plugin.test.js +104 -0
  21. package/__tests__/uipathcli-plugin.test.js +95 -0
  22. package/cli/adapters/mcp.js +2 -0
  23. package/cli/adapters/process.js +49 -2
  24. package/cli/config.js +240 -3
  25. package/cli/discover.js +157 -0
  26. package/cli/help-json.js +16 -1
  27. package/cli/plugin-install-guidance.js +92 -37
  28. package/cli/plugins-manager.js +1 -0
  29. package/cli/plugins-registry.js +74 -8
  30. package/cli/plugins-store.js +78 -17
  31. package/cli/skills-mcp.js +1 -1
  32. package/cli/skills.js +39 -2
  33. package/cli/supercli.js +87 -11
  34. package/docs/feature-gaps.md +8 -8
  35. package/docs/features/azd-uipath-plugins.md +43 -0
  36. package/docs/features/server-plugins.md +62 -0
  37. package/docs/features/skills.md +9 -5
  38. package/docs/{supported-harnesses.md → plugins-available.md} +4 -3
  39. package/docs/{plugin-harness-guide.md → plugins-how-to.md} +1 -1
  40. package/docs/plugins.md +26 -20
  41. package/docs/server-plugins-usage-guide.md +182 -0
  42. package/docs/skills-catalog.md +12 -10
  43. package/package.json +1 -1
  44. package/plugins/agent-browser/README.md +69 -0
  45. package/plugins/agent-browser/plugin.json +111 -0
  46. package/plugins/agent-browser/skills/quickstart/SKILL.md +66 -0
  47. package/plugins/aider/README.md +53 -0
  48. package/plugins/aider/plugin.json +105 -0
  49. package/plugins/aider/scripts/aider-wrapper.js +243 -0
  50. package/plugins/aider/scripts/setup-aider.js +37 -0
  51. package/plugins/aider/skills/dry-run-review.md +24 -0
  52. package/plugins/aider/skills/model-and-provider.md +24 -0
  53. package/plugins/aider/skills/one-shot-edits.md +30 -0
  54. package/plugins/aider/skills/quickstart/SKILL.md +51 -0
  55. package/plugins/azd/README.md +28 -0
  56. package/plugins/azd/plugin.json +87 -0
  57. package/plugins/azd/skills/quickstart/SKILL.md +41 -0
  58. package/plugins/blogwatcher/README.md +3 -3
  59. package/plugins/boxlite/Dockerfile +9 -0
  60. package/plugins/boxlite/README.md +62 -0
  61. package/plugins/boxlite/plugin.json +201 -0
  62. package/plugins/boxlite/scripts/run-boxlite.js +106 -0
  63. package/plugins/boxlite/skills/quickstart/SKILL.md +40 -0
  64. package/plugins/cass/plugin.json +150 -0
  65. package/plugins/cass/scripts/setup-cass.js +47 -0
  66. package/plugins/cass/skills/quickstart/SKILL.md +46 -0
  67. package/plugins/clever/README.md +46 -0
  68. package/plugins/clever/plugin.json +119 -0
  69. package/plugins/clever/scripts/setup-clever.js +28 -0
  70. package/plugins/clever/skills/auth-and-profile.md +29 -0
  71. package/plugins/clever/skills/passthrough-safety.md +21 -0
  72. package/plugins/clever/skills/quickstart/SKILL.md +45 -0
  73. package/plugins/clever/skills/resource-inventory.md +24 -0
  74. package/plugins/clix/README.md +4 -4
  75. package/plugins/cocoindex-code/README.md +64 -0
  76. package/plugins/cocoindex-code/plugin.json +81 -0
  77. package/plugins/cocoindex-code/scripts/__pycache__/query.cpython-310.pyc +0 -0
  78. package/plugins/cocoindex-code/scripts/__pycache__/query.cpython-311.pyc +0 -0
  79. package/plugins/cocoindex-code/scripts/post-install.js +61 -0
  80. package/plugins/cocoindex-code/scripts/post-uninstall.js +25 -0
  81. package/plugins/cocoindex-code/scripts/query.py +88 -0
  82. package/plugins/cocoindex-code/scripts/run-query.js +50 -0
  83. package/plugins/cocoindex-code/skills/quickstart/SKILL.md +73 -0
  84. package/plugins/copilot/README.md +24 -0
  85. package/plugins/copilot/plugin.json +80 -0
  86. package/plugins/copilot/skills/quickstart/SKILL.md +44 -0
  87. package/plugins/gemini/README.md +24 -0
  88. package/plugins/gemini/plugin.json +98 -0
  89. package/plugins/gemini/skills/quickstart/SKILL.md +44 -0
  90. package/plugins/gifcap/plugin.json +119 -0
  91. package/plugins/gifcap/scripts/setup-gifcap.js +44 -0
  92. package/plugins/gifcap/skills/quickstart/SKILL.md +34 -0
  93. package/plugins/gifcap/test-record-quiet.gif +0 -0
  94. package/plugins/gifcap/test-record.gif +0 -0
  95. package/plugins/goose/README.md +36 -0
  96. package/plugins/goose/plugin.json +183 -0
  97. package/plugins/goose/skills/quickstart/SKILL.md +44 -0
  98. package/plugins/json-server/README.md +58 -0
  99. package/plugins/json-server/plugin.json +113 -0
  100. package/plugins/json-server/skills/quickstart/SKILL.md +57 -0
  101. package/plugins/lightpanda/README.md +145 -0
  102. package/plugins/lightpanda/package-lock.json +1375 -0
  103. package/plugins/lightpanda/package.json +12 -0
  104. package/plugins/lightpanda/plugin.json +116 -0
  105. package/plugins/lightpanda/scripts/lightpanda-contacts.js +494 -0
  106. package/plugins/lightpanda/scripts/lightpanda-generic-extract.js +403 -0
  107. package/plugins/lightpanda/scripts/lightpanda-wrapper.js +480 -0
  108. package/plugins/lightpanda/scripts/setup-lightpanda.js +39 -0
  109. package/plugins/lightpanda/skills/contact-discovery.md +51 -0
  110. package/plugins/lightpanda/skills/generic-extraction.md +66 -0
  111. package/plugins/lightpanda/skills/quickstart/SKILL.md +103 -0
  112. package/plugins/lightpanda/skills/resilient-navigation.md +42 -0
  113. package/plugins/monty/README.md +2 -2
  114. package/plugins/nullclaw/README.md +3 -3
  115. package/plugins/offline-ai/README.md +23 -0
  116. package/plugins/offline-ai/plugin.json +82 -0
  117. package/plugins/offline-ai/skills/quickstart/SKILL.md +43 -0
  118. package/plugins/openhands/README.md +25 -0
  119. package/plugins/openhands/plugin.json +116 -0
  120. package/plugins/openhands/skills/quickstart/SKILL.md +26 -0
  121. package/plugins/plandex/README.md +25 -0
  122. package/plugins/plandex/plugin.json +130 -0
  123. package/plugins/plandex/skills/quickstart/SKILL.md +50 -0
  124. package/plugins/plugins.json +190 -2
  125. package/plugins/resend/plugin.json +279 -2
  126. package/plugins/resend/skills/quickstart/SKILL.md +32 -13
  127. package/plugins/squirrelscan/Dockerfile +5 -0
  128. package/plugins/squirrelscan/README.md +47 -0
  129. package/plugins/squirrelscan/plugin.json +493 -0
  130. package/plugins/squirrelscan/scripts/post-install.js +33 -0
  131. package/plugins/squirrelscan/scripts/post-uninstall.js +25 -0
  132. package/plugins/squirrelscan/scripts/run-squirrel.js +73 -0
  133. package/plugins/squirrelscan/skills/audit-workflow/SKILL.md +33 -0
  134. package/plugins/squirrelscan/skills/publish-report/SKILL.md +33 -0
  135. package/plugins/squirrelscan/skills/quickstart/SKILL.md +41 -0
  136. package/plugins/uipath/README.md +27 -0
  137. package/plugins/uipath/plugin.json +86 -0
  138. package/plugins/uipath/skills/quickstart/SKILL.md +47 -0
  139. package/plugins/uipathcli/README.md +28 -0
  140. package/plugins/uipathcli/plugin.json +120 -0
  141. package/plugins/uipathcli/scripts/run-uipath-cli.js +49 -0
  142. package/plugins/uipathcli/skills/quickstart/SKILL.md +22 -0
  143. package/plugins/xurl/README.md +4 -4
  144. package/server/app.js +5 -2
  145. package/server/public/app.js +3 -0
  146. package/server/routes/commands.js +95 -12
  147. package/server/routes/plugins.js +262 -0
  148. package/server/services/pluginsService.js +303 -0
  149. package/server/views/command-edit.ejs +196 -14
  150. package/server/views/partials/head.ejs +1 -0
  151. package/server/views/plugins.ejs +264 -0
  152. package/tests/test-plugins-registry.js +30 -0
  153. package/tests/test-resend-smoke.sh +7 -3
  154. package/.beads/.br_history/issues.20260308_200823_636718328.jsonl +0 -20
  155. package/.beads/.br_history/issues.20260308_200823_636718328.jsonl.meta.json +0 -1
  156. package/.beads/.br_history/issues.20260308_200827_033159453.jsonl +0 -21
  157. package/.beads/.br_history/issues.20260308_200827_033159453.jsonl.meta.json +0 -1
  158. package/.beads/.br_history/issues.20260308_200829_595900053.jsonl +0 -22
  159. package/.beads/.br_history/issues.20260308_200829_595900053.jsonl.meta.json +0 -1
  160. package/.beads/.br_history/issues.20260308_200834_079930100.jsonl +0 -23
  161. package/.beads/.br_history/issues.20260308_200834_079930100.jsonl.meta.json +0 -1
  162. package/.beads/.br_history/issues.20260308_200858_370924996.jsonl +0 -24
  163. package/.beads/.br_history/issues.20260308_200858_370924996.jsonl.meta.json +0 -1
  164. package/.beads/.br_history/issues.20260308_201031_019730855.jsonl +0 -24
  165. package/.beads/.br_history/issues.20260308_201031_019730855.jsonl.meta.json +0 -1
  166. package/.beads/.br_history/issues.20260308_201031_578974884.jsonl +0 -24
  167. package/.beads/.br_history/issues.20260308_201031_578974884.jsonl.meta.json +0 -1
  168. package/.beads/.br_history/issues.20260308_201054_780345548.jsonl +0 -24
  169. package/.beads/.br_history/issues.20260308_201054_780345548.jsonl.meta.json +0 -1
  170. package/.beads/.br_history/issues.20260308_201054_896980019.jsonl +0 -24
  171. package/.beads/.br_history/issues.20260308_201054_896980019.jsonl.meta.json +0 -1
  172. package/.beads/.br_history/issues.20260308_201128_599819688.jsonl +0 -24
  173. package/.beads/.br_history/issues.20260308_201128_599819688.jsonl.meta.json +0 -1
  174. package/.beads/.br_history/issues.20260308_201128_710221699.jsonl +0 -24
  175. package/.beads/.br_history/issues.20260308_201128_710221699.jsonl.meta.json +0 -1
  176. package/.beads/.br_history/issues.20260308_201204_745649213.jsonl +0 -24
  177. package/.beads/.br_history/issues.20260308_201204_745649213.jsonl.meta.json +0 -1
  178. package/.beads/.br_history/issues.20260308_201338_908436144.jsonl +0 -24
  179. package/.beads/.br_history/issues.20260308_201338_908436144.jsonl.meta.json +0 -1
  180. package/.beads/.br_history/issues.20260308_201344_734860714.jsonl +0 -25
  181. package/.beads/.br_history/issues.20260308_201344_734860714.jsonl.meta.json +0 -1
  182. package/.beads/.br_history/issues.20260308_201630_819282295.jsonl +0 -25
  183. package/.beads/.br_history/issues.20260308_201630_819282295.jsonl.meta.json +0 -1
  184. package/.beads/.br_history/issues.20260308_203538_054279699.jsonl +0 -25
  185. package/.beads/.br_history/issues.20260308_203538_054279699.jsonl.meta.json +0 -1
  186. package/.beads/.br_history/issues.20260308_203547_597113070.jsonl +0 -26
  187. package/.beads/.br_history/issues.20260308_203547_597113070.jsonl.meta.json +0 -1
  188. package/.beads/.br_history/issues.20260308_203547_775139216.jsonl +0 -27
  189. package/.beads/.br_history/issues.20260308_203547_775139216.jsonl.meta.json +0 -1
  190. package/.beads/.br_history/issues.20260308_203547_950724773.jsonl +0 -28
  191. package/.beads/.br_history/issues.20260308_203547_950724773.jsonl.meta.json +0 -1
  192. package/.beads/.br_history/issues.20260308_203548_107684523.jsonl +0 -29
  193. package/.beads/.br_history/issues.20260308_203548_107684523.jsonl.meta.json +0 -1
  194. package/.beads/.br_history/issues.20260308_203548_310389993.jsonl +0 -30
  195. package/.beads/.br_history/issues.20260308_203548_310389993.jsonl.meta.json +0 -1
  196. package/.beads/.br_history/issues.20260308_203825_953337320.jsonl +0 -31
  197. package/.beads/.br_history/issues.20260308_203825_953337320.jsonl.meta.json +0 -1
  198. package/.beads/.br_history/issues.20260308_204056_071377736.jsonl +0 -32
  199. package/.beads/.br_history/issues.20260308_204056_071377736.jsonl.meta.json +0 -1
  200. package/.beads/.br_history/issues.20260308_205141_517616844.jsonl +0 -32
  201. package/.beads/.br_history/issues.20260308_205141_517616844.jsonl.meta.json +0 -1
  202. package/.beads/.br_history/issues.20260308_205141_648994024.jsonl +0 -32
  203. package/.beads/.br_history/issues.20260308_205141_648994024.jsonl.meta.json +0 -1
  204. package/.beads/.br_history/issues.20260308_205141_867598036.jsonl +0 -32
  205. package/.beads/.br_history/issues.20260308_205141_867598036.jsonl.meta.json +0 -1
  206. package/.beads/.br_history/issues.20260308_205142_094157355.jsonl +0 -32
  207. package/.beads/.br_history/issues.20260308_205142_094157355.jsonl.meta.json +0 -1
  208. package/.beads/.br_history/issues.20260308_205142_327315677.jsonl +0 -32
  209. package/.beads/.br_history/issues.20260308_205142_327315677.jsonl.meta.json +0 -1
  210. package/.beads/.br_history/issues.20260308_205142_545563822.jsonl +0 -32
  211. package/.beads/.br_history/issues.20260308_205142_545563822.jsonl.meta.json +0 -1
  212. package/.beads/.br_history/issues.20260308_205213_061989333.jsonl +0 -32
  213. package/.beads/.br_history/issues.20260308_205213_061989333.jsonl.meta.json +0 -1
  214. package/.beads/.br_history/issues.20260308_205213_181103364.jsonl +0 -32
  215. package/.beads/.br_history/issues.20260308_205213_181103364.jsonl.meta.json +0 -1
  216. package/.beads/.br_history/issues.20260308_205213_408872234.jsonl +0 -32
  217. package/.beads/.br_history/issues.20260308_205213_408872234.jsonl.meta.json +0 -1
  218. package/.beads/.br_history/issues.20260308_205213_616681652.jsonl +0 -32
  219. package/.beads/.br_history/issues.20260308_205213_616681652.jsonl.meta.json +0 -1
  220. package/.beads/.br_history/issues.20260308_205213_821507069.jsonl +0 -32
  221. package/.beads/.br_history/issues.20260308_205213_821507069.jsonl.meta.json +0 -1
  222. package/.beads/.br_history/issues.20260308_205214_026661112.jsonl +0 -32
  223. package/.beads/.br_history/issues.20260308_205214_026661112.jsonl.meta.json +0 -1
  224. package/.beads/.br_history/issues.20260308_205454_955250554.jsonl +0 -32
  225. package/.beads/.br_history/issues.20260308_205454_955250554.jsonl.meta.json +0 -1
  226. package/.beads/.br_history/issues.20260308_205556_337800392.jsonl +0 -33
  227. package/.beads/.br_history/issues.20260308_205556_337800392.jsonl.meta.json +0 -1
  228. package/.beads/.br_history/issues.20260308_205824_274686694.jsonl +0 -33
  229. package/.beads/.br_history/issues.20260308_205824_274686694.jsonl.meta.json +0 -1
  230. package/.beads/.br_history/issues.20260308_210240_583768328.jsonl +0 -34
  231. package/.beads/.br_history/issues.20260308_210240_583768328.jsonl.meta.json +0 -1
  232. package/.beads/.br_history/issues.20260308_212223_641541494.jsonl +0 -34
  233. package/.beads/.br_history/issues.20260308_212223_641541494.jsonl.meta.json +0 -1
  234. package/.beads/.br_history/issues.20260308_212227_735550996.jsonl +0 -35
  235. package/.beads/.br_history/issues.20260308_212227_735550996.jsonl.meta.json +0 -1
  236. package/.beads/.br_history/issues.20260308_212232_547298548.jsonl +0 -36
  237. package/.beads/.br_history/issues.20260308_212232_547298548.jsonl.meta.json +0 -1
  238. package/.beads/.br_history/issues.20260308_212528_843628125.jsonl +0 -37
  239. package/.beads/.br_history/issues.20260308_212528_843628125.jsonl.meta.json +0 -1
  240. package/.beads/.br_history/issues.20260308_212529_094530502.jsonl +0 -38
  241. package/.beads/.br_history/issues.20260308_212529_094530502.jsonl.meta.json +0 -1
  242. package/.beads/.br_history/issues.20260308_212529_331000853.jsonl +0 -39
  243. package/.beads/.br_history/issues.20260308_212529_331000853.jsonl.meta.json +0 -1
  244. package/.beads/.br_history/issues.20260308_212529_587925652.jsonl +0 -40
  245. package/.beads/.br_history/issues.20260308_212529_587925652.jsonl.meta.json +0 -1
  246. package/.beads/.br_history/issues.20260308_212804_927764103.jsonl +0 -41
  247. package/.beads/.br_history/issues.20260308_212804_927764103.jsonl.meta.json +0 -1
  248. package/.beads/.br_history/issues.20260308_212805_153673453.jsonl +0 -42
  249. package/.beads/.br_history/issues.20260308_212805_153673453.jsonl.meta.json +0 -1
  250. package/.beads/.br_history/issues.20260308_212805_415982363.jsonl +0 -43
  251. package/.beads/.br_history/issues.20260308_212805_415982363.jsonl.meta.json +0 -1
  252. package/.beads/.br_history/issues.20260308_212805_657497741.jsonl +0 -44
  253. package/.beads/.br_history/issues.20260308_212805_657497741.jsonl.meta.json +0 -1
  254. package/.beads/.br_history/issues.20260308_212805_952838724.jsonl +0 -45
  255. package/.beads/.br_history/issues.20260308_212805_952838724.jsonl.meta.json +0 -1
  256. package/.beads/.br_history/issues.20260308_212806_325433779.jsonl +0 -46
  257. package/.beads/.br_history/issues.20260308_212806_325433779.jsonl.meta.json +0 -1
  258. package/.beads/.br_history/issues.20260308_212806_584685598.jsonl +0 -47
  259. package/.beads/.br_history/issues.20260308_212806_584685598.jsonl.meta.json +0 -1
  260. package/.beads/.br_history/issues.20260308_212806_827817208.jsonl +0 -48
  261. package/.beads/.br_history/issues.20260308_212806_827817208.jsonl.meta.json +0 -1
  262. package/.beads/.br_history/issues.20260308_212807_111320451.jsonl +0 -49
  263. package/.beads/.br_history/issues.20260308_212807_111320451.jsonl.meta.json +0 -1
  264. package/.beads/.br_history/issues.20260308_212807_409545536.jsonl +0 -50
  265. package/.beads/.br_history/issues.20260308_212807_409545536.jsonl.meta.json +0 -1
  266. package/.beads/.br_history/issues.20260308_212807_625063294.jsonl +0 -51
  267. package/.beads/.br_history/issues.20260308_212807_625063294.jsonl.meta.json +0 -1
  268. package/.beads/.br_history/issues.20260308_212807_843906551.jsonl +0 -52
  269. package/.beads/.br_history/issues.20260308_212807_843906551.jsonl.meta.json +0 -1
  270. package/.beads/.br_history/issues.20260308_212808_100304073.jsonl +0 -53
  271. package/.beads/.br_history/issues.20260308_212808_100304073.jsonl.meta.json +0 -1
  272. package/.beads/.br_history/issues.20260308_212808_324723976.jsonl +0 -54
  273. package/.beads/.br_history/issues.20260308_212808_324723976.jsonl.meta.json +0 -1
  274. package/.beads/.br_history/issues.20260308_212808_557513104.jsonl +0 -55
  275. package/.beads/.br_history/issues.20260308_212808_557513104.jsonl.meta.json +0 -1
  276. package/.beads/.br_history/issues.20260308_212808_788048322.jsonl +0 -56
  277. package/.beads/.br_history/issues.20260308_212808_788048322.jsonl.meta.json +0 -1
  278. package/.beads/.br_history/issues.20260308_213702_613249728.jsonl +0 -57
  279. package/.beads/.br_history/issues.20260308_213702_613249728.jsonl.meta.json +0 -1
  280. package/.beads/.br_history/issues.20260308_213715_115792063.jsonl +0 -57
  281. package/.beads/.br_history/issues.20260308_213715_115792063.jsonl.meta.json +0 -1
  282. package/.beads/.br_history/issues.20260308_213715_462220666.jsonl +0 -57
  283. package/.beads/.br_history/issues.20260308_213715_462220666.jsonl.meta.json +0 -1
  284. package/.beads/.br_history/issues.20260308_213727_191258923.jsonl +0 -57
  285. package/.beads/.br_history/issues.20260308_213727_191258923.jsonl.meta.json +0 -1
  286. package/.beads/.br_history/issues.20260308_213727_684383652.jsonl +0 -57
  287. package/.beads/.br_history/issues.20260308_213727_684383652.jsonl.meta.json +0 -1
  288. package/.beads/.br_history/issues.20260308_213735_751882991.jsonl +0 -57
  289. package/.beads/.br_history/issues.20260308_213735_751882991.jsonl.meta.json +0 -1
  290. package/.beads/.br_history/issues.20260308_222052_279844960.jsonl +0 -57
  291. package/.beads/.br_history/issues.20260308_222052_279844960.jsonl.meta.json +0 -1
  292. package/.beads/.br_history/issues.20260308_222056_873282114.jsonl +0 -57
  293. package/.beads/.br_history/issues.20260308_222056_873282114.jsonl.meta.json +0 -1
  294. package/.beads/.br_history/issues.20260308_222103_402410761.jsonl +0 -57
  295. package/.beads/.br_history/issues.20260308_222103_402410761.jsonl.meta.json +0 -1
  296. package/.beads/.br_history/issues.20260308_235202_180577215.jsonl +0 -57
  297. package/.beads/.br_history/issues.20260308_235202_180577215.jsonl.meta.json +0 -1
  298. package/.beads/.br_history/issues.20260308_235202_387414163.jsonl +0 -57
  299. package/.beads/.br_history/issues.20260308_235202_387414163.jsonl.meta.json +0 -1
  300. package/.beads/.br_history/issues.20260308_235202_564422794.jsonl +0 -57
  301. package/.beads/.br_history/issues.20260308_235202_564422794.jsonl.meta.json +0 -1
  302. package/.beads/.br_history/issues.20260308_235202_742600597.jsonl +0 -57
  303. package/.beads/.br_history/issues.20260308_235202_742600597.jsonl.meta.json +0 -1
  304. package/.beads/.br_history/issues.20260308_235208_133360069.jsonl +0 -57
  305. package/.beads/.br_history/issues.20260308_235208_133360069.jsonl.meta.json +0 -1
  306. package/.beads/.br_history/issues.20260308_235505_473406307.jsonl +0 -57
  307. package/.beads/.br_history/issues.20260308_235505_473406307.jsonl.meta.json +0 -1
  308. package/.beads/.br_history/issues.20260308_235505_662360489.jsonl +0 -57
  309. package/.beads/.br_history/issues.20260308_235505_662360489.jsonl.meta.json +0 -1
  310. package/.beads/.br_history/issues.20260308_235505_843935624.jsonl +0 -57
  311. package/.beads/.br_history/issues.20260308_235505_843935624.jsonl.meta.json +0 -1
  312. package/.beads/.br_history/issues.20260308_235506_044530221.jsonl +0 -57
  313. package/.beads/.br_history/issues.20260308_235506_044530221.jsonl.meta.json +0 -1
  314. package/.beads/.br_history/issues.20260309_002618_115728731.jsonl +0 -57
  315. package/.beads/.br_history/issues.20260309_002618_115728731.jsonl.meta.json +0 -1
  316. package/.beads/.br_history/issues.20260309_003748_878174586.jsonl +0 -57
  317. package/.beads/.br_history/issues.20260309_003748_878174586.jsonl.meta.json +0 -1
  318. package/.beads/.br_history/issues.20260309_004057_868755623.jsonl +0 -57
  319. package/.beads/.br_history/issues.20260309_004057_868755623.jsonl.meta.json +0 -1
  320. package/.beads/.br_history/issues.20260309_004058_512842163.jsonl +0 -57
  321. package/.beads/.br_history/issues.20260309_004058_512842163.jsonl.meta.json +0 -1
  322. package/.beads/.br_history/issues.20260309_004058_994445226.jsonl +0 -57
  323. package/.beads/.br_history/issues.20260309_004058_994445226.jsonl.meta.json +0 -1
  324. package/.beads/.br_history/issues.20260309_004059_475988596.jsonl +0 -57
  325. package/.beads/.br_history/issues.20260309_004059_475988596.jsonl.meta.json +0 -1
  326. package/.beads/.br_history/issues.20260309_161902_566857851.jsonl +0 -57
  327. package/.beads/.br_history/issues.20260309_161902_566857851.jsonl.meta.json +0 -1
  328. package/.beads/.br_history/issues.20260309_170512_277017739.jsonl +0 -57
  329. package/.beads/.br_history/issues.20260309_170512_277017739.jsonl.meta.json +0 -1
  330. package/.beads/.br_history/issues.20260309_170512_477876921.jsonl +0 -57
  331. package/.beads/.br_history/issues.20260309_170512_477876921.jsonl.meta.json +0 -1
  332. package/.beads/.br_history/issues.20260309_170512_664382701.jsonl +0 -57
  333. package/.beads/.br_history/issues.20260309_170512_664382701.jsonl.meta.json +0 -1
  334. package/.beads/.br_history/issues.20260309_170512_859400333.jsonl +0 -57
  335. package/.beads/.br_history/issues.20260309_170512_859400333.jsonl.meta.json +0 -1
  336. package/.beads/.br_history/issues.20260309_212326_082771164.jsonl +0 -57
  337. package/.beads/.br_history/issues.20260309_212326_082771164.jsonl.meta.json +0 -1
  338. package/.beads/.br_history/issues.20260309_212326_245619716.jsonl +0 -58
  339. package/.beads/.br_history/issues.20260309_212326_245619716.jsonl.meta.json +0 -1
  340. package/.beads/.br_history/issues.20260309_212326_403198317.jsonl +0 -59
  341. package/.beads/.br_history/issues.20260309_212326_403198317.jsonl.meta.json +0 -1
  342. package/.beads/.br_history/issues.20260309_212332_539197678.jsonl +0 -60
  343. package/.beads/.br_history/issues.20260309_212332_539197678.jsonl.meta.json +0 -1
  344. package/.beads/.br_history/issues.20260309_212332_731373599.jsonl +0 -60
  345. package/.beads/.br_history/issues.20260309_212332_731373599.jsonl.meta.json +0 -1
  346. package/.beads/.br_history/issues.20260309_212332_928710953.jsonl +0 -60
  347. package/.beads/.br_history/issues.20260309_212332_928710953.jsonl.meta.json +0 -1
  348. package/.beads/.br_history/issues.20260309_213021_341505240.jsonl +0 -60
  349. package/.beads/.br_history/issues.20260309_213021_341505240.jsonl.meta.json +0 -1
  350. package/.beads/.br_history/issues.20260309_213022_023136934.jsonl +0 -60
  351. package/.beads/.br_history/issues.20260309_213022_023136934.jsonl.meta.json +0 -1
  352. package/.beads/.br_history/issues.20260309_213022_400050719.jsonl +0 -60
  353. package/.beads/.br_history/issues.20260309_213022_400050719.jsonl.meta.json +0 -1
  354. package/.beads/config.yaml +0 -4
  355. package/.beads/issues.jsonl +0 -60
  356. package/.beads/metadata.json +0 -4
  357. package/docs/mcp-cheatsheet.md +0 -324
  358. package/docs/visual-overview.md +0 -21
  359. package/ref-monty/.cargo/config.toml +0 -3
  360. package/ref-monty/.claude/settings.json +0 -60
  361. package/ref-monty/.claude/skills/fastmod/SKILL.md +0 -22
  362. package/ref-monty/.claude/skills/python-playground/SKILL.md +0 -47
  363. package/ref-monty/.codecov.yml +0 -12
  364. package/ref-monty/.github/actions/build-pgo-wheel/action.yml +0 -72
  365. package/ref-monty/.github/workflows/ci.yml +0 -776
  366. package/ref-monty/.github/workflows/codspeed.yml +0 -45
  367. package/ref-monty/.github/workflows/init-npm-packages.yml +0 -82
  368. package/ref-monty/.pre-commit-config.yaml +0 -47
  369. package/ref-monty/.python-version +0 -1
  370. package/ref-monty/.rustfmt.toml +0 -4
  371. package/ref-monty/.zed/settings.json +0 -11
  372. package/ref-monty/CLAUDE.md +0 -535
  373. package/ref-monty/Cargo.lock +0 -3798
  374. package/ref-monty/Cargo.toml +0 -87
  375. package/ref-monty/LICENSE +0 -21
  376. package/ref-monty/Makefile +0 -216
  377. package/ref-monty/README.md +0 -430
  378. package/ref-monty/RELEASING.md +0 -47
  379. package/ref-monty/crates/fuzz/Cargo.toml +0 -30
  380. package/ref-monty/crates/fuzz/fuzz_targets/string_input_panic.rs +0 -37
  381. package/ref-monty/crates/fuzz/fuzz_targets/tokens_input_panic.rs +0 -552
  382. package/ref-monty/crates/monty/Cargo.toml +0 -68
  383. package/ref-monty/crates/monty/benches/main.rs +0 -247
  384. package/ref-monty/crates/monty/build.rs +0 -10
  385. package/ref-monty/crates/monty/src/args.rs +0 -733
  386. package/ref-monty/crates/monty/src/asyncio.rs +0 -179
  387. package/ref-monty/crates/monty/src/builtins/abs.rs +0 -55
  388. package/ref-monty/crates/monty/src/builtins/all.rs +0 -30
  389. package/ref-monty/crates/monty/src/builtins/any.rs +0 -30
  390. package/ref-monty/crates/monty/src/builtins/bin.rs +0 -59
  391. package/ref-monty/crates/monty/src/builtins/chr.rs +0 -46
  392. package/ref-monty/crates/monty/src/builtins/divmod.rs +0 -164
  393. package/ref-monty/crates/monty/src/builtins/enumerate.rs +0 -52
  394. package/ref-monty/crates/monty/src/builtins/filter.rs +0 -67
  395. package/ref-monty/crates/monty/src/builtins/getattr.rs +0 -65
  396. package/ref-monty/crates/monty/src/builtins/hash.rs +0 -28
  397. package/ref-monty/crates/monty/src/builtins/hex.rs +0 -58
  398. package/ref-monty/crates/monty/src/builtins/id.rs +0 -24
  399. package/ref-monty/crates/monty/src/builtins/isinstance.rs +0 -68
  400. package/ref-monty/crates/monty/src/builtins/len.rs +0 -25
  401. package/ref-monty/crates/monty/src/builtins/map.rs +0 -98
  402. package/ref-monty/crates/monty/src/builtins/min_max.rs +0 -113
  403. package/ref-monty/crates/monty/src/builtins/mod.rs +0 -246
  404. package/ref-monty/crates/monty/src/builtins/next.rs +0 -21
  405. package/ref-monty/crates/monty/src/builtins/oct.rs +0 -59
  406. package/ref-monty/crates/monty/src/builtins/ord.rs +0 -67
  407. package/ref-monty/crates/monty/src/builtins/pow.rs +0 -365
  408. package/ref-monty/crates/monty/src/builtins/print.rs +0 -141
  409. package/ref-monty/crates/monty/src/builtins/repr.rs +0 -16
  410. package/ref-monty/crates/monty/src/builtins/reversed.rs +0 -28
  411. package/ref-monty/crates/monty/src/builtins/round.rs +0 -174
  412. package/ref-monty/crates/monty/src/builtins/sorted.rs +0 -151
  413. package/ref-monty/crates/monty/src/builtins/sum.rs +0 -66
  414. package/ref-monty/crates/monty/src/builtins/type_.rs +0 -16
  415. package/ref-monty/crates/monty/src/builtins/zip.rs +0 -77
  416. package/ref-monty/crates/monty/src/bytecode/builder.rs +0 -699
  417. package/ref-monty/crates/monty/src/bytecode/code.rs +0 -310
  418. package/ref-monty/crates/monty/src/bytecode/compiler.rs +0 -3206
  419. package/ref-monty/crates/monty/src/bytecode/mod.rs +0 -24
  420. package/ref-monty/crates/monty/src/bytecode/op.rs +0 -617
  421. package/ref-monty/crates/monty/src/bytecode/vm/async_exec.rs +0 -1058
  422. package/ref-monty/crates/monty/src/bytecode/vm/attr.rs +0 -63
  423. package/ref-monty/crates/monty/src/bytecode/vm/binary.rs +0 -487
  424. package/ref-monty/crates/monty/src/bytecode/vm/call.rs +0 -767
  425. package/ref-monty/crates/monty/src/bytecode/vm/collections.rs +0 -741
  426. package/ref-monty/crates/monty/src/bytecode/vm/compare.rs +0 -147
  427. package/ref-monty/crates/monty/src/bytecode/vm/exceptions.rs +0 -297
  428. package/ref-monty/crates/monty/src/bytecode/vm/format.rs +0 -132
  429. package/ref-monty/crates/monty/src/bytecode/vm/mod.rs +0 -1958
  430. package/ref-monty/crates/monty/src/bytecode/vm/scheduler.rs +0 -620
  431. package/ref-monty/crates/monty/src/exception_private.rs +0 -1513
  432. package/ref-monty/crates/monty/src/exception_public.rs +0 -346
  433. package/ref-monty/crates/monty/src/expressions.rs +0 -694
  434. package/ref-monty/crates/monty/src/fstring.rs +0 -854
  435. package/ref-monty/crates/monty/src/function.rs +0 -119
  436. package/ref-monty/crates/monty/src/heap.rs +0 -1073
  437. package/ref-monty/crates/monty/src/heap_data.rs +0 -985
  438. package/ref-monty/crates/monty/src/heap_traits.rs +0 -312
  439. package/ref-monty/crates/monty/src/intern.rs +0 -837
  440. package/ref-monty/crates/monty/src/io.rs +0 -106
  441. package/ref-monty/crates/monty/src/lib.rs +0 -52
  442. package/ref-monty/crates/monty/src/modules/asyncio.rs +0 -144
  443. package/ref-monty/crates/monty/src/modules/math.rs +0 -1453
  444. package/ref-monty/crates/monty/src/modules/mod.rs +0 -120
  445. package/ref-monty/crates/monty/src/modules/os.rs +0 -116
  446. package/ref-monty/crates/monty/src/modules/pathlib.rs +0 -33
  447. package/ref-monty/crates/monty/src/modules/re.rs +0 -606
  448. package/ref-monty/crates/monty/src/modules/sys.rs +0 -60
  449. package/ref-monty/crates/monty/src/modules/typing.rs +0 -70
  450. package/ref-monty/crates/monty/src/namespace.rs +0 -21
  451. package/ref-monty/crates/monty/src/object.rs +0 -1040
  452. package/ref-monty/crates/monty/src/os.rs +0 -215
  453. package/ref-monty/crates/monty/src/parse.rs +0 -1730
  454. package/ref-monty/crates/monty/src/prepare.rs +0 -3015
  455. package/ref-monty/crates/monty/src/repl.rs +0 -1109
  456. package/ref-monty/crates/monty/src/resource.rs +0 -559
  457. package/ref-monty/crates/monty/src/run.rs +0 -457
  458. package/ref-monty/crates/monty/src/run_progress.rs +0 -821
  459. package/ref-monty/crates/monty/src/signature.rs +0 -651
  460. package/ref-monty/crates/monty/src/sorting.rs +0 -100
  461. package/ref-monty/crates/monty/src/types/bytes.rs +0 -2356
  462. package/ref-monty/crates/monty/src/types/dataclass.rs +0 -345
  463. package/ref-monty/crates/monty/src/types/dict.rs +0 -879
  464. package/ref-monty/crates/monty/src/types/dict_view.rs +0 -619
  465. package/ref-monty/crates/monty/src/types/iter.rs +0 -799
  466. package/ref-monty/crates/monty/src/types/list.rs +0 -929
  467. package/ref-monty/crates/monty/src/types/long_int.rs +0 -211
  468. package/ref-monty/crates/monty/src/types/mod.rs +0 -48
  469. package/ref-monty/crates/monty/src/types/module.rs +0 -146
  470. package/ref-monty/crates/monty/src/types/namedtuple.rs +0 -261
  471. package/ref-monty/crates/monty/src/types/path.rs +0 -596
  472. package/ref-monty/crates/monty/src/types/property.rs +0 -35
  473. package/ref-monty/crates/monty/src/types/py_trait.rs +0 -322
  474. package/ref-monty/crates/monty/src/types/range.rs +0 -285
  475. package/ref-monty/crates/monty/src/types/re_match.rs +0 -522
  476. package/ref-monty/crates/monty/src/types/re_pattern.rs +0 -726
  477. package/ref-monty/crates/monty/src/types/set.rs +0 -1373
  478. package/ref-monty/crates/monty/src/types/slice.rs +0 -257
  479. package/ref-monty/crates/monty/src/types/str.rs +0 -2051
  480. package/ref-monty/crates/monty/src/types/tuple.rs +0 -376
  481. package/ref-monty/crates/monty/src/types/type.rs +0 -407
  482. package/ref-monty/crates/monty/src/value.rs +0 -2558
  483. package/ref-monty/crates/monty/test_cases/args__dict_get_no_args.py +0 -3
  484. package/ref-monty/crates/monty/test_cases/args__dict_get_too_many.py +0 -3
  485. package/ref-monty/crates/monty/test_cases/args__dict_items_with_args.py +0 -3
  486. package/ref-monty/crates/monty/test_cases/args__dict_keys_with_args.py +0 -3
  487. package/ref-monty/crates/monty/test_cases/args__dict_pop_no_args.py +0 -3
  488. package/ref-monty/crates/monty/test_cases/args__dict_pop_too_many.py +0 -3
  489. package/ref-monty/crates/monty/test_cases/args__dict_values_with_args.py +0 -3
  490. package/ref-monty/crates/monty/test_cases/args__id_too_many.py +0 -2
  491. package/ref-monty/crates/monty/test_cases/args__len_no_args.py +0 -2
  492. package/ref-monty/crates/monty/test_cases/args__len_too_many.py +0 -2
  493. package/ref-monty/crates/monty/test_cases/args__len_type_error_int.py +0 -9
  494. package/ref-monty/crates/monty/test_cases/args__len_type_error_none.py +0 -9
  495. package/ref-monty/crates/monty/test_cases/args__list_append_no_args.py +0 -3
  496. package/ref-monty/crates/monty/test_cases/args__list_append_too_many.py +0 -3
  497. package/ref-monty/crates/monty/test_cases/args__list_insert_too_few.py +0 -3
  498. package/ref-monty/crates/monty/test_cases/args__list_insert_too_many.py +0 -3
  499. package/ref-monty/crates/monty/test_cases/args__repr_no_args.py +0 -2
  500. package/ref-monty/crates/monty/test_cases/arith__div_zero_float.py +0 -2
  501. package/ref-monty/crates/monty/test_cases/arith__div_zero_int.py +0 -2
  502. package/ref-monty/crates/monty/test_cases/arith__floordiv_zero_float.py +0 -2
  503. package/ref-monty/crates/monty/test_cases/arith__floordiv_zero_int.py +0 -2
  504. package/ref-monty/crates/monty/test_cases/arith__pow_zero_neg.py +0 -2
  505. package/ref-monty/crates/monty/test_cases/arith__pow_zero_neg_builtin.py +0 -9
  506. package/ref-monty/crates/monty/test_cases/assert__expr_fail.py +0 -2
  507. package/ref-monty/crates/monty/test_cases/assert__fail.py +0 -2
  508. package/ref-monty/crates/monty/test_cases/assert__fail_msg.py +0 -2
  509. package/ref-monty/crates/monty/test_cases/assert__fn_fail.py +0 -3
  510. package/ref-monty/crates/monty/test_cases/assert__ops.py +0 -11
  511. package/ref-monty/crates/monty/test_cases/async__asyncio_run.py +0 -47
  512. package/ref-monty/crates/monty/test_cases/async__basic.py +0 -10
  513. package/ref-monty/crates/monty/test_cases/async__closure.py +0 -14
  514. package/ref-monty/crates/monty/test_cases/async__double_await_coroutine.py +0 -16
  515. package/ref-monty/crates/monty/test_cases/async__exception.py +0 -10
  516. package/ref-monty/crates/monty/test_cases/async__ext_call.py +0 -73
  517. package/ref-monty/crates/monty/test_cases/async__gather_all.py +0 -85
  518. package/ref-monty/crates/monty/test_cases/async__nested_await.py +0 -15
  519. package/ref-monty/crates/monty/test_cases/async__nested_gather_ext.py +0 -37
  520. package/ref-monty/crates/monty/test_cases/async__not_awaitable.py +0 -10
  521. package/ref-monty/crates/monty/test_cases/async__not_imported.py +0 -14
  522. package/ref-monty/crates/monty/test_cases/async__recursion_depth_isolation.py +0 -27
  523. package/ref-monty/crates/monty/test_cases/async__return_types.py +0 -31
  524. package/ref-monty/crates/monty/test_cases/async__sequential.py +0 -16
  525. package/ref-monty/crates/monty/test_cases/async__traceback.py +0 -19
  526. package/ref-monty/crates/monty/test_cases/async__with_args.py +0 -14
  527. package/ref-monty/crates/monty/test_cases/attr__get_int_error.py +0 -9
  528. package/ref-monty/crates/monty/test_cases/attr__get_list_error.py +0 -9
  529. package/ref-monty/crates/monty/test_cases/attr__set_frozen_nonfield.py +0 -12
  530. package/ref-monty/crates/monty/test_cases/attr__set_int_error.py +0 -10
  531. package/ref-monty/crates/monty/test_cases/attr__set_list_error.py +0 -10
  532. package/ref-monty/crates/monty/test_cases/bench__kitchen_sink.py +0 -68
  533. package/ref-monty/crates/monty/test_cases/bool__ops.py +0 -20
  534. package/ref-monty/crates/monty/test_cases/builtin__add_type_error.py +0 -2
  535. package/ref-monty/crates/monty/test_cases/builtin__filter.py +0 -62
  536. package/ref-monty/crates/monty/test_cases/builtin__filter_not_iterable.py +0 -11
  537. package/ref-monty/crates/monty/test_cases/builtin__getattr.py +0 -84
  538. package/ref-monty/crates/monty/test_cases/builtin__iter_funcs.py +0 -42
  539. package/ref-monty/crates/monty/test_cases/builtin__iter_next.py +0 -66
  540. package/ref-monty/crates/monty/test_cases/builtin__map.py +0 -74
  541. package/ref-monty/crates/monty/test_cases/builtin__map_not_iterable.py +0 -11
  542. package/ref-monty/crates/monty/test_cases/builtin__math_funcs.py +0 -154
  543. package/ref-monty/crates/monty/test_cases/builtin__more_iter_funcs.py +0 -148
  544. package/ref-monty/crates/monty/test_cases/builtin__next_stop_iteration.py +0 -10
  545. package/ref-monty/crates/monty/test_cases/builtin__print_invalid_kwarg.py +0 -9
  546. package/ref-monty/crates/monty/test_cases/builtin__print_kwargs.py +0 -12
  547. package/ref-monty/crates/monty/test_cases/builtin__repr.py +0 -3
  548. package/ref-monty/crates/monty/test_cases/builtin__string_funcs.py +0 -73
  549. package/ref-monty/crates/monty/test_cases/bytes__decode_invalid_utf8.py +0 -18
  550. package/ref-monty/crates/monty/test_cases/bytes__endswith_str_error.py +0 -10
  551. package/ref-monty/crates/monty/test_cases/bytes__getitem_index_error.py +0 -10
  552. package/ref-monty/crates/monty/test_cases/bytes__index_start_gt_end.py +0 -10
  553. package/ref-monty/crates/monty/test_cases/bytes__methods.py +0 -394
  554. package/ref-monty/crates/monty/test_cases/bytes__negative_count.py +0 -9
  555. package/ref-monty/crates/monty/test_cases/bytes__ops.py +0 -90
  556. package/ref-monty/crates/monty/test_cases/bytes__startswith_str_error.py +0 -10
  557. package/ref-monty/crates/monty/test_cases/call_object.py +0 -3
  558. package/ref-monty/crates/monty/test_cases/chain_comparison__all.py +0 -79
  559. package/ref-monty/crates/monty/test_cases/closure__param_shadows_outer.py +0 -81
  560. package/ref-monty/crates/monty/test_cases/closure__pep448.py +0 -203
  561. package/ref-monty/crates/monty/test_cases/closure__undefined_nonlocal.py +0 -13
  562. package/ref-monty/crates/monty/test_cases/compare__mixed_types.py +0 -120
  563. package/ref-monty/crates/monty/test_cases/comprehension__all.py +0 -208
  564. package/ref-monty/crates/monty/test_cases/comprehension__scope.py +0 -7
  565. package/ref-monty/crates/monty/test_cases/comprehension__unbound_local.py +0 -14
  566. package/ref-monty/crates/monty/test_cases/dataclass__basic.py +0 -238
  567. package/ref-monty/crates/monty/test_cases/dataclass__call_field_error.py +0 -12
  568. package/ref-monty/crates/monty/test_cases/dataclass__frozen_set_error.py +0 -12
  569. package/ref-monty/crates/monty/test_cases/dataclass__get_missing_attr_error.py +0 -11
  570. package/ref-monty/crates/monty/test_cases/dict__get_unhashable_key.py +0 -3
  571. package/ref-monty/crates/monty/test_cases/dict__literal_unhashable_key.py +0 -2
  572. package/ref-monty/crates/monty/test_cases/dict__method_pop_missing_error.py +0 -3
  573. package/ref-monty/crates/monty/test_cases/dict__methods.py +0 -151
  574. package/ref-monty/crates/monty/test_cases/dict__ops.py +0 -133
  575. package/ref-monty/crates/monty/test_cases/dict__pop_unhashable_key.py +0 -4
  576. package/ref-monty/crates/monty/test_cases/dict__popitem_empty.py +0 -9
  577. package/ref-monty/crates/monty/test_cases/dict__subscript_missing_key.py +0 -3
  578. package/ref-monty/crates/monty/test_cases/dict__unhashable_dict_key.py +0 -2
  579. package/ref-monty/crates/monty/test_cases/dict__unhashable_list_key.py +0 -2
  580. package/ref-monty/crates/monty/test_cases/dict__unpack_type_error.py +0 -2
  581. package/ref-monty/crates/monty/test_cases/dict__views.py +0 -165
  582. package/ref-monty/crates/monty/test_cases/edge__all.py +0 -26
  583. package/ref-monty/crates/monty/test_cases/edge__float_int_mod.py +0 -2
  584. package/ref-monty/crates/monty/test_cases/edge__int_float_mod.py +0 -2
  585. package/ref-monty/crates/monty/test_cases/exc__args.py +0 -16
  586. package/ref-monty/crates/monty/test_cases/exc__str.py +0 -15
  587. package/ref-monty/crates/monty/test_cases/execute_ok__all.py +0 -54
  588. package/ref-monty/crates/monty/test_cases/execute_raise__error_instance_str.py +0 -2
  589. package/ref-monty/crates/monty/test_cases/execute_raise__error_no_args.py +0 -2
  590. package/ref-monty/crates/monty/test_cases/execute_raise__error_string_arg.py +0 -2
  591. package/ref-monty/crates/monty/test_cases/execute_raise__error_string_arg_quotes.py +0 -2
  592. package/ref-monty/crates/monty/test_cases/execute_raise__error_type.py +0 -2
  593. package/ref-monty/crates/monty/test_cases/execute_raise__raise_instance_via_var.py +0 -4
  594. package/ref-monty/crates/monty/test_cases/execute_raise__raise_list.py +0 -2
  595. package/ref-monty/crates/monty/test_cases/execute_raise__raise_number.py +0 -2
  596. package/ref-monty/crates/monty/test_cases/execute_raise__raise_type_call_via_var.py +0 -4
  597. package/ref-monty/crates/monty/test_cases/execute_raise__raise_type_direct.py +0 -3
  598. package/ref-monty/crates/monty/test_cases/execute_raise__raise_type_via_var.py +0 -4
  599. package/ref-monty/crates/monty/test_cases/ext_call__arg_side_effect_bug.py +0 -22
  600. package/ref-monty/crates/monty/test_cases/ext_call__augmented.py +0 -17
  601. package/ref-monty/crates/monty/test_cases/ext_call__augmented_refcount_bug.py +0 -7
  602. package/ref-monty/crates/monty/test_cases/ext_call__bare_raise_after_resume.py +0 -34
  603. package/ref-monty/crates/monty/test_cases/ext_call__basic.py +0 -99
  604. package/ref-monty/crates/monty/test_cases/ext_call__boolean.py +0 -37
  605. package/ref-monty/crates/monty/test_cases/ext_call__boolean_side_effect_hang.py +0 -17
  606. package/ref-monty/crates/monty/test_cases/ext_call__closure_bug.py +0 -16
  607. package/ref-monty/crates/monty/test_cases/ext_call__comparison.py +0 -26
  608. package/ref-monty/crates/monty/test_cases/ext_call__deep_call_stack.py +0 -18
  609. package/ref-monty/crates/monty/test_cases/ext_call__elif.py +0 -171
  610. package/ref-monty/crates/monty/test_cases/ext_call__exc.py +0 -4
  611. package/ref-monty/crates/monty/test_cases/ext_call__exc_deep_stack.py +0 -39
  612. package/ref-monty/crates/monty/test_cases/ext_call__exc_in_function.py +0 -17
  613. package/ref-monty/crates/monty/test_cases/ext_call__exc_nested_functions.py +0 -31
  614. package/ref-monty/crates/monty/test_cases/ext_call__ext_exc.py +0 -171
  615. package/ref-monty/crates/monty/test_cases/ext_call__for.py +0 -114
  616. package/ref-monty/crates/monty/test_cases/ext_call__fstring.py +0 -12
  617. package/ref-monty/crates/monty/test_cases/ext_call__if.py +0 -135
  618. package/ref-monty/crates/monty/test_cases/ext_call__if_condition.py +0 -37
  619. package/ref-monty/crates/monty/test_cases/ext_call__in_closure.py +0 -14
  620. package/ref-monty/crates/monty/test_cases/ext_call__in_function.py +0 -40
  621. package/ref-monty/crates/monty/test_cases/ext_call__in_function_simple.py +0 -7
  622. package/ref-monty/crates/monty/test_cases/ext_call__literals.py +0 -17
  623. package/ref-monty/crates/monty/test_cases/ext_call__multi_in_func.py +0 -32
  624. package/ref-monty/crates/monty/test_cases/ext_call__name_lookup.py +0 -69
  625. package/ref-monty/crates/monty/test_cases/ext_call__name_lookup_undefined.py +0 -4
  626. package/ref-monty/crates/monty/test_cases/ext_call__nested_calls.py +0 -14
  627. package/ref-monty/crates/monty/test_cases/ext_call__recursion_bug.py +0 -19
  628. package/ref-monty/crates/monty/test_cases/ext_call__return.py +0 -28
  629. package/ref-monty/crates/monty/test_cases/ext_call__side_effects.py +0 -25
  630. package/ref-monty/crates/monty/test_cases/ext_call__subscript.py +0 -7
  631. package/ref-monty/crates/monty/test_cases/ext_call__ternary.py +0 -28
  632. package/ref-monty/crates/monty/test_cases/ext_call__try.py +0 -280
  633. package/ref-monty/crates/monty/test_cases/ext_call__try_simple.py +0 -10
  634. package/ref-monty/crates/monty/test_cases/ext_call__unary.py +0 -13
  635. package/ref-monty/crates/monty/test_cases/frozenset__ops.py +0 -178
  636. package/ref-monty/crates/monty/test_cases/fstring__all.py +0 -236
  637. package/ref-monty/crates/monty/test_cases/fstring__error_eq_align_on_str.py +0 -3
  638. package/ref-monty/crates/monty/test_cases/fstring__error_float_f_on_str.py +0 -3
  639. package/ref-monty/crates/monty/test_cases/fstring__error_int_d_on_float.py +0 -3
  640. package/ref-monty/crates/monty/test_cases/fstring__error_int_d_on_str.py +0 -3
  641. package/ref-monty/crates/monty/test_cases/fstring__error_invalid_spec.py +0 -4
  642. package/ref-monty/crates/monty/test_cases/fstring__error_invalid_spec_dynamic.py +0 -4
  643. package/ref-monty/crates/monty/test_cases/fstring__error_invalid_spec_str.py +0 -4
  644. package/ref-monty/crates/monty/test_cases/fstring__error_str_s_on_int.py +0 -3
  645. package/ref-monty/crates/monty/test_cases/function__call_duplicate_kwargs.py +0 -6
  646. package/ref-monty/crates/monty/test_cases/function__call_unpack.py +0 -42
  647. package/ref-monty/crates/monty/test_cases/function__defaults.py +0 -117
  648. package/ref-monty/crates/monty/test_cases/function__err_duplicate_arg.py +0 -7
  649. package/ref-monty/crates/monty/test_cases/function__err_duplicate_first_arg.py +0 -7
  650. package/ref-monty/crates/monty/test_cases/function__err_duplicate_kwarg_cleanup.py +0 -9
  651. package/ref-monty/crates/monty/test_cases/function__err_kwonly_as_positional.py +0 -7
  652. package/ref-monty/crates/monty/test_cases/function__err_missing_all_posonly.py +0 -7
  653. package/ref-monty/crates/monty/test_cases/function__err_missing_heap_cleanup.py +0 -9
  654. package/ref-monty/crates/monty/test_cases/function__err_missing_kwonly.py +0 -7
  655. package/ref-monty/crates/monty/test_cases/function__err_missing_posonly_with_kwarg.py +0 -7
  656. package/ref-monty/crates/monty/test_cases/function__err_missing_with_posonly.py +0 -7
  657. package/ref-monty/crates/monty/test_cases/function__err_posonly_as_kwarg.py +0 -7
  658. package/ref-monty/crates/monty/test_cases/function__err_posonly_first_as_kwarg.py +0 -7
  659. package/ref-monty/crates/monty/test_cases/function__err_too_many_posonly.py +0 -7
  660. package/ref-monty/crates/monty/test_cases/function__err_too_many_with_kwonly.py +0 -7
  661. package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg.py +0 -7
  662. package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg_cleanup.py +0 -9
  663. package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg_quote.py +0 -13
  664. package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg_simple.py +0 -7
  665. package/ref-monty/crates/monty/test_cases/function__err_unpack_duplicate_arg.py +0 -6
  666. package/ref-monty/crates/monty/test_cases/function__err_unpack_duplicate_heap.py +0 -8
  667. package/ref-monty/crates/monty/test_cases/function__err_unpack_int.py +0 -6
  668. package/ref-monty/crates/monty/test_cases/function__err_unpack_nonstring_key.py +0 -6
  669. package/ref-monty/crates/monty/test_cases/function__err_unpack_not_mapping.py +0 -6
  670. package/ref-monty/crates/monty/test_cases/function__kwargs_unpacking.py +0 -173
  671. package/ref-monty/crates/monty/test_cases/function__ops.py +0 -294
  672. package/ref-monty/crates/monty/test_cases/function__return_none.py +0 -42
  673. package/ref-monty/crates/monty/test_cases/function__signatures.py +0 -47
  674. package/ref-monty/crates/monty/test_cases/function__too_few_args_all.py +0 -6
  675. package/ref-monty/crates/monty/test_cases/function__too_few_args_one.py +0 -6
  676. package/ref-monty/crates/monty/test_cases/function__too_few_args_two.py +0 -6
  677. package/ref-monty/crates/monty/test_cases/function__too_many_args_one.py +0 -6
  678. package/ref-monty/crates/monty/test_cases/function__too_many_args_two.py +0 -6
  679. package/ref-monty/crates/monty/test_cases/function__too_many_args_zero.py +0 -6
  680. package/ref-monty/crates/monty/test_cases/global__error_assigned_before.py +0 -7
  681. package/ref-monty/crates/monty/test_cases/global__ops.py +0 -163
  682. package/ref-monty/crates/monty/test_cases/hash__dict_unhashable.py +0 -2
  683. package/ref-monty/crates/monty/test_cases/hash__list_unhashable.py +0 -2
  684. package/ref-monty/crates/monty/test_cases/hash__ops.py +0 -153
  685. package/ref-monty/crates/monty/test_cases/id__bytes_literals_distinct.py +0 -3
  686. package/ref-monty/crates/monty/test_cases/id__int_copy_distinct.py +0 -5
  687. package/ref-monty/crates/monty/test_cases/id__is_number_is_number.py +0 -3
  688. package/ref-monty/crates/monty/test_cases/id__non_overlapping_lifetimes_distinct_types.py +0 -10
  689. package/ref-monty/crates/monty/test_cases/id__non_overlapping_lifetimes_same_types.py +0 -6
  690. package/ref-monty/crates/monty/test_cases/id__ops.py +0 -97
  691. package/ref-monty/crates/monty/test_cases/id__str_literals_same.py +0 -3
  692. package/ref-monty/crates/monty/test_cases/if__elif_else.py +0 -207
  693. package/ref-monty/crates/monty/test_cases/if__raise_elif.py +0 -11
  694. package/ref-monty/crates/monty/test_cases/if__raise_else.py +0 -13
  695. package/ref-monty/crates/monty/test_cases/if__raise_if.py +0 -9
  696. package/ref-monty/crates/monty/test_cases/if__raise_in_elif_condition.py +0 -18
  697. package/ref-monty/crates/monty/test_cases/if__raise_in_if_condition.py +0 -16
  698. package/ref-monty/crates/monty/test_cases/if_else_expr__all.py +0 -55
  699. package/ref-monty/crates/monty/test_cases/import__error_cannot_import.py +0 -9
  700. package/ref-monty/crates/monty/test_cases/import__error_module_not_found.py +0 -9
  701. package/ref-monty/crates/monty/test_cases/import__local_scope.py +0 -68
  702. package/ref-monty/crates/monty/test_cases/import__os.py +0 -25
  703. package/ref-monty/crates/monty/test_cases/import__relative_error.py +0 -9
  704. package/ref-monty/crates/monty/test_cases/import__relative_no_module_error.py +0 -9
  705. package/ref-monty/crates/monty/test_cases/import__runtime_error_when_executed.py +0 -14
  706. package/ref-monty/crates/monty/test_cases/import__star_error.py +0 -11
  707. package/ref-monty/crates/monty/test_cases/import__sys.py +0 -47
  708. package/ref-monty/crates/monty/test_cases/import__sys_monty.py +0 -28
  709. package/ref-monty/crates/monty/test_cases/import__type_checking_guard.py +0 -37
  710. package/ref-monty/crates/monty/test_cases/import__typing.py +0 -25
  711. package/ref-monty/crates/monty/test_cases/import__typing_type_ignore.py +0 -4
  712. package/ref-monty/crates/monty/test_cases/int__bigint.py +0 -467
  713. package/ref-monty/crates/monty/test_cases/int__bigint_errors.py +0 -260
  714. package/ref-monty/crates/monty/test_cases/int__ops.py +0 -219
  715. package/ref-monty/crates/monty/test_cases/int__overflow_division.py +0 -84
  716. package/ref-monty/crates/monty/test_cases/is_variant__all.py +0 -36
  717. package/ref-monty/crates/monty/test_cases/isinstance__arg2_list_error.py +0 -2
  718. package/ref-monty/crates/monty/test_cases/isinstance__arg2_type_error.py +0 -2
  719. package/ref-monty/crates/monty/test_cases/iter__dict_mutation.py +0 -4
  720. package/ref-monty/crates/monty/test_cases/iter__for.py +0 -243
  721. package/ref-monty/crates/monty/test_cases/iter__for_loop_unpacking.py +0 -66
  722. package/ref-monty/crates/monty/test_cases/iter__generator_expr.py +0 -20
  723. package/ref-monty/crates/monty/test_cases/iter__generator_expr_type.py +0 -7
  724. package/ref-monty/crates/monty/test_cases/iter__not_iterable.py +0 -3
  725. package/ref-monty/crates/monty/test_cases/lambda__all.py +0 -145
  726. package/ref-monty/crates/monty/test_cases/list__extend_not_iterable.py +0 -7
  727. package/ref-monty/crates/monty/test_cases/list__getitem_out_of_bounds.py +0 -3
  728. package/ref-monty/crates/monty/test_cases/list__index_not_found.py +0 -9
  729. package/ref-monty/crates/monty/test_cases/list__index_start_gt_end.py +0 -10
  730. package/ref-monty/crates/monty/test_cases/list__ops.py +0 -473
  731. package/ref-monty/crates/monty/test_cases/list__pop_empty.py +0 -9
  732. package/ref-monty/crates/monty/test_cases/list__pop_out_of_range.py +0 -9
  733. package/ref-monty/crates/monty/test_cases/list__pop_type_error.py +0 -9
  734. package/ref-monty/crates/monty/test_cases/list__remove_not_found.py +0 -9
  735. package/ref-monty/crates/monty/test_cases/list__setitem_dict_index.py +0 -13
  736. package/ref-monty/crates/monty/test_cases/list__setitem_huge_int_index.py +0 -13
  737. package/ref-monty/crates/monty/test_cases/list__setitem_index_error.py +0 -10
  738. package/ref-monty/crates/monty/test_cases/list__setitem_type_error.py +0 -10
  739. package/ref-monty/crates/monty/test_cases/list__unpack_type_error.py +0 -2
  740. package/ref-monty/crates/monty/test_cases/longint__index_error.py +0 -3
  741. package/ref-monty/crates/monty/test_cases/longint__repeat_error.py +0 -3
  742. package/ref-monty/crates/monty/test_cases/loop__break_continue.py +0 -113
  743. package/ref-monty/crates/monty/test_cases/loop__break_finally.py +0 -69
  744. package/ref-monty/crates/monty/test_cases/loop__break_in_function_error.py +0 -13
  745. package/ref-monty/crates/monty/test_cases/loop__break_in_if_error.py +0 -11
  746. package/ref-monty/crates/monty/test_cases/loop__break_nested_except_clears.py +0 -55
  747. package/ref-monty/crates/monty/test_cases/loop__break_outside_error.py +0 -9
  748. package/ref-monty/crates/monty/test_cases/loop__continue_finally.py +0 -81
  749. package/ref-monty/crates/monty/test_cases/loop__continue_in_function_error.py +0 -13
  750. package/ref-monty/crates/monty/test_cases/loop__continue_in_if_error.py +0 -11
  751. package/ref-monty/crates/monty/test_cases/loop__continue_nested_except_clears.py +0 -60
  752. package/ref-monty/crates/monty/test_cases/loop__continue_outside_error.py +0 -9
  753. package/ref-monty/crates/monty/test_cases/math__acos_domain_error.py +0 -11
  754. package/ref-monty/crates/monty/test_cases/math__acosh_domain_error.py +0 -11
  755. package/ref-monty/crates/monty/test_cases/math__asin_domain_error.py +0 -11
  756. package/ref-monty/crates/monty/test_cases/math__atanh_domain_error.py +0 -11
  757. package/ref-monty/crates/monty/test_cases/math__cos_inf_error.py +0 -11
  758. package/ref-monty/crates/monty/test_cases/math__cosh_overflow_error.py +0 -11
  759. package/ref-monty/crates/monty/test_cases/math__exp_overflow_error.py +0 -11
  760. package/ref-monty/crates/monty/test_cases/math__factorial_float_error.py +0 -11
  761. package/ref-monty/crates/monty/test_cases/math__factorial_negative_error.py +0 -11
  762. package/ref-monty/crates/monty/test_cases/math__floor_inf_error.py +0 -11
  763. package/ref-monty/crates/monty/test_cases/math__floor_nan_error.py +0 -11
  764. package/ref-monty/crates/monty/test_cases/math__floor_str_error.py +0 -11
  765. package/ref-monty/crates/monty/test_cases/math__fmod_inf_error.py +0 -11
  766. package/ref-monty/crates/monty/test_cases/math__gamma_neg_int_error.py +0 -11
  767. package/ref-monty/crates/monty/test_cases/math__gcd_float_error.py +0 -11
  768. package/ref-monty/crates/monty/test_cases/math__isqrt_negative_error.py +0 -11
  769. package/ref-monty/crates/monty/test_cases/math__ldexp_overflow_error.py +0 -11
  770. package/ref-monty/crates/monty/test_cases/math__log1p_domain_error.py +0 -11
  771. package/ref-monty/crates/monty/test_cases/math__log_base1_error.py +0 -11
  772. package/ref-monty/crates/monty/test_cases/math__log_zero_error.py +0 -11
  773. package/ref-monty/crates/monty/test_cases/math__module.py +0 -1432
  774. package/ref-monty/crates/monty/test_cases/math__pow_domain_error.py +0 -11
  775. package/ref-monty/crates/monty/test_cases/math__sin_inf_error.py +0 -11
  776. package/ref-monty/crates/monty/test_cases/math__sqrt_negative_error.py +0 -11
  777. package/ref-monty/crates/monty/test_cases/math__tan_inf_error.py +0 -11
  778. package/ref-monty/crates/monty/test_cases/math__trunc_str_error.py +0 -11
  779. package/ref-monty/crates/monty/test_cases/method__args_kwargs_unpacking.py +0 -259
  780. package/ref-monty/crates/monty/test_cases/name_error__unbound_local_func.py +0 -19
  781. package/ref-monty/crates/monty/test_cases/name_error__unbound_local_module.py +0 -12
  782. package/ref-monty/crates/monty/test_cases/name_error__undefined_call_chained.py +0 -9
  783. package/ref-monty/crates/monty/test_cases/name_error__undefined_call_in_expr.py +0 -9
  784. package/ref-monty/crates/monty/test_cases/name_error__undefined_call_in_function.py +0 -16
  785. package/ref-monty/crates/monty/test_cases/name_error__undefined_call_with_args.py +0 -9
  786. package/ref-monty/crates/monty/test_cases/name_error__undefined_global.py +0 -10
  787. package/ref-monty/crates/monty/test_cases/namedtuple__missing_attr.py +0 -11
  788. package/ref-monty/crates/monty/test_cases/namedtuple__ops.py +0 -34
  789. package/ref-monty/crates/monty/test_cases/nonlocal__error_module_level.py +0 -3
  790. package/ref-monty/crates/monty/test_cases/nonlocal__ops.py +0 -353
  791. package/ref-monty/crates/monty/test_cases/os__environ.py +0 -40
  792. package/ref-monty/crates/monty/test_cases/os__getenv_key_list_error.py +0 -5
  793. package/ref-monty/crates/monty/test_cases/os__getenv_key_type_error.py +0 -5
  794. package/ref-monty/crates/monty/test_cases/parse_error__complex.py +0 -3
  795. package/ref-monty/crates/monty/test_cases/pathlib__import.py +0 -11
  796. package/ref-monty/crates/monty/test_cases/pathlib__os.py +0 -136
  797. package/ref-monty/crates/monty/test_cases/pathlib__os_read_error.py +0 -12
  798. package/ref-monty/crates/monty/test_cases/pathlib__pure.py +0 -81
  799. package/ref-monty/crates/monty/test_cases/pyobject__cycle_dict_self.py +0 -5
  800. package/ref-monty/crates/monty/test_cases/pyobject__cycle_list_dict.py +0 -6
  801. package/ref-monty/crates/monty/test_cases/pyobject__cycle_list_self.py +0 -5
  802. package/ref-monty/crates/monty/test_cases/pyobject__cycle_multiple_refs.py +0 -6
  803. package/ref-monty/crates/monty/test_cases/range__error_no_args.py +0 -2
  804. package/ref-monty/crates/monty/test_cases/range__error_step_zero.py +0 -2
  805. package/ref-monty/crates/monty/test_cases/range__error_too_many_args.py +0 -2
  806. package/ref-monty/crates/monty/test_cases/range__getitem_index_error.py +0 -10
  807. package/ref-monty/crates/monty/test_cases/range__ops.py +0 -236
  808. package/ref-monty/crates/monty/test_cases/re__basic.py +0 -756
  809. package/ref-monty/crates/monty/test_cases/re__grouping.py +0 -241
  810. package/ref-monty/crates/monty/test_cases/re__match.py +0 -148
  811. package/ref-monty/crates/monty/test_cases/recursion__deep_drop.py +0 -26
  812. package/ref-monty/crates/monty/test_cases/recursion__deep_eq.py +0 -23
  813. package/ref-monty/crates/monty/test_cases/recursion__deep_hash.py +0 -46
  814. package/ref-monty/crates/monty/test_cases/recursion__deep_repr.py +0 -12
  815. package/ref-monty/crates/monty/test_cases/recursion__function_depth.py +0 -13
  816. package/ref-monty/crates/monty/test_cases/refcount__cycle_mutual_reference.py +0 -18
  817. package/ref-monty/crates/monty/test_cases/refcount__cycle_self_reference.py +0 -12
  818. package/ref-monty/crates/monty/test_cases/refcount__dict_basic.py +0 -5
  819. package/ref-monty/crates/monty/test_cases/refcount__dict_get.py +0 -5
  820. package/ref-monty/crates/monty/test_cases/refcount__dict_keys_and.py +0 -14
  821. package/ref-monty/crates/monty/test_cases/refcount__dict_overwrite.py +0 -6
  822. package/ref-monty/crates/monty/test_cases/refcount__gather_cleanup.py +0 -16
  823. package/ref-monty/crates/monty/test_cases/refcount__gather_exception.py +0 -18
  824. package/ref-monty/crates/monty/test_cases/refcount__gather_nested_cancel.py +0 -25
  825. package/ref-monty/crates/monty/test_cases/refcount__immediate_skipped.py +0 -4
  826. package/ref-monty/crates/monty/test_cases/refcount__kwargs_unpacking.py +0 -27
  827. package/ref-monty/crates/monty/test_cases/refcount__list_append_multiple.py +0 -6
  828. package/ref-monty/crates/monty/test_cases/refcount__list_append_ref.py +0 -5
  829. package/ref-monty/crates/monty/test_cases/refcount__list_concat.py +0 -5
  830. package/ref-monty/crates/monty/test_cases/refcount__list_getitem.py +0 -5
  831. package/ref-monty/crates/monty/test_cases/refcount__list_iadd.py +0 -5
  832. package/ref-monty/crates/monty/test_cases/refcount__nested_list.py +0 -4
  833. package/ref-monty/crates/monty/test_cases/refcount__re_pattern_sub_error_paths.py +0 -37
  834. package/ref-monty/crates/monty/test_cases/refcount__re_search_match.py +0 -34
  835. package/ref-monty/crates/monty/test_cases/refcount__re_sub_error_paths.py +0 -31
  836. package/ref-monty/crates/monty/test_cases/refcount__shared_reference.py +0 -4
  837. package/ref-monty/crates/monty/test_cases/refcount__single_list.py +0 -3
  838. package/ref-monty/crates/monty/test_cases/repr__cycle_detection.py +0 -24
  839. package/ref-monty/crates/monty/test_cases/set__ops.py +0 -191
  840. package/ref-monty/crates/monty/test_cases/set__review_bugs.py +0 -35
  841. package/ref-monty/crates/monty/test_cases/set__unpack_type_error.py +0 -2
  842. package/ref-monty/crates/monty/test_cases/slice__invalid_indices.py +0 -2
  843. package/ref-monty/crates/monty/test_cases/slice__kwargs.py +0 -9
  844. package/ref-monty/crates/monty/test_cases/slice__no_args.py +0 -9
  845. package/ref-monty/crates/monty/test_cases/slice__ops.py +0 -149
  846. package/ref-monty/crates/monty/test_cases/slice__step_zero.py +0 -9
  847. package/ref-monty/crates/monty/test_cases/slice__step_zero_bytes.py +0 -9
  848. package/ref-monty/crates/monty/test_cases/slice__step_zero_range.py +0 -9
  849. package/ref-monty/crates/monty/test_cases/slice__step_zero_str.py +0 -9
  850. package/ref-monty/crates/monty/test_cases/slice__step_zero_tuple.py +0 -9
  851. package/ref-monty/crates/monty/test_cases/slice__too_many_args.py +0 -9
  852. package/ref-monty/crates/monty/test_cases/str__getitem_index_error.py +0 -10
  853. package/ref-monty/crates/monty/test_cases/str__index_not_found.py +0 -9
  854. package/ref-monty/crates/monty/test_cases/str__join_no_args.py +0 -9
  855. package/ref-monty/crates/monty/test_cases/str__join_non_string.py +0 -9
  856. package/ref-monty/crates/monty/test_cases/str__join_not_iterable.py +0 -9
  857. package/ref-monty/crates/monty/test_cases/str__join_too_many_args.py +0 -9
  858. package/ref-monty/crates/monty/test_cases/str__methods.py +0 -327
  859. package/ref-monty/crates/monty/test_cases/str__ops.py +0 -162
  860. package/ref-monty/crates/monty/test_cases/str__partition_empty.py +0 -9
  861. package/ref-monty/crates/monty/test_cases/str__rsplit_empty_sep.py +0 -9
  862. package/ref-monty/crates/monty/test_cases/str__split_empty_sep.py +0 -9
  863. package/ref-monty/crates/monty/test_cases/sys__types.py +0 -7
  864. package/ref-monty/crates/monty/test_cases/traceback__division_error.py +0 -30
  865. package/ref-monty/crates/monty/test_cases/traceback__index_error.py +0 -17
  866. package/ref-monty/crates/monty/test_cases/traceback__insert_as_int.py +0 -10
  867. package/ref-monty/crates/monty/test_cases/traceback__nested_call.py +0 -29
  868. package/ref-monty/crates/monty/test_cases/traceback__nonlocal_module_scope.py +0 -10
  869. package/ref-monty/crates/monty/test_cases/traceback__nonlocal_unbound.py +0 -24
  870. package/ref-monty/crates/monty/test_cases/traceback__range_as_int.py +0 -9
  871. package/ref-monty/crates/monty/test_cases/traceback__recursion_error.py +0 -23
  872. package/ref-monty/crates/monty/test_cases/traceback__set_mutation.py +0 -11
  873. package/ref-monty/crates/monty/test_cases/traceback__undefined_attr_call.py +0 -16
  874. package/ref-monty/crates/monty/test_cases/traceback__undefined_call.py +0 -16
  875. package/ref-monty/crates/monty/test_cases/traceback__undefined_raise.py +0 -16
  876. package/ref-monty/crates/monty/test_cases/try_except__all.py +0 -472
  877. package/ref-monty/crates/monty/test_cases/try_except__bare_raise_no_context.py +0 -2
  878. package/ref-monty/crates/monty/test_cases/try_except__invalid_type.py +0 -5
  879. package/ref-monty/crates/monty/test_cases/tuple__getitem_out_of_bounds.py +0 -3
  880. package/ref-monty/crates/monty/test_cases/tuple__index_not_found.py +0 -9
  881. package/ref-monty/crates/monty/test_cases/tuple__index_start_gt_end.py +0 -10
  882. package/ref-monty/crates/monty/test_cases/tuple__methods.py +0 -19
  883. package/ref-monty/crates/monty/test_cases/tuple__ops.py +0 -133
  884. package/ref-monty/crates/monty/test_cases/tuple__unpack_type_error.py +0 -2
  885. package/ref-monty/crates/monty/test_cases/type__builtin_attr_error.py +0 -9
  886. package/ref-monty/crates/monty/test_cases/type__bytes_negative.py +0 -2
  887. package/ref-monty/crates/monty/test_cases/type__cell_not_builtin.py +0 -9
  888. package/ref-monty/crates/monty/test_cases/type__exception_attr_error.py +0 -11
  889. package/ref-monty/crates/monty/test_cases/type__float_conversion_error.py +0 -2
  890. package/ref-monty/crates/monty/test_cases/type__float_repr_both_quotes.py +0 -9
  891. package/ref-monty/crates/monty/test_cases/type__float_repr_newline.py +0 -9
  892. package/ref-monty/crates/monty/test_cases/type__float_repr_single_quote.py +0 -9
  893. package/ref-monty/crates/monty/test_cases/type__int_conversion_error.py +0 -2
  894. package/ref-monty/crates/monty/test_cases/type__list_not_iterable.py +0 -2
  895. package/ref-monty/crates/monty/test_cases/type__non_builtin_name_error.py +0 -9
  896. package/ref-monty/crates/monty/test_cases/type__ops.py +0 -200
  897. package/ref-monty/crates/monty/test_cases/type__shadow_exc.py +0 -3
  898. package/ref-monty/crates/monty/test_cases/type__shadow_int.py +0 -9
  899. package/ref-monty/crates/monty/test_cases/type__shadow_len.py +0 -3
  900. package/ref-monty/crates/monty/test_cases/type__tuple_not_iterable.py +0 -2
  901. package/ref-monty/crates/monty/test_cases/type_error__int_add_list.py +0 -2
  902. package/ref-monty/crates/monty/test_cases/type_error__int_div_str.py +0 -2
  903. package/ref-monty/crates/monty/test_cases/type_error__int_floordiv_str.py +0 -2
  904. package/ref-monty/crates/monty/test_cases/type_error__int_iadd_str.py +0 -3
  905. package/ref-monty/crates/monty/test_cases/type_error__int_mod_str.py +0 -2
  906. package/ref-monty/crates/monty/test_cases/type_error__int_pow_str.py +0 -2
  907. package/ref-monty/crates/monty/test_cases/type_error__int_sub_str.py +0 -2
  908. package/ref-monty/crates/monty/test_cases/type_error__list_add_int.py +0 -2
  909. package/ref-monty/crates/monty/test_cases/type_error__list_add_str.py +0 -2
  910. package/ref-monty/crates/monty/test_cases/type_error__list_iadd_int.py +0 -6
  911. package/ref-monty/crates/monty/test_cases/type_error__str_add_int.py +0 -2
  912. package/ref-monty/crates/monty/test_cases/type_error__str_iadd_int.py +0 -3
  913. package/ref-monty/crates/monty/test_cases/type_error__unary_invert_str.py +0 -3
  914. package/ref-monty/crates/monty/test_cases/type_error__unary_minus_str.py +0 -4
  915. package/ref-monty/crates/monty/test_cases/type_error__unary_neg_str.py +0 -3
  916. package/ref-monty/crates/monty/test_cases/type_error__unary_plus_str.py +0 -4
  917. package/ref-monty/crates/monty/test_cases/typing__types.py +0 -24
  918. package/ref-monty/crates/monty/test_cases/unpack__nested.py +0 -48
  919. package/ref-monty/crates/monty/test_cases/unpack__non_sequence.py +0 -9
  920. package/ref-monty/crates/monty/test_cases/unpack__not_enough.py +0 -9
  921. package/ref-monty/crates/monty/test_cases/unpack__ops.py +0 -153
  922. package/ref-monty/crates/monty/test_cases/unpack__star_not_enough.py +0 -9
  923. package/ref-monty/crates/monty/test_cases/unpack__too_many.py +0 -9
  924. package/ref-monty/crates/monty/test_cases/version__cpython.py +0 -4
  925. package/ref-monty/crates/monty/test_cases/walrus__all.py +0 -178
  926. package/ref-monty/crates/monty/test_cases/while__all.py +0 -206
  927. package/ref-monty/crates/monty/tests/asyncio.rs +0 -764
  928. package/ref-monty/crates/monty/tests/binary_serde.rs +0 -185
  929. package/ref-monty/crates/monty/tests/bytecode_limits.rs +0 -248
  930. package/ref-monty/crates/monty/tests/datatest_runner.rs +0 -2029
  931. package/ref-monty/crates/monty/tests/inputs.rs +0 -420
  932. package/ref-monty/crates/monty/tests/json_serde.rs +0 -250
  933. package/ref-monty/crates/monty/tests/main.rs +0 -71
  934. package/ref-monty/crates/monty/tests/math_module.rs +0 -114
  935. package/ref-monty/crates/monty/tests/name_lookup.rs +0 -482
  936. package/ref-monty/crates/monty/tests/os_tests.rs +0 -459
  937. package/ref-monty/crates/monty/tests/parse_errors.rs +0 -441
  938. package/ref-monty/crates/monty/tests/print_writer.rs +0 -238
  939. package/ref-monty/crates/monty/tests/py_object.rs +0 -121
  940. package/ref-monty/crates/monty/tests/regex.rs +0 -90
  941. package/ref-monty/crates/monty/tests/repl.rs +0 -344
  942. package/ref-monty/crates/monty/tests/resource_limits.rs +0 -1826
  943. package/ref-monty/crates/monty/tests/try_from.rs +0 -167
  944. package/ref-monty/crates/monty-cli/Cargo.toml +0 -25
  945. package/ref-monty/crates/monty-cli/src/main.rs +0 -541
  946. package/ref-monty/crates/monty-js/.cargo/config.toml +0 -2
  947. package/ref-monty/crates/monty-js/.prettierignore +0 -8
  948. package/ref-monty/crates/monty-js/Cargo.toml +0 -32
  949. package/ref-monty/crates/monty-js/README.md +0 -207
  950. package/ref-monty/crates/monty-js/__test__/async.spec.ts +0 -350
  951. package/ref-monty/crates/monty-js/__test__/basic.spec.ts +0 -114
  952. package/ref-monty/crates/monty-js/__test__/exceptions.spec.ts +0 -427
  953. package/ref-monty/crates/monty-js/__test__/external.spec.ts +0 -354
  954. package/ref-monty/crates/monty-js/__test__/inputs.spec.ts +0 -143
  955. package/ref-monty/crates/monty-js/__test__/limits.spec.ts +0 -162
  956. package/ref-monty/crates/monty-js/__test__/package.json +0 -3
  957. package/ref-monty/crates/monty-js/__test__/print.spec.ts +0 -229
  958. package/ref-monty/crates/monty-js/__test__/repl.spec.ts +0 -34
  959. package/ref-monty/crates/monty-js/__test__/serialize.spec.ts +0 -205
  960. package/ref-monty/crates/monty-js/__test__/start.spec.ts +0 -443
  961. package/ref-monty/crates/monty-js/__test__/type_check.spec.ts +0 -147
  962. package/ref-monty/crates/monty-js/__test__/types.spec.ts +0 -319
  963. package/ref-monty/crates/monty-js/build.rs +0 -61
  964. package/ref-monty/crates/monty-js/index-header.d.ts +0 -3
  965. package/ref-monty/crates/monty-js/package-lock.json +0 -4694
  966. package/ref-monty/crates/monty-js/package.json +0 -100
  967. package/ref-monty/crates/monty-js/scripts/smoke-test.sh +0 -69
  968. package/ref-monty/crates/monty-js/smoke-test/package.json +0 -17
  969. package/ref-monty/crates/monty-js/smoke-test/test.ts +0 -171
  970. package/ref-monty/crates/monty-js/smoke-test/tsconfig.json +0 -11
  971. package/ref-monty/crates/monty-js/src/convert.rs +0 -648
  972. package/ref-monty/crates/monty-js/src/exceptions.rs +0 -293
  973. package/ref-monty/crates/monty-js/src/lib.rs +0 -41
  974. package/ref-monty/crates/monty-js/src/limits.rs +0 -53
  975. package/ref-monty/crates/monty-js/src/monty_cls.rs +0 -1407
  976. package/ref-monty/crates/monty-js/tsconfig.json +0 -17
  977. package/ref-monty/crates/monty-js/wrapper.ts +0 -701
  978. package/ref-monty/crates/monty-python/Cargo.toml +0 -38
  979. package/ref-monty/crates/monty-python/README.md +0 -134
  980. package/ref-monty/crates/monty-python/build.rs +0 -4
  981. package/ref-monty/crates/monty-python/example.py +0 -40
  982. package/ref-monty/crates/monty-python/exercise.py +0 -46
  983. package/ref-monty/crates/monty-python/pyproject.toml +0 -57
  984. package/ref-monty/crates/monty-python/python/pydantic_monty/__init__.py +0 -281
  985. package/ref-monty/crates/monty-python/python/pydantic_monty/_monty.pyi +0 -677
  986. package/ref-monty/crates/monty-python/python/pydantic_monty/os_access.py +0 -933
  987. package/ref-monty/crates/monty-python/python/pydantic_monty/py.typed +0 -0
  988. package/ref-monty/crates/monty-python/src/convert.rs +0 -273
  989. package/ref-monty/crates/monty-python/src/dataclass.rs +0 -461
  990. package/ref-monty/crates/monty-python/src/exceptions.rs +0 -557
  991. package/ref-monty/crates/monty-python/src/external.rs +0 -165
  992. package/ref-monty/crates/monty-python/src/lib.rs +0 -77
  993. package/ref-monty/crates/monty-python/src/limits.rs +0 -142
  994. package/ref-monty/crates/monty-python/src/monty_cls.rs +0 -1650
  995. package/ref-monty/crates/monty-python/src/repl.rs +0 -470
  996. package/ref-monty/crates/monty-python/src/serialization.rs +0 -761
  997. package/ref-monty/crates/monty-python/tests/test_async.py +0 -1201
  998. package/ref-monty/crates/monty-python/tests/test_basic.py +0 -66
  999. package/ref-monty/crates/monty-python/tests/test_dataclasses.py +0 -971
  1000. package/ref-monty/crates/monty-python/tests/test_exceptions.py +0 -361
  1001. package/ref-monty/crates/monty-python/tests/test_external.py +0 -367
  1002. package/ref-monty/crates/monty-python/tests/test_inputs.py +0 -126
  1003. package/ref-monty/crates/monty-python/tests/test_limits.py +0 -257
  1004. package/ref-monty/crates/monty-python/tests/test_os_access.py +0 -1286
  1005. package/ref-monty/crates/monty-python/tests/test_os_access_compat.py +0 -731
  1006. package/ref-monty/crates/monty-python/tests/test_os_access_raw.py +0 -483
  1007. package/ref-monty/crates/monty-python/tests/test_os_calls.py +0 -819
  1008. package/ref-monty/crates/monty-python/tests/test_print.py +0 -208
  1009. package/ref-monty/crates/monty-python/tests/test_re.py +0 -170
  1010. package/ref-monty/crates/monty-python/tests/test_readme_examples.py +0 -20
  1011. package/ref-monty/crates/monty-python/tests/test_repl.py +0 -749
  1012. package/ref-monty/crates/monty-python/tests/test_serialize.py +0 -284
  1013. package/ref-monty/crates/monty-python/tests/test_start.py +0 -346
  1014. package/ref-monty/crates/monty-python/tests/test_threading.py +0 -163
  1015. package/ref-monty/crates/monty-python/tests/test_type_check.py +0 -344
  1016. package/ref-monty/crates/monty-python/tests/test_types.py +0 -553
  1017. package/ref-monty/crates/monty-type-checking/Cargo.toml +0 -32
  1018. package/ref-monty/crates/monty-type-checking/src/db.rs +0 -116
  1019. package/ref-monty/crates/monty-type-checking/src/lib.rs +0 -4
  1020. package/ref-monty/crates/monty-type-checking/src/type_check.rs +0 -280
  1021. package/ref-monty/crates/monty-type-checking/tests/bad_types.py +0 -109
  1022. package/ref-monty/crates/monty-type-checking/tests/bad_types_output.txt +0 -21
  1023. package/ref-monty/crates/monty-type-checking/tests/good_types.py +0 -475
  1024. package/ref-monty/crates/monty-type-checking/tests/main.rs +0 -205
  1025. package/ref-monty/crates/monty-type-checking/tests/reveal_types.py +0 -56
  1026. package/ref-monty/crates/monty-type-checking/tests/reveal_types_output.txt +0 -41
  1027. package/ref-monty/crates/monty-typeshed/Cargo.toml +0 -29
  1028. package/ref-monty/crates/monty-typeshed/README.md +0 -11
  1029. package/ref-monty/crates/monty-typeshed/build.rs +0 -101
  1030. package/ref-monty/crates/monty-typeshed/custom/README.md +0 -1
  1031. package/ref-monty/crates/monty-typeshed/custom/asyncio.pyi +0 -138
  1032. package/ref-monty/crates/monty-typeshed/custom/os.pyi +0 -87
  1033. package/ref-monty/crates/monty-typeshed/custom/sys.pyi +0 -33
  1034. package/ref-monty/crates/monty-typeshed/src/lib.rs +0 -56
  1035. package/ref-monty/crates/monty-typeshed/update.py +0 -321
  1036. package/ref-monty/crates/monty-typeshed/vendor/typeshed/source_commit.txt +0 -1
  1037. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/VERSIONS +0 -20
  1038. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/_collections_abc.pyi +0 -105
  1039. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/_typeshed/__init__.pyi +0 -394
  1040. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/asyncio.pyi +0 -138
  1041. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/builtins.pyi +0 -1434
  1042. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/collections/__init__.pyi +0 -527
  1043. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/collections/abc.pyi +0 -2
  1044. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/dataclasses.pyi +0 -502
  1045. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/enum.pyi +0 -376
  1046. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/math.pyi +0 -149
  1047. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/os.pyi +0 -87
  1048. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/pathlib/__init__.pyi +0 -395
  1049. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/pathlib/types.pyi +0 -8
  1050. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/re.pyi +0 -337
  1051. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/sys.pyi +0 -33
  1052. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/types.pyi +0 -741
  1053. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/typing.pyi +0 -1217
  1054. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/typing_extensions.pyi +0 -716
  1055. package/ref-monty/docs/usage-guide.md +0 -117
  1056. package/ref-monty/examples/README.md +0 -3
  1057. package/ref-monty/examples/expense_analysis/README.md +0 -3
  1058. package/ref-monty/examples/expense_analysis/data.py +0 -124
  1059. package/ref-monty/examples/expense_analysis/main.py +0 -115
  1060. package/ref-monty/examples/sql_playground/README.md +0 -20
  1061. package/ref-monty/examples/sql_playground/external_functions.py +0 -129
  1062. package/ref-monty/examples/sql_playground/main.py +0 -81
  1063. package/ref-monty/examples/sql_playground/sandbox_code.py +0 -82
  1064. package/ref-monty/examples/sql_playground/type_stubs.pyi +0 -14
  1065. package/ref-monty/examples/web_scraper/README.md +0 -15
  1066. package/ref-monty/examples/web_scraper/browser.py +0 -56
  1067. package/ref-monty/examples/web_scraper/example_code.py +0 -59
  1068. package/ref-monty/examples/web_scraper/external_functions.py +0 -324
  1069. package/ref-monty/examples/web_scraper/main.py +0 -193
  1070. package/ref-monty/examples/web_scraper/sub_agent.py +0 -79
  1071. package/ref-monty/monty-npm.md +0 -235
  1072. package/ref-monty/pyproject.toml +0 -162
  1073. package/ref-monty/scripts/check_imports.py +0 -91
  1074. package/ref-monty/scripts/codecov_diff.py +0 -412
  1075. package/ref-monty/scripts/complete_tests.py +0 -146
  1076. package/ref-monty/scripts/flamegraph_to_text.py +0 -208
  1077. package/ref-monty/scripts/iter_test_methods.py +0 -540
  1078. package/ref-monty/scripts/run_traceback.py +0 -180
  1079. package/ref-monty/scripts/startup_performance.py +0 -130
  1080. package/ref-monty/uv.lock +0 -1779
  1081. /package/docs/{plugin-examples.md → plugins-examples.md} +0 -0
@@ -1,2558 +0,0 @@
1
- use std::{
2
- borrow::Cow,
3
- cmp::Ordering,
4
- collections::hash_map::DefaultHasher,
5
- fmt::{self, Write},
6
- hash::{Hash, Hasher},
7
- mem::discriminant,
8
- str::FromStr,
9
- };
10
-
11
- use ahash::AHashSet;
12
- use num_bigint::BigInt;
13
- use num_integer::Integer;
14
- use num_traits::{ToPrimitive, Zero};
15
-
16
- use crate::{
17
- asyncio::CallId,
18
- builtins::Builtins,
19
- bytecode::{CallResult, VM},
20
- exception_private::{ExcType, RunError, RunResult, SimpleException},
21
- heap::{ContainsHeap, Heap, HeapData, HeapGuard, HeapId},
22
- heap_data::HeapDataMut,
23
- intern::{BytesId, FunctionId, Interns, LongIntId, StaticStrings, StringId},
24
- modules::ModuleFunctions,
25
- resource::{ResourceError, ResourceTracker, check_div_size, check_lshift_size, check_pow_size, check_repeat_size},
26
- types::{
27
- LongInt, Property, PyTrait, Str, Type,
28
- bytes::{bytes_repr_fmt, get_byte_at_index, get_bytes_slice},
29
- path,
30
- str::{allocate_char, get_char_at_index, get_str_slice, string_repr_fmt},
31
- },
32
- };
33
-
34
- /// Primary value type representing Python objects at runtime.
35
- ///
36
- /// This enum uses a hybrid design: small immediate values (Int, Bool, None) are stored
37
- /// inline, while heap-allocated values (List, Str, Dict, etc.) are stored in the arena
38
- /// and referenced via `Ref(HeapId)`.
39
- ///
40
- /// NOTE: `Clone` is intentionally NOT derived. Use `clone_with_heap()` for heap values
41
- /// or `clone_immediate()` for immediate values only. Direct cloning via `.clone()` would
42
- /// bypass reference counting and cause memory leaks.
43
- ///
44
- /// NOTE: it's important to keep this size small to minimize memory overhead!
45
- #[derive(Debug, serde::Serialize, serde::Deserialize)]
46
- pub(crate) enum Value {
47
- // Immediate values (stored inline, no heap allocation)
48
- Undefined,
49
- Ellipsis,
50
- None,
51
- Bool(bool),
52
- Int(i64),
53
- Float(f64),
54
- /// An interned string literal. The StringId references the string in the Interns table.
55
- /// To get the actual string content, use `interns.get(string_id)`.
56
- InternString(StringId),
57
- /// An interned bytes literal. The BytesId references the bytes in the Interns table.
58
- /// To get the actual bytes content, use `interns.get_bytes(bytes_id)`.
59
- InternBytes(BytesId),
60
- /// An interned long integer literal. The `LongIntId` references the `BigInt` in the Interns table.
61
- /// Used for integer literals exceeding i64 range. Converted to heap-allocated `LongInt` on load.
62
- InternLongInt(LongIntId),
63
- /// A builtin function or exception type
64
- Builtin(Builtins),
65
- /// A function from a module (not a global builtin).
66
- /// Module functions require importing a module to access (e.g., `asyncio.gather`).
67
- ModuleFunction(ModuleFunctions),
68
- /// A function defined in the module (not a closure, doesn't capture any variables)
69
- DefFunction(FunctionId),
70
- /// Reference to an external function defined on the host.
71
- ///
72
- /// The `StringId` stores the interned function name. When called, the VM yields
73
- /// a `FrameExit::ExternalCall` with this `StringId` so the host can look up and
74
- /// execute the function by name.
75
- ExtFunction(StringId),
76
- /// A marker value representing special objects like sys.stdout/stderr.
77
- /// These exist but have minimal functionality in the sandboxed environment.
78
- Marker(Marker),
79
- /// A property descriptor that computes its value when accessed.
80
- /// When retrieved via `py_getattr`, the property's getter is invoked.
81
- Property(Property),
82
- /// A pending external function call result.
83
- ///
84
- /// Created when the host calls `run_pending()` instead of `run(result)` for an
85
- /// external function call. The CallId correlates with the call that created it.
86
- /// When awaited, blocks the task until the host provides a result via `resume()`.
87
- ///
88
- /// ExternalFutures follow single-shot semantics like coroutines - awaiting an
89
- /// already-awaited ExternalFuture raises RuntimeError.
90
- ExternalFuture(CallId),
91
-
92
- // Heap-allocated values (stored in arena)
93
- Ref(HeapId),
94
-
95
- /// Sentinel value indicating this Value was properly cleaned up via `drop_with_heap`.
96
- /// Only exists when `ref-count-panic` feature is enabled. Used to verify reference counting
97
- /// correctness - if a `Ref` variant is dropped without calling `drop_with_heap`, the
98
- /// Drop impl will panic.
99
- #[cfg(feature = "ref-count-panic")]
100
- Dereferenced,
101
- }
102
-
103
- /// Drop implementation that panics if a `Ref` variant is dropped without calling `drop_with_heap`.
104
- /// This helps catch reference counting bugs during development/testing.
105
- /// Only enabled when the `ref-count-panic` feature is active.
106
- #[cfg(feature = "ref-count-panic")]
107
- impl Drop for Value {
108
- fn drop(&mut self) {
109
- if let Self::Ref(id) = self {
110
- panic!("Value::Ref({id:?}) dropped without calling drop_with_heap() - this is a reference counting bug");
111
- }
112
- }
113
- }
114
-
115
- impl From<bool> for Value {
116
- fn from(v: bool) -> Self {
117
- Self::Bool(v)
118
- }
119
- }
120
-
121
- impl PyTrait for Value {
122
- fn py_type(&self, heap: &Heap<impl ResourceTracker>) -> Type {
123
- match self {
124
- Self::Undefined => panic!("Cannot get type of undefined value"),
125
- Self::Ellipsis => Type::Ellipsis,
126
- Self::None => Type::NoneType,
127
- Self::Bool(_) => Type::Bool,
128
- Self::Int(_) | Self::InternLongInt(_) => Type::Int,
129
- Self::Float(_) => Type::Float,
130
- Self::InternString(_) => Type::Str,
131
- Self::InternBytes(_) => Type::Bytes,
132
- Self::Builtin(c) => c.py_type(),
133
- Self::ModuleFunction(_) => Type::BuiltinFunction,
134
- Self::DefFunction(_) | Self::ExtFunction(_) => Type::Function,
135
- Self::Marker(m) => m.py_type(),
136
- Self::Property(_) => Type::Property,
137
- Self::ExternalFuture(_) => Type::Coroutine,
138
- Self::Ref(id) => heap.get(*id).py_type(heap),
139
- #[cfg(feature = "ref-count-panic")]
140
- Self::Dereferenced => panic!("Cannot access Dereferenced object"),
141
- }
142
- }
143
-
144
- /// Returns 0 for Value since immediate values are stack-allocated.
145
- ///
146
- /// Heap-allocated values (Ref variants) have their size tracked when
147
- /// the HeapData is allocated, not here.
148
- fn py_estimate_size(&self) -> usize {
149
- // Value is stack-allocated; heap data is sized separately when allocated
150
- 0
151
- }
152
-
153
- fn py_len(&self, vm: &VM<'_, '_, impl ResourceTracker>) -> Option<usize> {
154
- match self {
155
- // Count Unicode characters, not bytes, to match Python semantics
156
- Self::InternString(string_id) => Some(vm.interns.get_str(*string_id).chars().count()),
157
- Self::InternBytes(bytes_id) => Some(vm.interns.get_bytes(*bytes_id).len()),
158
- Self::Ref(id) => vm.heap.get(*id).py_len(vm),
159
- _ => None,
160
- }
161
- }
162
-
163
- fn py_eq(&self, other: &Self, vm: &mut VM<'_, '_, impl ResourceTracker>) -> Result<bool, ResourceError> {
164
- let interns = vm.interns;
165
- match (self, other) {
166
- (Self::Undefined, _) => Ok(false),
167
- (_, Self::Undefined) => Ok(false),
168
- (Self::Int(v1), Self::Int(v2)) => Ok(v1 == v2),
169
- (Self::Bool(v1), Self::Bool(v2)) => Ok(v1 == v2),
170
- (Self::Bool(v1), Self::Int(v2)) => Ok(i64::from(*v1) == *v2),
171
- (Self::Int(v1), Self::Bool(v2)) => Ok(*v1 == i64::from(*v2)),
172
- (Self::Float(v1), Self::Float(v2)) => Ok(v1 == v2),
173
- (Self::Int(v1), Self::Float(v2)) => Ok((*v1 as f64) == *v2),
174
- (Self::Float(v1), Self::Int(v2)) => Ok(*v1 == (*v2 as f64)),
175
- (Self::Bool(v1), Self::Float(v2)) => Ok((i64::from(*v1) as f64) == *v2),
176
- (Self::Float(v1), Self::Bool(v2)) => Ok(*v1 == (i64::from(*v2) as f64)),
177
- (Self::None, Self::None) => Ok(true),
178
-
179
- // Int == LongInt comparison
180
- (Self::Int(a), Self::Ref(id)) => {
181
- if let HeapData::LongInt(li) = vm.heap.get(*id) {
182
- Ok(BigInt::from(*a) == *li.inner())
183
- } else {
184
- Ok(false)
185
- }
186
- }
187
- // LongInt == Int comparison
188
- (Self::Ref(id), Self::Int(b)) => {
189
- if let HeapData::LongInt(li) = vm.heap.get(*id) {
190
- Ok(*li.inner() == BigInt::from(*b))
191
- } else {
192
- Ok(false)
193
- }
194
- }
195
-
196
- // For interned interns, compare by StringId first (fast path for same interned string)
197
- (Self::InternString(s1), Self::InternString(s2)) => Ok(s1 == s2),
198
- // for strings we need to account for the fact they might be either interned or not
199
- (Self::InternString(string_id), Self::Ref(id2)) => {
200
- if let HeapData::Str(s2) = vm.heap.get(*id2) {
201
- Ok(interns.get_str(*string_id) == s2.as_str())
202
- } else {
203
- Ok(false)
204
- }
205
- }
206
- (Self::Ref(id1), Self::InternString(string_id)) => {
207
- if let HeapData::Str(s1) = vm.heap.get(*id1) {
208
- Ok(s1.as_str() == interns.get_str(*string_id))
209
- } else {
210
- Ok(false)
211
- }
212
- }
213
-
214
- // For interned bytes, compare by content (bytes are not deduplicated unlike interns)
215
- (Self::InternBytes(b1), Self::InternBytes(b2)) => {
216
- // Fast path: same BytesId means same content
217
- Ok(b1 == b2 || interns.get_bytes(*b1) == interns.get_bytes(*b2))
218
- }
219
- // same for bytes
220
- (Self::InternBytes(bytes_id), Self::Ref(id2)) => {
221
- if let HeapData::Bytes(b2) = vm.heap.get(*id2) {
222
- Ok(interns.get_bytes(*bytes_id) == b2.as_slice())
223
- } else {
224
- Ok(false)
225
- }
226
- }
227
- (Self::Ref(id1), Self::InternBytes(bytes_id)) => {
228
- if let HeapData::Bytes(b1) = vm.heap.get(*id1) {
229
- Ok(b1.as_slice() == interns.get_bytes(*bytes_id))
230
- } else {
231
- Ok(false)
232
- }
233
- }
234
-
235
- (Self::Ref(id1), Self::Ref(id2)) => {
236
- if *id1 == *id2 {
237
- return Ok(true);
238
- }
239
- Heap::with_two(vm, *id1, *id2, |vm, left, right| left.py_eq(right, vm))
240
- }
241
-
242
- // Builtins equality - just check the enums are equal
243
- (Self::Builtin(b1), Self::Builtin(b2)) => Ok(b1 == b2),
244
- // Module functions equality
245
- (Self::ModuleFunction(mf1), Self::ModuleFunction(mf2)) => Ok(mf1 == mf2),
246
- (Self::DefFunction(f1), Self::DefFunction(f2)) => Ok(f1 == f2),
247
- // Markers compare equal if they're the same variant
248
- (Self::Marker(m1), Self::Marker(m2)) => Ok(m1 == m2),
249
- // Properties compare equal if they're the same variant
250
- (Self::Property(p1), Self::Property(p2)) => Ok(p1 == p2),
251
-
252
- _ => Ok(false),
253
- }
254
- }
255
-
256
- fn py_cmp(
257
- &self,
258
- other: &Self,
259
- vm: &mut VM<'_, '_, impl ResourceTracker>,
260
- ) -> Result<Option<Ordering>, ResourceError> {
261
- let interns = vm.interns;
262
- // py_cmp handles numbers, strings, bytes, and tuples.
263
- // Recursion depth tracking for tuples is handled in Tuple::py_cmp.
264
- match (self, other) {
265
- (Self::Int(s), Self::Int(o)) => Ok(s.partial_cmp(o)),
266
- (Self::Float(s), Self::Float(o)) => Ok(s.partial_cmp(o)),
267
- (Self::Int(s), Self::Float(o)) => Ok((*s as f64).partial_cmp(o)),
268
- (Self::Float(s), Self::Int(o)) => Ok(s.partial_cmp(&(*o as f64))),
269
- // Bool promotion: convert to Int and re-dispatch. Recursion is bounded
270
- // to at most 2 levels (Bool→Int, then Int matches directly above).
271
- (Self::Bool(s), _) => Self::Int(i64::from(*s)).py_cmp(other, vm),
272
- (_, Self::Bool(s)) => self.py_cmp(&Self::Int(i64::from(*s)), vm),
273
- // Int vs LongInt comparison
274
- (Self::Int(a), Self::Ref(id)) => {
275
- if let HeapData::LongInt(li) = vm.heap.get(*id) {
276
- Ok(BigInt::from(*a).partial_cmp(li.inner()))
277
- } else {
278
- Ok(None)
279
- }
280
- }
281
- // LongInt vs Int comparison
282
- (Self::Ref(id), Self::Int(b)) => {
283
- if let HeapData::LongInt(li) = vm.heap.get(*id) {
284
- Ok(li.inner().partial_cmp(&BigInt::from(*b)))
285
- } else {
286
- Ok(None)
287
- }
288
- }
289
- // Ref vs Ref comparison: handles LongInt, Str, and Tuple
290
- (Self::Ref(id1), Self::Ref(id2)) => match (vm.heap.get(*id1), vm.heap.get(*id2)) {
291
- (HeapData::LongInt(a), HeapData::LongInt(b)) => Ok(a.inner().partial_cmp(b.inner())),
292
- (HeapData::Str(a), HeapData::Str(b)) => Ok(a.as_str().partial_cmp(b.as_str())),
293
- (HeapData::Tuple(_), HeapData::Tuple(_)) => {
294
- Heap::with_two(vm, *id1, *id2, |vm, left, right| left.py_cmp(right, vm))
295
- }
296
- _ => Ok(None),
297
- },
298
- // Interned string comparisons
299
- (Self::InternString(s1), Self::InternString(s2)) => {
300
- Ok(interns.get_str(*s1).partial_cmp(interns.get_str(*s2)))
301
- }
302
- // Cross-type string comparisons: interned vs heap-allocated
303
- (Self::InternString(s1), Self::Ref(id2)) => {
304
- if let HeapData::Str(s2) = vm.heap.get(*id2) {
305
- Ok(interns.get_str(*s1).partial_cmp(s2.as_str()))
306
- } else {
307
- Ok(None)
308
- }
309
- }
310
- (Self::Ref(id1), Self::InternString(s2)) => {
311
- if let HeapData::Str(s1) = vm.heap.get(*id1) {
312
- Ok(s1.as_str().partial_cmp(interns.get_str(*s2)))
313
- } else {
314
- Ok(None)
315
- }
316
- }
317
- (Self::InternBytes(b1), Self::InternBytes(b2)) => {
318
- Ok(interns.get_bytes(*b1).partial_cmp(interns.get_bytes(*b2)))
319
- }
320
- _ => Ok(None),
321
- }
322
- }
323
-
324
- fn py_dec_ref_ids(&mut self, stack: &mut Vec<HeapId>) {
325
- if let Self::Ref(id) = self {
326
- stack.push(*id);
327
- // Mark as Dereferenced to prevent Drop panic
328
- #[cfg(feature = "ref-count-panic")]
329
- self.dec_ref_forget();
330
- }
331
- }
332
-
333
- fn py_bool(&self, vm: &VM<'_, '_, impl ResourceTracker>) -> bool {
334
- match self {
335
- Self::Undefined => false,
336
- Self::Ellipsis => true,
337
- Self::None => false,
338
- Self::Bool(b) => *b,
339
- Self::Int(v) => *v != 0,
340
- Self::Float(f) => *f != 0.0,
341
- // InternLongInt is always truthy (if it were zero, it would fit in i64)
342
- Self::InternLongInt(_) => true,
343
- Self::Builtin(_) | Self::ModuleFunction(_) => true, // Builtins are always truthy
344
- Self::DefFunction(_) | Self::ExtFunction(_) => true, // Functions are always truthy
345
- Self::Marker(_) => true, // Markers are always truthy
346
- Self::Property(_) => true, // Properties are always truthy
347
- Self::ExternalFuture(_) => true, // ExternalFutures are always truthy
348
- Self::InternString(string_id) => !vm.interns.get_str(*string_id).is_empty(),
349
- Self::InternBytes(bytes_id) => !vm.interns.get_bytes(*bytes_id).is_empty(),
350
- Self::Ref(id) => vm.heap.get(*id).py_bool(vm),
351
- #[cfg(feature = "ref-count-panic")]
352
- Self::Dereferenced => panic!("Cannot access Dereferenced object"),
353
- }
354
- }
355
-
356
- fn py_repr_fmt(
357
- &self,
358
- f: &mut impl Write,
359
- vm: &VM<'_, '_, impl ResourceTracker>,
360
- heap_ids: &mut AHashSet<HeapId>,
361
- ) -> std::fmt::Result {
362
- let interns = vm.interns;
363
- match self {
364
- Self::Undefined => f.write_str("Undefined"),
365
- Self::Ellipsis => f.write_str("Ellipsis"),
366
- Self::None => f.write_str("None"),
367
- Self::Bool(true) => f.write_str("True"),
368
- Self::Bool(false) => f.write_str("False"),
369
- Self::Int(v) => write!(f, "{v}"),
370
- Self::InternLongInt(long_int_id) => write!(f, "{}", interns.get_long_int(*long_int_id)),
371
- Self::Float(v) => {
372
- let s = v.to_string();
373
- if s.contains('.') {
374
- f.write_str(&s)
375
- } else {
376
- write!(f, "{s}.0")
377
- }
378
- }
379
- Self::Builtin(b) => b.py_repr_fmt(f),
380
- Self::ModuleFunction(mf) => mf.py_repr_fmt(f, self.id()),
381
- Self::DefFunction(f_id) => interns.get_function(*f_id).py_repr_fmt(f, interns, self.id()),
382
- Self::ExtFunction(name_id) => {
383
- write!(f, "<function '{}' external>", interns.get_str(*name_id))
384
- }
385
- Self::InternString(string_id) => string_repr_fmt(interns.get_str(*string_id), f),
386
- Self::InternBytes(bytes_id) => bytes_repr_fmt(interns.get_bytes(*bytes_id), f),
387
- Self::Marker(m) => m.py_repr_fmt(f),
388
- Self::Property(p) => write!(f, "<property {p:?}>"),
389
- Self::ExternalFuture(call_id) => write!(f, "<coroutine external_future({})>", call_id.raw()),
390
- Self::Ref(id) => {
391
- if heap_ids.contains(id) {
392
- // Cycle detected - write type-specific placeholder following Python semantics
393
- match vm.heap.get(*id) {
394
- HeapData::List(_) => f.write_str("[...]"),
395
- HeapData::Tuple(_) => f.write_str("(...)"),
396
- HeapData::Dict(_) => f.write_str("{...}"),
397
- // Other types don't typically have cycles, but handle gracefully
398
- _ => f.write_str("..."),
399
- }
400
- } else {
401
- heap_ids.insert(*id);
402
- let result = vm.heap.get(*id).py_repr_fmt(f, vm, heap_ids);
403
- heap_ids.remove(id);
404
- result
405
- }
406
- }
407
- #[cfg(feature = "ref-count-panic")]
408
- Self::Dereferenced => panic!("Cannot access Dereferenced object"),
409
- }
410
- }
411
-
412
- fn py_str(&self, vm: &VM<'_, '_, impl ResourceTracker>) -> Cow<'static, str> {
413
- match self {
414
- Self::InternString(string_id) => vm.interns.get_str(*string_id).to_owned().into(),
415
- Self::Ref(id) => vm.heap.get(*id).py_str(vm),
416
- _ => self.py_repr(vm),
417
- }
418
- }
419
-
420
- fn py_add(
421
- &self,
422
- other: &Self,
423
- vm: &mut VM<'_, '_, impl ResourceTracker>,
424
- ) -> Result<Option<Value>, crate::resource::ResourceError> {
425
- let interns = vm.interns;
426
- match (self, other) {
427
- // Int + Int with overflow detection
428
- (Self::Int(a), Self::Int(b)) => {
429
- if let Some(result) = a.checked_add(*b) {
430
- Ok(Some(Self::Int(result)))
431
- } else {
432
- // Overflow - promote to LongInt
433
- let li = LongInt::from(*a) + LongInt::from(*b);
434
- li.into_value(vm.heap).map(Some)
435
- }
436
- }
437
- // Int + LongInt
438
- (Self::Int(i), Self::Ref(id)) | (Self::Ref(id), Self::Int(i)) => {
439
- if let HeapData::LongInt(li) = vm.heap.get(*id) {
440
- let result = LongInt::new(li.inner() + i);
441
- result.into_value(vm.heap).map(Some)
442
- } else {
443
- Ok(None)
444
- }
445
- }
446
- (Self::Float(v1), Self::Float(v2)) => Ok(Some(Self::Float(v1 + v2))),
447
- // Int + Float and Float + Int
448
- (Self::Int(a), Self::Float(b)) => Ok(Some(Self::Float(*a as f64 + b))),
449
- (Self::Float(a), Self::Int(b)) => Ok(Some(Self::Float(a + *b as f64))),
450
- (Self::Ref(id1), Self::Ref(id2)) => {
451
- Heap::with_two(vm, *id1, *id2, |vm, left, right| left.py_add(right, vm))
452
- }
453
- (Self::InternString(s1), Self::InternString(s2)) => {
454
- let concat = format!("{}{}", interns.get_str(*s1), interns.get_str(*s2));
455
- Ok(Some(Self::Ref(vm.heap.allocate(HeapData::Str(concat.into()))?)))
456
- }
457
- // for strings we need to account for the fact they might be either interned or not
458
- (Self::InternString(string_id), Self::Ref(id2)) => {
459
- if let HeapData::Str(s2) = vm.heap.get(*id2) {
460
- let concat = format!("{}{}", interns.get_str(*string_id), s2.as_str());
461
- Ok(Some(Self::Ref(vm.heap.allocate(HeapData::Str(concat.into()))?)))
462
- } else {
463
- Ok(None)
464
- }
465
- }
466
- (Self::Ref(id1), Self::InternString(string_id)) => {
467
- if let HeapData::Str(s1) = vm.heap.get(*id1) {
468
- let concat = format!("{}{}", s1.as_str(), interns.get_str(*string_id));
469
- Ok(Some(Self::Ref(vm.heap.allocate(HeapData::Str(concat.into()))?)))
470
- } else {
471
- Ok(None)
472
- }
473
- }
474
- // same for bytes
475
- (Self::InternBytes(b1), Self::InternBytes(b2)) => {
476
- let bytes1 = interns.get_bytes(*b1);
477
- let bytes2 = interns.get_bytes(*b2);
478
- let mut b = Vec::with_capacity(bytes1.len() + bytes2.len());
479
- b.extend_from_slice(bytes1);
480
- b.extend_from_slice(bytes2);
481
- Ok(Some(Self::Ref(vm.heap.allocate(HeapData::Bytes(b.into()))?)))
482
- }
483
- (Self::InternBytes(bytes_id), Self::Ref(id2)) => {
484
- if let HeapData::Bytes(b2) = vm.heap.get(*id2) {
485
- let bytes1 = interns.get_bytes(*bytes_id);
486
- let mut b = Vec::with_capacity(bytes1.len() + b2.len());
487
- b.extend_from_slice(bytes1);
488
- b.extend_from_slice(b2);
489
- Ok(Some(Self::Ref(vm.heap.allocate(HeapData::Bytes(b.into()))?)))
490
- } else {
491
- Ok(None)
492
- }
493
- }
494
- (Self::Ref(id1), Self::InternBytes(bytes_id)) => {
495
- if let HeapData::Bytes(b1) = vm.heap.get(*id1) {
496
- let bytes2 = interns.get_bytes(*bytes_id);
497
- let mut b = Vec::with_capacity(b1.len() + bytes2.len());
498
- b.extend_from_slice(b1);
499
- b.extend_from_slice(bytes2);
500
- Ok(Some(Self::Ref(vm.heap.allocate(HeapData::Bytes(b.into()))?)))
501
- } else {
502
- Ok(None)
503
- }
504
- }
505
- _ => Ok(None),
506
- }
507
- }
508
-
509
- fn py_sub(
510
- &self,
511
- other: &Self,
512
- vm: &mut VM<'_, '_, impl ResourceTracker>,
513
- ) -> Result<Option<Self>, crate::resource::ResourceError> {
514
- match (self, other) {
515
- // Int - Int with overflow detection
516
- (Self::Int(a), Self::Int(b)) => {
517
- if let Some(result) = a.checked_sub(*b) {
518
- Ok(Some(Self::Int(result)))
519
- } else {
520
- // Overflow - promote to LongInt
521
- let li = LongInt::from(*a) - LongInt::from(*b);
522
- li.into_value(vm.heap).map(Some)
523
- }
524
- }
525
- // Int - LongInt
526
- (Self::Int(a), Self::Ref(id)) => {
527
- if let HeapData::LongInt(li) = vm.heap.get(*id) {
528
- let result = LongInt::from(*a) - LongInt::new(li.inner().clone());
529
- result.into_value(vm.heap).map(Some)
530
- } else {
531
- Ok(None)
532
- }
533
- }
534
- // LongInt - Int
535
- (Self::Ref(id), Self::Int(b)) => {
536
- if let HeapData::LongInt(li) = vm.heap.get(*id) {
537
- let result = LongInt::new(li.inner().clone()) - LongInt::from(*b);
538
- result.into_value(vm.heap).map(Some)
539
- } else {
540
- Ok(None)
541
- }
542
- }
543
- // LongInt - LongInt
544
- (Self::Ref(id1), Self::Ref(id2)) => {
545
- Heap::with_two(vm, *id1, *id2, |vm, left, right| left.py_sub(right, vm))
546
- }
547
- // Float - Float
548
- (Self::Float(a), Self::Float(b)) => Ok(Some(Self::Float(a - b))),
549
- // Int - Float and Float - Int
550
- (Self::Int(a), Self::Float(b)) => Ok(Some(Self::Float(*a as f64 - b))),
551
- (Self::Float(a), Self::Int(b)) => Ok(Some(Self::Float(a - *b as f64))),
552
- _ => Ok(None),
553
- }
554
- }
555
-
556
- fn py_mod(&self, other: &Self, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Option<Self>> {
557
- match (self, other) {
558
- (Self::Int(a), Self::Int(b)) => {
559
- if *b == 0 {
560
- Err(ExcType::zero_division().into())
561
- } else if let Some(r) = a.checked_rem(*b) {
562
- // Python modulo: result has the same sign as divisor (b)
563
- let result = if r != 0 && (*a < 0) != (*b < 0) { r + *b } else { r };
564
- Ok(Some(Self::Int(result)))
565
- } else {
566
- // Overflow - i64::MIN % -1 is 0
567
- Ok(Some(Self::Int(0)))
568
- }
569
- }
570
- // Int % LongInt
571
- (Self::Int(a), Self::Ref(id)) => {
572
- // Clone to avoid borrow conflict with heap mutation
573
- let b_clone = if let HeapData::LongInt(li) = vm.heap.get(*id) {
574
- if li.is_zero() {
575
- return Err(ExcType::zero_division().into());
576
- }
577
- li.inner().clone()
578
- } else {
579
- return Ok(None);
580
- };
581
- let bi = BigInt::from(*a).mod_floor(&b_clone);
582
- Ok(Some(LongInt::new(bi).into_value(vm.heap)?))
583
- }
584
- // LongInt % Int
585
- (Self::Ref(id), Self::Int(b)) => {
586
- if *b == 0 {
587
- return Err(ExcType::zero_division().into());
588
- }
589
- // Clone to avoid borrow conflict with heap mutation
590
- let a_clone = if let HeapData::LongInt(li) = vm.heap.get(*id) {
591
- li.inner().clone()
592
- } else {
593
- return Ok(None);
594
- };
595
- let bi = a_clone.mod_floor(&BigInt::from(*b));
596
- Ok(Some(LongInt::new(bi).into_value(vm.heap)?))
597
- }
598
- // LongInt % LongInt
599
- (Self::Ref(id1), Self::Ref(id2)) => {
600
- Heap::with_two(vm, *id1, *id2, |vm, left, right| left.py_mod(right, vm))
601
- }
602
- (Self::Float(v1), Self::Float(v2)) => {
603
- if *v2 == 0.0 {
604
- Err(ExcType::zero_division().into())
605
- } else {
606
- Ok(Some(Self::Float(v1 % v2)))
607
- }
608
- }
609
- (Self::Float(v1), Self::Int(v2)) => {
610
- if *v2 == 0 {
611
- Err(ExcType::zero_division().into())
612
- } else {
613
- Ok(Some(Self::Float(v1 % (*v2 as f64))))
614
- }
615
- }
616
- (Self::Int(v1), Self::Float(v2)) => {
617
- if *v2 == 0.0 {
618
- Err(ExcType::zero_division().into())
619
- } else {
620
- Ok(Some(Self::Float((*v1 as f64) % v2)))
621
- }
622
- }
623
- _ => Ok(None),
624
- }
625
- }
626
-
627
- fn py_mod_eq(&self, other: &Self, right_value: i64) -> Option<bool> {
628
- match (self, other) {
629
- (Self::Int(v1), Self::Int(v2)) => {
630
- if let Some(r) = v1.checked_rem(*v2) {
631
- // Python modulo: result has same sign as divisor
632
- let result = if r != 0 && (*v1 < 0) != (*v2 < 0) { r + *v2 } else { r };
633
- Some(result == right_value)
634
- } else {
635
- // checked_rem returns None for overflow (i64::MIN % -1) or zero division
636
- (*v2 != 0).then_some(0 == right_value)
637
- }
638
- }
639
- (Self::Float(v1), Self::Float(v2)) => Some(v1 % v2 == right_value as f64),
640
- (Self::Float(v1), Self::Int(v2)) => Some(v1 % (*v2 as f64) == right_value as f64),
641
- (Self::Int(v1), Self::Float(v2)) => Some((*v1 as f64) % v2 == right_value as f64),
642
- _ => None,
643
- }
644
- }
645
-
646
- fn py_iadd(
647
- &mut self,
648
- other: &Self,
649
- vm: &mut VM<'_, '_, impl ResourceTracker>,
650
- _self_id: Option<HeapId>,
651
- ) -> Result<bool, crate::resource::ResourceError> {
652
- let interns = vm.interns;
653
- match (&self, other) {
654
- (Self::Int(v1), Self::Int(v2)) => {
655
- if let Some(result) = v1.checked_add(*v2) {
656
- *self = Self::Int(result);
657
- } else {
658
- // Overflow - promote to LongInt
659
- let li = LongInt::from(*v1) + LongInt::from(*v2);
660
- *self = li.into_value(vm.heap)?;
661
- }
662
- Ok(true)
663
- }
664
- (Self::Float(v1), Self::Float(v2)) => {
665
- *self = Self::Float(*v1 + *v2);
666
- Ok(true)
667
- }
668
- (Self::InternString(s1), Self::InternString(s2)) => {
669
- let concat = format!("{}{}", interns.get_str(*s1), interns.get_str(*s2));
670
- *self = Self::Ref(vm.heap.allocate(HeapData::Str(concat.into()))?);
671
- Ok(true)
672
- }
673
- (Self::InternString(string_id), Self::Ref(id2)) => {
674
- let result = if let HeapData::Str(s2) = vm.heap.get(*id2) {
675
- let concat = format!("{}{}", interns.get_str(*string_id), s2.as_str());
676
- *self = Self::Ref(vm.heap.allocate(HeapData::Str(concat.into()))?);
677
- true
678
- } else {
679
- false
680
- };
681
- Ok(result)
682
- }
683
- // same for bytes
684
- (Self::InternBytes(b1), Self::InternBytes(b2)) => {
685
- let bytes1 = interns.get_bytes(*b1);
686
- let bytes2 = interns.get_bytes(*b2);
687
- let mut b = Vec::with_capacity(bytes1.len() + bytes2.len());
688
- b.extend_from_slice(bytes1);
689
- b.extend_from_slice(bytes2);
690
- *self = Self::Ref(vm.heap.allocate(HeapData::Bytes(b.into()))?);
691
- Ok(true)
692
- }
693
- (Self::InternBytes(bytes_id), Self::Ref(id2)) => {
694
- let result = if let HeapData::Bytes(b2) = vm.heap.get(*id2) {
695
- let bytes1 = interns.get_bytes(*bytes_id);
696
- let mut b = Vec::with_capacity(bytes1.len() + b2.len());
697
- b.extend_from_slice(bytes1);
698
- b.extend_from_slice(b2);
699
- *self = Self::Ref(vm.heap.allocate(HeapData::Bytes(b.into()))?);
700
- true
701
- } else {
702
- false
703
- };
704
- Ok(result)
705
- }
706
- (Self::Ref(id), Self::Ref(_)) => {
707
- Heap::with_entry_mut(vm, *id, |vm, mut data| data.py_iadd(other, vm, Some(*id)))
708
- }
709
- _ => Ok(false),
710
- }
711
- }
712
-
713
- fn py_mult(&self, other: &Self, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Option<Value>> {
714
- let interns = vm.interns;
715
- match (self, other) {
716
- // Numeric multiplication with overflow promotion to LongInt
717
- (Self::Int(a), Self::Int(b)) => {
718
- if let Some(result) = a.checked_mul(*b) {
719
- Ok(Some(Self::Int(result)))
720
- } else {
721
- // Overflow - promote to LongInt
722
- let li = LongInt::from(*a) * LongInt::from(*b);
723
- Ok(Some(li.into_value(vm.heap)?))
724
- }
725
- }
726
- // Int * Ref (LongInt or sequence)
727
- (Self::Int(a), Self::Ref(id)) => vm.heap.mult_ref_by_i64(*id, *a),
728
- // Ref * Int (LongInt or sequence)
729
- (Self::Ref(id), Self::Int(b)) => vm.heap.mult_ref_by_i64(*id, *b),
730
- // Ref * Ref (LongInt * LongInt, sequence * LongInt, etc.)
731
- (Self::Ref(id1), Self::Ref(id2)) => vm.heap.mult_heap_values(*id1, *id2),
732
- (Self::Float(a), Self::Float(b)) => Ok(Some(Self::Float(a * b))),
733
- (Self::Int(a), Self::Float(b)) => Ok(Some(Self::Float(*a as f64 * b))),
734
- (Self::Float(a), Self::Int(b)) => Ok(Some(Self::Float(a * *b as f64))),
735
-
736
- // Bool numeric multiplication (True=1, False=0)
737
- (Self::Bool(a), Self::Int(b)) => {
738
- let a_int = i64::from(*a);
739
- Ok(Some(Self::Int(a_int * b)))
740
- }
741
- (Self::Int(a), Self::Bool(b)) => {
742
- let b_int = i64::from(*b);
743
- Ok(Some(Self::Int(a * b_int)))
744
- }
745
- (Self::Bool(a), Self::Float(b)) => {
746
- let a_float = if *a { 1.0 } else { 0.0 };
747
- Ok(Some(Self::Float(a_float * b)))
748
- }
749
- (Self::Float(a), Self::Bool(b)) => {
750
- let b_float = if *b { 1.0 } else { 0.0 };
751
- Ok(Some(Self::Float(a * b_float)))
752
- }
753
- (Self::Bool(a), Self::Bool(b)) => {
754
- let result = i64::from(*a) * i64::from(*b);
755
- Ok(Some(Self::Int(result)))
756
- }
757
-
758
- // String repetition: "ab" * 3 or 3 * "ab"
759
- (Self::InternString(s), Self::Int(n)) | (Self::Int(n), Self::InternString(s)) => {
760
- let count = i64_to_repeat_count(*n)?;
761
- let str_ref = interns.get_str(*s);
762
- check_repeat_size(str_ref.len(), count, vm.heap.tracker())?;
763
- let result = str_ref.repeat(count);
764
- Ok(Some(Self::Ref(vm.heap.allocate(HeapData::Str(result.into()))?)))
765
- }
766
-
767
- // Bytes repetition: b"ab" * 3 or 3 * b"ab"
768
- (Self::InternBytes(b), Self::Int(n)) | (Self::Int(n), Self::InternBytes(b)) => {
769
- let count = i64_to_repeat_count(*n)?;
770
- let bytes_ref = interns.get_bytes(*b);
771
- check_repeat_size(bytes_ref.len(), count, vm.heap.tracker())?;
772
- let result: Vec<u8> = bytes_ref.repeat(count);
773
- Ok(Some(Self::Ref(vm.heap.allocate(HeapData::Bytes(result.into()))?)))
774
- }
775
-
776
- // String repetition with LongInt: "ab" * bigint or bigint * "ab"
777
- (Self::InternString(s), Self::Ref(id)) | (Self::Ref(id), Self::InternString(s)) => {
778
- if let HeapData::LongInt(li) = vm.heap.get(*id) {
779
- let count = longint_to_repeat_count(li)?;
780
- let str_ref = interns.get_str(*s);
781
- check_repeat_size(str_ref.len(), count, vm.heap.tracker())?;
782
- let result = str_ref.repeat(count);
783
- Ok(Some(Self::Ref(vm.heap.allocate(HeapData::Str(result.into()))?)))
784
- } else {
785
- Ok(None)
786
- }
787
- }
788
-
789
- // Bytes repetition with LongInt: b"ab" * bigint or bigint * b"ab"
790
- (Self::InternBytes(b), Self::Ref(id)) | (Self::Ref(id), Self::InternBytes(b)) => {
791
- if let HeapData::LongInt(li) = vm.heap.get(*id) {
792
- let count = longint_to_repeat_count(li)?;
793
- let bytes_ref = interns.get_bytes(*b);
794
- check_repeat_size(bytes_ref.len(), count, vm.heap.tracker())?;
795
- let result: Vec<u8> = bytes_ref.repeat(count);
796
- Ok(Some(Self::Ref(vm.heap.allocate(HeapData::Bytes(result.into()))?)))
797
- } else {
798
- Ok(None)
799
- }
800
- }
801
-
802
- _ => Ok(None),
803
- }
804
- }
805
-
806
- fn py_div(&self, other: &Self, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Option<Value>> {
807
- let interns = vm.interns;
808
- match (self, other) {
809
- // True division always returns float
810
- (Self::Int(a), Self::Int(b)) => {
811
- if *b == 0 {
812
- Err(ExcType::zero_division().into())
813
- } else {
814
- Ok(Some(Self::Float(*a as f64 / *b as f64)))
815
- }
816
- }
817
- // Int / LongInt
818
- (Self::Int(a), Self::Ref(id)) => {
819
- if let HeapData::LongInt(li) = vm.heap.get(*id) {
820
- if li.is_zero() {
821
- Err(ExcType::zero_division().into())
822
- } else {
823
- // Convert both to f64 for division
824
- let a_f64 = *a as f64;
825
- let b_f64 = li.to_f64().unwrap_or(f64::INFINITY);
826
- Ok(Some(Self::Float(a_f64 / b_f64)))
827
- }
828
- } else {
829
- Ok(None)
830
- }
831
- }
832
- // LongInt / Int
833
- (Self::Ref(id), Self::Int(b)) => {
834
- if let HeapData::LongInt(li) = vm.heap.get(*id) {
835
- if *b == 0 {
836
- Err(ExcType::zero_division().into())
837
- } else {
838
- // Convert both to f64 for division
839
- let a_f64 = li.to_f64().unwrap_or(f64::INFINITY);
840
- let b_f64 = *b as f64;
841
- Ok(Some(Self::Float(a_f64 / b_f64)))
842
- }
843
- } else {
844
- Ok(None)
845
- }
846
- }
847
- // LongInt / LongInt
848
- (Self::Ref(id1), Self::Ref(id2)) => match (vm.heap.get(*id1), vm.heap.get(*id2)) {
849
- (HeapData::LongInt(li1), HeapData::LongInt(li2)) => {
850
- if li2.is_zero() {
851
- Err(ExcType::zero_division().into())
852
- } else {
853
- let a_f64 = li1.to_f64().unwrap_or(f64::INFINITY);
854
- let b_f64 = li2.to_f64().unwrap_or(f64::INFINITY);
855
- Ok(Some(Self::Float(a_f64 / b_f64)))
856
- }
857
- }
858
- _ => Ok(None),
859
- },
860
- // LongInt / Float
861
- (Self::Ref(id), Self::Float(b)) => {
862
- if let HeapData::LongInt(li) = vm.heap.get(*id) {
863
- if *b == 0.0 {
864
- Err(ExcType::zero_division().into())
865
- } else {
866
- let a_f64 = li.to_f64().unwrap_or(f64::INFINITY);
867
- Ok(Some(Self::Float(a_f64 / b)))
868
- }
869
- } else {
870
- Ok(None)
871
- }
872
- }
873
- // Float / LongInt
874
- (Self::Float(a), Self::Ref(id)) => {
875
- if let HeapData::LongInt(li) = vm.heap.get(*id) {
876
- if li.is_zero() {
877
- Err(ExcType::zero_division().into())
878
- } else {
879
- let b_f64 = li.to_f64().unwrap_or(f64::INFINITY);
880
- Ok(Some(Self::Float(a / b_f64)))
881
- }
882
- } else {
883
- Ok(None)
884
- }
885
- }
886
- (Self::Float(a), Self::Float(b)) => {
887
- if *b == 0.0 {
888
- Err(ExcType::zero_division().into())
889
- } else {
890
- Ok(Some(Self::Float(a / b)))
891
- }
892
- }
893
- (Self::Int(a), Self::Float(b)) => {
894
- if *b == 0.0 {
895
- Err(ExcType::zero_division().into())
896
- } else {
897
- Ok(Some(Self::Float(*a as f64 / b)))
898
- }
899
- }
900
- (Self::Float(a), Self::Int(b)) => {
901
- if *b == 0 {
902
- Err(ExcType::zero_division().into())
903
- } else {
904
- Ok(Some(Self::Float(a / *b as f64)))
905
- }
906
- }
907
- // Bool division (True=1, False=0)
908
- (Self::Bool(a), Self::Int(b)) => {
909
- if *b == 0 {
910
- Err(ExcType::zero_division().into())
911
- } else {
912
- Ok(Some(Self::Float(f64::from(*a) / *b as f64)))
913
- }
914
- }
915
- (Self::Int(a), Self::Bool(b)) => {
916
- if *b {
917
- Ok(Some(Self::Float(*a as f64))) // a / 1 = a
918
- } else {
919
- Err(ExcType::zero_division().into())
920
- }
921
- }
922
- (Self::Bool(a), Self::Float(b)) => {
923
- if *b == 0.0 {
924
- Err(ExcType::zero_division().into())
925
- } else {
926
- Ok(Some(Self::Float(f64::from(*a) / b)))
927
- }
928
- }
929
- (Self::Float(a), Self::Bool(b)) => {
930
- if *b {
931
- Ok(Some(Self::Float(*a))) // a / 1.0 = a
932
- } else {
933
- Err(ExcType::zero_division().into())
934
- }
935
- }
936
- (Self::Bool(a), Self::Bool(b)) => {
937
- if *b {
938
- Ok(Some(Self::Float(f64::from(*a)))) // a / 1 = a
939
- } else {
940
- Err(ExcType::zero_division().into())
941
- }
942
- }
943
- _ => {
944
- // Check for Path / (str or Path) - path concatenation
945
- if let Self::Ref(id) = self
946
- && matches!(vm.heap.get(*id), HeapData::Path(_))
947
- {
948
- return path::path_div(*id, other, vm.heap, interns);
949
- }
950
- Ok(None)
951
- }
952
- }
953
- }
954
-
955
- fn py_floordiv(&self, other: &Self, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Option<Value>> {
956
- match (self, other) {
957
- // Floor division: int // int returns int
958
- (Self::Int(a), Self::Int(b)) => {
959
- if *b == 0 {
960
- Err(ExcType::zero_division().into())
961
- } else if let Some((d, _)) = floor_divmod(*a, *b) {
962
- Ok(Some(Self::Int(d)))
963
- } else {
964
- // Overflow - promote to LongInt
965
- check_div_size(i64_bits(*a), vm.heap.tracker())?;
966
- let bi = BigInt::from(*a).div_floor(&BigInt::from(*b));
967
- Ok(Some(LongInt::new(bi).into_value(vm.heap)?))
968
- }
969
- }
970
- // Int // LongInt
971
- (Self::Int(a), Self::Ref(id)) => {
972
- if let HeapData::LongInt(li) = vm.heap.get(*id) {
973
- if li.is_zero() {
974
- Err(ExcType::zero_division().into())
975
- } else {
976
- let bi = BigInt::from(*a).div_floor(li.inner());
977
- Ok(Some(LongInt::new(bi).into_value(vm.heap)?))
978
- }
979
- } else {
980
- Ok(None)
981
- }
982
- }
983
- // LongInt // Int
984
- (Self::Ref(id), Self::Int(b)) => {
985
- if let HeapData::LongInt(li) = vm.heap.get(*id) {
986
- if *b == 0 {
987
- Err(ExcType::zero_division().into())
988
- } else {
989
- let bi = li.inner().div_floor(&BigInt::from(*b));
990
- Ok(Some(LongInt::new(bi).into_value(vm.heap)?))
991
- }
992
- } else {
993
- Ok(None)
994
- }
995
- }
996
- // LongInt // LongInt
997
- (Self::Ref(id1), Self::Ref(id2)) => match (vm.heap.get(*id1), vm.heap.get(*id2)) {
998
- (HeapData::LongInt(li1), HeapData::LongInt(li2)) => {
999
- if li2.is_zero() {
1000
- Err(ExcType::zero_division().into())
1001
- } else {
1002
- let bi = li1.inner().div_floor(li2.inner());
1003
- Ok(Some(LongInt::new(bi).into_value(vm.heap)?))
1004
- }
1005
- }
1006
- _ => Ok(None),
1007
- },
1008
- // Float floor division returns float
1009
- (Self::Float(a), Self::Float(b)) => {
1010
- if *b == 0.0 {
1011
- Err(ExcType::zero_division().into())
1012
- } else {
1013
- Ok(Some(Self::Float((a / b).floor())))
1014
- }
1015
- }
1016
- (Self::Int(a), Self::Float(b)) => {
1017
- if *b == 0.0 {
1018
- Err(ExcType::zero_division().into())
1019
- } else {
1020
- Ok(Some(Self::Float((*a as f64 / b).floor())))
1021
- }
1022
- }
1023
- (Self::Float(a), Self::Int(b)) => {
1024
- if *b == 0 {
1025
- Err(ExcType::zero_division().into())
1026
- } else {
1027
- Ok(Some(Self::Float((a / *b as f64).floor())))
1028
- }
1029
- }
1030
- // Bool floor division (True=1, False=0)
1031
- (Self::Bool(a), Self::Int(b)) => {
1032
- if *b == 0 {
1033
- Err(ExcType::zero_division().into())
1034
- } else {
1035
- let a_int = i64::from(*a);
1036
- // Use same floor division logic as Int // Int
1037
- let d = a_int / b;
1038
- let r = a_int % b;
1039
- let result = if r != 0 && (a_int < 0) != (*b < 0) { d - 1 } else { d };
1040
- Ok(Some(Self::Int(result)))
1041
- }
1042
- }
1043
- (Self::Int(a), Self::Bool(b)) => {
1044
- if *b {
1045
- Ok(Some(Self::Int(*a))) // a // 1 = a
1046
- } else {
1047
- Err(ExcType::zero_division().into())
1048
- }
1049
- }
1050
- (Self::Bool(a), Self::Float(b)) => {
1051
- if *b == 0.0 {
1052
- Err(ExcType::zero_division().into())
1053
- } else {
1054
- Ok(Some(Self::Float((f64::from(*a) / b).floor())))
1055
- }
1056
- }
1057
- (Self::Float(a), Self::Bool(b)) => {
1058
- if *b {
1059
- Ok(Some(Self::Float(a.floor()))) // a // 1.0 = floor(a)
1060
- } else {
1061
- Err(ExcType::zero_division().into())
1062
- }
1063
- }
1064
- (Self::Bool(a), Self::Bool(b)) => {
1065
- if *b {
1066
- Ok(Some(Self::Int(i64::from(*a)))) // a // 1 = a
1067
- } else {
1068
- Err(ExcType::zero_division().into())
1069
- }
1070
- }
1071
- _ => Ok(None),
1072
- }
1073
- }
1074
-
1075
- fn py_pow(&self, other: &Self, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Option<Value>> {
1076
- match (self, other) {
1077
- (Self::Int(base), Self::Int(exp)) => {
1078
- if *base == 0 && *exp < 0 {
1079
- Err(ExcType::zero_negative_power())
1080
- } else if *exp >= 0 {
1081
- // Positive exponent: try to return int, promote to LongInt on overflow
1082
- if let Ok(exp_u32) = u32::try_from(*exp) {
1083
- if let Some(result) = base.checked_pow(exp_u32) {
1084
- Ok(Some(Self::Int(result)))
1085
- } else {
1086
- // Overflow - promote to LongInt
1087
- // Check size before computing to prevent DoS
1088
- check_pow_size(i64_bits(*base), u64::from(exp_u32), vm.heap.tracker())?;
1089
- let bi = BigInt::from(*base).pow(exp_u32);
1090
- Ok(Some(LongInt::new(bi).into_value(vm.heap)?))
1091
- }
1092
- } else {
1093
- // exp > u32::MAX - use BigInt with modpow-style exponentiation
1094
- // For very large exponents, we still need LongInt
1095
- // Safety: exp >= 0 is guaranteed by the outer if condition
1096
- #[expect(clippy::cast_sign_loss)]
1097
- let exp_u64 = *exp as u64;
1098
- // Check size before computing to prevent DoS
1099
- check_pow_size(i64_bits(*base), exp_u64, vm.heap.tracker())?;
1100
- let bi = bigint_pow(BigInt::from(*base), exp_u64);
1101
- Ok(Some(LongInt::new(bi).into_value(vm.heap)?))
1102
- }
1103
- } else {
1104
- // Negative exponent: return float
1105
- // Use powi if exp fits in i32, otherwise use powf
1106
- if let Ok(exp_i32) = i32::try_from(*exp) {
1107
- Ok(Some(Self::Float((*base as f64).powi(exp_i32))))
1108
- } else {
1109
- Ok(Some(Self::Float((*base as f64).powf(*exp as f64))))
1110
- }
1111
- }
1112
- }
1113
- // LongInt ** Int
1114
- (Self::Ref(id), Self::Int(exp)) => {
1115
- if let HeapData::LongInt(li) = vm.heap.get(*id) {
1116
- if li.is_zero() && *exp < 0 {
1117
- Err(ExcType::zero_negative_power())
1118
- } else if *exp >= 0 {
1119
- // Use BigInt pow for positive exponents
1120
- if let Ok(exp_u32) = u32::try_from(*exp) {
1121
- // Check size before computing to prevent DoS
1122
- check_pow_size(li.bits(), u64::from(exp_u32), vm.heap.tracker())?;
1123
- let bi = li.inner().pow(exp_u32);
1124
- Ok(Some(LongInt::new(bi).into_value(vm.heap)?))
1125
- } else {
1126
- // Safety: exp >= 0 is guaranteed by the outer if condition
1127
- #[expect(clippy::cast_sign_loss)]
1128
- let exp_u64 = *exp as u64;
1129
- // Check size before computing to prevent DoS
1130
- check_pow_size(li.bits(), exp_u64, vm.heap.tracker())?;
1131
- let bi = bigint_pow(li.inner().clone(), exp_u64);
1132
- Ok(Some(LongInt::new(bi).into_value(vm.heap)?))
1133
- }
1134
- } else {
1135
- // Negative exponent: return float (LongInt base becomes 0.0 for large values)
1136
- if let Some(base_f64) = li.to_f64() {
1137
- if let Ok(exp_i32) = i32::try_from(*exp) {
1138
- Ok(Some(Self::Float(base_f64.powi(exp_i32))))
1139
- } else {
1140
- Ok(Some(Self::Float(base_f64.powf(*exp as f64))))
1141
- }
1142
- } else {
1143
- // Base too large for f64, result approaches 0
1144
- Ok(Some(Self::Float(0.0)))
1145
- }
1146
- }
1147
- } else {
1148
- Ok(None)
1149
- }
1150
- }
1151
- // Int ** LongInt (only small positive exponents make sense)
1152
- (Self::Int(base), Self::Ref(id)) => {
1153
- if let HeapData::LongInt(li) = vm.heap.get(*id) {
1154
- if *base == 0 && li.is_negative() {
1155
- Err(ExcType::zero_negative_power())
1156
- } else if !li.is_negative() {
1157
- // For very large exponents, most results are huge or 0/1
1158
- // Check for x ** 0 = 1 first (including 0 ** 0 = 1)
1159
- if li.is_zero() {
1160
- Ok(Some(Self::Int(1)))
1161
- } else if *base == 0 {
1162
- Ok(Some(Self::Int(0)))
1163
- } else if *base == 1 {
1164
- Ok(Some(Self::Int(1)))
1165
- } else if *base == -1 {
1166
- // (-1) ** n = 1 if n is even, -1 if n is odd
1167
- let is_even = (li.inner() % 2i32).is_zero();
1168
- Ok(Some(Self::Int(if is_even { 1 } else { -1 })))
1169
- } else if let Some(exp_u32) = li.to_u32() {
1170
- // Reasonable exponent size
1171
- if let Some(result) = base.checked_pow(exp_u32) {
1172
- Ok(Some(Self::Int(result)))
1173
- } else {
1174
- // Check size before computing to prevent DoS
1175
- check_pow_size(i64_bits(*base), u64::from(exp_u32), vm.heap.tracker())?;
1176
- let bi = BigInt::from(*base).pow(exp_u32);
1177
- Ok(Some(LongInt::new(bi).into_value(vm.heap)?))
1178
- }
1179
- } else {
1180
- // Exponent too large - result would be astronomically large
1181
- // Python handles this, but it would take forever. Use OverflowError
1182
- Err(SimpleException::new_msg(ExcType::OverflowError, "exponent too large").into())
1183
- }
1184
- } else {
1185
- // Negative LongInt exponent: return float
1186
- if let (Some(base_f64), Some(exp_f64)) = (Some(*base as f64), li.to_f64()) {
1187
- Ok(Some(Self::Float(base_f64.powf(exp_f64))))
1188
- } else {
1189
- Ok(Some(Self::Float(0.0)))
1190
- }
1191
- }
1192
- } else {
1193
- Ok(None)
1194
- }
1195
- }
1196
- (Self::Float(base), Self::Float(exp)) => {
1197
- if *base == 0.0 && *exp < 0.0 {
1198
- Err(ExcType::zero_negative_power())
1199
- } else {
1200
- Ok(Some(Self::Float(base.powf(*exp))))
1201
- }
1202
- }
1203
- (Self::Int(base), Self::Float(exp)) => {
1204
- if *base == 0 && *exp < 0.0 {
1205
- Err(ExcType::zero_negative_power())
1206
- } else {
1207
- Ok(Some(Self::Float((*base as f64).powf(*exp))))
1208
- }
1209
- }
1210
- (Self::Float(base), Self::Int(exp)) => {
1211
- if *base == 0.0 && *exp < 0 {
1212
- Err(ExcType::zero_negative_power())
1213
- } else if let Ok(exp_i32) = i32::try_from(*exp) {
1214
- // Use powi if exp fits in i32
1215
- Ok(Some(Self::Float(base.powi(exp_i32))))
1216
- } else {
1217
- // Fall back to powf for exponents outside i32 range
1218
- Ok(Some(Self::Float(base.powf(*exp as f64))))
1219
- }
1220
- }
1221
- // Bool power operations (True=1, False=0)
1222
- (Self::Bool(base), Self::Int(exp)) => {
1223
- let base_int = i64::from(*base);
1224
- if base_int == 0 && *exp < 0 {
1225
- Err(ExcType::zero_negative_power())
1226
- } else if *exp >= 0 {
1227
- // Positive exponent: 1**n=1, 0**n=0 (for n>0), 0**0=1
1228
- if let Ok(exp_u32) = u32::try_from(*exp) {
1229
- match base_int.checked_pow(exp_u32) {
1230
- Some(result) => Ok(Some(Self::Int(result))),
1231
- None => Ok(Some(Self::Float((base_int as f64).powf(*exp as f64)))),
1232
- }
1233
- } else {
1234
- Ok(Some(Self::Float((base_int as f64).powf(*exp as f64))))
1235
- }
1236
- } else {
1237
- // Negative exponent: return float (1**-n=1.0)
1238
- if let Ok(exp_i32) = i32::try_from(*exp) {
1239
- Ok(Some(Self::Float((base_int as f64).powi(exp_i32))))
1240
- } else {
1241
- Ok(Some(Self::Float((base_int as f64).powf(*exp as f64))))
1242
- }
1243
- }
1244
- }
1245
- (Self::Int(base), Self::Bool(exp)) => {
1246
- // n ** True = n, n ** False = 1
1247
- if *exp {
1248
- Ok(Some(Self::Int(*base)))
1249
- } else {
1250
- Ok(Some(Self::Int(1)))
1251
- }
1252
- }
1253
- (Self::Bool(base), Self::Float(exp)) => {
1254
- let base_float = f64::from(*base);
1255
- if base_float == 0.0 && *exp < 0.0 {
1256
- Err(ExcType::zero_negative_power())
1257
- } else {
1258
- Ok(Some(Self::Float(base_float.powf(*exp))))
1259
- }
1260
- }
1261
- (Self::Float(base), Self::Bool(exp)) => {
1262
- // base ** True = base, base ** False = 1.0
1263
- if *exp {
1264
- Ok(Some(Self::Float(*base)))
1265
- } else {
1266
- Ok(Some(Self::Float(1.0)))
1267
- }
1268
- }
1269
- (Self::Bool(base), Self::Bool(exp)) => {
1270
- // True ** True = 1, True ** False = 1, False ** True = 0, False ** False = 1
1271
- let base_int = i64::from(*base);
1272
- let exp_int = i64::from(*exp);
1273
- if exp_int == 0 {
1274
- Ok(Some(Self::Int(1))) // anything ** 0 = 1
1275
- } else {
1276
- Ok(Some(Self::Int(base_int))) // base ** 1 = base
1277
- }
1278
- }
1279
- _ => Ok(None),
1280
- }
1281
- }
1282
-
1283
- fn py_getitem(&self, key: &Self, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Self> {
1284
- let interns = vm.interns;
1285
- match self {
1286
- Self::Ref(id) => Heap::with_entry_mut(vm, *id, |vm, data| data.py_getitem(key, vm)),
1287
- Self::InternString(string_id) => {
1288
- // Check for slice first
1289
- if let Self::Ref(key_id) = key
1290
- && let HeapData::Slice(slice_obj) = vm.heap.get(*key_id)
1291
- {
1292
- let s = interns.get_str(*string_id);
1293
- let char_count = s.chars().count();
1294
- let (start, stop, step) = slice_obj
1295
- .indices(char_count)
1296
- .map_err(|()| ExcType::value_error_slice_step_zero())?;
1297
- let result_str = get_str_slice(s, start, stop, step);
1298
- let heap_id = vm.heap.allocate(HeapData::Str(Str::from(result_str)))?;
1299
- return Ok(Self::Ref(heap_id));
1300
- }
1301
-
1302
- // Handle interned string indexing, accepting Int and Bool
1303
- let index = match key {
1304
- Self::Int(i) => *i,
1305
- Self::Bool(b) => i64::from(*b),
1306
- _ => return Err(ExcType::type_error_indices(Type::Str, key.py_type(vm.heap))),
1307
- };
1308
-
1309
- let s = interns.get_str(*string_id);
1310
- let c = get_char_at_index(s, index).ok_or_else(ExcType::str_index_error)?;
1311
- Ok(allocate_char(c, vm.heap)?)
1312
- }
1313
- Self::InternBytes(bytes_id) => {
1314
- // Check for slice first
1315
- if let Self::Ref(key_id) = key
1316
- && let HeapData::Slice(slice_obj) = vm.heap.get(*key_id)
1317
- {
1318
- let bytes = interns.get_bytes(*bytes_id);
1319
- let (start, stop, step) = slice_obj
1320
- .indices(bytes.len())
1321
- .map_err(|()| ExcType::value_error_slice_step_zero())?;
1322
- let result_bytes = get_bytes_slice(bytes, start, stop, step);
1323
- let heap_id = vm
1324
- .heap
1325
- .allocate(HeapData::Bytes(crate::types::Bytes::new(result_bytes)))?;
1326
- return Ok(Self::Ref(heap_id));
1327
- }
1328
-
1329
- // Handle interned bytes indexing - returns integer byte value
1330
- let index = match key {
1331
- Self::Int(i) => *i,
1332
- Self::Bool(b) => i64::from(*b),
1333
- _ => return Err(ExcType::type_error_indices(Type::Bytes, key.py_type(vm.heap))),
1334
- };
1335
-
1336
- let bytes = interns.get_bytes(*bytes_id);
1337
- let byte = get_byte_at_index(bytes, index).ok_or_else(ExcType::bytes_index_error)?;
1338
- Ok(Self::Int(i64::from(byte)))
1339
- }
1340
- _ => Err(ExcType::type_error_not_sub(self.py_type(vm.heap))),
1341
- }
1342
- }
1343
-
1344
- fn py_setitem(&mut self, key: Self, value: Self, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<()> {
1345
- match self {
1346
- Self::Ref(id) => Heap::with_entry_mut(vm, *id, |vm, mut data| data.py_setitem(key, value, vm)),
1347
- _ => Err(ExcType::type_error(format!(
1348
- "'{}' object does not support item assignment",
1349
- self.py_type(vm.heap)
1350
- ))),
1351
- }
1352
- }
1353
- }
1354
-
1355
- impl Value {
1356
- /// Returns a stable, unique identifier for this value.
1357
- ///
1358
- /// Should match Python's `id()` function conceptually.
1359
- ///
1360
- /// For immediate values (Int, Float, Builtins), this computes a deterministic ID
1361
- /// based on the value's hash, avoiding heap allocation. This means `id(5) == id(5)` will
1362
- /// return True (unlike CPython for large integers outside the interning range).
1363
- ///
1364
- /// Singletons (None, True, False, etc.) return IDs from a dedicated tagged range.
1365
- /// Interned strings/bytes use their interner index for stable identity.
1366
- /// Heap-allocated values (Ref) reuse their `HeapId` inside the heap-tagged range.
1367
- pub fn id(&self) -> usize {
1368
- match self {
1369
- // Singletons have fixed tagged IDs
1370
- Self::Undefined => singleton_id(SingletonSlot::Undefined),
1371
- Self::Ellipsis => singleton_id(SingletonSlot::Ellipsis),
1372
- Self::None => singleton_id(SingletonSlot::None),
1373
- Self::Bool(b) => {
1374
- if *b {
1375
- singleton_id(SingletonSlot::True)
1376
- } else {
1377
- singleton_id(SingletonSlot::False)
1378
- }
1379
- }
1380
- // Interned strings/bytes/bigints use their index directly - the index is the stable identifier
1381
- Self::InternString(string_id) => INTERN_STR_ID_TAG | (string_id.index() & INTERN_STR_ID_MASK),
1382
- Self::InternBytes(bytes_id) => INTERN_BYTES_ID_TAG | (bytes_id.index() & INTERN_BYTES_ID_MASK),
1383
- Self::InternLongInt(long_int_id) => {
1384
- INTERN_LONG_INT_ID_TAG | (long_int_id.index() & INTERN_LONG_INT_ID_MASK)
1385
- }
1386
- // Already heap-allocated (includes Range and Exception), return id within a dedicated tag range
1387
- Self::Ref(id) => heap_tagged_id(*id),
1388
- // Value-based IDs for immediate types (no heap allocation!)
1389
- Self::Int(v) => int_value_id(*v),
1390
- Self::Float(v) => float_value_id(*v),
1391
- Self::Builtin(c) => builtin_value_id(*c),
1392
- Self::ModuleFunction(mf) => module_function_value_id(*mf),
1393
- Self::DefFunction(f_id) => function_value_id(*f_id),
1394
- Self::ExtFunction(name_id) => ext_function_value_id(*name_id),
1395
- // Markers get deterministic IDs based on discriminant
1396
- Self::Marker(m) => marker_value_id(*m),
1397
- // Properties get deterministic IDs based on discriminant
1398
- Self::Property(p) => property_value_id(*p),
1399
- // ExternalFutures get IDs based on their call_id
1400
- Self::ExternalFuture(call_id) => external_future_value_id(*call_id),
1401
- #[cfg(feature = "ref-count-panic")]
1402
- Self::Dereferenced => panic!("Cannot get id of Dereferenced object"),
1403
- }
1404
- }
1405
-
1406
- /// Returns the Ref ID if this value is a reference, otherwise returns None.
1407
- pub fn ref_id(&self) -> Option<HeapId> {
1408
- match self {
1409
- Self::Ref(id) => Some(*id),
1410
- _ => None,
1411
- }
1412
- }
1413
-
1414
- /// Returns the module name if this value is a module, otherwise returns "<unknown>".
1415
- ///
1416
- /// Used for error messages in `from module import name` when the name doesn't exist.
1417
- pub fn module_name(&self, heap: &Heap<impl ResourceTracker>, interns: &Interns) -> String {
1418
- match self {
1419
- Self::Ref(id) => match heap.get(*id) {
1420
- HeapData::Module(module) => interns.get_str(module.name()).to_string(),
1421
- _ => "<unknown>".to_string(),
1422
- },
1423
- _ => "<unknown>".to_string(),
1424
- }
1425
- }
1426
-
1427
- /// Equivalent of Python's `is` operator.
1428
- ///
1429
- /// Compares value identity by comparing their IDs.
1430
- pub fn is(&self, other: &Self) -> bool {
1431
- self.id() == other.id()
1432
- }
1433
-
1434
- /// Computes the hash value for this value, used for dict keys.
1435
- ///
1436
- /// Returns `Ok(Some(hash))` for hashable types (immediate values and immutable heap types).
1437
- /// Returns `Ok(None)` for unhashable types (list, dict).
1438
- /// Returns `Err(ResourceError::Recursion)` if the recursion limit is exceeded
1439
- /// while hashing deeply nested containers (e.g., tuples of tuples).
1440
- ///
1441
- /// For heap-allocated values (Ref variant), this computes the hash lazily
1442
- /// on first use and caches it for subsequent calls.
1443
- ///
1444
- /// The `interns` parameter is needed for InternString/InternBytes to look up
1445
- /// their actual content and hash it consistently with equivalent heap Str/Bytes.
1446
- pub fn py_hash(
1447
- &self,
1448
- heap: &mut Heap<impl ResourceTracker>,
1449
- interns: &Interns,
1450
- ) -> Result<Option<u64>, ResourceError> {
1451
- // strings bytes bigints and heap allocated values have their own hashing logic
1452
- match self {
1453
- // Hash just the actual string or bytes content for consistency with heap Str/Bytes
1454
- // hence we don't include the discriminant
1455
- Self::InternString(string_id) => {
1456
- let mut hasher = DefaultHasher::new();
1457
- interns.get_str(*string_id).hash(&mut hasher);
1458
- return Ok(Some(hasher.finish()));
1459
- }
1460
- Self::InternBytes(bytes_id) => {
1461
- let mut hasher = DefaultHasher::new();
1462
- interns.get_bytes(*bytes_id).hash(&mut hasher);
1463
- return Ok(Some(hasher.finish()));
1464
- }
1465
- // Hash BigInt consistently with LongInt (using sign and bytes for large values)
1466
- Self::InternLongInt(long_int_id) => {
1467
- let bi = interns.get_long_int(*long_int_id);
1468
- let mut hasher = DefaultHasher::new();
1469
- let (sign, bytes) = bi.to_bytes_le();
1470
- sign.hash(&mut hasher);
1471
- bytes.hash(&mut hasher);
1472
- return Ok(Some(hasher.finish()));
1473
- }
1474
- // For heap-allocated values (includes Range and Exception), compute hash lazily and cache it
1475
- Self::Ref(id) => return heap.get_or_compute_hash(*id, interns),
1476
- _ => {}
1477
- }
1478
-
1479
- let mut hasher = DefaultHasher::new();
1480
- // hash based on discriminant to avoid collisions with different types
1481
- discriminant(self).hash(&mut hasher);
1482
- match self {
1483
- // Immediate values can be hashed directly
1484
- Self::Undefined | Self::Ellipsis | Self::None => {}
1485
- Self::Bool(b) => b.hash(&mut hasher),
1486
- Self::Int(i) => i.hash(&mut hasher),
1487
- // Hash the bit representation of float for consistency
1488
- Self::Float(f) => f.to_bits().hash(&mut hasher),
1489
- Self::Builtin(b) => b.hash(&mut hasher),
1490
- Self::ModuleFunction(mf) => mf.hash(&mut hasher),
1491
- // Hash functions based on function ID
1492
- Self::DefFunction(f_id) => f_id.hash(&mut hasher),
1493
- Self::ExtFunction(name_id) => name_id.hash(&mut hasher),
1494
- // Markers are hashable based on their discriminant (already included above)
1495
- Self::Marker(m) => m.hash(&mut hasher),
1496
- // Properties are hashable based on their OS function discriminant
1497
- Self::Property(p) => p.hash(&mut hasher),
1498
- // ExternalFutures are hashable based on their call ID
1499
- Self::ExternalFuture(call_id) => call_id.raw().hash(&mut hasher),
1500
- Self::InternString(_) | Self::InternBytes(_) | Self::InternLongInt(_) | Self::Ref(_) => {
1501
- unreachable!("covered above")
1502
- }
1503
- #[cfg(feature = "ref-count-panic")]
1504
- Self::Dereferenced => panic!("Cannot access Dereferenced object"),
1505
- }
1506
- Ok(Some(hasher.finish()))
1507
- }
1508
-
1509
- /// TODO this doesn't have many tests!!! also doesn't cover bytes
1510
- /// Checks if `item` is contained in `self` (the container).
1511
- ///
1512
- /// Implements Python's `in` operator for various container types:
1513
- /// - List/Tuple: linear search with equality
1514
- /// - Dict: key lookup
1515
- /// - Set/FrozenSet: element lookup
1516
- /// - Str: substring search
1517
- pub fn py_contains(&self, item: &Self, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<bool> {
1518
- match self {
1519
- Self::Ref(heap_id) => Heap::with_entry_mut(vm, *heap_id, |vm, data| match data {
1520
- HeapDataMut::List(list) => {
1521
- for el in list.as_slice() {
1522
- if item.py_eq(el, vm)? {
1523
- return Ok(true);
1524
- }
1525
- }
1526
- Ok(false)
1527
- }
1528
- HeapDataMut::Tuple(tuple) => {
1529
- for el in tuple.as_slice() {
1530
- if item.py_eq(el, vm)? {
1531
- return Ok(true);
1532
- }
1533
- }
1534
- Ok(false)
1535
- }
1536
- HeapDataMut::Dict(dict) => dict.get(item, vm).map(|m| m.is_some()),
1537
- HeapDataMut::DictKeysView(view) => Heap::with_entry_mut(vm, view.dict_id(), |vm, dict_data| {
1538
- let HeapDataMut::Dict(dict) = dict_data else {
1539
- panic!("dict_keys view must reference a dict");
1540
- };
1541
- dict.get(item, vm).map(|m| m.is_some())
1542
- }),
1543
- HeapDataMut::DictItemsView(view) => {
1544
- let Some((key, value)) = cloned_items_view_candidate(item, vm) else {
1545
- return Ok(false);
1546
- };
1547
- let mut key_guard = HeapGuard::new(key, vm);
1548
- let (key, vm) = key_guard.as_parts_mut();
1549
- let mut value_guard = HeapGuard::new(value, vm);
1550
- let (value, vm) = value_guard.as_parts_mut();
1551
- Heap::with_entry_mut(vm, view.dict_id(), |vm, dict_data| {
1552
- let HeapDataMut::Dict(dict) = dict_data else {
1553
- panic!("dict_items view must reference a dict");
1554
- };
1555
- match dict.get(key, vm) {
1556
- Ok(Some(existing_value)) => value.py_eq(existing_value, vm).map_err(RunError::from),
1557
- Ok(None) => Ok(false),
1558
- Err(e) => Err(e),
1559
- }
1560
- })
1561
- }
1562
- HeapDataMut::DictValuesView(view) => Heap::with_entry_mut(vm, view.dict_id(), |vm, dict_data| {
1563
- let HeapDataMut::Dict(dict) = dict_data else {
1564
- panic!("dict_values view must reference a dict");
1565
- };
1566
- for (_, value) in dict.iter() {
1567
- if item.py_eq(value, vm)? {
1568
- return Ok(true);
1569
- }
1570
- }
1571
- Ok(false)
1572
- }),
1573
- HeapDataMut::Set(set) => set.contains(item, vm),
1574
- HeapDataMut::FrozenSet(fset) => fset.contains(item, vm),
1575
- HeapDataMut::Str(s) => str_contains(s.as_str(), item, vm.heap, vm.interns),
1576
- HeapDataMut::Range(range) => {
1577
- // Range containment is O(1) - check bounds and step alignment
1578
- let n = match item {
1579
- Self::Int(i) => *i,
1580
- Self::Bool(b) => i64::from(*b),
1581
- Self::Float(f) => {
1582
- // Floats are contained if they equal an integer in the range
1583
- // e.g., 3.0 in range(5) is True, but 3.5 in range(5) is False
1584
- if f.fract() != 0.0 {
1585
- return Ok(false);
1586
- }
1587
- // Check if float is within i64 range and convert safely
1588
- // f64 can represent integers up to 2^53 exactly
1589
- let int_val = f.trunc();
1590
- if int_val < i64::MIN as f64 || int_val > i64::MAX as f64 {
1591
- return Ok(false);
1592
- }
1593
- // Safe conversion: we've verified it's a whole number in i64 range
1594
- #[expect(clippy::cast_possible_truncation)]
1595
- let n = int_val as i64;
1596
- n
1597
- }
1598
- _ => return Ok(false),
1599
- };
1600
- Ok(range.contains(n))
1601
- }
1602
- other => {
1603
- let type_name = other.py_type(vm.heap);
1604
- Err(ExcType::type_error(format!(
1605
- "argument of type '{type_name}' is not iterable"
1606
- )))
1607
- }
1608
- }),
1609
- Self::InternString(string_id) => {
1610
- let container_str = vm.interns.get_str(*string_id);
1611
- str_contains(container_str, item, vm.heap, vm.interns)
1612
- }
1613
- _ => {
1614
- let type_name = self.py_type(vm.heap);
1615
- Err(ExcType::type_error(format!(
1616
- "argument of type '{type_name}' is not iterable"
1617
- )))
1618
- }
1619
- }
1620
- }
1621
-
1622
- /// Gets an attribute from this value.
1623
- ///
1624
- /// Dispatches to `py_getattr` on the underlying types where appropriate.
1625
- /// Accepts `EitherStr` to support both interned and heap-allocated attribute names.
1626
- ///
1627
- /// Returns `AttributeError` for other types or unknown attributes.
1628
- pub fn py_getattr(&self, attr: &EitherStr, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<CallResult> {
1629
- match self {
1630
- Self::Ref(heap_id) => {
1631
- // Use with_entry_mut to get access to both data and heap without borrow conflicts.
1632
- // This allows py_getattr to allocate (for computed attributes) while we hold the data.
1633
- let opt_result = Heap::with_entry_mut(vm, *heap_id, |vm, data| data.py_getattr(attr, vm))?;
1634
- if let Some(call_result) = opt_result {
1635
- return Ok(call_result);
1636
- }
1637
- }
1638
- Self::Builtin(Builtins::Type(t)) => {
1639
- // Handle type object attributes like __name__
1640
- let is_dunder_name = attr.static_string().map_or_else(
1641
- || attr.as_str(vm.interns) == "__name__",
1642
- |ss| ss == StaticStrings::DunderName,
1643
- );
1644
- if is_dunder_name {
1645
- let name_str = t.to_string();
1646
- let str_id = vm.heap.allocate(HeapData::Str(Str::from(name_str)))?;
1647
- return Ok(CallResult::Value(Self::Ref(str_id)));
1648
- }
1649
- }
1650
- _ => {}
1651
- }
1652
- let type_name = self.py_type(vm.heap);
1653
- Err(ExcType::attribute_error(type_name, attr.as_str(vm.interns)))
1654
- }
1655
-
1656
- /// Sets an attribute on this value.
1657
- ///
1658
- /// Currently only Dataclass objects support attribute setting.
1659
- /// Returns AttributeError for other types.
1660
- ///
1661
- /// Takes ownership of `value` and drops it on error.
1662
- /// On success, drops the old attribute value if one existed.
1663
- pub fn py_set_attr(
1664
- &self,
1665
- name_id: StringId,
1666
- value: Self,
1667
- vm: &mut VM<'_, '_, impl ResourceTracker>,
1668
- ) -> RunResult<()> {
1669
- let attr_name = vm.interns.get_str(name_id);
1670
-
1671
- if let Self::Ref(heap_id) = self {
1672
- let heap_id = *heap_id;
1673
- let is_dataclass = matches!(vm.heap.get(heap_id), HeapData::Dataclass(_));
1674
-
1675
- if is_dataclass {
1676
- let name_value = Self::InternString(name_id);
1677
- Heap::with_entry_mut(vm, heap_id, |vm, data| {
1678
- if let HeapDataMut::Dataclass(dc) = data {
1679
- match dc.set_attr(name_value, value, vm) {
1680
- Ok(old_value) => {
1681
- if let Some(old) = old_value {
1682
- old.drop_with_heap(vm.heap);
1683
- }
1684
- Ok(())
1685
- }
1686
- Err(e) => Err(e),
1687
- }
1688
- } else {
1689
- unreachable!("type changed during borrow")
1690
- }
1691
- })
1692
- } else {
1693
- let type_name = vm.heap.get(heap_id).py_type(vm.heap);
1694
- value.drop_with_heap(vm.heap);
1695
- Err(ExcType::attribute_error_no_setattr(type_name, attr_name))
1696
- }
1697
- } else {
1698
- let type_name = self.py_type(vm.heap);
1699
- value.drop_with_heap(vm.heap);
1700
- Err(ExcType::attribute_error_no_setattr(type_name, attr_name))
1701
- }
1702
- }
1703
-
1704
- /// Extracts an integer value from the Value.
1705
- ///
1706
- /// Accepts `Int` and `LongInt` (if it fits in i64). Returns a `TypeError` for other types
1707
- /// and an `OverflowError` if the `LongInt` value is too large.
1708
- ///
1709
- /// Note: The LongInt-to-i64 conversion path is defensive code. In normal execution,
1710
- /// heap-allocated `LongInt` values always exceed i64 range because `LongInt::into_value()`
1711
- /// automatically demotes i64-fitting values to `Value::Int`. However, this path could be
1712
- /// reached via deserialization of crafted snapshot data.
1713
- pub fn as_int(&self, heap: &Heap<impl ResourceTracker>) -> RunResult<i64> {
1714
- match self {
1715
- Self::Int(i) => Ok(*i),
1716
- Self::Ref(heap_id) => {
1717
- if let HeapData::LongInt(li) = heap.get(*heap_id) {
1718
- li.to_i64().ok_or_else(ExcType::overflow_shift_count)
1719
- } else {
1720
- let msg = format!("'{}' object cannot be interpreted as an integer", self.py_type(heap));
1721
- Err(SimpleException::new_msg(ExcType::TypeError, msg).into())
1722
- }
1723
- }
1724
- _ => {
1725
- let msg = format!("'{}' object cannot be interpreted as an integer", self.py_type(heap));
1726
- Err(SimpleException::new_msg(ExcType::TypeError, msg).into())
1727
- }
1728
- }
1729
- }
1730
-
1731
- /// Extracts an index value for sequence operations.
1732
- ///
1733
- /// Accepts `Int`, `Bool` (True=1, False=0), and `LongInt` (if it fits in i64).
1734
- /// Returns a `TypeError` for other types with the container type name included.
1735
- /// Returns an `IndexError` if the `LongInt` value is too large to use as an index.
1736
- ///
1737
- /// Note: The LongInt-to-i64 conversion path is defensive code. In normal execution,
1738
- /// heap-allocated `LongInt` values always exceed i64 range because `LongInt::into_value()`
1739
- /// automatically demotes i64-fitting values to `Value::Int`. However, this path could be
1740
- /// reached via deserialization of crafted snapshot data.
1741
- pub fn as_index(&self, heap: &Heap<impl ResourceTracker>, container_type: Type) -> RunResult<i64> {
1742
- match self {
1743
- Self::Int(i) => Ok(*i),
1744
- Self::Bool(b) => Ok(i64::from(*b)),
1745
- Self::Ref(heap_id) => {
1746
- if let HeapData::LongInt(li) = heap.get(*heap_id) {
1747
- li.to_i64().ok_or_else(ExcType::index_error_int_too_large)
1748
- } else {
1749
- Err(ExcType::type_error_indices(container_type, self.py_type(heap)))
1750
- }
1751
- }
1752
- _ => Err(ExcType::type_error_indices(container_type, self.py_type(heap))),
1753
- }
1754
- }
1755
-
1756
- /// Performs a binary bitwise operation on two values.
1757
- ///
1758
- /// Python only supports bitwise operations on integers (and bools, which coerce to int).
1759
- /// Returns a `TypeError` if either operand is not an integer, bool, or LongInt.
1760
- ///
1761
- /// For shift operations:
1762
- /// - Negative shift counts raise `ValueError`
1763
- /// - Left shifts may produce LongInt results for large shifts
1764
- /// - Right shifts with large counts return 0 (or -1 for negative numbers)
1765
- pub fn py_bitwise(
1766
- &self,
1767
- other: &Self,
1768
- op: BitwiseOp,
1769
- heap: &mut Heap<impl ResourceTracker>,
1770
- ) -> Result<Self, RunError> {
1771
- // Capture types for error messages
1772
- let lhs_type = self.py_type(heap);
1773
- let rhs_type = other.py_type(heap);
1774
-
1775
- // Extract BigInt from all numeric types
1776
- let lhs_bigint = extract_bigint(self, heap);
1777
- let rhs_bigint = extract_bigint(other, heap);
1778
-
1779
- if let (Some(l), Some(r)) = (lhs_bigint, rhs_bigint) {
1780
- let result = match op {
1781
- BitwiseOp::And => l & r,
1782
- BitwiseOp::Or => l | r,
1783
- BitwiseOp::Xor => l ^ r,
1784
- BitwiseOp::LShift => {
1785
- // Get shift amount as i64 for validation
1786
- let shift_amount = r.to_i64();
1787
- if let Some(shift) = shift_amount {
1788
- if shift < 0 {
1789
- return Err(ExcType::value_error_negative_shift_count());
1790
- }
1791
- // Python allows arbitrarily large left shifts - use BigInt's shift
1792
- // Safety: shift >= 0 is guaranteed by the check above
1793
- #[expect(clippy::cast_sign_loss)]
1794
- let shift_u64 = shift as u64;
1795
- // Check size before computing to prevent DoS
1796
- check_lshift_size(l.bits(), shift_u64, heap.tracker())?;
1797
- l << shift_u64
1798
- } else if r.sign() == num_bigint::Sign::Minus {
1799
- return Err(ExcType::value_error_negative_shift_count());
1800
- } else {
1801
- // Shift amount too large to fit in i64 - this would be astronomically large
1802
- return Err(ExcType::overflow_shift_count());
1803
- }
1804
- }
1805
- BitwiseOp::RShift => {
1806
- // Get shift amount as i64 for validation
1807
- let shift_amount = r.to_i64();
1808
- if let Some(shift) = shift_amount {
1809
- if shift < 0 {
1810
- return Err(ExcType::value_error_negative_shift_count());
1811
- }
1812
- // Safety: shift >= 0 is guaranteed by the check above
1813
- #[expect(clippy::cast_sign_loss)]
1814
- let shift_u64 = shift as u64;
1815
- l >> shift_u64
1816
- } else if r.sign() == num_bigint::Sign::Minus {
1817
- return Err(ExcType::value_error_negative_shift_count());
1818
- } else {
1819
- // Shift amount too large - result is 0 or -1 depending on sign
1820
- if l.sign() == num_bigint::Sign::Minus {
1821
- BigInt::from(-1)
1822
- } else {
1823
- BigInt::from(0)
1824
- }
1825
- }
1826
- }
1827
- };
1828
- // Convert result back to Value, demoting to i64 if it fits
1829
- LongInt::new(result).into_value(heap).map_err(Into::into)
1830
- } else {
1831
- Err(ExcType::binary_type_error(op.as_str(), lhs_type, rhs_type))
1832
- }
1833
- }
1834
-
1835
- /// Clones an value with proper heap reference counting.
1836
- ///
1837
- /// For immediate values (Int, Bool, None, etc.), this performs a simple copy.
1838
- /// For heap-allocated values (Ref variant), this increments the reference count
1839
- /// and returns a new reference to the same heap value.
1840
- ///
1841
- /// Takes `ContainsHeap` to allow directly passing the `VM` in many contexts. Where
1842
- /// borrow checking creates conflicts, it may be preferred to pass `&Heap` directly
1843
- /// (e.g. as `vm.heap` / `self.heap` etc.).
1844
- ///
1845
- /// # Important
1846
- /// This method MUST be used instead of the derived `Clone` implementation to ensure
1847
- /// proper reference counting. Using `.clone()` directly will bypass reference counting
1848
- /// and cause memory leaks or double-frees.
1849
- #[must_use]
1850
- pub fn clone_with_heap(&self, heap: &impl ContainsHeap) -> Self {
1851
- match self {
1852
- Self::Ref(id) => {
1853
- heap.heap().inc_ref(*id);
1854
- Self::Ref(*id)
1855
- }
1856
- // Immediate values can be copied without heap interaction
1857
- other => other.clone_immediate(),
1858
- }
1859
- }
1860
-
1861
- /// Drops an value, decrementing its heap reference count if applicable.
1862
- ///
1863
- /// For immediate values, this is a no-op. For heap-allocated values (Ref variant),
1864
- /// this decrements the reference count and frees the value (and any children) when
1865
- /// the count reaches zero. For Closure variants, this decrements ref counts on all
1866
- /// captured cells.
1867
- ///
1868
- /// Takes `ContainsHeap` to allow directly passing the `VM` in many contexts. Where
1869
- /// borrow checking creates conflicts, it may be preferred to pass `&mut Heap` directly
1870
- /// (e.g. as `vm.heap` / `self.heap` etc.).
1871
- ///
1872
- /// # Important
1873
- /// This method MUST be called before overwriting a namespace slot or discarding
1874
- /// a value to prevent memory leaks.
1875
- #[cfg(not(feature = "ref-count-panic"))]
1876
- #[inline]
1877
- pub fn drop_with_heap(self, heap: &mut impl ContainsHeap) {
1878
- if let Self::Ref(id) = self {
1879
- heap.heap_mut().dec_ref(id);
1880
- }
1881
- }
1882
- /// With `ref-count-panic` enabled, `Ref` variants are replaced with `Dereferenced` and
1883
- /// the original is forgotten to prevent the Drop impl from panicking. Non-Ref variants
1884
- /// are left unchanged since they don't trigger the Drop panic.
1885
- #[cfg(feature = "ref-count-panic")]
1886
- pub fn drop_with_heap(mut self, heap: &mut impl ContainsHeap) {
1887
- let old = std::mem::replace(&mut self, Self::Dereferenced);
1888
- if let Self::Ref(id) = &old {
1889
- heap.heap_mut().dec_ref(*id);
1890
- std::mem::forget(old);
1891
- }
1892
- }
1893
-
1894
- /// Internal helper for copying immediate values without heap interaction.
1895
- ///
1896
- /// This method should only be called by `clone_with_heap()` for immediate values.
1897
- /// Attempting to clone a Ref variant will panic.
1898
- pub fn clone_immediate(&self) -> Self {
1899
- match self {
1900
- Self::Undefined => Self::Undefined,
1901
- Self::Ellipsis => Self::Ellipsis,
1902
- Self::None => Self::None,
1903
- Self::Bool(b) => Self::Bool(*b),
1904
- Self::Int(v) => Self::Int(*v),
1905
- Self::Float(v) => Self::Float(*v),
1906
- Self::Builtin(b) => Self::Builtin(*b),
1907
- Self::ModuleFunction(mf) => Self::ModuleFunction(*mf),
1908
- Self::DefFunction(f) => Self::DefFunction(*f),
1909
- Self::ExtFunction(f) => Self::ExtFunction(*f),
1910
- Self::InternString(s) => Self::InternString(*s),
1911
- Self::InternBytes(b) => Self::InternBytes(*b),
1912
- Self::InternLongInt(bi) => Self::InternLongInt(*bi),
1913
- Self::Marker(m) => Self::Marker(*m),
1914
- Self::Property(p) => Self::Property(*p),
1915
- Self::ExternalFuture(call_id) => Self::ExternalFuture(*call_id),
1916
- Self::Ref(_) => panic!("Ref clones must go through clone_with_heap to maintain refcounts"),
1917
- #[cfg(feature = "ref-count-panic")]
1918
- Self::Dereferenced => panic!("Cannot copy Dereferenced object"),
1919
- }
1920
- }
1921
-
1922
- /// Mark as Dereferenced to prevent Drop panic
1923
- ///
1924
- /// This should be called from `py_dec_ref_ids` methods only
1925
- #[cfg(feature = "ref-count-panic")]
1926
- pub fn dec_ref_forget(&mut self) {
1927
- let old = std::mem::replace(self, Self::Dereferenced);
1928
- std::mem::forget(old);
1929
- }
1930
-
1931
- /// Converts the value into a keyword string representation if possible.
1932
- ///
1933
- /// Returns `Some(KeywordStr)` for `InternString` values or heap `str`
1934
- /// objects, otherwise returns `None`.
1935
- pub fn as_either_str(&self, heap: &Heap<impl ResourceTracker>) -> Option<EitherStr> {
1936
- match self {
1937
- Self::InternString(id) => Some(EitherStr::Interned(*id)),
1938
- Self::Ref(heap_id) => match heap.get(*heap_id) {
1939
- HeapData::Str(s) => Some(EitherStr::Heap(s.as_str().to_owned())),
1940
- _ => None,
1941
- },
1942
- _ => None,
1943
- }
1944
- }
1945
-
1946
- /// check if the value is a string.
1947
- pub fn is_str(&self, heap: &Heap<impl ResourceTracker>) -> bool {
1948
- match self {
1949
- Self::InternString(_) => true,
1950
- Self::Ref(heap_id) => matches!(heap.get(*heap_id), HeapData::Str(_)),
1951
- _ => false,
1952
- }
1953
- }
1954
- }
1955
-
1956
- /// Interned or heap-owned string identifier.
1957
- ///
1958
- /// Used when a string value can come from either the intern table (for known
1959
- /// static strings and keywords) or from a heap-allocated Python string object.
1960
- #[derive(Debug, Clone, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
1961
- pub(crate) enum EitherStr {
1962
- /// Interned string identifier (cheap comparisons and no allocation).
1963
- Interned(StringId),
1964
- /// Heap-owned string extracted from a `str` object.
1965
- Heap(String),
1966
- }
1967
-
1968
- impl From<StringId> for EitherStr {
1969
- fn from(id: StringId) -> Self {
1970
- Self::Interned(id)
1971
- }
1972
- }
1973
-
1974
- impl From<StaticStrings> for EitherStr {
1975
- fn from(s: StaticStrings) -> Self {
1976
- Self::Interned(s.into())
1977
- }
1978
- }
1979
-
1980
- /// Convert String to EitherStr: use Interned for known static strings,
1981
- /// otherwise use Heap for user-defined field names.
1982
- impl From<String> for EitherStr {
1983
- fn from(s: String) -> Self {
1984
- match StaticStrings::from_str(&s) {
1985
- Ok(s) => s.into(),
1986
- Err(_) => Self::Heap(s),
1987
- }
1988
- }
1989
- }
1990
-
1991
- impl EitherStr {
1992
- /// Returns the keyword as a str slice for error messages or comparisons.
1993
- pub fn as_str<'a>(&'a self, interns: &'a Interns) -> &'a str {
1994
- match self {
1995
- Self::Interned(id) => interns.get_str(*id),
1996
- Self::Heap(s) => s.as_str(),
1997
- }
1998
- }
1999
-
2000
- /// Checks whether this keyword matches the given interned identifier.
2001
- pub fn matches(&self, target: StringId, interns: &Interns) -> bool {
2002
- match self {
2003
- Self::Interned(id) => *id == target,
2004
- Self::Heap(s) => s == interns.get_str(target),
2005
- }
2006
- }
2007
-
2008
- /// Returns the `StringId` if this is an interned attribute.
2009
- #[inline]
2010
- pub fn string_id(&self) -> Option<StringId> {
2011
- match self {
2012
- Self::Interned(id) => Some(*id),
2013
- Self::Heap(_) => None,
2014
- }
2015
- }
2016
-
2017
- /// Returns the `StaticStrings` if this is an interned attribute from `StaticStrings`s.
2018
- #[inline]
2019
- pub fn static_string(&self) -> Option<StaticStrings> {
2020
- match self {
2021
- Self::Interned(id) => StaticStrings::from_string_id(*id),
2022
- Self::Heap(_) => None,
2023
- }
2024
- }
2025
-
2026
- /// Converts this `EitherStr` into an owned `String`.
2027
- ///
2028
- /// For interned strings, looks up and clones the string content.
2029
- /// For heap strings, returns the owned string directly.
2030
- pub fn into_string(self, interns: &Interns) -> String {
2031
- match self {
2032
- Self::Interned(id) => interns.get_str(id).to_owned(),
2033
- Self::Heap(s) => s,
2034
- }
2035
- }
2036
-
2037
- pub fn py_estimate_size(&self) -> usize {
2038
- match self {
2039
- Self::Interned(_) => 0,
2040
- Self::Heap(s) => s.capacity(),
2041
- }
2042
- }
2043
- }
2044
-
2045
- /// Bitwise operation type for `py_bitwise`.
2046
- #[derive(Debug, Clone, Copy)]
2047
- pub enum BitwiseOp {
2048
- And,
2049
- Or,
2050
- Xor,
2051
- LShift,
2052
- RShift,
2053
- }
2054
-
2055
- impl BitwiseOp {
2056
- /// Returns the operator symbol for error messages.
2057
- pub fn as_str(self) -> &'static str {
2058
- match self {
2059
- Self::And => "&",
2060
- Self::Or => "|",
2061
- Self::Xor => "^",
2062
- Self::LShift => "<<",
2063
- Self::RShift => ">>",
2064
- }
2065
- }
2066
- }
2067
-
2068
- /// Marker values for special objects that exist but have minimal functionality.
2069
- ///
2070
- /// These are used for:
2071
- /// - System objects like `sys.stdout` and `sys.stderr` that need to exist but don't
2072
- /// provide functionality in the sandboxed environment
2073
- /// - Typing constructs from the `typing` module that are imported for type hints but
2074
- /// don't need runtime functionality
2075
- ///
2076
- /// Wraps a `StaticStrings` variant to leverage its string conversion capabilities.
2077
- #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
2078
- pub(crate) struct Marker(pub StaticStrings);
2079
-
2080
- impl Marker {
2081
- /// Returns the Python type of this marker.
2082
- ///
2083
- /// System markers (stdout, stderr) are `TextIOWrapper`.
2084
- /// `typing.Union` has type `type` (matching CPython).
2085
- /// Other typing markers (Any, Optional, etc.) are `_SpecialForm`.
2086
- pub(crate) fn py_type(self) -> Type {
2087
- match self.0 {
2088
- StaticStrings::Stdout | StaticStrings::Stderr => Type::TextIOWrapper,
2089
- StaticStrings::UnionType => Type::Type,
2090
- _ => Type::SpecialForm,
2091
- }
2092
- }
2093
-
2094
- /// Writes the Python repr for this marker.
2095
- ///
2096
- /// System markers have special repr formats ("<stdout>", "<stderr>").
2097
- /// `typing.Union` uses `<class 'typing.Union'>` format (matching CPython).
2098
- /// Other typing markers are prefixed with "typing." (e.g., "typing.Any").
2099
- fn py_repr_fmt(self, f: &mut impl Write) -> fmt::Result {
2100
- let s: &'static str = self.0.into();
2101
- match self.0 {
2102
- StaticStrings::Stdout => f.write_str("<stdout>")?,
2103
- StaticStrings::Stderr => f.write_str("<stderr>")?,
2104
- StaticStrings::UnionType => f.write_str("<class 'typing.Union'>")?,
2105
- _ => write!(f, "typing.{s}")?,
2106
- }
2107
- Ok(())
2108
- }
2109
- }
2110
-
2111
- /// High-bit tag reserved for literal singletons (None, Ellipsis, booleans).
2112
- const SINGLETON_ID_TAG: usize = 1usize << (usize::BITS - 1);
2113
- /// High-bit tag reserved for interned string `id()` values.
2114
- const INTERN_STR_ID_TAG: usize = 1usize << (usize::BITS - 2);
2115
- /// High-bit tag reserved for interned bytes `id()` values to avoid colliding with any other space.
2116
- const INTERN_BYTES_ID_TAG: usize = 1usize << (usize::BITS - 3);
2117
- /// High-bit tag reserved for heap-backed `HeapId`s.
2118
- const HEAP_ID_TAG: usize = 1usize << (usize::BITS - 4);
2119
-
2120
- /// Mask that keeps pointer-derived bits below the bytes tag bit.
2121
- const INTERN_BYTES_ID_MASK: usize = INTERN_BYTES_ID_TAG - 1;
2122
- /// Mask that keeps pointer-derived bits below the string tag bit.
2123
- const INTERN_STR_ID_MASK: usize = INTERN_STR_ID_TAG - 1;
2124
- /// Mask that keeps per-singleton offsets below the singleton tag bit.
2125
- const SINGLETON_ID_MASK: usize = SINGLETON_ID_TAG - 1;
2126
- /// Mask that keeps heap value IDs below the heap tag bit.
2127
- const HEAP_ID_MASK: usize = HEAP_ID_TAG - 1;
2128
-
2129
- /// High-bit tag for Int value-based IDs (no heap allocation needed).
2130
- const INT_ID_TAG: usize = 1usize << (usize::BITS - 5);
2131
- /// High-bit tag for Float value-based IDs.
2132
- const FLOAT_ID_TAG: usize = 1usize << (usize::BITS - 6);
2133
- /// High-bit tag for Callable value-based IDs.
2134
- const BUILTIN_ID_TAG: usize = 1usize << (usize::BITS - 7);
2135
- /// High-bit tag for Function value-based IDs.
2136
- const FUNCTION_ID_TAG: usize = 1usize << (usize::BITS - 8);
2137
- /// High-bit tag for External Function value-based IDs.
2138
- const EXTFUNCTION_ID_TAG: usize = 1usize << (usize::BITS - 9);
2139
- /// High-bit tag for Marker value-based IDs (stdout, stderr, etc.).
2140
- const MARKER_ID_TAG: usize = 1usize << (usize::BITS - 10);
2141
- /// High-bit tag for ExternalFuture value-based IDs.
2142
- const EXTERNAL_FUTURE_ID_TAG: usize = 1usize << (usize::BITS - 11);
2143
- /// High-bit tag for ModuleFunction value-based IDs.
2144
- const MODULE_FUNCTION_ID_TAG: usize = 1usize << (usize::BITS - 12);
2145
- /// High-bit tag for interned LongInt `id()` values.
2146
- const INTERN_LONG_INT_ID_TAG: usize = 1usize << (usize::BITS - 13);
2147
- /// High-bit tag for Property value-based IDs.
2148
- const PROPERTY_ID_TAG: usize = 1usize << (usize::BITS - 14);
2149
-
2150
- /// Masks for value-based ID tags (keep bits below the tag bit).
2151
- const INT_ID_MASK: usize = INT_ID_TAG - 1;
2152
- const FLOAT_ID_MASK: usize = FLOAT_ID_TAG - 1;
2153
- const BUILTIN_ID_MASK: usize = BUILTIN_ID_TAG - 1;
2154
- const FUNCTION_ID_MASK: usize = FUNCTION_ID_TAG - 1;
2155
- const EXTFUNCTION_ID_MASK: usize = EXTFUNCTION_ID_TAG - 1;
2156
- const MARKER_ID_MASK: usize = MARKER_ID_TAG - 1;
2157
- const EXTERNAL_FUTURE_ID_MASK: usize = EXTERNAL_FUTURE_ID_TAG - 1;
2158
- const MODULE_FUNCTION_ID_MASK: usize = MODULE_FUNCTION_ID_TAG - 1;
2159
- const INTERN_LONG_INT_ID_MASK: usize = INTERN_LONG_INT_ID_TAG - 1;
2160
- const PROPERTY_ID_MASK: usize = PROPERTY_ID_TAG - 1;
2161
-
2162
- /// Enumerates singleton literal slots so we can issue stable `id()` values without heap allocation.
2163
- #[repr(usize)]
2164
- #[derive(Copy, Clone)]
2165
- enum SingletonSlot {
2166
- Undefined = 0,
2167
- Ellipsis = 1,
2168
- None = 2,
2169
- False = 3,
2170
- True = 4,
2171
- }
2172
-
2173
- /// Returns the fully tagged `id()` value for the requested singleton literal.
2174
- #[inline]
2175
- const fn singleton_id(slot: SingletonSlot) -> usize {
2176
- SINGLETON_ID_TAG | ((slot as usize) & SINGLETON_ID_MASK)
2177
- }
2178
-
2179
- /// Computes Python-style floor division and modulo.
2180
- ///
2181
- /// Python's division rounds toward negative infinity (floor division),
2182
- /// and the remainder has the same sign as the divisor.
2183
- /// This differs from Rust's truncating division.
2184
- ///
2185
- /// Returns `None` on overflow (i64::MIN / -1 doesn't fit in i64).
2186
- pub(crate) fn floor_divmod(a: i64, b: i64) -> Option<(i64, i64)> {
2187
- let quot = a.checked_div(b)?;
2188
- let rem = a.checked_rem(b)?;
2189
-
2190
- if rem != 0 && (rem < 0) != (b < 0) {
2191
- Some((quot - 1, rem + b))
2192
- } else {
2193
- Some((quot, rem))
2194
- }
2195
- }
2196
-
2197
- /// Converts a heap `HeapId` into its tagged `id()` value, ensuring it never collides with other spaces.
2198
- #[inline]
2199
- pub fn heap_tagged_id(heap_id: HeapId) -> usize {
2200
- HEAP_ID_TAG | (heap_id.index() & HEAP_ID_MASK)
2201
- }
2202
-
2203
- /// Computes a deterministic ID for an i64 integer value.
2204
- /// Uses the value's hash combined with a type tag to ensure uniqueness across types.
2205
- #[inline]
2206
- fn int_value_id(value: i64) -> usize {
2207
- let mut hasher = DefaultHasher::new();
2208
- value.hash(&mut hasher);
2209
- let hash_u64 = hasher.finish();
2210
- // Mask to usize range before conversion to handle 32-bit platforms
2211
- let masked = hash_u64 & (usize::MAX as u64);
2212
- let hash_usize = usize::try_from(masked).expect("masked value fits in usize");
2213
- INT_ID_TAG | (hash_usize & INT_ID_MASK)
2214
- }
2215
-
2216
- /// Computes a deterministic ID for an f64 float value.
2217
- /// Uses the bit representation's hash for consistency (handles NaN, infinities, etc.).
2218
- #[inline]
2219
- fn float_value_id(value: f64) -> usize {
2220
- let mut hasher = DefaultHasher::new();
2221
- value.to_bits().hash(&mut hasher);
2222
- let hash_u64 = hasher.finish();
2223
- // Mask to usize range before conversion to handle 32-bit platforms
2224
- let masked = hash_u64 & (usize::MAX as u64);
2225
- let hash_usize = usize::try_from(masked).expect("masked value fits in usize");
2226
- FLOAT_ID_TAG | (hash_usize & FLOAT_ID_MASK)
2227
- }
2228
-
2229
- /// Computes a deterministic ID for a builtin based on its discriminant.
2230
- #[inline]
2231
- fn builtin_value_id(b: Builtins) -> usize {
2232
- let mut hasher = DefaultHasher::new();
2233
- b.hash(&mut hasher);
2234
- let hash_u64 = hasher.finish();
2235
- // wrapping here is fine
2236
- #[expect(clippy::cast_possible_truncation)]
2237
- let hash_usize = hash_u64 as usize;
2238
- BUILTIN_ID_TAG | (hash_usize & BUILTIN_ID_MASK)
2239
- }
2240
-
2241
- /// Computes a deterministic ID for a function based on its id.
2242
- #[inline]
2243
- fn function_value_id(f_id: FunctionId) -> usize {
2244
- FUNCTION_ID_TAG | (f_id.index() & FUNCTION_ID_MASK)
2245
- }
2246
-
2247
- /// Computes a deterministic ID for an external function based on its interned name.
2248
- #[inline]
2249
- fn ext_function_value_id(name_id: StringId) -> usize {
2250
- EXTFUNCTION_ID_TAG | (name_id.index() & EXTFUNCTION_ID_MASK)
2251
- }
2252
-
2253
- /// Computes a deterministic ID for a marker value based on its discriminant.
2254
- #[inline]
2255
- fn marker_value_id(m: Marker) -> usize {
2256
- MARKER_ID_TAG | ((m.0 as usize) & MARKER_ID_MASK)
2257
- }
2258
-
2259
- /// Computes a deterministic ID for a property value based on its discriminant.
2260
- #[inline]
2261
- fn property_value_id(p: Property) -> usize {
2262
- let discriminant = match p {
2263
- Property::Os(os_fn) => os_fn as usize,
2264
- };
2265
- PROPERTY_ID_TAG | (discriminant & PROPERTY_ID_MASK)
2266
- }
2267
-
2268
- /// Computes a deterministic ID for an external future based on its call ID.
2269
- #[inline]
2270
- fn external_future_value_id(call_id: CallId) -> usize {
2271
- EXTERNAL_FUTURE_ID_TAG | ((call_id.raw() as usize) & EXTERNAL_FUTURE_ID_MASK)
2272
- }
2273
-
2274
- /// Computes a deterministic ID for a module function based on its discriminant.
2275
- #[inline]
2276
- fn module_function_value_id(mf: ModuleFunctions) -> usize {
2277
- let mut hasher = DefaultHasher::new();
2278
- mf.hash(&mut hasher);
2279
- let hash_u64 = hasher.finish();
2280
- // wrapping here is fine
2281
- #[expect(clippy::cast_possible_truncation)]
2282
- let hash_usize = hash_u64 as usize;
2283
- MODULE_FUNCTION_ID_TAG | (hash_usize & MODULE_FUNCTION_ID_MASK)
2284
- }
2285
-
2286
- /// Converts an i64 repeat count to usize, handling negative values and overflow.
2287
- ///
2288
- /// Returns 0 for negative values (Python treats negative repeat counts as 0).
2289
- /// Returns `OverflowError` if the value exceeds `usize::MAX`.
2290
- #[inline]
2291
- fn i64_to_repeat_count(n: i64) -> RunResult<usize> {
2292
- if n <= 0 {
2293
- Ok(0)
2294
- } else {
2295
- usize::try_from(n).map_err(|_| ExcType::overflow_repeat_count().into())
2296
- }
2297
- }
2298
-
2299
- /// Converts a LongInt repeat count to usize, handling negative values and overflow.
2300
- ///
2301
- /// Returns 0 for negative values (Python treats negative repeat counts as 0).
2302
- /// Returns `OverflowError` if the value exceeds `usize::MAX`.
2303
- #[inline]
2304
- fn longint_to_repeat_count(li: &LongInt) -> RunResult<usize> {
2305
- if li.is_negative() {
2306
- Ok(0)
2307
- } else if let Some(count) = li.to_usize() {
2308
- Ok(count)
2309
- } else {
2310
- Err(ExcType::overflow_repeat_count().into())
2311
- }
2312
- }
2313
-
2314
- /// Extracts a BigInt from a Value for bitwise operations.
2315
- ///
2316
- /// Returns `Some(BigInt)` for Int, Bool, and LongInt values.
2317
- /// Returns `None` for other types (Float, Str, etc.).
2318
- fn extract_bigint(value: &Value, heap: &Heap<impl ResourceTracker>) -> Option<BigInt> {
2319
- match value {
2320
- Value::Int(i) => Some(BigInt::from(*i)),
2321
- Value::Bool(b) => Some(BigInt::from(i64::from(*b))),
2322
- Value::Ref(id) => {
2323
- if let HeapData::LongInt(li) = heap.get(*id) {
2324
- Some(li.inner().clone())
2325
- } else {
2326
- None
2327
- }
2328
- }
2329
- _ => None,
2330
- }
2331
- }
2332
-
2333
- /// Extracts and clones the `(key, value)` probe accepted by `dict_items.__contains__`.
2334
- ///
2335
- /// CPython treats only 2-tuples as valid probes for items-view membership. Monty
2336
- /// also accepts namedtuples of length two so tuple-like runtime values behave
2337
- /// sensibly even though namedtuples are not modeled as a true tuple subclass.
2338
- fn cloned_items_view_candidate(item: &Value, heap: &impl ContainsHeap) -> Option<(Value, Value)> {
2339
- let Value::Ref(heap_id) = item else {
2340
- return None;
2341
- };
2342
-
2343
- match heap.heap().get(*heap_id) {
2344
- HeapData::Tuple(tuple) => {
2345
- let items = tuple.as_slice();
2346
- if items.len() == 2 {
2347
- Some((items[0].clone_with_heap(heap), items[1].clone_with_heap(heap)))
2348
- } else {
2349
- None
2350
- }
2351
- }
2352
- HeapData::NamedTuple(namedtuple) => {
2353
- let items = namedtuple.as_vec();
2354
- if items.len() == 2 {
2355
- Some((items[0].clone_with_heap(heap), items[1].clone_with_heap(heap)))
2356
- } else {
2357
- None
2358
- }
2359
- }
2360
- _ => None,
2361
- }
2362
- }
2363
-
2364
- /// Helper for substring containment check in strings.
2365
- ///
2366
- /// Called by `py_contains` when the container is a string.
2367
- /// The item must also be a string (either interned or heap-allocated).
2368
- fn str_contains(
2369
- container_str: &str,
2370
- item: &Value,
2371
- heap: &mut Heap<impl ResourceTracker>,
2372
- interns: &Interns,
2373
- ) -> RunResult<bool> {
2374
- match item {
2375
- Value::InternString(item_id) => {
2376
- let item_str = interns.get_str(*item_id);
2377
- Ok(container_str.contains(item_str))
2378
- }
2379
- Value::Ref(item_heap_id) => {
2380
- if let HeapData::Str(item_str) = heap.get(*item_heap_id) {
2381
- Ok(container_str.contains(item_str.as_str()))
2382
- } else {
2383
- Err(ExcType::type_error("'in <str>' requires string as left operand"))
2384
- }
2385
- }
2386
- _ => Err(ExcType::type_error("'in <str>' requires string as left operand")),
2387
- }
2388
- }
2389
-
2390
- /// Computes the number of significant bits in an i64.
2391
- ///
2392
- /// Returns 0 for 0, otherwise returns ceil(log2(|value|)) + 1 (accounting for sign).
2393
- /// For example: 0 -> 0, 1 -> 1, 2 -> 2, 255 -> 8, 256 -> 9.
2394
- fn i64_bits(value: i64) -> u64 {
2395
- if value == 0 {
2396
- 0
2397
- } else {
2398
- // For negative numbers, use unsigned_abs to get magnitude
2399
- u64::from(64 - value.unsigned_abs().leading_zeros())
2400
- }
2401
- }
2402
-
2403
- /// Computes BigInt exponentiation for exponents larger than u32::MAX.
2404
- ///
2405
- /// Uses repeated squaring for efficiency. This is needed when the exponent
2406
- /// doesn't fit in a u32, which is required by the `num-bigint` pow method.
2407
- fn bigint_pow(base: BigInt, exp: u64) -> BigInt {
2408
- if exp == 0 {
2409
- return BigInt::from(1);
2410
- }
2411
- if exp == 1 {
2412
- return base;
2413
- }
2414
-
2415
- // Use repeated squaring
2416
- let mut result = BigInt::from(1);
2417
- let mut b = base;
2418
- let mut e = exp;
2419
-
2420
- while e > 0 {
2421
- if e & 1 == 1 {
2422
- result *= &b;
2423
- }
2424
- b = &b * &b;
2425
- e >>= 1;
2426
- }
2427
-
2428
- result
2429
- }
2430
-
2431
- #[cfg(test)]
2432
- mod tests {
2433
- use num_bigint::BigInt;
2434
-
2435
- use super::*;
2436
- use crate::resource::NoLimitTracker;
2437
-
2438
- /// Creates a heap and directly allocates a LongInt with the given BigInt value.
2439
- ///
2440
- /// This bypasses `LongInt::into_value()` which would demote i64-fitting values.
2441
- /// Used to test defensive code paths that handle LongInt-as-index scenarios.
2442
- fn create_heap_with_longint(value: BigInt) -> (Heap<NoLimitTracker>, HeapId) {
2443
- let mut heap = Heap::new(16, NoLimitTracker);
2444
- let long_int = LongInt::new(value);
2445
- let heap_id = heap.allocate(HeapData::LongInt(long_int)).unwrap();
2446
- (heap, heap_id)
2447
- }
2448
-
2449
- /// Tests that `as_index()` correctly handles a LongInt containing an i64-fitting value.
2450
- ///
2451
- /// This tests a defensive code path that's normally unreachable because
2452
- /// `LongInt::into_value()` demotes i64-fitting values to `Value::Int`.
2453
- /// However, this path could be reached via deserialization of crafted data.
2454
- #[test]
2455
- fn as_index_longint_fits_in_i64() {
2456
- let (mut heap, heap_id) = create_heap_with_longint(BigInt::from(42));
2457
- let value = Value::Ref(heap_id);
2458
-
2459
- let result = value.as_index(&heap, Type::List);
2460
- assert_eq!(result.unwrap(), 42);
2461
- value.drop_with_heap(&mut heap);
2462
- }
2463
-
2464
- /// Tests that `as_index()` correctly handles a negative LongInt that fits in i64.
2465
- #[test]
2466
- fn as_index_longint_negative_fits_in_i64() {
2467
- let (mut heap, heap_id) = create_heap_with_longint(BigInt::from(-100));
2468
- let value = Value::Ref(heap_id);
2469
-
2470
- let result = value.as_index(&heap, Type::List);
2471
- assert_eq!(result.unwrap(), -100);
2472
- value.drop_with_heap(&mut heap);
2473
- }
2474
-
2475
- /// Tests that `as_index()` returns IndexError for LongInt values too large for i64.
2476
- #[test]
2477
- fn as_index_longint_too_large() {
2478
- // 2^100 is way larger than i64::MAX
2479
- let big_value = BigInt::from(2).pow(100);
2480
- let (mut heap, heap_id) = create_heap_with_longint(big_value);
2481
- let value = Value::Ref(heap_id);
2482
-
2483
- let result = value.as_index(&heap, Type::List);
2484
- assert!(result.is_err());
2485
- value.drop_with_heap(&mut heap);
2486
- }
2487
-
2488
- /// Tests that `as_int()` correctly handles a LongInt containing an i64-fitting value.
2489
- ///
2490
- /// Similar to `as_index`, this tests a defensive code path normally unreachable.
2491
- #[test]
2492
- fn as_int_longint_fits_in_i64() {
2493
- let (mut heap, heap_id) = create_heap_with_longint(BigInt::from(12345));
2494
- let value = Value::Ref(heap_id);
2495
-
2496
- let result = value.as_int(&heap);
2497
- assert_eq!(result.unwrap(), 12345);
2498
- value.drop_with_heap(&mut heap);
2499
- }
2500
-
2501
- /// Tests that `as_int()` returns an error for LongInt values too large for i64.
2502
- #[test]
2503
- fn as_int_longint_too_large() {
2504
- let big_value = BigInt::from(2).pow(100);
2505
- let (mut heap, heap_id) = create_heap_with_longint(big_value);
2506
- let value = Value::Ref(heap_id);
2507
-
2508
- let result = value.as_int(&heap);
2509
- assert!(result.is_err());
2510
- value.drop_with_heap(&mut heap);
2511
- }
2512
-
2513
- /// Tests boundary values: i64::MAX as a LongInt.
2514
- #[test]
2515
- fn as_index_longint_at_i64_max() {
2516
- let (mut heap, heap_id) = create_heap_with_longint(BigInt::from(i64::MAX));
2517
- let value = Value::Ref(heap_id);
2518
-
2519
- let result = value.as_index(&heap, Type::List);
2520
- assert_eq!(result.unwrap(), i64::MAX);
2521
- value.drop_with_heap(&mut heap);
2522
- }
2523
-
2524
- /// Tests boundary values: i64::MIN as a LongInt.
2525
- #[test]
2526
- fn as_index_longint_at_i64_min() {
2527
- let (mut heap, heap_id) = create_heap_with_longint(BigInt::from(i64::MIN));
2528
- let value = Value::Ref(heap_id);
2529
-
2530
- let result = value.as_index(&heap, Type::List);
2531
- assert_eq!(result.unwrap(), i64::MIN);
2532
- value.drop_with_heap(&mut heap);
2533
- }
2534
-
2535
- /// Tests boundary values: i64::MAX + 1 as a LongInt (should fail).
2536
- #[test]
2537
- fn as_index_longint_just_over_i64_max() {
2538
- let big_value = BigInt::from(i64::MAX) + BigInt::from(1);
2539
- let (mut heap, heap_id) = create_heap_with_longint(big_value);
2540
- let value = Value::Ref(heap_id);
2541
-
2542
- let result = value.as_index(&heap, Type::List);
2543
- assert!(result.is_err());
2544
- value.drop_with_heap(&mut heap);
2545
- }
2546
-
2547
- /// Tests boundary values: i64::MIN - 1 as a LongInt (should fail).
2548
- #[test]
2549
- fn as_index_longint_just_under_i64_min() {
2550
- let big_value = BigInt::from(i64::MIN) - BigInt::from(1);
2551
- let (mut heap, heap_id) = create_heap_with_longint(big_value);
2552
- let value = Value::Ref(heap_id);
2553
-
2554
- let result = value.as_index(&heap, Type::List);
2555
- assert!(result.is_err());
2556
- value.drop_with_heap(&mut heap);
2557
- }
2558
- }