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,2051 +0,0 @@
1
- /// Python string type, wrapping a Rust `String`.
2
- ///
3
- /// This type provides Python string semantics. Currently supports basic
4
- /// operations like length and equality comparison.
5
- use std::{borrow::Cow, fmt};
6
- use std::{cmp::Ordering, fmt::Write};
7
-
8
- use ahash::AHashSet;
9
- use smallvec::smallvec;
10
-
11
- use super::{Bytes, MontyIter, PyTrait};
12
- use crate::{
13
- args::ArgValues,
14
- bytecode::{CallResult, VM},
15
- defer_drop, defer_drop_mut,
16
- exception_private::{ExcType, RunResult},
17
- heap::{DropWithHeap, Heap, HeapData, HeapGuard, HeapId},
18
- intern::{StaticStrings, StringId},
19
- resource::{ResourceError, ResourceTracker, check_repeat_size, check_replace_size},
20
- types::Type,
21
- value::{EitherStr, Value},
22
- };
23
-
24
- /// Python string value stored on the heap.
25
- ///
26
- /// Wraps a Rust `String` and provides Python-compatible operations.
27
- /// `len()` returns the number of Unicode codepoints (characters), matching Python semantics.
28
- #[derive(Debug, Clone, PartialEq, Default, serde::Serialize, serde::Deserialize)]
29
- pub(crate) struct Str(Box<str>);
30
-
31
- impl Str {
32
- /// Creates a new Str from a Rust String.
33
- #[must_use]
34
- pub fn new(s: String) -> Self {
35
- Self(s.into())
36
- }
37
-
38
- /// Returns a reference to the inner string.
39
- #[must_use]
40
- pub fn as_str(&self) -> &str {
41
- &self.0
42
- }
43
-
44
- /// Creates a string from the `str()` constructor call.
45
- ///
46
- /// - `str()` with no args returns an empty string
47
- /// - `str(x)` converts x to its string representation using `py_str`
48
- pub fn init(vm: &mut VM<'_, '_, impl ResourceTracker>, args: ArgValues) -> RunResult<Value> {
49
- let value = args.get_zero_one_arg("str", vm.heap)?;
50
- match value {
51
- None => Ok(Value::InternString(StaticStrings::EmptyString.into())),
52
- Some(v) => {
53
- defer_drop!(v, vm);
54
- let s = v.py_str(vm).into_owned();
55
- allocate_string(s, vm.heap)
56
- }
57
- }
58
- }
59
-
60
- /// Handles slice-based indexing for strings.
61
- ///
62
- /// Returns a new string containing the selected characters (Unicode-aware).
63
- fn getitem_slice(&self, slice: &crate::types::Slice, heap: &mut Heap<impl ResourceTracker>) -> RunResult<Value> {
64
- let char_count = self.0.chars().count();
65
- let (start, stop, step) = slice
66
- .indices(char_count)
67
- .map_err(|()| ExcType::value_error_slice_step_zero())?;
68
-
69
- let result_str = get_str_slice(&self.0, start, stop, step);
70
- let heap_id = heap.allocate(HeapData::Str(Self::from(result_str)))?;
71
- Ok(Value::Ref(heap_id))
72
- }
73
- }
74
-
75
- impl From<String> for Str {
76
- fn from(s: String) -> Self {
77
- Self(s.into())
78
- }
79
- }
80
-
81
- impl From<&str> for Str {
82
- fn from(s: &str) -> Self {
83
- Self(s.into())
84
- }
85
- }
86
-
87
- impl From<Str> for String {
88
- fn from(value: Str) -> Self {
89
- value.0.into_string()
90
- }
91
- }
92
-
93
- /// Allocates a string, using interned versions when possible.
94
- ///
95
- /// Optimizations:
96
- /// - Empty strings return the pre-interned `StaticStrings::EmptyString`
97
- /// - Single ASCII characters return pre-interned ASCII strings
98
- /// - Other strings are allocated on the heap
99
- ///
100
- /// This avoids heap allocation for common cases like results from `strip()`,
101
- /// `split()`, string iteration, etc.
102
- pub fn allocate_string(s: String, heap: &mut Heap<impl ResourceTracker>) -> RunResult<Value> {
103
- match s.len() {
104
- 0 => Ok(Value::InternString(StaticStrings::EmptyString.into())),
105
- 1 => {
106
- // Single byte means single ASCII character
107
- let byte = s.as_bytes()[0];
108
- Ok(Value::InternString(StringId::from_ascii(byte)))
109
- }
110
- _ => {
111
- let heap_id = heap.allocate(HeapData::Str(Str::new(s)))?;
112
- Ok(Value::Ref(heap_id))
113
- }
114
- }
115
- }
116
-
117
- /// Allocates a single character as a string value.
118
- ///
119
- /// ASCII characters use pre-interned strings for efficiency.
120
- /// Non-ASCII characters are allocated on the heap.
121
- ///
122
- /// This is used by string iteration and `chr()` builtin.
123
- pub fn allocate_char(c: char, heap: &mut Heap<impl ResourceTracker>) -> Result<Value, ResourceError> {
124
- if c.is_ascii() {
125
- Ok(Value::InternString(StringId::from_ascii(c as u8)))
126
- } else {
127
- let heap_id = heap.allocate(HeapData::Str(Str::new(c.to_string())))?;
128
- Ok(Value::Ref(heap_id))
129
- }
130
- }
131
-
132
- /// Gets the character at a given index in a string, handling negative indices.
133
- ///
134
- /// Returns `None` if the index is out of bounds. This uses a single-pass scan
135
- /// to avoid allocating a `Vec<char>`.
136
- ///
137
- /// Negative indices count from the end: -1 is the last character.
138
- pub fn get_char_at_index(s: &str, index: i64) -> Option<char> {
139
- let char_count = s.chars().count();
140
- let len = i64::try_from(char_count).ok()?;
141
- let normalized = if index < 0 { index + len } else { index };
142
-
143
- if normalized < 0 || normalized >= len {
144
- return None;
145
- }
146
-
147
- let idx = usize::try_from(normalized).ok()?;
148
- s.chars().nth(idx)
149
- }
150
-
151
- /// Extracts a slice of a string (Unicode-aware).
152
- ///
153
- /// Handles both positive and negative step values. For negative step,
154
- /// iterates backward from start down to (but not including) stop.
155
- /// The `stop` parameter uses a sentinel value of `len + 1` for negative
156
- /// step to indicate "go to the beginning".
157
- ///
158
- /// Note: step must be non-zero (callers should validate this via `slice.indices()`).
159
- pub(crate) fn get_str_slice(s: &str, start: usize, stop: usize, step: i64) -> String {
160
- let chars: Vec<char> = s.chars().collect();
161
- let mut result = String::new();
162
-
163
- // try_from succeeds for non-negative step; step==0 rejected upstream by slice.indices()
164
- if let Ok(step_usize) = usize::try_from(step) {
165
- // Positive step: iterate forward
166
- let mut i = start;
167
- while i < stop && i < chars.len() {
168
- result.push(chars[i]);
169
- i += step_usize;
170
- }
171
- } else {
172
- // Negative step: iterate backward
173
- // start is the highest index, stop is the sentinel
174
- // stop > chars.len() means "go to the beginning"
175
- let step_abs = usize::try_from(-step).expect("step is negative so -step is positive");
176
- let step_abs_i64 = i64::try_from(step_abs).expect("step magnitude fits in i64");
177
- let mut i = i64::try_from(start).expect("start index fits in i64");
178
- // stop > chars.len() is sentinel meaning "go to beginning", use -1
179
- let stop_i64 = if stop > chars.len() {
180
- -1
181
- } else {
182
- i64::try_from(stop).expect("stop bounded by chars.len() fits in i64")
183
- };
184
-
185
- while let Ok(i_usize) = usize::try_from(i) {
186
- if i_usize >= chars.len() || i <= stop_i64 {
187
- break;
188
- }
189
- result.push(chars[i_usize]);
190
- i -= step_abs_i64;
191
- }
192
- }
193
-
194
- result
195
- }
196
-
197
- impl std::ops::Deref for Str {
198
- type Target = str;
199
-
200
- fn deref(&self) -> &Self::Target {
201
- &self.0
202
- }
203
- }
204
-
205
- impl PyTrait for Str {
206
- fn py_type(&self, _heap: &Heap<impl ResourceTracker>) -> Type {
207
- Type::Str
208
- }
209
-
210
- fn py_estimate_size(&self) -> usize {
211
- std::mem::size_of::<Self>() + self.0.len()
212
- }
213
-
214
- fn py_len(&self, _vm: &VM<'_, '_, impl ResourceTracker>) -> Option<usize> {
215
- // Count Unicode characters, not bytes, to match Python semantics
216
- Some(self.0.chars().count())
217
- }
218
-
219
- fn py_getitem(&self, key: &Value, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
220
- let heap = &mut *vm.heap;
221
- // Check for slice first (Value::Ref pointing to HeapData::Slice)
222
- if let Value::Ref(id) = key
223
- && let HeapData::Slice(slice) = heap.get(*id)
224
- {
225
- // Clone the slice to release the borrow on heap before calling getitem_slice
226
- let slice = slice.clone();
227
- return self.getitem_slice(&slice, heap);
228
- }
229
-
230
- // Extract integer index, accepting Int, Bool (True=1, False=0), and LongInt
231
- let index = key.as_index(heap, Type::Str)?;
232
-
233
- // Use single-pass indexing to avoid Vec<char> allocation
234
- let c = get_char_at_index(&self.0, index).ok_or_else(ExcType::str_index_error)?;
235
- Ok(allocate_char(c, heap)?)
236
- }
237
-
238
- fn py_eq(&self, other: &Self, _vm: &mut VM<'_, '_, impl ResourceTracker>) -> Result<bool, ResourceError> {
239
- Ok(self.0 == other.0)
240
- }
241
-
242
- /// Interns don't contain nested heap references.
243
- fn py_dec_ref_ids(&mut self, _stack: &mut Vec<HeapId>) {
244
- // No-op: strings don't hold Value references
245
- }
246
-
247
- fn py_bool(&self, _vm: &VM<'_, '_, impl ResourceTracker>) -> bool {
248
- !self.0.is_empty()
249
- }
250
-
251
- fn py_cmp(
252
- &self,
253
- other: &Self,
254
- _vm: &mut VM<'_, '_, impl ResourceTracker>,
255
- ) -> Result<Option<Ordering>, ResourceError> {
256
- Ok(Some(self.0.cmp(&other.0)))
257
- }
258
-
259
- fn py_repr_fmt(
260
- &self,
261
- f: &mut impl Write,
262
- _vm: &VM<'_, '_, impl ResourceTracker>,
263
- _heap_ids: &mut AHashSet<HeapId>,
264
- ) -> fmt::Result {
265
- string_repr_fmt(&self.0, f)
266
- }
267
-
268
- fn py_str(&self, _vm: &VM<'_, '_, impl ResourceTracker>) -> Cow<'static, str> {
269
- self.0.clone().into_string().into()
270
- }
271
-
272
- fn py_add(
273
- &self,
274
- other: &Self,
275
- vm: &mut VM<'_, '_, impl ResourceTracker>,
276
- ) -> Result<Option<Value>, crate::resource::ResourceError> {
277
- let result = format!("{}{}", self.0, other.0);
278
- let id = vm.heap.allocate(HeapData::Str(result.into()))?;
279
- Ok(Some(Value::Ref(id)))
280
- }
281
-
282
- fn py_call_attr(
283
- &mut self,
284
- _self_id: HeapId,
285
- vm: &mut VM<'_, '_, impl ResourceTracker>,
286
- attr: &EitherStr,
287
- args: ArgValues,
288
- ) -> RunResult<CallResult> {
289
- let args_guard = HeapGuard::new(args, vm.heap);
290
- let Some(method) = attr.static_string() else {
291
- return Err(ExcType::attribute_error(Type::Str, attr.as_str(vm.interns)));
292
- };
293
-
294
- let args = args_guard.into_inner();
295
- call_str_method_impl(&self.0, method, args, vm).map(CallResult::Value)
296
- }
297
- }
298
-
299
- /// Dispatches a method call on a string value by method name.
300
- ///
301
- /// This is the entry point for string method calls from the VM on interned strings.
302
- /// Converts the `StringId` to `StaticStrings` and delegates to `call_str_method_impl`.
303
- pub fn call_str_method(
304
- s: &str,
305
- method_id: StringId,
306
- args: ArgValues,
307
- vm: &mut VM<'_, '_, impl ResourceTracker>,
308
- ) -> RunResult<Value> {
309
- let args_guard = HeapGuard::new(args, vm.heap);
310
- let Some(method) = StaticStrings::from_string_id(method_id) else {
311
- return Err(ExcType::attribute_error(Type::Str, vm.interns.get_str(method_id)));
312
- };
313
- let args = args_guard.into_inner();
314
- call_str_method_impl(s, method, args, vm)
315
- }
316
-
317
- /// Dispatches a method call on a string value.
318
- ///
319
- /// This is the unified implementation for string method calls, used by both:
320
- /// - `Str::py_call_attr()` for heap-allocated strings
321
- /// - `call_str_method()` for interned string literals from the VM
322
- ///
323
- /// # Not Yet Implemented
324
- ///
325
- /// The following Python string methods are not yet implemented:
326
- ///
327
- /// - `format()` - Requires implementing the format spec mini-language (PEP 3101),
328
- /// which is complex and involves parsing format specifications like `{:>10.2f}`.
329
- /// - `format_map(mapping)` - Similar to `format()` but takes a mapping; depends on
330
- /// `format()` implementation.
331
- /// - `maketrans()` / `translate()` - Character translation tables; moderate complexity,
332
- /// requires building and applying Unicode translation maps.
333
- /// - `expandtabs(tabsize=8)` - Tab expansion; simple but rarely used in practice.
334
- /// - `isprintable()` - Checks if all characters are printable; requires accurate Unicode
335
- /// category data for the "printable" property.
336
- fn call_str_method_impl(
337
- s: &str,
338
- method: StaticStrings,
339
- args: ArgValues,
340
- vm: &mut VM<'_, '_, impl ResourceTracker>,
341
- ) -> RunResult<Value> {
342
- match method {
343
- // Simple transformations (no arguments)
344
- StaticStrings::Lower => {
345
- args.check_zero_args("str.lower", vm.heap)?;
346
- str_lower(s, vm.heap)
347
- }
348
- StaticStrings::Upper => {
349
- args.check_zero_args("str.upper", vm.heap)?;
350
- str_upper(s, vm.heap)
351
- }
352
- StaticStrings::Capitalize => {
353
- args.check_zero_args("str.capitalize", vm.heap)?;
354
- str_capitalize(s, vm.heap)
355
- }
356
- StaticStrings::Title => {
357
- args.check_zero_args("str.title", vm.heap)?;
358
- str_title(s, vm.heap)
359
- }
360
- StaticStrings::Swapcase => {
361
- args.check_zero_args("str.swapcase", vm.heap)?;
362
- str_swapcase(s, vm.heap)
363
- }
364
- StaticStrings::Casefold => {
365
- args.check_zero_args("str.casefold", vm.heap)?;
366
- str_casefold(s, vm.heap)
367
- }
368
- // Predicate methods (no arguments, return bool)
369
- StaticStrings::Isalpha => {
370
- args.check_zero_args("str.isalpha", vm.heap)?;
371
- Ok(Value::Bool(str_isalpha(s)))
372
- }
373
- StaticStrings::Isdigit => {
374
- args.check_zero_args("str.isdigit", vm.heap)?;
375
- Ok(Value::Bool(str_isdigit(s)))
376
- }
377
- StaticStrings::Isalnum => {
378
- args.check_zero_args("str.isalnum", vm.heap)?;
379
- Ok(Value::Bool(str_isalnum(s)))
380
- }
381
- StaticStrings::Isnumeric => {
382
- args.check_zero_args("str.isnumeric", vm.heap)?;
383
- Ok(Value::Bool(str_isnumeric(s)))
384
- }
385
- StaticStrings::Isspace => {
386
- args.check_zero_args("str.isspace", vm.heap)?;
387
- Ok(Value::Bool(str_isspace(s)))
388
- }
389
- StaticStrings::Islower => {
390
- args.check_zero_args("str.islower", vm.heap)?;
391
- Ok(Value::Bool(str_islower(s)))
392
- }
393
- StaticStrings::Isupper => {
394
- args.check_zero_args("str.isupper", vm.heap)?;
395
- Ok(Value::Bool(str_isupper(s)))
396
- }
397
- StaticStrings::Isascii => {
398
- args.check_zero_args("str.isascii", vm.heap)?;
399
- Ok(Value::Bool(s.is_ascii()))
400
- }
401
- StaticStrings::Isdecimal => {
402
- args.check_zero_args("str.isdecimal", vm.heap)?;
403
- Ok(Value::Bool(str_isdecimal(s)))
404
- }
405
- // Search methods
406
- StaticStrings::Find => str_find(s, args, vm),
407
- StaticStrings::Rfind => str_rfind(s, args, vm),
408
- StaticStrings::Index => str_index(s, args, vm),
409
- StaticStrings::Rindex => str_rindex(s, args, vm),
410
- StaticStrings::Count => str_count(s, args, vm),
411
- StaticStrings::Startswith => str_startswith(s, args, vm),
412
- StaticStrings::Endswith => str_endswith(s, args, vm),
413
- // Strip/trim methods
414
- StaticStrings::Strip => str_strip(s, args, vm),
415
- StaticStrings::Lstrip => str_lstrip(s, args, vm),
416
- StaticStrings::Rstrip => str_rstrip(s, args, vm),
417
- StaticStrings::Removeprefix => str_removeprefix(s, args, vm),
418
- StaticStrings::Removesuffix => str_removesuffix(s, args, vm),
419
- // Split methods
420
- StaticStrings::Split => str_split(s, args, vm),
421
- StaticStrings::Rsplit => str_rsplit(s, args, vm),
422
- StaticStrings::Splitlines => str_splitlines(s, args, vm),
423
- StaticStrings::Partition => str_partition(s, args, vm),
424
- StaticStrings::Rpartition => str_rpartition(s, args, vm),
425
- // Replace/modify methods
426
- StaticStrings::Replace => str_replace(s, args, vm),
427
- StaticStrings::Center => str_center(s, args, vm),
428
- StaticStrings::Ljust => str_ljust(s, args, vm),
429
- StaticStrings::Rjust => str_rjust(s, args, vm),
430
- StaticStrings::Zfill => str_zfill(s, args, vm.heap),
431
- // Additional methods
432
- StaticStrings::Encode => str_encode(s, args, vm),
433
- StaticStrings::Isidentifier => {
434
- args.check_zero_args("str.isidentifier", vm.heap)?;
435
- Ok(Value::Bool(str_isidentifier(s)))
436
- }
437
- StaticStrings::Istitle => {
438
- args.check_zero_args("str.istitle", vm.heap)?;
439
- Ok(Value::Bool(str_istitle(s)))
440
- }
441
- // Existing method
442
- StaticStrings::Join => {
443
- let iterable = args.get_one_arg("str.join", vm.heap)?;
444
- str_join(s, iterable, vm)
445
- }
446
- _ => {
447
- args.drop_with_heap(vm.heap);
448
- Err(ExcType::attribute_error(Type::Str, method.into()))
449
- }
450
- }
451
- }
452
-
453
- /// Implements Python's `str.join(iterable)` method.
454
- ///
455
- /// Joins elements of the iterable with the separator string, returning
456
- /// a new heap-allocated string. Each element must be a string.
457
- ///
458
- /// # Arguments
459
- /// * `separator` - The separator string (e.g., "," for comma-separated)
460
- /// * `iterable` - The iterable containing string elements to join
461
- /// * `heap` - The heap for allocation and reference counting
462
- /// * `interns` - The interns table for resolving interned strings
463
- ///
464
- /// # Errors
465
- /// Returns `TypeError` if the argument is not iterable or if any element is not a string.
466
- fn str_join(separator: &str, iterable: Value, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
467
- // Create MontyIter from the iterable, with join-specific error message
468
- let Ok(iter) = MontyIter::new(iterable, vm) else {
469
- return Err(ExcType::type_error_join_not_iterable());
470
- };
471
- defer_drop_mut!(iter, vm);
472
-
473
- // Build result string, tracking index for error messages
474
- let mut result = String::new();
475
- let mut index = 0usize;
476
-
477
- while let Some(item) = iter.for_next(vm)? {
478
- defer_drop!(item, vm);
479
- if index > 0 {
480
- result.push_str(separator);
481
- }
482
-
483
- // Check item is a string and extract its content
484
- match item {
485
- Value::InternString(id) => {
486
- result.push_str(vm.interns.get_str(*id));
487
- }
488
- Value::Ref(heap_id) => {
489
- if let HeapData::Str(s) = vm.heap.get(*heap_id) {
490
- result.push_str(s.as_str());
491
- } else {
492
- let t = item.py_type(vm.heap);
493
- return Err(ExcType::type_error_join_item(index, t));
494
- }
495
- }
496
- _ => {
497
- let t = item.py_type(vm.heap);
498
- return Err(ExcType::type_error_join_item(index, t));
499
- }
500
- }
501
- index += 1;
502
- }
503
-
504
- // Allocate result (uses interned empty string if result is empty)
505
- allocate_string(result, vm.heap)
506
- }
507
-
508
- /// Writes a Python repr() string for a given string slice to a formatter.
509
- ///
510
- /// Chooses between single and double quotes based on the string content:
511
- /// - Uses double quotes if the string contains single quotes but not double quotes
512
- /// - Uses single quotes by default, escaping any contained single quotes
513
- ///
514
- /// Common escape sequences (backslash, newline, tab, carriage return) are always escaped.
515
- pub fn string_repr_fmt(s: &str, f: &mut impl Write) -> fmt::Result {
516
- // Check if the string contains single quotes but not double quotes
517
- if s.contains('\'') && !s.contains('"') {
518
- // Use double quotes if string contains only single quotes
519
- f.write_char('"')?;
520
- for c in s.chars() {
521
- match c {
522
- '\\' => f.write_str("\\\\")?,
523
- '\n' => f.write_str("\\n")?,
524
- '\t' => f.write_str("\\t")?,
525
- '\r' => f.write_str("\\r")?,
526
- _ => f.write_char(c)?,
527
- }
528
- }
529
- f.write_char('"')
530
- } else {
531
- // Use single quotes by default, escape any single quotes in the string
532
- f.write_char('\'')?;
533
- for c in s.chars() {
534
- match c {
535
- '\\' => f.write_str("\\\\")?,
536
- '\n' => f.write_str("\\n")?,
537
- '\t' => f.write_str("\\t")?,
538
- '\r' => f.write_str("\\r")?,
539
- '\'' => f.write_str("\\'")?,
540
- _ => f.write_char(c)?,
541
- }
542
- }
543
- f.write_char('\'')
544
- }
545
- }
546
-
547
- /// Formatter for a Python repr() string.
548
- #[derive(Debug)]
549
- pub struct StringRepr<'a>(pub &'a str);
550
-
551
- impl fmt::Display for StringRepr<'_> {
552
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
553
- string_repr_fmt(self.0, f)
554
- }
555
- }
556
-
557
- // =============================================================================
558
- // Simple transformations (no arguments)
559
- // =============================================================================
560
-
561
- /// Implements Python's `str.lower()` method.
562
- fn str_lower(s: &str, heap: &mut Heap<impl ResourceTracker>) -> RunResult<Value> {
563
- allocate_string(s.to_lowercase(), heap)
564
- }
565
-
566
- /// Implements Python's `str.upper()` method.
567
- fn str_upper(s: &str, heap: &mut Heap<impl ResourceTracker>) -> RunResult<Value> {
568
- allocate_string(s.to_uppercase(), heap)
569
- }
570
-
571
- /// Implements Python's `str.capitalize()` method.
572
- ///
573
- /// Returns a copy of the string with its first character capitalized and the rest lowercased.
574
- fn str_capitalize(s: &str, heap: &mut Heap<impl ResourceTracker>) -> RunResult<Value> {
575
- let mut chars = s.chars();
576
- let result = match chars.next() {
577
- None => String::new(),
578
- Some(first) => {
579
- let mut result = first.to_uppercase().to_string();
580
- for c in chars {
581
- result.extend(c.to_lowercase());
582
- }
583
- result
584
- }
585
- };
586
- allocate_string(result, heap)
587
- }
588
-
589
- /// Implements Python's `str.title()` method.
590
- ///
591
- /// Returns a titlecased version of the string where words start with an uppercase
592
- /// character and the remaining characters are lowercase.
593
- fn str_title(s: &str, heap: &mut Heap<impl ResourceTracker>) -> RunResult<Value> {
594
- let mut result = String::with_capacity(s.len());
595
- let mut prev_is_cased = false;
596
-
597
- for c in s.chars() {
598
- if prev_is_cased {
599
- result.extend(c.to_lowercase());
600
- } else {
601
- result.extend(c.to_uppercase());
602
- }
603
- prev_is_cased = c.is_alphabetic();
604
- }
605
-
606
- allocate_string(result, heap)
607
- }
608
-
609
- /// Implements Python's `str.swapcase()` method.
610
- ///
611
- /// Returns a copy of the string with uppercase characters converted to lowercase and vice versa.
612
- fn str_swapcase(s: &str, heap: &mut Heap<impl ResourceTracker>) -> RunResult<Value> {
613
- let mut result = String::with_capacity(s.len());
614
-
615
- for c in s.chars() {
616
- if c.is_uppercase() {
617
- result.extend(c.to_lowercase());
618
- } else if c.is_lowercase() {
619
- result.extend(c.to_uppercase());
620
- } else {
621
- result.push(c);
622
- }
623
- }
624
-
625
- allocate_string(result, heap)
626
- }
627
-
628
- /// Implements Python's `str.casefold()` method.
629
- ///
630
- /// Returns a casefolded copy of the string. Casefolding is similar to lowercasing
631
- /// but more aggressive because it is intended for caseless string matching.
632
- fn str_casefold(s: &str, heap: &mut Heap<impl ResourceTracker>) -> RunResult<Value> {
633
- // Rust's to_lowercase() is equivalent to Unicode casefolding for most purposes
634
- allocate_string(s.to_lowercase(), heap)
635
- }
636
-
637
- // =============================================================================
638
- // Predicate methods (no arguments, return bool)
639
- // =============================================================================
640
-
641
- /// Implements Python's `str.isalpha()` method.
642
- ///
643
- /// Returns True if all characters in the string are alphabetic and there is at least one character.
644
- fn str_isalpha(s: &str) -> bool {
645
- !s.is_empty() && s.chars().all(char::is_alphabetic)
646
- }
647
-
648
- /// Implements Python's `str.isdigit()` method.
649
- ///
650
- /// Returns True if all characters in the string are digits and there is at least one character.
651
- /// In Python, digits include decimal digits (Nd) plus characters with Numeric_Type=Digit
652
- /// (superscripts, subscripts, circled digits, etc.).
653
- fn str_isdigit(s: &str) -> bool {
654
- !s.is_empty() && s.chars().all(is_unicode_digit)
655
- }
656
-
657
- /// Implements Python's `str.isalnum()` method.
658
- ///
659
- /// Returns True if all characters in the string are alphanumeric and there is at least one character.
660
- fn str_isalnum(s: &str) -> bool {
661
- !s.is_empty() && s.chars().all(char::is_alphanumeric)
662
- }
663
-
664
- /// Implements Python's `str.isnumeric()` method.
665
- ///
666
- /// Returns True if all characters in the string are numeric and there is at least one character.
667
- /// In Python, numeric includes decimal digits (Nd), letter numerals (Nl), and other numerals (No).
668
- /// Rust's `char::is_numeric()` checks for all of these categories.
669
- fn str_isnumeric(s: &str) -> bool {
670
- !s.is_empty() && s.chars().all(char::is_numeric)
671
- }
672
-
673
- /// Implements Python's `str.isspace()` method.
674
- ///
675
- /// Returns True if all characters in the string are whitespace and there is at least one character.
676
- fn str_isspace(s: &str) -> bool {
677
- !s.is_empty() && s.chars().all(char::is_whitespace)
678
- }
679
-
680
- /// Implements Python's `str.islower()` method.
681
- ///
682
- /// Returns True if all cased characters in the string are lowercase and there is at least one cased character.
683
- fn str_islower(s: &str) -> bool {
684
- let mut has_cased = false;
685
- for c in s.chars() {
686
- if c.is_uppercase() {
687
- return false;
688
- }
689
- if c.is_lowercase() {
690
- has_cased = true;
691
- }
692
- }
693
- has_cased
694
- }
695
-
696
- /// Implements Python's `str.isupper()` method.
697
- ///
698
- /// Returns True if all cased characters in the string are uppercase and there is at least one cased character.
699
- fn str_isupper(s: &str) -> bool {
700
- let mut has_cased = false;
701
- for c in s.chars() {
702
- if c.is_lowercase() {
703
- return false;
704
- }
705
- if c.is_uppercase() {
706
- has_cased = true;
707
- }
708
- }
709
- has_cased
710
- }
711
-
712
- /// Implements Python's `str.isdecimal()` method.
713
- ///
714
- /// Returns True if all characters in the string are decimal characters and there is at least one character.
715
- /// Decimal characters are those in Unicode category Nd (Decimal_Number) - digits that can be used
716
- /// to form numbers in base 10.
717
- fn str_isdecimal(s: &str) -> bool {
718
- !s.is_empty() && s.chars().all(is_unicode_decimal)
719
- }
720
-
721
- /// Checks if a character is a Unicode decimal digit (Nd category).
722
- ///
723
- /// This covers decimal digit ranges from various scripts including ASCII, Arabic-Indic,
724
- /// Devanagari, Bengali, Thai, Fullwidth, and many others.
725
- fn is_unicode_decimal(c: char) -> bool {
726
- let cp = c as u32;
727
- matches!(
728
- cp,
729
- // Basic Latin (ASCII digits)
730
- 0x0030..=0x0039
731
- // Arabic-Indic digits
732
- | 0x0660..=0x0669
733
- // Extended Arabic-Indic digits
734
- | 0x06F0..=0x06F9
735
- // NKo digits
736
- | 0x07C0..=0x07C9
737
- // Devanagari digits
738
- | 0x0966..=0x096F
739
- // Bengali digits
740
- | 0x09E6..=0x09EF
741
- // Gurmukhi digits
742
- | 0x0A66..=0x0A6F
743
- // Gujarati digits
744
- | 0x0AE6..=0x0AEF
745
- // Oriya digits
746
- | 0x0B66..=0x0B6F
747
- // Tamil digits
748
- | 0x0BE6..=0x0BEF
749
- // Telugu digits
750
- | 0x0C66..=0x0C6F
751
- // Kannada digits
752
- | 0x0CE6..=0x0CEF
753
- // Malayalam digits
754
- | 0x0D66..=0x0D6F
755
- // Sinhala Lith digits
756
- | 0x0DE6..=0x0DEF
757
- // Thai digits
758
- | 0x0E50..=0x0E59
759
- // Lao digits
760
- | 0x0ED0..=0x0ED9
761
- // Tibetan digits
762
- | 0x0F20..=0x0F29
763
- // Myanmar digits
764
- | 0x1040..=0x1049
765
- // Myanmar Shan digits
766
- | 0x1090..=0x1099
767
- // Khmer digits
768
- | 0x17E0..=0x17E9
769
- // Mongolian digits
770
- | 0x1810..=0x1819
771
- // Limbu digits
772
- | 0x1946..=0x194F
773
- // New Tai Lue digits
774
- | 0x19D0..=0x19D9
775
- // Tai Tham Hora digits
776
- | 0x1A80..=0x1A89
777
- // Tai Tham Tham digits
778
- | 0x1A90..=0x1A99
779
- // Balinese digits
780
- | 0x1B50..=0x1B59
781
- // Sundanese digits
782
- | 0x1BB0..=0x1BB9
783
- // Lepcha digits
784
- | 0x1C40..=0x1C49
785
- // Ol Chiki digits
786
- | 0x1C50..=0x1C59
787
- // Vai digits
788
- | 0xA620..=0xA629
789
- // Saurashtra digits
790
- | 0xA8D0..=0xA8D9
791
- // Kayah Li digits
792
- | 0xA900..=0xA909
793
- // Javanese digits
794
- | 0xA9D0..=0xA9D9
795
- // Myanmar Tai Laing digits
796
- | 0xA9F0..=0xA9F9
797
- // Cham digits
798
- | 0xAA50..=0xAA59
799
- // Meetei Mayek digits
800
- | 0xABF0..=0xABF9
801
- // Fullwidth digits
802
- | 0xFF10..=0xFF19
803
- // Osmanya digits
804
- | 0x104A0..=0x104A9
805
- // Hanifi Rohingya digits
806
- | 0x10D30..=0x10D39
807
- // Brahmi digits
808
- | 0x11066..=0x1106F
809
- // Sora Sompeng digits
810
- | 0x110F0..=0x110F9
811
- // Chakma digits
812
- | 0x11136..=0x1113F
813
- // Sharada digits
814
- | 0x111D0..=0x111D9
815
- // Khudawadi digits
816
- | 0x112F0..=0x112F9
817
- // Newa digits
818
- | 0x11450..=0x11459
819
- // Tirhuta digits
820
- | 0x114D0..=0x114D9
821
- // Modi digits
822
- | 0x11650..=0x11659
823
- // Takri digits
824
- | 0x116C0..=0x116C9
825
- // Ahom digits
826
- | 0x11730..=0x11739
827
- // Warang Citi digits
828
- | 0x118E0..=0x118E9
829
- // Dives Akuru digits
830
- | 0x11950..=0x11959
831
- // Bhaiksuki digits
832
- | 0x11C50..=0x11C59
833
- // Masaram Gondi digits
834
- | 0x11D50..=0x11D59
835
- // Gunjala Gondi digits
836
- | 0x11DA0..=0x11DA9
837
- // Adlam digits
838
- | 0x1E950..=0x1E959
839
- // Segmented digits
840
- | 0x1FBF0..=0x1FBF9
841
- )
842
- }
843
-
844
- /// Checks if a character is a Unicode digit (isdigit).
845
- ///
846
- /// This includes decimal digits (Nd) plus characters with Numeric_Type=Digit
847
- /// such as superscripts, subscripts, and circled digits.
848
- fn is_unicode_digit(c: char) -> bool {
849
- // First check if it's a decimal digit
850
- if is_unicode_decimal(c) {
851
- return true;
852
- }
853
-
854
- let cp = c as u32;
855
- matches!(
856
- cp,
857
- // Superscripts (², ³)
858
- 0x00B2..=0x00B3
859
- // Superscript 1
860
- | 0x00B9
861
- // Superscript digits 0, 4-9
862
- | 0x2070
863
- | 0x2074..=0x2079
864
- // Subscript digits 0-9
865
- | 0x2080..=0x2089
866
- // Circled digits 1-9
867
- | 0x2460..=0x2468
868
- // Circled digit 0
869
- | 0x24EA
870
- // Circled digits 10-20
871
- | 0x2469..=0x2473
872
- // Parenthesized digits 1-9
873
- | 0x2474..=0x247C
874
- // Period digits 1-9
875
- | 0x2488..=0x2490
876
- // Double circled digits 1-10
877
- | 0x24F5..=0x24FE
878
- // Dingbat circled sans-serif digits 1-10
879
- | 0x2780..=0x2789
880
- // Dingbat negative circled digits 1-10
881
- | 0x278A..=0x2793
882
- // Dingbat circled sans-serif digits 1-10
883
- | 0x24FF
884
- // Fullwidth digit zero (already in decimal, but include for completeness)
885
- // | 0xFF10..=0xFF19 // Already covered by is_unicode_decimal
886
- )
887
- }
888
-
889
- // =============================================================================
890
- // Search methods
891
- // =============================================================================
892
-
893
- /// Implements Python's `str.find(sub, start?, end?)` method.
894
- ///
895
- /// Returns the lowest index in the string where substring sub is found within
896
- /// the slice s[start:end]. Returns -1 if sub is not found.
897
- fn str_find(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
898
- let (sub, start, end) = parse_search_args("str.find", s, args, vm)?;
899
- let slice = slice_string(s, start, end);
900
- let result = match slice.find(&sub) {
901
- Some(pos) => {
902
- // Convert byte offset to char offset, then add start offset
903
- let char_pos = slice[..pos].chars().count();
904
- i64::try_from(start + char_pos).unwrap_or(i64::MAX)
905
- }
906
- None => -1,
907
- };
908
- Ok(Value::Int(result))
909
- }
910
-
911
- /// Implements Python's `str.rfind(sub, start?, end?)` method.
912
- ///
913
- /// Returns the highest index in the string where substring sub is found within
914
- /// the slice s[start:end]. Returns -1 if sub is not found.
915
- fn str_rfind(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
916
- let (sub, start, end) = parse_search_args("str.rfind", s, args, vm)?;
917
- let slice = slice_string(s, start, end);
918
- let result = match slice.rfind(&sub) {
919
- Some(pos) => {
920
- // Convert byte offset to char offset, then add start offset
921
- let char_pos = slice[..pos].chars().count();
922
- i64::try_from(start + char_pos).unwrap_or(i64::MAX)
923
- }
924
- None => -1,
925
- };
926
- Ok(Value::Int(result))
927
- }
928
-
929
- /// Implements Python's `str.index(sub, start?, end?)` method.
930
- ///
931
- /// Like find(), but raises ValueError when the substring is not found.
932
- fn str_index(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
933
- let (sub, start, end) = parse_search_args("str.index", s, args, vm)?;
934
- let slice = slice_string(s, start, end);
935
- match slice.find(&sub) {
936
- Some(pos) => {
937
- let char_pos = slice[..pos].chars().count();
938
- let result = i64::try_from(start + char_pos).unwrap_or(i64::MAX);
939
- Ok(Value::Int(result))
940
- }
941
- None => Err(ExcType::value_error_substring_not_found()),
942
- }
943
- }
944
-
945
- /// Implements Python's `str.rindex(sub, start?, end?)` method.
946
- ///
947
- /// Like rfind(), but raises ValueError when the substring is not found.
948
- fn str_rindex(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
949
- let (sub, start, end) = parse_search_args("str.rindex", s, args, vm)?;
950
- let slice = slice_string(s, start, end);
951
- match slice.rfind(&sub) {
952
- Some(pos) => {
953
- let char_pos = slice[..pos].chars().count();
954
- let result = i64::try_from(start + char_pos).unwrap_or(i64::MAX);
955
- Ok(Value::Int(result))
956
- }
957
- None => Err(ExcType::value_error_substring_not_found()),
958
- }
959
- }
960
-
961
- /// Implements Python's `str.count(sub, start?, end?)` method.
962
- ///
963
- /// Returns the number of non-overlapping occurrences of substring sub in
964
- /// the string s[start:end].
965
- fn str_count(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
966
- let (sub, start, end) = parse_search_args("str.count", s, args, vm)?;
967
- let slice = slice_string(s, start, end);
968
- let count = if sub.is_empty() {
969
- // Empty string matches between every character, plus start and end
970
- slice.chars().count() + 1
971
- } else {
972
- slice.matches(&sub).count()
973
- };
974
- let result = i64::try_from(count).unwrap_or(i64::MAX);
975
- Ok(Value::Int(result))
976
- }
977
-
978
- /// Implements Python's `str.startswith(prefix, start?, end?)` method.
979
- ///
980
- /// Returns True if the string starts with the prefix, otherwise returns False.
981
- /// The prefix argument can be a string or a tuple of strings.
982
- fn str_startswith(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
983
- let (prefixes, start, end) = parse_prefix_suffix_args("str.startswith", s, args, vm)?;
984
- let slice = slice_string(s, start, end);
985
- let result = prefixes.iter().any(|prefix| slice.starts_with(prefix));
986
- Ok(Value::Bool(result))
987
- }
988
-
989
- /// Implements Python's `str.endswith(suffix, start?, end?)` method.
990
- ///
991
- /// Returns True if the string ends with the suffix, otherwise returns False.
992
- /// The suffix argument can be a string or a tuple of strings.
993
- fn str_endswith(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
994
- let (suffixes, start, end) = parse_prefix_suffix_args("str.endswith", s, args, vm)?;
995
- let slice = slice_string(s, start, end);
996
- let result = suffixes.iter().any(|suffix| slice.ends_with(suffix));
997
- Ok(Value::Bool(result))
998
- }
999
-
1000
- /// Parses arguments for search methods (find, rfind, index, rindex, count, startswith, endswith).
1001
- ///
1002
- /// Returns (substring, start, end) where start and end are character indices.
1003
- fn parse_search_args(
1004
- method: &str,
1005
- s: &str,
1006
- args: ArgValues,
1007
- vm: &mut VM<'_, '_, impl ResourceTracker>,
1008
- ) -> RunResult<(String, usize, usize)> {
1009
- let pos = args.into_pos_only(method, vm.heap)?;
1010
- defer_drop!(pos, vm);
1011
-
1012
- let str_len = s.chars().count();
1013
- match pos.as_slice() {
1014
- [sub_value] => {
1015
- let sub = extract_string_arg(sub_value, vm)?;
1016
- Ok((sub, 0, str_len))
1017
- }
1018
- [sub_value, start_value] => {
1019
- let sub = extract_string_arg(sub_value, vm)?;
1020
- let start = optional_index(start_value, 0, str_len, vm.heap)?;
1021
- Ok((sub, start, str_len))
1022
- }
1023
- [sub_value, start_value, end_value] => {
1024
- let sub = extract_string_arg(sub_value, vm)?;
1025
- let start = optional_index(start_value, 0, str_len, vm.heap)?;
1026
- let end = optional_index(end_value, str_len, str_len, vm.heap)?;
1027
- Ok((sub, start, end))
1028
- }
1029
- [] => Err(ExcType::type_error_at_least(method, 1, 0)),
1030
- _ => Err(ExcType::type_error_at_most(method, 3, pos.len())),
1031
- }
1032
- }
1033
-
1034
- /// Parses arguments for startswith/endswith methods.
1035
- ///
1036
- /// Returns (prefixes/suffixes as Vec, start, end) where start and end are character indices.
1037
- /// The first argument can be either a string or a tuple of strings.
1038
- fn parse_prefix_suffix_args(
1039
- method: &str,
1040
- s: &str,
1041
- args: ArgValues,
1042
- vm: &mut VM<'_, '_, impl ResourceTracker>,
1043
- ) -> RunResult<(Vec<String>, usize, usize)> {
1044
- let pos = args.into_pos_only(method, vm.heap)?;
1045
- defer_drop!(pos, vm);
1046
-
1047
- let str_len = s.chars().count();
1048
- match pos.as_slice() {
1049
- [prefix_value] => {
1050
- let prefixes = extract_str_or_tuple_of_str(prefix_value, vm)?;
1051
- Ok((prefixes, 0, str_len))
1052
- }
1053
- [prefix_value, start_value] => {
1054
- let prefixes = extract_str_or_tuple_of_str(prefix_value, vm)?;
1055
- let start = optional_index(start_value, 0, str_len, vm.heap)?;
1056
- Ok((prefixes, start, str_len))
1057
- }
1058
- [prefix_value, start_value, end_value] => {
1059
- let prefixes = extract_str_or_tuple_of_str(prefix_value, vm)?;
1060
- let start = optional_index(start_value, 0, str_len, vm.heap)?;
1061
- let end = optional_index(end_value, str_len, str_len, vm.heap)?;
1062
- Ok((prefixes, start, end))
1063
- }
1064
- [] => Err(ExcType::type_error_at_least(method, 1, 0)),
1065
- _ => Err(ExcType::type_error_at_most(method, 3, pos.len())),
1066
- }
1067
- }
1068
-
1069
- /// Extracts a string or tuple of strings from a Value.
1070
- ///
1071
- /// Returns a Vec of strings - a single-element Vec if given a string,
1072
- /// or multiple elements if given a tuple of strings.
1073
- fn extract_str_or_tuple_of_str(value: &Value, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Vec<String>> {
1074
- match value {
1075
- Value::InternString(id) => Ok(vec![vm.interns.get_str(*id).to_owned()]),
1076
- Value::Ref(heap_id) => match vm.heap.get(*heap_id) {
1077
- HeapData::Str(s) => Ok(vec![s.as_str().to_owned()]),
1078
- HeapData::Tuple(tuple) => {
1079
- // Inline string extraction to avoid borrow conflict — vm.heap is
1080
- // already borrowed immutably to access the tuple's items.
1081
- let items = tuple.as_slice();
1082
- let mut strings = Vec::with_capacity(items.len());
1083
- for item in items {
1084
- match item {
1085
- Value::InternString(id) => {
1086
- strings.push(vm.interns.get_str(*id).to_owned());
1087
- }
1088
- Value::Ref(hid) => {
1089
- if let HeapData::Str(s) = vm.heap.get(*hid) {
1090
- strings.push(s.as_str().to_owned());
1091
- } else {
1092
- return Err(ExcType::type_error("expected str or tuple of str"));
1093
- }
1094
- }
1095
- _ => return Err(ExcType::type_error("expected str or tuple of str")),
1096
- }
1097
- }
1098
- Ok(strings)
1099
- }
1100
- _ => Err(ExcType::type_error("expected str or tuple of str")),
1101
- },
1102
- _ => Err(ExcType::type_error("expected str or tuple of str")),
1103
- }
1104
- }
1105
-
1106
- /// Extracts a string from a Value, returning an error if not a string.
1107
- fn extract_string_arg(value: &Value, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<String> {
1108
- match value {
1109
- Value::InternString(id) => Ok(vm.interns.get_str(*id).to_owned()),
1110
- Value::Ref(heap_id) => {
1111
- if let HeapData::Str(s) = vm.heap.get(*heap_id) {
1112
- Ok(s.as_str().to_owned())
1113
- } else {
1114
- Err(ExcType::type_error("expected str"))
1115
- }
1116
- }
1117
- _ => Err(ExcType::type_error("expected str")),
1118
- }
1119
- }
1120
-
1121
- /// Extracts an integer from a Value, returning an error if not an integer.
1122
- fn extract_int_arg(value: &Value, heap: &Heap<impl ResourceTracker>) -> RunResult<i64> {
1123
- match value {
1124
- Value::Int(i) => Ok(*i),
1125
- Value::Ref(heap_id) => {
1126
- if let HeapData::LongInt(li) = heap.get(*heap_id) {
1127
- // Try to convert to i64
1128
- li.to_i64().ok_or_else(|| ExcType::type_error("integer too large"))
1129
- } else {
1130
- Err(ExcType::type_error("expected int"))
1131
- }
1132
- }
1133
- _ => Err(ExcType::type_error("expected int")),
1134
- }
1135
- }
1136
-
1137
- /// Normalizes a Python-style index to a valid index in range [0, len].
1138
- fn normalize_index(index: i64, len: usize) -> usize {
1139
- if index < 0 {
1140
- // Safe cast: we've checked index is negative, so -index is positive
1141
- // For very large negative numbers that don't fit in usize, saturate to usize::MAX
1142
- let abs_index = usize::try_from(-index).unwrap_or(usize::MAX);
1143
- len.saturating_sub(abs_index)
1144
- } else {
1145
- // Safe cast: we've checked index is non-negative
1146
- // For values > usize::MAX, saturate to len
1147
- usize::try_from(index).unwrap_or(len).min(len)
1148
- }
1149
- }
1150
-
1151
- /// Extracts an optional index from a `Value`, treating `None` as `default`.
1152
- ///
1153
- /// Used by argument parsers where `None` means "use the default index" and
1154
- /// any other value is interpreted as an integer and normalized against `str_len`.
1155
- fn optional_index(
1156
- value: &Value,
1157
- default: usize,
1158
- str_len: usize,
1159
- heap: &Heap<impl ResourceTracker>,
1160
- ) -> RunResult<usize> {
1161
- if matches!(value, Value::None) {
1162
- Ok(default)
1163
- } else {
1164
- Ok(normalize_index(extract_int_arg(value, heap)?, str_len))
1165
- }
1166
- }
1167
-
1168
- /// Returns a substring of s from character index start to end.
1169
- fn slice_string(s: &str, start: usize, end: usize) -> &str {
1170
- if start >= end {
1171
- return "";
1172
- }
1173
-
1174
- let mut start_byte = s.len();
1175
- let mut end_byte = s.len();
1176
-
1177
- for (char_idx, (byte_idx, _)) in s.char_indices().enumerate() {
1178
- if char_idx == start {
1179
- start_byte = byte_idx;
1180
- }
1181
- if char_idx == end {
1182
- end_byte = byte_idx;
1183
- break;
1184
- }
1185
- }
1186
-
1187
- &s[start_byte..end_byte]
1188
- }
1189
-
1190
- // =============================================================================
1191
- // Strip/trim methods
1192
- // =============================================================================
1193
-
1194
- /// Implements Python's `str.strip(chars?)` method.
1195
- ///
1196
- /// Returns a copy of the string with leading and trailing characters removed.
1197
- /// If chars is not specified, whitespace characters are removed.
1198
- fn str_strip(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
1199
- let chars = parse_strip_arg("str.strip", args, vm)?;
1200
- let result = match &chars {
1201
- Some(c) => s.trim_matches(|ch| c.contains(ch)).to_owned(),
1202
- None => s.trim().to_owned(),
1203
- };
1204
- allocate_string(result, vm.heap)
1205
- }
1206
-
1207
- /// Implements Python's `str.lstrip(chars?)` method.
1208
- ///
1209
- /// Returns a copy of the string with leading characters removed.
1210
- fn str_lstrip(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
1211
- let chars = parse_strip_arg("str.lstrip", args, vm)?;
1212
- let result = match &chars {
1213
- Some(c) => s.trim_start_matches(|ch| c.contains(ch)).to_owned(),
1214
- None => s.trim_start().to_owned(),
1215
- };
1216
- allocate_string(result, vm.heap)
1217
- }
1218
-
1219
- /// Implements Python's `str.rstrip(chars?)` method.
1220
- ///
1221
- /// Returns a copy of the string with trailing characters removed.
1222
- fn str_rstrip(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
1223
- let chars = parse_strip_arg("str.rstrip", args, vm)?;
1224
- let result = match &chars {
1225
- Some(c) => s.trim_end_matches(|ch| c.contains(ch)).to_owned(),
1226
- None => s.trim_end().to_owned(),
1227
- };
1228
- allocate_string(result, vm.heap)
1229
- }
1230
-
1231
- /// Parses the optional chars argument for strip methods.
1232
- ///
1233
- /// Accepts None as a value meaning "use default whitespace stripping".
1234
- fn parse_strip_arg(
1235
- method: &str,
1236
- args: ArgValues,
1237
- vm: &mut VM<'_, '_, impl ResourceTracker>,
1238
- ) -> RunResult<Option<String>> {
1239
- let value = args.get_zero_one_arg(method, vm.heap)?;
1240
- match value {
1241
- None => Ok(None),
1242
- Some(Value::None) => Ok(None), // Explicit None means default whitespace
1243
- Some(v) => {
1244
- defer_drop!(v, vm);
1245
- let result = extract_string_arg(v, vm)?;
1246
- Ok(Some(result))
1247
- }
1248
- }
1249
- }
1250
-
1251
- /// Implements Python's `str.removeprefix(prefix)` method.
1252
- ///
1253
- /// If the string starts with the prefix string, return string[len(prefix):].
1254
- /// Otherwise, return a copy of the original string.
1255
- fn str_removeprefix(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
1256
- let prefix_value = args.get_one_arg("str.removeprefix", vm.heap)?;
1257
- defer_drop!(prefix_value, vm);
1258
- let prefix = extract_string_arg(prefix_value, vm)?;
1259
-
1260
- let result = s.strip_prefix(&prefix).unwrap_or(s).to_owned();
1261
- allocate_string(result, vm.heap)
1262
- }
1263
-
1264
- /// Implements Python's `str.removesuffix(suffix)` method.
1265
- ///
1266
- /// If the string ends with the suffix string, return string[:-len(suffix)].
1267
- /// Otherwise, return a copy of the original string.
1268
- fn str_removesuffix(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
1269
- let suffix_value = args.get_one_arg("str.removesuffix", vm.heap)?;
1270
- defer_drop!(suffix_value, vm);
1271
- let suffix = extract_string_arg(suffix_value, vm)?;
1272
-
1273
- let result = s.strip_suffix(&suffix).unwrap_or(s).to_owned();
1274
- allocate_string(result, vm.heap)
1275
- }
1276
-
1277
- // =============================================================================
1278
- // Split methods
1279
- // =============================================================================
1280
-
1281
- /// Implements Python's `str.split(sep?, maxsplit?)` method.
1282
- ///
1283
- /// Returns a list of the words in the string, using sep as the delimiter string.
1284
- fn str_split(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
1285
- let (sep, maxsplit) = parse_split_args("str.split", args, vm)?;
1286
-
1287
- let parts: Vec<&str> = match &sep {
1288
- Some(sep) => {
1289
- // Empty separator raises ValueError
1290
- if sep.is_empty() {
1291
- return Err(ExcType::value_error_empty_separator());
1292
- }
1293
- if maxsplit < 0 {
1294
- s.split(sep.as_str()).collect()
1295
- } else {
1296
- // Safe cast: we've checked maxsplit >= 0
1297
- let max = usize::try_from(maxsplit).unwrap_or(usize::MAX);
1298
- s.splitn(max.saturating_add(1), sep.as_str()).collect()
1299
- }
1300
- }
1301
- None => {
1302
- // Split on whitespace, filtering empty strings
1303
- if maxsplit < 0 {
1304
- s.split_whitespace().collect()
1305
- } else {
1306
- // Safe cast: we've checked maxsplit >= 0
1307
- let max = usize::try_from(maxsplit).unwrap_or(usize::MAX);
1308
- split_whitespace_n(s, max)
1309
- }
1310
- }
1311
- };
1312
-
1313
- // Convert to list of strings (using interned empty string when applicable)
1314
- let mut list_items = Vec::with_capacity(parts.len());
1315
- for part in parts {
1316
- vm.heap.check_time()?;
1317
- list_items.push(allocate_string(part.to_owned(), vm.heap)?);
1318
- }
1319
-
1320
- let list = crate::types::List::new(list_items);
1321
- let heap_id = vm.heap.allocate(HeapData::List(list))?;
1322
- Ok(Value::Ref(heap_id))
1323
- }
1324
-
1325
- /// Implements Python's `str.rsplit(sep?, maxsplit?)` method.
1326
- ///
1327
- /// Returns a list of the words in the string, using sep as the delimiter string,
1328
- /// splitting from the right.
1329
- fn str_rsplit(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
1330
- let (sep, maxsplit) = parse_split_args("str.rsplit", args, vm)?;
1331
-
1332
- let parts: Vec<&str> = match &sep {
1333
- Some(sep) => {
1334
- // Empty separator raises ValueError
1335
- if sep.is_empty() {
1336
- return Err(ExcType::value_error_empty_separator());
1337
- }
1338
- if maxsplit < 0 {
1339
- s.rsplit(sep.as_str()).collect::<Vec<_>>().into_iter().rev().collect()
1340
- } else {
1341
- // Safe cast: we've checked maxsplit >= 0
1342
- let max = usize::try_from(maxsplit).unwrap_or(usize::MAX);
1343
- let mut parts: Vec<_> = s.rsplitn(max.saturating_add(1), sep.as_str()).collect();
1344
- parts.reverse();
1345
- parts
1346
- }
1347
- }
1348
- None => {
1349
- // Split on whitespace from right
1350
- if maxsplit < 0 {
1351
- s.split_whitespace().collect()
1352
- } else {
1353
- // Safe cast: we've checked maxsplit >= 0
1354
- let max = usize::try_from(maxsplit).unwrap_or(usize::MAX);
1355
- rsplit_whitespace_n(s, max)
1356
- }
1357
- }
1358
- };
1359
-
1360
- // Convert to list of strings (using interned empty string when applicable)
1361
- let mut list_items = Vec::with_capacity(parts.len());
1362
- for part in parts {
1363
- vm.heap.check_time()?;
1364
- list_items.push(allocate_string(part.to_owned(), vm.heap)?);
1365
- }
1366
-
1367
- let list = crate::types::List::new(list_items);
1368
- let heap_id = vm.heap.allocate(HeapData::List(list))?;
1369
- Ok(Value::Ref(heap_id))
1370
- }
1371
-
1372
- /// Parses arguments for split methods.
1373
- ///
1374
- /// Supports both positional and keyword arguments for sep and maxsplit.
1375
- fn parse_split_args(
1376
- method: &str,
1377
- args: ArgValues,
1378
- vm: &mut VM<'_, '_, impl ResourceTracker>,
1379
- ) -> RunResult<(Option<String>, i64)> {
1380
- let (pos, kwargs) = args.into_parts();
1381
- let kwargs_iter = kwargs.into_iter();
1382
- defer_drop_mut!(kwargs_iter, vm);
1383
-
1384
- let mut pos_iter = pos;
1385
- let sep_value = pos_iter.next();
1386
- defer_drop_mut!(sep_value, vm);
1387
- let maxsplit_value = pos_iter.next();
1388
- defer_drop_mut!(maxsplit_value, vm);
1389
-
1390
- // Check no extra positional arguments
1391
- if pos_iter.len() != 0 {
1392
- return Err(ExcType::type_error_at_most(method, 2, 3));
1393
- }
1394
-
1395
- // Extract positional sep (default None)
1396
- let mut has_pos_sep = sep_value.is_some();
1397
- let mut sep = if let Some(v) = sep_value.as_ref() {
1398
- if matches!(v, Value::None) {
1399
- None
1400
- } else {
1401
- Some(extract_string_arg(v, vm)?)
1402
- }
1403
- } else {
1404
- None
1405
- };
1406
-
1407
- // Extract positional maxsplit (default -1)
1408
- let mut has_pos_maxsplit = maxsplit_value.is_some();
1409
- let mut maxsplit = if let Some(v) = maxsplit_value.as_ref() {
1410
- extract_int_arg(v, vm.heap)?
1411
- } else {
1412
- -1
1413
- };
1414
-
1415
- // Process kwargs
1416
- for (key, value) in kwargs_iter {
1417
- defer_drop!(key, vm);
1418
- defer_drop!(value, vm);
1419
-
1420
- let Some(keyword_name) = key.as_either_str(vm.heap) else {
1421
- return Err(ExcType::type_error("keywords must be strings"));
1422
- };
1423
-
1424
- let key_str = keyword_name.as_str(vm.interns);
1425
- match key_str {
1426
- "sep" => {
1427
- if has_pos_sep {
1428
- return Err(ExcType::type_error(format!(
1429
- "{method}() got multiple values for argument 'sep'"
1430
- )));
1431
- }
1432
- if matches!(value, Value::None) {
1433
- sep = None;
1434
- } else {
1435
- sep = Some(extract_string_arg(value, vm)?);
1436
- }
1437
- has_pos_sep = true;
1438
- }
1439
- "maxsplit" => {
1440
- if has_pos_maxsplit {
1441
- return Err(ExcType::type_error(format!(
1442
- "{method}() got multiple values for argument 'maxsplit'"
1443
- )));
1444
- }
1445
- maxsplit = extract_int_arg(value, vm.heap)?;
1446
- has_pos_maxsplit = true;
1447
- }
1448
- _ => {
1449
- return Err(ExcType::type_error(format!(
1450
- "'{key_str}' is an invalid keyword argument for {method}()"
1451
- )));
1452
- }
1453
- }
1454
- }
1455
-
1456
- Ok((sep, maxsplit))
1457
- }
1458
-
1459
- /// Split string on whitespace, returning at most `maxsplit + 1` parts.
1460
- fn split_whitespace_n(s: &str, maxsplit: usize) -> Vec<&str> {
1461
- let mut parts = Vec::new();
1462
- let mut remaining = s.trim_start();
1463
- let mut count = 0;
1464
-
1465
- while !remaining.is_empty() && count < maxsplit {
1466
- if let Some(end) = remaining.find(|c: char| c.is_whitespace()) {
1467
- parts.push(&remaining[..end]);
1468
- remaining = remaining[end..].trim_start();
1469
- count += 1;
1470
- } else {
1471
- break;
1472
- }
1473
- }
1474
-
1475
- if !remaining.is_empty() {
1476
- parts.push(remaining);
1477
- }
1478
-
1479
- parts
1480
- }
1481
-
1482
- /// Split string on whitespace from the right, returning at most `maxsplit + 1` parts.
1483
- fn rsplit_whitespace_n(s: &str, maxsplit: usize) -> Vec<&str> {
1484
- let mut parts = Vec::new();
1485
- let mut remaining = s.trim_end();
1486
- let mut count = 0;
1487
-
1488
- while !remaining.is_empty() && count < maxsplit {
1489
- if let Some(start) = remaining.rfind(|c: char| c.is_whitespace()) {
1490
- parts.push(&remaining[start + 1..]);
1491
- remaining = remaining[..start].trim_end();
1492
- count += 1;
1493
- } else {
1494
- break;
1495
- }
1496
- }
1497
-
1498
- if !remaining.is_empty() {
1499
- parts.push(remaining);
1500
- }
1501
-
1502
- parts.reverse();
1503
- parts
1504
- }
1505
-
1506
- /// Implements Python's `str.splitlines(keepends?)` method.
1507
- ///
1508
- /// Returns a list of the lines in the string, breaking at line boundaries.
1509
- /// Accepts keepends as either positional or keyword argument.
1510
- fn str_splitlines(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
1511
- let keepends = parse_splitlines_args(args, vm)?;
1512
-
1513
- let mut lines = Vec::new();
1514
- let mut start = 0;
1515
- let bytes = s.as_bytes();
1516
- let len = bytes.len();
1517
-
1518
- while start < len {
1519
- vm.heap.check_time()?;
1520
-
1521
- // Find the next line ending
1522
- let mut end = start;
1523
- let mut line_end = start;
1524
-
1525
- while end < len {
1526
- match bytes[end] {
1527
- b'\n' => {
1528
- line_end = end;
1529
- end += 1;
1530
- break;
1531
- }
1532
- b'\r' => {
1533
- line_end = end;
1534
- end += 1;
1535
- // Check for \r\n
1536
- if end < len && bytes[end] == b'\n' {
1537
- end += 1;
1538
- }
1539
- break;
1540
- }
1541
- _ => {
1542
- end += 1;
1543
- line_end = end;
1544
- }
1545
- }
1546
- }
1547
-
1548
- let line = if keepends { &s[start..end] } else { &s[start..line_end] };
1549
-
1550
- lines.push(allocate_string(line.to_owned(), vm.heap)?);
1551
-
1552
- start = end;
1553
- }
1554
-
1555
- let list = crate::types::List::new(lines);
1556
- let heap_id = vm.heap.allocate(HeapData::List(list))?;
1557
- Ok(Value::Ref(heap_id))
1558
- }
1559
-
1560
- /// Parses arguments for splitlines method.
1561
- ///
1562
- /// Supports both positional and keyword arguments for keepends.
1563
- fn parse_splitlines_args(args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<bool> {
1564
- let (pos, kwargs) = args.into_parts();
1565
- let kwargs_iter = kwargs.into_iter();
1566
- defer_drop_mut!(kwargs_iter, vm);
1567
-
1568
- let mut pos_iter = pos;
1569
- let keepends_value = pos_iter.next();
1570
- defer_drop_mut!(keepends_value, vm);
1571
-
1572
- // Check no extra positional arguments
1573
- if pos_iter.len() != 0 {
1574
- return Err(ExcType::type_error_at_most("str.splitlines", 1, 2));
1575
- }
1576
-
1577
- // Extract positional keepends (default false)
1578
- let mut has_pos_keepends = keepends_value.is_some();
1579
- let mut keepends = if let Some(v) = keepends_value.as_ref() {
1580
- value_is_truthy(v)
1581
- } else {
1582
- false
1583
- };
1584
-
1585
- // Process kwargs
1586
- for (key, value) in kwargs_iter {
1587
- defer_drop!(key, vm);
1588
- defer_drop!(value, vm);
1589
-
1590
- let Some(keyword_name) = key.as_either_str(vm.heap) else {
1591
- return Err(ExcType::type_error("keywords must be strings"));
1592
- };
1593
-
1594
- let key_str = keyword_name.as_str(vm.interns);
1595
- if key_str == "keepends" {
1596
- if has_pos_keepends {
1597
- return Err(ExcType::type_error(
1598
- "str.splitlines() got multiple values for argument 'keepends'",
1599
- ));
1600
- }
1601
- keepends = value_is_truthy(value);
1602
- has_pos_keepends = true;
1603
- } else {
1604
- return Err(ExcType::type_error(format!(
1605
- "'{key_str}' is an invalid keyword argument for str.splitlines()"
1606
- )));
1607
- }
1608
- }
1609
-
1610
- Ok(keepends)
1611
- }
1612
-
1613
- /// Checks if a value is truthy for bool conversion.
1614
- fn value_is_truthy(v: &Value) -> bool {
1615
- match v {
1616
- Value::Bool(b) => *b,
1617
- Value::Int(i) => *i != 0,
1618
- Value::None => false,
1619
- _ => true, // Most other values are truthy
1620
- }
1621
- }
1622
-
1623
- /// Implements Python's `str.partition(sep)` method.
1624
- ///
1625
- /// Splits the string at the first occurrence of sep, and returns a 3-tuple
1626
- /// containing the part before the separator, the separator itself, and the part after.
1627
- fn str_partition(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
1628
- let sep_value = args.get_one_arg("str.partition", vm.heap)?;
1629
- defer_drop!(sep_value, vm);
1630
- let sep = extract_string_arg(sep_value, vm)?;
1631
-
1632
- if sep.is_empty() {
1633
- return Err(ExcType::value_error_empty_separator());
1634
- }
1635
-
1636
- let (before, sep_found, after) = match s.find(&sep) {
1637
- Some(pos) => (&s[..pos], &sep[..], &s[pos + sep.len()..]),
1638
- None => (s, "", ""),
1639
- };
1640
-
1641
- let before_val = allocate_string(before.to_owned(), vm.heap)?;
1642
- let sep_val = allocate_string(sep_found.to_owned(), vm.heap)?;
1643
- let after_val = allocate_string(after.to_owned(), vm.heap)?;
1644
-
1645
- Ok(crate::types::allocate_tuple(
1646
- smallvec![before_val, sep_val, after_val],
1647
- vm.heap,
1648
- )?)
1649
- }
1650
-
1651
- /// Implements Python's `str.rpartition(sep)` method.
1652
- ///
1653
- /// Splits the string at the last occurrence of sep, and returns a 3-tuple
1654
- /// containing the part before the separator, the separator itself, and the part after.
1655
- fn str_rpartition(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
1656
- let sep_value = args.get_one_arg("str.rpartition", vm.heap)?;
1657
- defer_drop!(sep_value, vm);
1658
- let sep = extract_string_arg(sep_value, vm)?;
1659
-
1660
- if sep.is_empty() {
1661
- return Err(ExcType::value_error_empty_separator());
1662
- }
1663
-
1664
- let (before, sep_found, after) = match s.rfind(&sep) {
1665
- Some(pos) => (&s[..pos], &sep[..], &s[pos + sep.len()..]),
1666
- None => ("", "", s),
1667
- };
1668
-
1669
- let before_val = allocate_string(before.to_owned(), vm.heap)?;
1670
- let sep_val = allocate_string(sep_found.to_owned(), vm.heap)?;
1671
- let after_val = allocate_string(after.to_owned(), vm.heap)?;
1672
-
1673
- Ok(crate::types::allocate_tuple(
1674
- smallvec![before_val, sep_val, after_val],
1675
- vm.heap,
1676
- )?)
1677
- }
1678
-
1679
- // =============================================================================
1680
- // Replace/modify methods
1681
- // =============================================================================
1682
-
1683
- /// Implements Python's `str.replace(old, new, count?)` method.
1684
- ///
1685
- /// Returns a copy with all occurrences of substring old replaced by new.
1686
- /// If count is given, only the first count occurrences are replaced.
1687
- fn str_replace(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
1688
- let (old, new, count) = parse_replace_args("str.replace", args, vm)?;
1689
-
1690
- check_replace_size(s.len(), old.len(), new.len(), count, vm.heap.tracker())?;
1691
-
1692
- let result = if count < 0 {
1693
- s.replace(&old, &new)
1694
- } else {
1695
- // Safe cast: we've checked count >= 0
1696
- let n = usize::try_from(count).unwrap_or(usize::MAX);
1697
- s.replacen(&old, &new, n)
1698
- };
1699
-
1700
- allocate_string(result, vm.heap)
1701
- }
1702
-
1703
- /// Parses arguments for the replace method.
1704
- ///
1705
- /// Supports both positional and keyword arguments for count (Python 3.13+).
1706
- fn parse_replace_args(
1707
- method: &str,
1708
- args: ArgValues,
1709
- vm: &mut VM<'_, '_, impl ResourceTracker>,
1710
- ) -> RunResult<(String, String, i64)> {
1711
- let (pos, kwargs) = args.into_parts();
1712
- let kwargs_iter = kwargs.into_iter();
1713
- defer_drop_mut!(kwargs_iter, vm);
1714
-
1715
- let mut pos_iter = pos;
1716
- let Some(old_value) = pos_iter.next() else {
1717
- return Err(ExcType::type_error_at_least(method, 2, 0));
1718
- };
1719
- defer_drop!(old_value, vm);
1720
-
1721
- let Some(new_value) = pos_iter.next() else {
1722
- return Err(ExcType::type_error_at_least(method, 2, 1));
1723
- };
1724
- defer_drop!(new_value, vm);
1725
-
1726
- let count_value = pos_iter.next();
1727
- defer_drop_mut!(count_value, vm);
1728
-
1729
- // Check no extra positional arguments
1730
- if pos_iter.len() != 0 {
1731
- return Err(ExcType::type_error_at_most(method, 3, 4));
1732
- }
1733
-
1734
- let old = extract_string_arg(old_value, vm)?;
1735
- let new = extract_string_arg(new_value, vm)?;
1736
-
1737
- let mut has_pos_count = count_value.is_some();
1738
- let mut count = if let Some(v) = count_value.as_ref() {
1739
- extract_int_arg(v, vm.heap)?
1740
- } else {
1741
- -1
1742
- };
1743
-
1744
- // Process kwargs (Python 3.13+ allows count as keyword)
1745
- for (key, value) in kwargs_iter {
1746
- defer_drop!(key, vm);
1747
- defer_drop!(value, vm);
1748
-
1749
- let Some(keyword_name) = key.as_either_str(vm.heap) else {
1750
- return Err(ExcType::type_error("keywords must be strings"));
1751
- };
1752
-
1753
- let key_str = keyword_name.as_str(vm.interns);
1754
- if key_str == "count" {
1755
- if has_pos_count {
1756
- return Err(ExcType::type_error(format!(
1757
- "{method}() got multiple values for argument 'count'"
1758
- )));
1759
- }
1760
- count = extract_int_arg(value, vm.heap)?;
1761
- has_pos_count = true;
1762
- } else {
1763
- return Err(ExcType::type_error(format!(
1764
- "'{key_str}' is an invalid keyword argument for {method}()"
1765
- )));
1766
- }
1767
- }
1768
-
1769
- Ok((old, new, count))
1770
- }
1771
-
1772
- /// Implements Python's `str.center(width, fillchar?)` method.
1773
- ///
1774
- /// Returns centered in a string of length width. Padding is done using the
1775
- /// specified fill character (default is a space).
1776
- fn str_center(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
1777
- let (width, fillchar) = parse_justify_args("str.center", args, vm)?;
1778
- let len = s.chars().count();
1779
-
1780
- let result = if width <= len {
1781
- s.to_owned()
1782
- } else {
1783
- check_repeat_size(width, fillchar.len_utf8(), vm.heap.tracker())?;
1784
- let total_pad = width - len;
1785
- let left_pad = total_pad / 2;
1786
- let right_pad = total_pad - left_pad;
1787
- let mut result = String::with_capacity(width);
1788
- for _ in 0..left_pad {
1789
- result.push(fillchar);
1790
- }
1791
- result.push_str(s);
1792
- for _ in 0..right_pad {
1793
- result.push(fillchar);
1794
- }
1795
- result
1796
- };
1797
-
1798
- allocate_string(result, vm.heap)
1799
- }
1800
-
1801
- /// Implements Python's `str.ljust(width, fillchar?)` method.
1802
- ///
1803
- /// Returns left-justified in a string of length width.
1804
- fn str_ljust(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
1805
- let (width, fillchar) = parse_justify_args("str.ljust", args, vm)?;
1806
- let len = s.chars().count();
1807
-
1808
- let result = if width <= len {
1809
- s.to_owned()
1810
- } else {
1811
- check_repeat_size(width, fillchar.len_utf8(), vm.heap.tracker())?;
1812
- let pad = width - len;
1813
- let mut result = String::with_capacity(width);
1814
- result.push_str(s);
1815
- for _ in 0..pad {
1816
- result.push(fillchar);
1817
- }
1818
- result
1819
- };
1820
-
1821
- allocate_string(result, vm.heap)
1822
- }
1823
-
1824
- /// Implements Python's `str.rjust(width, fillchar?)` method.
1825
- ///
1826
- /// Returns right-justified in a string of length width.
1827
- fn str_rjust(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
1828
- let (width, fillchar) = parse_justify_args("str.rjust", args, vm)?;
1829
- let len = s.chars().count();
1830
-
1831
- let result = if width <= len {
1832
- s.to_owned()
1833
- } else {
1834
- check_repeat_size(width, fillchar.len_utf8(), vm.heap.tracker())?;
1835
- let pad = width - len;
1836
- let mut result = String::with_capacity(width);
1837
- for _ in 0..pad {
1838
- result.push(fillchar);
1839
- }
1840
- result.push_str(s);
1841
- result
1842
- };
1843
-
1844
- allocate_string(result, vm.heap)
1845
- }
1846
-
1847
- /// Parses arguments for justify methods (center, ljust, rjust).
1848
- fn parse_justify_args(
1849
- method: &str,
1850
- args: ArgValues,
1851
- vm: &mut VM<'_, '_, impl ResourceTracker>,
1852
- ) -> RunResult<(usize, char)> {
1853
- let pos = args.into_pos_only(method, vm.heap)?;
1854
- defer_drop!(pos, vm);
1855
-
1856
- match pos.as_slice() {
1857
- [width_value] => {
1858
- let w = extract_int_arg(width_value, vm.heap)?;
1859
- let width = if w < 0 {
1860
- 0
1861
- } else {
1862
- usize::try_from(w).unwrap_or(usize::MAX)
1863
- };
1864
- Ok((width, ' '))
1865
- }
1866
- [width_value, fillchar_value] => {
1867
- let w = extract_int_arg(width_value, vm.heap)?;
1868
- let width = if w < 0 {
1869
- 0
1870
- } else {
1871
- usize::try_from(w).unwrap_or(usize::MAX)
1872
- };
1873
- let fill_str = extract_string_arg(fillchar_value, vm)?;
1874
- if fill_str.chars().count() != 1 {
1875
- return Err(ExcType::type_error_fillchar_must_be_single_char());
1876
- }
1877
- Ok((width, fill_str.chars().next().unwrap()))
1878
- }
1879
- [] => Err(ExcType::type_error_at_least(method, 1, 0)),
1880
- _ => Err(ExcType::type_error_at_most(method, 2, pos.len())),
1881
- }
1882
- }
1883
-
1884
- /// Implements Python's `str.zfill(width)` method.
1885
- ///
1886
- /// Returns a copy of the string left filled with ASCII '0' digits to make a
1887
- /// string of length width. A sign prefix is handled correctly.
1888
- fn str_zfill(s: &str, args: ArgValues, heap: &mut Heap<impl ResourceTracker>) -> RunResult<Value> {
1889
- let width_value = args.get_one_arg("str.zfill", heap)?;
1890
- defer_drop!(width_value, heap);
1891
- let width_i64 = extract_int_arg(width_value, heap)?;
1892
-
1893
- // Safe cast: treat negative as 0, saturate large positive values
1894
- let width = if width_i64 < 0 {
1895
- 0
1896
- } else {
1897
- usize::try_from(width_i64).unwrap_or(usize::MAX)
1898
- };
1899
- let len = s.chars().count();
1900
-
1901
- let result = if width <= len {
1902
- s.to_owned()
1903
- } else {
1904
- // zfill always pads with ASCII '0' (1 byte)
1905
- check_repeat_size(width, 1, heap.tracker())?;
1906
- let pad = width - len;
1907
- let mut chars = s.chars();
1908
- let first = chars.next();
1909
-
1910
- let mut result = String::with_capacity(width);
1911
-
1912
- // Handle sign prefix
1913
- if matches!(first, Some('+' | '-')) {
1914
- result.push(first.unwrap());
1915
- for _ in 0..pad {
1916
- result.push('0');
1917
- }
1918
- result.extend(chars);
1919
- } else {
1920
- for _ in 0..pad {
1921
- result.push('0');
1922
- }
1923
- result.push_str(s);
1924
- }
1925
- result
1926
- };
1927
-
1928
- allocate_string(result, heap)
1929
- }
1930
-
1931
- /// Implements Python's `str.encode(encoding='utf-8', errors='strict')` method.
1932
- ///
1933
- /// Returns an encoded version of the string as a bytes object. Only supports
1934
- /// UTF-8 encoding (the native encoding for Rust strings).
1935
- fn str_encode(s: &str, args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<Value> {
1936
- let (encoding, errors) = parse_encode_args(args, vm)?;
1937
-
1938
- // Only UTF-8 is supported - Rust strings are always valid UTF-8
1939
- let encoding_lower = encoding.to_ascii_lowercase();
1940
- if encoding_lower != "utf-8" && encoding_lower != "utf8" {
1941
- return Err(ExcType::lookup_error_unknown_encoding(&encoding));
1942
- }
1943
-
1944
- // For UTF-8 encoding of a valid UTF-8 string, errors mode doesn't matter
1945
- // since there's nothing to handle - the string is already valid UTF-8
1946
- if errors != "strict" && errors != "ignore" && errors != "replace" && errors != "backslashreplace" {
1947
- return Err(ExcType::lookup_error_unknown_error_handler(&errors));
1948
- }
1949
-
1950
- let bytes = s.as_bytes().to_vec();
1951
- let heap_id = vm.heap.allocate(HeapData::Bytes(Bytes::new(bytes)))?;
1952
- Ok(Value::Ref(heap_id))
1953
- }
1954
-
1955
- /// Parses arguments for `str.encode()`.
1956
- ///
1957
- /// Returns (encoding, errors) with defaults "utf-8" and "strict".
1958
- fn parse_encode_args(args: ArgValues, vm: &mut VM<'_, '_, impl ResourceTracker>) -> RunResult<(String, String)> {
1959
- let (first, second) = args.get_zero_one_two_args("str.encode", vm.heap)?;
1960
-
1961
- let encoding = if let Some(v) = first {
1962
- defer_drop!(v, vm);
1963
- extract_string_arg(v, vm)?
1964
- } else {
1965
- "utf-8".to_owned()
1966
- };
1967
-
1968
- let errors = if let Some(v) = second {
1969
- defer_drop!(v, vm);
1970
- extract_string_arg(v, vm)?
1971
- } else {
1972
- "strict".to_owned()
1973
- };
1974
-
1975
- Ok((encoding, errors))
1976
- }
1977
-
1978
- /// Implements Python's `str.isidentifier()` predicate.
1979
- ///
1980
- /// Returns True if the string is a valid Python identifier according to
1981
- /// the language definition (starts with letter or underscore, followed by
1982
- /// letters, digits, or underscores). Empty strings return False.
1983
- fn str_isidentifier(s: &str) -> bool {
1984
- if s.is_empty() {
1985
- return false;
1986
- }
1987
-
1988
- let mut chars = s.chars();
1989
-
1990
- // First character must be a letter (Unicode) or underscore
1991
- let first = chars.next().unwrap();
1992
- if !is_xid_start(first) && first != '_' {
1993
- return false;
1994
- }
1995
-
1996
- // Remaining characters must be letters, digits (Unicode), or underscores
1997
- chars.all(is_xid_continue)
1998
- }
1999
-
2000
- /// Checks if a character is valid at the start of an identifier (XID_Start).
2001
- ///
2002
- /// This is a simplified implementation that covers ASCII and common Unicode letters.
2003
- /// Python uses the full Unicode XID_Start property.
2004
- fn is_xid_start(c: char) -> bool {
2005
- c.is_alphabetic()
2006
- }
2007
-
2008
- /// Checks if a character is valid in the continuation of an identifier (XID_Continue).
2009
- ///
2010
- /// This is a simplified implementation that covers ASCII and common Unicode.
2011
- /// Python uses the full Unicode XID_Continue property.
2012
- fn is_xid_continue(c: char) -> bool {
2013
- c.is_alphanumeric() || c == '_'
2014
- }
2015
-
2016
- /// Implements Python's `str.istitle()` predicate.
2017
- ///
2018
- /// Returns True if the string is titlecased: uppercase characters follow
2019
- /// uncased characters and lowercase characters follow cased characters.
2020
- /// Empty strings return False.
2021
- fn str_istitle(s: &str) -> bool {
2022
- if s.is_empty() {
2023
- return false;
2024
- }
2025
-
2026
- let mut prev_cased = false;
2027
- let mut has_cased = false;
2028
-
2029
- for c in s.chars() {
2030
- if c.is_uppercase() {
2031
- // Uppercase must follow uncased
2032
- if prev_cased {
2033
- return false;
2034
- }
2035
- prev_cased = true;
2036
- has_cased = true;
2037
- } else if c.is_lowercase() {
2038
- // Lowercase must follow cased
2039
- if !prev_cased {
2040
- return false;
2041
- }
2042
- prev_cased = true;
2043
- has_cased = true;
2044
- } else {
2045
- // Uncased character
2046
- prev_cased = false;
2047
- }
2048
- }
2049
-
2050
- has_cased
2051
- }