agentcore-cli 0.31.1__py3-none-any.whl

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 (655) hide show
  1. agentcore/__init__.py +1 -0
  2. agentcore/agent_inspector/NOTICE +9 -0
  3. agentcore/agent_inspector/favicon.svg +60 -0
  4. agentcore/agent_inspector/index.css +1 -0
  5. agentcore/agent_inspector/index.html +14 -0
  6. agentcore/agent_inspector/index.js +289 -0
  7. agentcore/assets/AGENTS.md +60 -0
  8. agentcore/assets/README.md +108 -0
  9. agentcore/assets/agents/AGENTS.md +218 -0
  10. agentcore/assets/cdk/.prettierrc +8 -0
  11. agentcore/assets/cdk/README.md +26 -0
  12. agentcore/assets/cdk/bin/cdk.ts +216 -0
  13. agentcore/assets/cdk/cdk.json +88 -0
  14. agentcore/assets/cdk/gitignore.template +9 -0
  15. agentcore/assets/cdk/jest.config.js +9 -0
  16. agentcore/assets/cdk/lib/cdk-stack.ts +291 -0
  17. agentcore/assets/cdk/npmignore.template +6 -0
  18. agentcore/assets/cdk/package.json +30 -0
  19. agentcore/assets/cdk/test/cdk.test.ts +31 -0
  20. agentcore/assets/cdk/tsconfig.json +28 -0
  21. agentcore/assets/container/python/Dockerfile +47 -0
  22. agentcore/assets/container/python/dockerignore.template +27 -0
  23. agentcore/assets/container/typescript/Dockerfile +25 -0
  24. agentcore/assets/container/typescript/dockerignore.template +24 -0
  25. agentcore/assets/datasets/predefined-v1.jsonl +3 -0
  26. agentcore/assets/datasets/simulated-v1.jsonl +3 -0
  27. agentcore/assets/evaluators/autoevals-lambda/execution-role-policy.json +15 -0
  28. agentcore/assets/evaluators/autoevals-lambda/lambda_function.py +37 -0
  29. agentcore/assets/evaluators/autoevals-lambda/pyproject.toml +22 -0
  30. agentcore/assets/evaluators/deepeval-lambda/execution-role-policy.json +15 -0
  31. agentcore/assets/evaluators/deepeval-lambda/lambda_function.py +29 -0
  32. agentcore/assets/evaluators/deepeval-lambda/pyproject.toml +19 -0
  33. agentcore/assets/evaluators/python-lambda/execution-role-policy.json +10 -0
  34. agentcore/assets/evaluators/python-lambda/lambda_function.py +19 -0
  35. agentcore/assets/evaluators/python-lambda/pyproject.toml +15 -0
  36. agentcore/assets/harness/invoke.py.template +74 -0
  37. agentcore/assets/mcp/python/README.md +27 -0
  38. agentcore/assets/mcp/python/pyproject.toml +22 -0
  39. agentcore/assets/mcp/python/server.py +117 -0
  40. agentcore/assets/mcp/python-lambda/README.md +22 -0
  41. agentcore/assets/mcp/python-lambda/handler.py +144 -0
  42. agentcore/assets/mcp/python-lambda/pyproject.toml +15 -0
  43. agentcore/assets/python/a2a/googleadk/base/README.md +22 -0
  44. agentcore/assets/python/a2a/googleadk/base/gitignore.template +41 -0
  45. agentcore/assets/python/a2a/googleadk/base/main.py +124 -0
  46. agentcore/assets/python/a2a/googleadk/base/model/__init__.py +1 -0
  47. agentcore/assets/python/a2a/googleadk/base/model/load.py +41 -0
  48. agentcore/assets/python/a2a/googleadk/base/pyproject.toml +22 -0
  49. agentcore/assets/python/a2a/langchain_langgraph/base/README.md +22 -0
  50. agentcore/assets/python/a2a/langchain_langgraph/base/gitignore.template +41 -0
  51. agentcore/assets/python/a2a/langchain_langgraph/base/main.py +144 -0
  52. agentcore/assets/python/a2a/langchain_langgraph/base/model/__init__.py +1 -0
  53. agentcore/assets/python/a2a/langchain_langgraph/base/model/load.py +123 -0
  54. agentcore/assets/python/a2a/langchain_langgraph/base/pyproject.toml +26 -0
  55. agentcore/assets/python/a2a/strands/base/README.md +22 -0
  56. agentcore/assets/python/a2a/strands/base/gitignore.template +41 -0
  57. agentcore/assets/python/a2a/strands/base/main.py +113 -0
  58. agentcore/assets/python/a2a/strands/base/model/__init__.py +1 -0
  59. agentcore/assets/python/a2a/strands/base/model/load.py +123 -0
  60. agentcore/assets/python/a2a/strands/base/pyproject.toml +23 -0
  61. agentcore/assets/python/a2a/strands/capabilities/memory/__init__.py +1 -0
  62. agentcore/assets/python/a2a/strands/capabilities/memory/session.py +46 -0
  63. agentcore/assets/python/agui/googleadk/base/README.md +30 -0
  64. agentcore/assets/python/agui/googleadk/base/gitignore.template +41 -0
  65. agentcore/assets/python/agui/googleadk/base/main.py +91 -0
  66. agentcore/assets/python/agui/googleadk/base/model/__init__.py +1 -0
  67. agentcore/assets/python/agui/googleadk/base/model/load.py +41 -0
  68. agentcore/assets/python/agui/googleadk/base/pyproject.toml +25 -0
  69. agentcore/assets/python/agui/langchain_langgraph/base/README.md +22 -0
  70. agentcore/assets/python/agui/langchain_langgraph/base/gitignore.template +41 -0
  71. agentcore/assets/python/agui/langchain_langgraph/base/main.py +130 -0
  72. agentcore/assets/python/agui/langchain_langgraph/base/model/__init__.py +1 -0
  73. agentcore/assets/python/agui/langchain_langgraph/base/model/load.py +123 -0
  74. agentcore/assets/python/agui/langchain_langgraph/base/pyproject.toml +31 -0
  75. agentcore/assets/python/agui/strands/base/README.md +22 -0
  76. agentcore/assets/python/agui/strands/base/gitignore.template +41 -0
  77. agentcore/assets/python/agui/strands/base/main.py +105 -0
  78. agentcore/assets/python/agui/strands/base/model/__init__.py +1 -0
  79. agentcore/assets/python/agui/strands/base/model/load.py +123 -0
  80. agentcore/assets/python/agui/strands/base/pyproject.toml +27 -0
  81. agentcore/assets/python/agui/strands/capabilities/memory/__init__.py +1 -0
  82. agentcore/assets/python/agui/strands/capabilities/memory/session.py +46 -0
  83. agentcore/assets/python/http/autogen/base/README.md +45 -0
  84. agentcore/assets/python/http/autogen/base/gitignore.template +41 -0
  85. agentcore/assets/python/http/autogen/base/main.py +145 -0
  86. agentcore/assets/python/http/autogen/base/mcp_client/__init__.py +1 -0
  87. agentcore/assets/python/http/autogen/base/mcp_client/client.py +28 -0
  88. agentcore/assets/python/http/autogen/base/model/__init__.py +1 -0
  89. agentcore/assets/python/http/autogen/base/model/load.py +136 -0
  90. agentcore/assets/python/http/autogen/base/pyproject.toml +34 -0
  91. agentcore/assets/python/http/bma/base/Dockerfile +41 -0
  92. agentcore/assets/python/http/bma/base/README.md +178 -0
  93. agentcore/assets/python/http/bma/base/client.py +200 -0
  94. agentcore/assets/python/http/bma/base/lifecycle/server.py +641 -0
  95. agentcore/assets/python/http/bma/base/otel/collector.yaml +69 -0
  96. agentcore/assets/python/http/bma/base/plugins/acr-report/.codex-plugin/plugin.json +6 -0
  97. agentcore/assets/python/http/bma/base/plugins/acr-report/skills/acr-report/SKILL.md +16 -0
  98. agentcore/assets/python/http/bma/base/policies/bma-acr-policy.json +11 -0
  99. agentcore/assets/python/http/bma/base/policies/bma-session-policy.json +35 -0
  100. agentcore/assets/python/http/bma/base/policies/bma-session-trust.json +20 -0
  101. agentcore/assets/python/http/bma/base/pyproject.toml +21 -0
  102. agentcore/assets/python/http/googleadk/base/README.md +45 -0
  103. agentcore/assets/python/http/googleadk/base/gitignore.template +41 -0
  104. agentcore/assets/python/http/googleadk/base/main.py +207 -0
  105. agentcore/assets/python/http/googleadk/base/mcp_client/__init__.py +1 -0
  106. agentcore/assets/python/http/googleadk/base/mcp_client/client.py +75 -0
  107. agentcore/assets/python/http/googleadk/base/model/__init__.py +1 -0
  108. agentcore/assets/python/http/googleadk/base/model/load.py +41 -0
  109. agentcore/assets/python/http/googleadk/base/pyproject.toml +24 -0
  110. agentcore/assets/python/http/langchain_langgraph/base/README.md +45 -0
  111. agentcore/assets/python/http/langchain_langgraph/base/gitignore.template +41 -0
  112. agentcore/assets/python/http/langchain_langgraph/base/main.py +227 -0
  113. agentcore/assets/python/http/langchain_langgraph/base/mcp_client/__init__.py +1 -0
  114. agentcore/assets/python/http/langchain_langgraph/base/mcp_client/client.py +79 -0
  115. agentcore/assets/python/http/langchain_langgraph/base/model/__init__.py +1 -0
  116. agentcore/assets/python/http/langchain_langgraph/base/model/load.py +123 -0
  117. agentcore/assets/python/http/langchain_langgraph/base/pyproject.toml +38 -0
  118. agentcore/assets/python/http/openaiagents/base/README.md +45 -0
  119. agentcore/assets/python/http/openaiagents/base/gitignore.template +41 -0
  120. agentcore/assets/python/http/openaiagents/base/main.py +200 -0
  121. agentcore/assets/python/http/openaiagents/base/mcp_client/__init__.py +1 -0
  122. agentcore/assets/python/http/openaiagents/base/mcp_client/client.py +74 -0
  123. agentcore/assets/python/http/openaiagents/base/model/__init__.py +1 -0
  124. agentcore/assets/python/http/openaiagents/base/model/load.py +37 -0
  125. agentcore/assets/python/http/openaiagents/base/pyproject.toml +21 -0
  126. agentcore/assets/python/http/strands/base/README.md +46 -0
  127. agentcore/assets/python/http/strands/base/gitignore.template +41 -0
  128. agentcore/assets/python/http/strands/base/main.py +714 -0
  129. agentcore/assets/python/http/strands/base/mcp_client/__init__.py +1 -0
  130. agentcore/assets/python/http/strands/base/mcp_client/client.py +116 -0
  131. agentcore/assets/python/http/strands/base/model/__init__.py +1 -0
  132. agentcore/assets/python/http/strands/base/model/load.py +239 -0
  133. agentcore/assets/python/http/strands/base/model/mantle_compat.py +21 -0
  134. agentcore/assets/python/http/strands/base/pyproject.toml +31 -0
  135. agentcore/assets/python/http/strands/base/skills/fetcher.py +279 -0
  136. agentcore/assets/python/http/strands/capabilities/execution-limits/hooks/execution_limits.py +54 -0
  137. agentcore/assets/python/http/strands/capabilities/memory/__init__.py +1 -0
  138. agentcore/assets/python/http/strands/capabilities/memory/session.py +47 -0
  139. agentcore/assets/python/http/strands/capabilities/payments/__init__.py +4 -0
  140. agentcore/assets/python/http/strands/capabilities/payments/payments.py +142 -0
  141. agentcore/assets/python/mcp/standalone/base/README.md +36 -0
  142. agentcore/assets/python/mcp/standalone/base/gitignore.template +41 -0
  143. agentcore/assets/python/mcp/standalone/base/main.py +83 -0
  144. agentcore/assets/python/mcp/standalone/base/pyproject.toml +16 -0
  145. agentcore/assets/typescript/.gitkeep +0 -0
  146. agentcore/assets/typescript/http/strands/base/README.md +42 -0
  147. agentcore/assets/typescript/http/strands/base/gitignore.template +22 -0
  148. agentcore/assets/typescript/http/strands/base/main.ts +149 -0
  149. agentcore/assets/typescript/http/strands/base/mcp_client/client.ts +11 -0
  150. agentcore/assets/typescript/http/strands/base/model/load.ts +102 -0
  151. agentcore/assets/typescript/http/strands/base/package.json +38 -0
  152. agentcore/assets/typescript/http/strands/base/tsconfig.json +19 -0
  153. agentcore/assets/typescript/http/strands/capabilities/memory/memory.ts +52 -0
  154. agentcore/assets/typescript/http/vercelai/base/README.md +42 -0
  155. agentcore/assets/typescript/http/vercelai/base/gitignore.template +22 -0
  156. agentcore/assets/typescript/http/vercelai/base/main.ts +70 -0
  157. agentcore/assets/typescript/http/vercelai/base/model/load.ts +121 -0
  158. agentcore/assets/typescript/http/vercelai/base/package.json +35 -0
  159. agentcore/assets/typescript/http/vercelai/base/tsconfig.json +19 -0
  160. agentcore/aws/__init__.py +263 -0
  161. agentcore/aws/_aws_errors.py +72 -0
  162. agentcore/aws/_http.py +176 -0
  163. agentcore/aws/_util.py +37 -0
  164. agentcore/aws/account.py +165 -0
  165. agentcore/aws/agentcore.py +971 -0
  166. agentcore/aws/agentcore_ab_tests.py +230 -0
  167. agentcore/aws/agentcore_batch_evaluation.py +227 -0
  168. agentcore/aws/agentcore_config_bundles.py +220 -0
  169. agentcore/aws/agentcore_control.py +994 -0
  170. agentcore/aws/agentcore_datasets.py +222 -0
  171. agentcore/aws/agentcore_harness.py +330 -0
  172. agentcore/aws/agentcore_payments.py +430 -0
  173. agentcore/aws/agentcore_recommendation.py +140 -0
  174. agentcore/aws/agui_parser.py +131 -0
  175. agentcore/aws/agui_types.py +342 -0
  176. agentcore/aws/api_client.py +87 -0
  177. agentcore/aws/arn.py +18 -0
  178. agentcore/aws/aws_context.py +23 -0
  179. agentcore/aws/bedrock.py +44 -0
  180. agentcore/aws/bedrock_agent.py +129 -0
  181. agentcore/aws/bedrock_import.py +252 -0
  182. agentcore/aws/bedrock_import_types.py +111 -0
  183. agentcore/aws/cloudwatch.py +136 -0
  184. agentcore/aws/connect_shell.py +266 -0
  185. agentcore/aws/iam.py +41 -0
  186. agentcore/aws/partition.py +58 -0
  187. agentcore/aws/policy_generation.py +116 -0
  188. agentcore/aws/region.py +54 -0
  189. agentcore/aws/retry.py +27 -0
  190. agentcore/aws/shell_framer.py +136 -0
  191. agentcore/aws/stage_endpoint.py +39 -0
  192. agentcore/aws/target_region.py +54 -0
  193. agentcore/aws/transaction_search.py +97 -0
  194. agentcore/cdk/__init__.py +10 -0
  195. agentcore/cdk/constants.py +13 -0
  196. agentcore/cdk/local_cdk_project.py +48 -0
  197. agentcore/cdk/toolkit.py +706 -0
  198. agentcore/cli/__init__.py +0 -0
  199. agentcore/cli/commands/__init__.py +0 -0
  200. agentcore/cli/commands/_jobs_common.py +39 -0
  201. agentcore/cli/commands/_run_wizards.py +639 -0
  202. agentcore/cli/commands/add/__init__.py +37 -0
  203. agentcore/cli/commands/add/_commander.py +52 -0
  204. agentcore/cli/commands/add/_common.py +47 -0
  205. agentcore/cli/commands/add/agent_wizard.py +528 -0
  206. agentcore/cli/commands/add/auth_options.py +154 -0
  207. agentcore/cli/commands/add/skill_action.py +105 -0
  208. agentcore/cli/commands/add/skill_command.py +87 -0
  209. agentcore/cli/commands/add/tool_action.py +160 -0
  210. agentcore/cli/commands/add/tool_command.py +117 -0
  211. agentcore/cli/commands/add/validate.py +333 -0
  212. agentcore/cli/commands/capacity_provider/__init__.py +86 -0
  213. agentcore/cli/commands/capacity_provider/action.py +114 -0
  214. agentcore/cli/commands/capacity_provider/constants.py +22 -0
  215. agentcore/cli/commands/config.py +91 -0
  216. agentcore/cli/commands/config_bundle.py +251 -0
  217. agentcore/cli/commands/create/__init__.py +643 -0
  218. agentcore/cli/commands/create/action.py +406 -0
  219. agentcore/cli/commands/create/dependencies.py +91 -0
  220. agentcore/cli/commands/create/harness_validate.py +82 -0
  221. agentcore/cli/commands/create/validate.py +195 -0
  222. agentcore/cli/commands/create/wizard.py +127 -0
  223. agentcore/cli/commands/dataset.py +119 -0
  224. agentcore/cli/commands/deploy/__init__.py +31 -0
  225. agentcore/cli/commands/deploy/_guards.py +25 -0
  226. agentcore/cli/commands/deploy/actions.py +840 -0
  227. agentcore/cli/commands/deploy/command.py +349 -0
  228. agentcore/cli/commands/deploy/progress.py +115 -0
  229. agentcore/cli/commands/deploy/types.py +68 -0
  230. agentcore/cli/commands/deploy/utils.py +43 -0
  231. agentcore/cli/commands/deploy/validate.py +21 -0
  232. agentcore/cli/commands/dev/__init__.py +5 -0
  233. agentcore/cli/commands/dev/_deps.py +50 -0
  234. agentcore/cli/commands/dev/browser_mode.py +270 -0
  235. agentcore/cli/commands/dev/command.py +450 -0
  236. agentcore/cli/commands/dev/terminal_mode.py +379 -0
  237. agentcore/cli/commands/exec/__init__.py +29 -0
  238. agentcore/cli/commands/exec/action.py +442 -0
  239. agentcore/cli/commands/exec/command.py +206 -0
  240. agentcore/cli/commands/exec/types.py +35 -0
  241. agentcore/cli/commands/export/__init__.py +114 -0
  242. agentcore/cli/commands/export/constants.py +21 -0
  243. agentcore/cli/commands/export/fetch_harness_spec.py +234 -0
  244. agentcore/cli/commands/export/harness_action.py +385 -0
  245. agentcore/cli/commands/export/harness_mapper.py +868 -0
  246. agentcore/cli/commands/export/harness_resolver.py +95 -0
  247. agentcore/cli/commands/export/types.py +90 -0
  248. agentcore/cli/commands/export/wizard.py +97 -0
  249. agentcore/cli/commands/feedback/__init__.py +69 -0
  250. agentcore/cli/commands/feedback/action.py +30 -0
  251. agentcore/cli/commands/feedback/consent_prompt.py +35 -0
  252. agentcore/cli/commands/feedback/wizard.py +49 -0
  253. agentcore/cli/commands/fetch/__init__.py +69 -0
  254. agentcore/cli/commands/fetch/action.py +62 -0
  255. agentcore/cli/commands/help.py +65 -0
  256. agentcore/cli/commands/import_/__init__.py +167 -0
  257. agentcore/cli/commands/import_/_js.py +72 -0
  258. agentcore/cli/commands/import_/actions.py +457 -0
  259. agentcore/cli/commands/import_/constants.py +45 -0
  260. agentcore/cli/commands/import_/import_evaluator.py +140 -0
  261. agentcore/cli/commands/import_/import_gateway.py +520 -0
  262. agentcore/cli/commands/import_/import_memory.py +128 -0
  263. agentcore/cli/commands/import_/import_online_eval.py +165 -0
  264. agentcore/cli/commands/import_/import_pipeline.py +145 -0
  265. agentcore/cli/commands/import_/import_runtime.py +214 -0
  266. agentcore/cli/commands/import_/import_utils.py +410 -0
  267. agentcore/cli/commands/import_/phase1_update.py +110 -0
  268. agentcore/cli/commands/import_/phase2_import.py +206 -0
  269. agentcore/cli/commands/import_/resource_import.py +228 -0
  270. agentcore/cli/commands/import_/template_utils.py +148 -0
  271. agentcore/cli/commands/import_/types.py +150 -0
  272. agentcore/cli/commands/import_/yaml_parser.py +265 -0
  273. agentcore/cli/commands/init.py +196 -0
  274. agentcore/cli/commands/invoke/__init__.py +32 -0
  275. agentcore/cli/commands/invoke/_deps.py +73 -0
  276. agentcore/cli/commands/invoke/action.py +840 -0
  277. agentcore/cli/commands/invoke/command.py +490 -0
  278. agentcore/cli/commands/invoke/constants.py +40 -0
  279. agentcore/cli/commands/invoke/guards.py +23 -0
  280. agentcore/cli/commands/invoke/interactive.py +604 -0
  281. agentcore/cli/commands/invoke/resolve.py +149 -0
  282. agentcore/cli/commands/invoke/resolve_prompt.py +60 -0
  283. agentcore/cli/commands/invoke/types.py +61 -0
  284. agentcore/cli/commands/invoke/validate.py +33 -0
  285. agentcore/cli/commands/job_actions.py +239 -0
  286. agentcore/cli/commands/job_history.py +380 -0
  287. agentcore/cli/commands/logs/__init__.py +53 -0
  288. agentcore/cli/commands/logs/action.py +159 -0
  289. agentcore/cli/commands/logs/filter_pattern.py +17 -0
  290. agentcore/cli/commands/logs/guards.py +51 -0
  291. agentcore/cli/commands/logs_evals.py +42 -0
  292. agentcore/cli/commands/package.py +118 -0
  293. agentcore/cli/commands/remove/__init__.py +172 -0
  294. agentcore/cli/commands/remove/skill_command.py +131 -0
  295. agentcore/cli/commands/remove/tool_command.py +59 -0
  296. agentcore/cli/commands/run.py +808 -0
  297. agentcore/cli/commands/run_ingest.py +97 -0
  298. agentcore/cli/commands/shared/__init__.py +0 -0
  299. agentcore/cli/commands/shared/arn_utils.py +9 -0
  300. agentcore/cli/commands/shared/filesystem_utils.py +421 -0
  301. agentcore/cli/commands/shared/header_utils.py +87 -0
  302. agentcore/cli/commands/shared/indexed_key_parser.py +76 -0
  303. agentcore/cli/commands/shared/lifecycle_utils.py +72 -0
  304. agentcore/cli/commands/shared/region_utils.py +20 -0
  305. agentcore/cli/commands/shared/vpc_utils.py +99 -0
  306. agentcore/cli/commands/status/__init__.py +30 -0
  307. agentcore/cli/commands/status/_guards.py +25 -0
  308. agentcore/cli/commands/status/action.py +741 -0
  309. agentcore/cli/commands/status/command.py +321 -0
  310. agentcore/cli/commands/status/constants.py +36 -0
  311. agentcore/cli/commands/status/format_knowledge_base.py +111 -0
  312. agentcore/cli/commands/telemetry.py +79 -0
  313. agentcore/cli/commands/traces/__init__.py +155 -0
  314. agentcore/cli/commands/traces/action.py +123 -0
  315. agentcore/cli/commands/update/__init__.py +63 -0
  316. agentcore/cli/commands/update/action.py +164 -0
  317. agentcore/cli/commands/validate.py +165 -0
  318. agentcore/cli/constants.py +96 -0
  319. agentcore/cli/errors.py +118 -0
  320. agentcore/cli/external_requirements/__init__.py +67 -0
  321. agentcore/cli/external_requirements/checks.py +280 -0
  322. agentcore/cli/external_requirements/detect.py +55 -0
  323. agentcore/cli/external_requirements/versions.py +57 -0
  324. agentcore/cli/groups.py +23 -0
  325. agentcore/cli/interactive.py +85 -0
  326. agentcore/cli/main.py +137 -0
  327. agentcore/cli/naming.py +11 -0
  328. agentcore/cli/notices.py +42 -0
  329. agentcore/cli/project.py +34 -0
  330. agentcore/cli/run_command.py +27 -0
  331. agentcore/cli/tree.py +135 -0
  332. agentcore/cli/update_notifier.py +123 -0
  333. agentcore/cli/variadic.py +50 -0
  334. agentcore/cloudformation/__init__.py +84 -0
  335. agentcore/cloudformation/bootstrap.py +64 -0
  336. agentcore/cloudformation/logical_ids.py +37 -0
  337. agentcore/cloudformation/outputs.py +566 -0
  338. agentcore/cloudformation/stack_discovery.py +75 -0
  339. agentcore/cloudformation/stack_events.py +107 -0
  340. agentcore/cloudformation/stack_status.py +98 -0
  341. agentcore/cloudformation/types.py +44 -0
  342. agentcore/lib/__init__.py +68 -0
  343. agentcore/lib/constants.py +77 -0
  344. agentcore/lib/dependency_management/__init__.py +23 -0
  345. agentcore/lib/dependency_management/messages.py +92 -0
  346. agentcore/lib/dependency_management/plan.py +98 -0
  347. agentcore/lib/dependency_management/semver.py +137 -0
  348. agentcore/lib/dependency_management/sync.py +139 -0
  349. agentcore/lib/dependency_management/types.py +56 -0
  350. agentcore/lib/errors/__init__.py +5 -0
  351. agentcore/lib/errors/types.py +352 -0
  352. agentcore/lib/errors/validation.py +121 -0
  353. agentcore/lib/packaging/__init__.py +81 -0
  354. agentcore/lib/packaging/build_args.py +23 -0
  355. agentcore/lib/packaging/build_context.py +26 -0
  356. agentcore/lib/packaging/build_context_dockerignore.py +45 -0
  357. agentcore/lib/packaging/container.py +95 -0
  358. agentcore/lib/packaging/helpers.py +289 -0
  359. agentcore/lib/packaging/node.py +153 -0
  360. agentcore/lib/packaging/python.py +135 -0
  361. agentcore/lib/packaging/types.py +69 -0
  362. agentcore/lib/packaging/uv.py +45 -0
  363. agentcore/lib/result.py +104 -0
  364. agentcore/lib/schemas/__init__.py +1 -0
  365. agentcore/lib/schemas/io/__init__.py +27 -0
  366. agentcore/lib/schemas/io/config_io.py +458 -0
  367. agentcore/lib/schemas/io/global_config.py +239 -0
  368. agentcore/lib/schemas/io/path_resolver.py +132 -0
  369. agentcore/lib/secrets/__init__.py +18 -0
  370. agentcore/lib/secrets/cipher.py +70 -0
  371. agentcore/lib/secrets/key_provider.py +163 -0
  372. agentcore/lib/secrets/sensitive_keys.py +55 -0
  373. agentcore/lib/time_constants.py +4 -0
  374. agentcore/lib/utils/__init__.py +47 -0
  375. agentcore/lib/utils/aws_account.py +15 -0
  376. agentcore/lib/utils/credentials.py +68 -0
  377. agentcore/lib/utils/dotenv.py +32 -0
  378. agentcore/lib/utils/env.py +105 -0
  379. agentcore/lib/utils/json_format.py +29 -0
  380. agentcore/lib/utils/json_rpc.py +22 -0
  381. agentcore/lib/utils/platform.py +33 -0
  382. agentcore/lib/utils/polling.py +86 -0
  383. agentcore/lib/utils/subprocess.py +135 -0
  384. agentcore/lib/utils/time_parser.py +48 -0
  385. agentcore/lib/utils/validation.py +106 -0
  386. agentcore/logging/__init__.py +10 -0
  387. agentcore/logging/_common.py +77 -0
  388. agentcore/logging/cdk_io_host.py +36 -0
  389. agentcore/logging/create_logger.py +107 -0
  390. agentcore/logging/dev_logger.py +63 -0
  391. agentcore/logging/exec_logger.py +159 -0
  392. agentcore/logging/invoke_logger.py +154 -0
  393. agentcore/logging/remove_logger.py +95 -0
  394. agentcore/operations/__init__.py +0 -0
  395. agentcore/operations/agent/__init__.py +0 -0
  396. agentcore/operations/agent/config_bundle_defaults.py +31 -0
  397. agentcore/operations/agent/generate/__init__.py +28 -0
  398. agentcore/operations/agent/generate/schema_mapper.py +319 -0
  399. agentcore/operations/agent/generate/types.py +172 -0
  400. agentcore/operations/agent/generate/write_agent_to_project.py +67 -0
  401. agentcore/operations/agent/import_/__init__.py +31 -0
  402. agentcore/operations/agent/import_/base_translator.py +449 -0
  403. agentcore/operations/agent/import_/constants.py +23 -0
  404. agentcore/operations/agent/import_/execute.py +127 -0
  405. agentcore/operations/agent/import_/langgraph_translator.py +294 -0
  406. agentcore/operations/agent/import_/pyproject_generator.py +73 -0
  407. agentcore/operations/agent/import_/strands_translator.py +289 -0
  408. agentcore/operations/config_bundle/__init__.py +1 -0
  409. agentcore/operations/config_bundle/bundle_name_variants.py +9 -0
  410. agentcore/operations/config_bundle/diff_versions.py +52 -0
  411. agentcore/operations/config_bundle/resolve_bundle.py +65 -0
  412. agentcore/operations/connectors/__init__.py +3 -0
  413. agentcore/operations/connectors/translators.py +63 -0
  414. agentcore/operations/dataset/__init__.py +21 -0
  415. agentcore/operations/dataset/delete.py +7 -0
  416. agentcore/operations/dataset/publish.py +16 -0
  417. agentcore/operations/dataset/pull.py +21 -0
  418. agentcore/operations/dataset/push.py +201 -0
  419. agentcore/operations/dataset/resolve_dataset.py +65 -0
  420. agentcore/operations/dataset/status.py +18 -0
  421. agentcore/operations/dataset/wait.py +25 -0
  422. agentcore/operations/deploy/__init__.py +152 -0
  423. agentcore/operations/deploy/backfill_vpc_id.py +90 -0
  424. agentcore/operations/deploy/change_detection.py +84 -0
  425. agentcore/operations/deploy/constants.py +58 -0
  426. agentcore/operations/deploy/dependency_sync.py +105 -0
  427. agentcore/operations/deploy/ensure_target.py +30 -0
  428. agentcore/operations/deploy/gateway_status.py +44 -0
  429. agentcore/operations/deploy/managed_memory_notice.py +36 -0
  430. agentcore/operations/deploy/payment_connector_authorization.py +106 -0
  431. agentcore/operations/deploy/post_deploy_datasets.py +96 -0
  432. agentcore/operations/deploy/post_deploy_knowledge_bases.py +104 -0
  433. agentcore/operations/deploy/post_deploy_observability.py +28 -0
  434. agentcore/operations/deploy/post_deploy_online_evals.py +60 -0
  435. agentcore/operations/deploy/pre_deploy_identity.py +505 -0
  436. agentcore/operations/deploy/preflight.py +385 -0
  437. agentcore/operations/deploy/region_override.py +45 -0
  438. agentcore/operations/deploy/teardown.py +153 -0
  439. agentcore/operations/dev/__init__.py +61 -0
  440. agentcore/operations/dev/_http.py +79 -0
  441. agentcore/operations/dev/_server.py +165 -0
  442. agentcore/operations/dev/codezip_dev_server.py +151 -0
  443. agentcore/operations/dev/config.py +128 -0
  444. agentcore/operations/dev/constants.py +9 -0
  445. agentcore/operations/dev/container_dev_server.py +237 -0
  446. agentcore/operations/dev/container_runtime.py +37 -0
  447. agentcore/operations/dev/dev_server.py +244 -0
  448. agentcore/operations/dev/gateway_env.py +25 -0
  449. agentcore/operations/dev/invoke.py +214 -0
  450. agentcore/operations/dev/invoke_a2a.py +204 -0
  451. agentcore/operations/dev/invoke_agui.py +88 -0
  452. agentcore/operations/dev/invoke_mcp.py +145 -0
  453. agentcore/operations/dev/invoke_types.py +30 -0
  454. agentcore/operations/dev/load_dev_env.py +26 -0
  455. agentcore/operations/dev/memory_env.py +19 -0
  456. agentcore/operations/dev/otel/__init__.py +5 -0
  457. agentcore/operations/dev/otel/collector.py +234 -0
  458. agentcore/operations/dev/otel/transforms.py +331 -0
  459. agentcore/operations/dev/payment_env.py +50 -0
  460. agentcore/operations/dev/server.py +28 -0
  461. agentcore/operations/dev/sse_transform.py +30 -0
  462. agentcore/operations/dev/utils.py +152 -0
  463. agentcore/operations/dev/web_ui/__init__.py +31 -0
  464. agentcore/operations/dev/web_ui/constants.py +25 -0
  465. agentcore/operations/dev/web_ui/handlers/__init__.py +36 -0
  466. agentcore/operations/dev/web_ui/handlers/_common.py +36 -0
  467. agentcore/operations/dev/web_ui/handlers/_deployed.py +37 -0
  468. agentcore/operations/dev/web_ui/handlers/a2a_proxy.py +37 -0
  469. agentcore/operations/dev/web_ui/handlers/cloudwatch_traces.py +71 -0
  470. agentcore/operations/dev/web_ui/handlers/harness_invocation.py +89 -0
  471. agentcore/operations/dev/web_ui/handlers/harness_utils.py +95 -0
  472. agentcore/operations/dev/web_ui/handlers/invocations.py +327 -0
  473. agentcore/operations/dev/web_ui/handlers/mcp_proxy.py +119 -0
  474. agentcore/operations/dev/web_ui/handlers/memory.py +84 -0
  475. agentcore/operations/dev/web_ui/handlers/resources.py +351 -0
  476. agentcore/operations/dev/web_ui/handlers/start.py +182 -0
  477. agentcore/operations/dev/web_ui/handlers/status.py +25 -0
  478. agentcore/operations/dev/web_ui/handlers/traces.py +50 -0
  479. agentcore/operations/dev/web_ui/run_web_ui.py +60 -0
  480. agentcore/operations/dev/web_ui/web_server.py +297 -0
  481. agentcore/operations/eval/__init__.py +8 -0
  482. agentcore/operations/eval/list_eval_runs.py +18 -0
  483. agentcore/operations/eval/logs_eval.py +119 -0
  484. agentcore/operations/eval/pause_resume.py +90 -0
  485. agentcore/operations/eval/run_eval.py +354 -0
  486. agentcore/operations/eval/shared/__init__.py +0 -0
  487. agentcore/operations/eval/shared/dataset_loader.py +68 -0
  488. agentcore/operations/eval/shared/dataset_session_provider.py +122 -0
  489. agentcore/operations/eval/shared/evaluator_runner.py +89 -0
  490. agentcore/operations/eval/shared/scenario_executor.py +71 -0
  491. agentcore/operations/eval/shared/span_collector.py +255 -0
  492. agentcore/operations/eval/storage.py +67 -0
  493. agentcore/operations/feedback/__init__.py +21 -0
  494. agentcore/operations/feedback/aperture_client.py +98 -0
  495. agentcore/operations/feedback/build_payload.py +97 -0
  496. agentcore/operations/feedback/constants.py +26 -0
  497. agentcore/operations/feedback/submit_feedback.py +150 -0
  498. agentcore/operations/fetch_access/__init__.py +36 -0
  499. agentcore/operations/fetch_access/_target.py +17 -0
  500. agentcore/operations/fetch_access/fetch_gateway_token.py +75 -0
  501. agentcore/operations/fetch_access/fetch_harness_token.py +72 -0
  502. agentcore/operations/fetch_access/fetch_runtime_token.py +76 -0
  503. agentcore/operations/fetch_access/list_agents.py +18 -0
  504. agentcore/operations/fetch_access/list_gateways.py +26 -0
  505. agentcore/operations/fetch_access/list_harnesses.py +23 -0
  506. agentcore/operations/fetch_access/oauth_token.py +162 -0
  507. agentcore/operations/fetch_access/types.py +34 -0
  508. agentcore/operations/harness/__init__.py +1 -0
  509. agentcore/operations/harness/orphan.py +84 -0
  510. agentcore/operations/harness/skill_utils.py +56 -0
  511. agentcore/operations/identity/__init__.py +33 -0
  512. agentcore/operations/identity/api_key_credential_provider.py +68 -0
  513. agentcore/operations/identity/oauth2_credential_provider.py +95 -0
  514. agentcore/operations/ingest/__init__.py +184 -0
  515. agentcore/operations/init/__init__.py +3 -0
  516. agentcore/operations/init/files.py +59 -0
  517. agentcore/operations/insights/__init__.py +21 -0
  518. agentcore/operations/insights/insights_storage.py +67 -0
  519. agentcore/operations/insights/run_insights.py +148 -0
  520. agentcore/operations/invoke/__init__.py +5 -0
  521. agentcore/operations/invoke/resolve_agent_context.py +85 -0
  522. agentcore/operations/jobs/__init__.py +38 -0
  523. agentcore/operations/jobs/ab_test/__init__.py +0 -0
  524. agentcore/operations/jobs/ab_test/build_options.py +126 -0
  525. agentcore/operations/jobs/ab_test/format.py +121 -0
  526. agentcore/operations/jobs/ab_test/handler.py +412 -0
  527. agentcore/operations/jobs/ab_test/promote.py +142 -0
  528. agentcore/operations/jobs/ab_test/resolve.py +221 -0
  529. agentcore/operations/jobs/batch_evaluation/__init__.py +0 -0
  530. agentcore/operations/jobs/batch_evaluation/build_source.py +68 -0
  531. agentcore/operations/jobs/batch_evaluation/dataset_phase1.py +95 -0
  532. agentcore/operations/jobs/batch_evaluation/format.py +58 -0
  533. agentcore/operations/jobs/batch_evaluation/handler.py +203 -0
  534. agentcore/operations/jobs/insights/__init__.py +0 -0
  535. agentcore/operations/jobs/insights/format.py +65 -0
  536. agentcore/operations/jobs/insights/handler.py +178 -0
  537. agentcore/operations/jobs/recommendation/__init__.py +0 -0
  538. agentcore/operations/jobs/recommendation/apply_to_bundle.py +76 -0
  539. agentcore/operations/jobs/recommendation/build_config.py +271 -0
  540. agentcore/operations/jobs/recommendation/fetch_session_spans.py +93 -0
  541. agentcore/operations/jobs/recommendation/format.py +77 -0
  542. agentcore/operations/jobs/recommendation/handler.py +331 -0
  543. agentcore/operations/jobs/shared/__init__.py +0 -0
  544. agentcore/operations/jobs/shared/constants.py +85 -0
  545. agentcore/operations/jobs/shared/engine.py +187 -0
  546. agentcore/operations/jobs/shared/format.py +104 -0
  547. agentcore/operations/jobs/shared/region.py +34 -0
  548. agentcore/operations/jobs/shared/resolve_agent_state.py +30 -0
  549. agentcore/operations/jobs/shared/storage.py +117 -0
  550. agentcore/operations/jobs/shared/wait.py +30 -0
  551. agentcore/operations/knowledge_base/__init__.py +33 -0
  552. agentcore/operations/knowledge_base/connector_config.py +105 -0
  553. agentcore/operations/knowledge_base/hydrate_data_sources.py +59 -0
  554. agentcore/operations/mcp/__init__.py +4 -0
  555. agentcore/operations/memory/__init__.py +13 -0
  556. agentcore/operations/memory/_records.py +72 -0
  557. agentcore/operations/memory/list_memory_records.py +45 -0
  558. agentcore/operations/memory/retrieve_memory_records.py +52 -0
  559. agentcore/operations/node/__init__.py +15 -0
  560. agentcore/operations/node/setup.py +36 -0
  561. agentcore/operations/python/__init__.py +17 -0
  562. agentcore/operations/python/setup.py +47 -0
  563. agentcore/operations/resolve_agent.py +159 -0
  564. agentcore/operations/session/__init__.py +86 -0
  565. agentcore/operations/traces/__init__.py +29 -0
  566. agentcore/operations/traces/get_trace.py +202 -0
  567. agentcore/operations/traces/insights_query.py +74 -0
  568. agentcore/operations/traces/list_traces.py +46 -0
  569. agentcore/operations/traces/trace_url.py +25 -0
  570. agentcore/operations/traces/types.py +55 -0
  571. agentcore/primitives/__init__.py +0 -0
  572. agentcore/primitives/_gateway_target_wizard.py +293 -0
  573. agentcore/primitives/_gateway_validation.py +442 -0
  574. agentcore/primitives/_harness_cli.py +557 -0
  575. agentcore/primitives/_harness_validation.py +163 -0
  576. agentcore/primitives/_harness_wizard.py +726 -0
  577. agentcore/primitives/_knowledge_base_wizard.py +259 -0
  578. agentcore/primitives/_payment_wizard.py +331 -0
  579. agentcore/primitives/_policy_form.py +160 -0
  580. agentcore/primitives/agent.py +817 -0
  581. agentcore/primitives/auth_utils.py +77 -0
  582. agentcore/primitives/base.py +178 -0
  583. agentcore/primitives/capacity_provider.py +501 -0
  584. agentcore/primitives/config_bundle.py +329 -0
  585. agentcore/primitives/credential.py +387 -0
  586. agentcore/primitives/credential_utils.py +45 -0
  587. agentcore/primitives/dataset.py +211 -0
  588. agentcore/primitives/deployed_gateways.py +34 -0
  589. agentcore/primitives/evaluator.py +909 -0
  590. agentcore/primitives/gateway.py +496 -0
  591. agentcore/primitives/gateway_target.py +988 -0
  592. agentcore/primitives/harness.py +706 -0
  593. agentcore/primitives/knowledge_base.py +477 -0
  594. agentcore/primitives/memory.py +387 -0
  595. agentcore/primitives/online_eval_config.py +365 -0
  596. agentcore/primitives/online_insights.py +275 -0
  597. agentcore/primitives/payment_connector.py +427 -0
  598. agentcore/primitives/payment_eligible.py +20 -0
  599. agentcore/primitives/payment_manager.py +605 -0
  600. agentcore/primitives/payment_validation.py +68 -0
  601. agentcore/primitives/policy.py +622 -0
  602. agentcore/primitives/policy_engine.py +358 -0
  603. agentcore/primitives/registry.py +56 -0
  604. agentcore/primitives/runtime_endpoint.py +361 -0
  605. agentcore/primitives/secret_flag_warning.py +17 -0
  606. agentcore/schema/__init__.py +14 -0
  607. agentcore/schema/_zod.py +395 -0
  608. agentcore/schema/constants.py +306 -0
  609. agentcore/schema/schemas/__init__.py +40 -0
  610. agentcore/schema/schemas/agent_env.py +545 -0
  611. agentcore/schema/schemas/agentcore_project.py +530 -0
  612. agentcore/schema/schemas/auth.py +286 -0
  613. agentcore/schema/schemas/aws_targets.py +72 -0
  614. agentcore/schema/schemas/connections.py +187 -0
  615. agentcore/schema/schemas/deployed_state.py +322 -0
  616. agentcore/schema/schemas/mcp.py +650 -0
  617. agentcore/schema/schemas/mcp_defs.py +54 -0
  618. agentcore/schema/schemas/primitives/__init__.py +52 -0
  619. agentcore/schema/schemas/primitives/ab_test.py +162 -0
  620. agentcore/schema/schemas/primitives/capacity_provider.py +214 -0
  621. agentcore/schema/schemas/primitives/config_bundle.py +48 -0
  622. agentcore/schema/schemas/primitives/dataset.py +55 -0
  623. agentcore/schema/schemas/primitives/evaluator.py +171 -0
  624. agentcore/schema/schemas/primitives/harness.py +710 -0
  625. agentcore/schema/schemas/primitives/knowledge_base.py +90 -0
  626. agentcore/schema/schemas/primitives/memory.py +113 -0
  627. agentcore/schema/schemas/primitives/online_eval_config.py +77 -0
  628. agentcore/schema/schemas/primitives/payment.py +114 -0
  629. agentcore/schema/schemas/primitives/policy.py +83 -0
  630. agentcore/schema/schemas/primitives/tags.py +50 -0
  631. agentcore/schema/schemas/zod_util.py +5 -0
  632. agentcore/schema/types/__init__.py +3 -0
  633. agentcore/schema/types/path.py +16 -0
  634. agentcore/templates/__init__.py +115 -0
  635. agentcore/templates/_handlebars.py +782 -0
  636. agentcore/templates/_js.py +164 -0
  637. agentcore/templates/base_renderer.py +107 -0
  638. agentcore/templates/cdk_renderer.py +129 -0
  639. agentcore/templates/evaluator_renderer.py +39 -0
  640. agentcore/templates/gateway_target_renderer.py +69 -0
  641. agentcore/templates/llm_context/README.md +16 -0
  642. agentcore/templates/llm_context/agentcore.ts +685 -0
  643. agentcore/templates/llm_context/aws-targets.ts +51 -0
  644. agentcore/templates/profiles.py +151 -0
  645. agentcore/templates/render.py +92 -0
  646. agentcore/templates/renderers.py +83 -0
  647. agentcore/templates/schema_assets.py +23 -0
  648. agentcore/templates/template_root.py +28 -0
  649. agentcore/templates/types.py +192 -0
  650. agentcore_cli-0.31.1.dist-info/METADATA +84 -0
  651. agentcore_cli-0.31.1.dist-info/RECORD +655 -0
  652. agentcore_cli-0.31.1.dist-info/WHEEL +4 -0
  653. agentcore_cli-0.31.1.dist-info/entry_points.txt +2 -0
  654. agentcore_cli-0.31.1.dist-info/licenses/LICENSE +175 -0
  655. agentcore_cli-0.31.1.dist-info/licenses/NOTICE +88 -0
@@ -0,0 +1,782 @@
1
+ """Handlebars rendering with handlebars.js 4.7 semantics on top of pybars3.
2
+
3
+ pybars3 differs from handlebars.js in ways that change the rendered output of the templates:
4
+
5
+ * its standalone-line removal is a regex approximation of handlebars' ``WhitespaceControl``;
6
+ * it does not parse ``{{else if ...}}`` chains;
7
+ * it does not escape ``=``, its ``unless`` ignores ``{{else}}``, and it uses Python truthiness
8
+ (``{}`` falsy) and Python ``str()`` (``1.0``, ``[1, 2]``) instead of JavaScript's.
9
+
10
+ So a template is first lexed and parsed here into handlebars' AST, whitespace control is applied by a
11
+ straight port of ``compiler/whitespace-control.js``, ``else``-chains are desugared into nested
12
+ blocks, and the resulting source (whose whitespace is now final) is compiled by pybars3 with its own
13
+ whitespace handling disabled. At runtime, the compiled module's ``prepare``/``resolve`` globals and the
14
+ built-in block helpers are replaced with JavaScript-faithful versions.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import re
20
+ import threading
21
+ from collections.abc import Callable, Mapping
22
+ from dataclasses import dataclass, field
23
+ from functools import lru_cache
24
+ from typing import Any
25
+
26
+ import pybars
27
+ from pybars._compiler import Scope, strlist
28
+
29
+ from agentcore.templates._js import (
30
+ JS_WS,
31
+ hb_is_empty,
32
+ js_object_keys,
33
+ js_same_value_zero,
34
+ js_strict_equals,
35
+ js_to_string,
36
+ js_truthy,
37
+ json_stringify,
38
+ utf16_len,
39
+ )
40
+
41
+ __all__ = ["SafeString", "TemplateSyntaxError", "compile_template", "render_template"]
42
+
43
+
44
+ class TemplateSyntaxError(ValueError):
45
+ pass
46
+
47
+
48
+ class SafeString(strlist):
49
+ """Helper output that must not be HTML-escaped (``Handlebars.SafeString``)."""
50
+
51
+ def __init__(self, value: str) -> None:
52
+ super().__init__([value])
53
+
54
+
55
+ # ---------------------------------------------------------------------------
56
+ # Lexing
57
+ # ---------------------------------------------------------------------------
58
+
59
+
60
+ @dataclass
61
+ class _Strip:
62
+ open: bool = False
63
+ close: bool = False
64
+
65
+
66
+ @dataclass
67
+ class _Tag:
68
+ kind: str # mustache | triple | amp | comment | open | open_inverse | open_chain | inverse | close
69
+ expr: str
70
+ strip: _Strip
71
+
72
+
73
+ _Token = str | _Tag
74
+
75
+ _TAG_START = re.compile(r"\{\{(~)?")
76
+ _INVERSE_RE = re.compile(r"(?:\^|" + JS_WS + r"*else)" + JS_WS + r"*(~)?\}\}")
77
+ _CHAIN_RE = re.compile(JS_WS + r"*else\b")
78
+
79
+
80
+ def _find_close(source: str, pos: int, closer: str) -> int:
81
+ """Index of the ``[~]closer`` ending a mustache, skipping string literals."""
82
+ quote = ""
83
+ i = pos
84
+ while i < len(source):
85
+ ch = source[i]
86
+ if quote:
87
+ if ch == "\\" and i + 1 < len(source):
88
+ i += 2
89
+ continue
90
+ if ch == quote:
91
+ quote = ""
92
+ elif ch in "\"'":
93
+ quote = ch
94
+ elif source.startswith(closer, i):
95
+ return i
96
+ i += 1
97
+ raise TemplateSyntaxError(f"Unclosed mustache starting near offset {pos}")
98
+
99
+
100
+ def _tokenize(source: str) -> list[_Token]:
101
+ tokens: list[_Token] = []
102
+ pos = 0
103
+ while pos < len(source):
104
+ start = source.find("{{", pos)
105
+ if start < 0:
106
+ tokens.append(source[pos:])
107
+ break
108
+ if start > pos:
109
+ if source[start - 1] == "\\":
110
+ raise TemplateSyntaxError("Escaped mustaches (\\{{) are not supported")
111
+ tokens.append(source[pos:start])
112
+ if source.startswith("{{{{", start):
113
+ raise TemplateSyntaxError("Raw blocks are not supported")
114
+ m = _TAG_START.match(source, start)
115
+ assert m is not None
116
+ open_strip = m.group(1) is not None
117
+ i = m.end()
118
+
119
+ if source.startswith("!--", i):
120
+ end = re.compile(r"--(~)?\}\}").search(source, i + 3)
121
+ if end is None:
122
+ raise TemplateSyntaxError("Unclosed comment")
123
+ tokens.append(_Tag("comment", "", _Strip(open_strip, end.group(1) is not None)))
124
+ pos = end.end()
125
+ continue
126
+ if source.startswith("!", i):
127
+ end_idx = source.find("}}", i)
128
+ if end_idx < 0:
129
+ raise TemplateSyntaxError("Unclosed comment")
130
+ text = source[start : end_idx + 2]
131
+ tokens.append(_Tag("comment", "", _Strip(open_strip, text[-3] == "~")))
132
+ pos = end_idx + 2
133
+ continue
134
+
135
+ inverse = _INVERSE_RE.match(source, i)
136
+ if inverse:
137
+ tokens.append(_Tag("inverse", "", _Strip(open_strip, inverse.group(1) is not None)))
138
+ pos = inverse.end()
139
+ continue
140
+
141
+ closer = "}}"
142
+ ch = source[i : i + 1]
143
+ if source.startswith("#>", i) or source.startswith("#*", i) or ch in (">", "*"):
144
+ raise TemplateSyntaxError("Partials and decorators are not supported")
145
+ if ch == "#":
146
+ kind, i = "open", i + 1
147
+ elif ch == "/":
148
+ kind, i = "close", i + 1
149
+ elif ch == "^":
150
+ kind, i = "open_inverse", i + 1
151
+ elif _CHAIN_RE.match(source, i):
152
+ chain = _CHAIN_RE.match(source, i)
153
+ assert chain is not None
154
+ kind, i = "open_chain", chain.end()
155
+ elif ch == "{":
156
+ kind, i, closer = "triple", i + 1, "}}}"
157
+ elif ch == "&":
158
+ kind, i = "amp", i + 1
159
+ else:
160
+ kind = "mustache"
161
+ end_idx = _find_close(source, i, closer)
162
+ expr = source[i:end_idx]
163
+ close_strip = expr.endswith("~")
164
+ if close_strip:
165
+ expr = expr[:-1]
166
+ tokens.append(_Tag(kind, expr.strip(), _Strip(open_strip, close_strip)))
167
+ pos = end_idx + len(closer)
168
+ return tokens
169
+
170
+
171
+ # ---------------------------------------------------------------------------
172
+ # AST (mirrors the handlebars.js node shapes the whitespace pass looks at)
173
+ # ---------------------------------------------------------------------------
174
+
175
+
176
+ @dataclass
177
+ class _Content:
178
+ original: str
179
+ value: str
180
+ left_stripped: bool = False
181
+ right_stripped: bool = False
182
+
183
+
184
+ @dataclass
185
+ class _Mustache:
186
+ kind: str
187
+ expr: str
188
+ strip: _Strip
189
+
190
+
191
+ @dataclass
192
+ class _Comment:
193
+ strip: _Strip
194
+
195
+
196
+ @dataclass
197
+ class _Program:
198
+ body: list[_Node] = field(default_factory=list)
199
+ chained: bool = False
200
+
201
+
202
+ @dataclass
203
+ class _Block:
204
+ name: str
205
+ expr: str
206
+ inverted: bool
207
+ # Source-order programs, used to emit the pybars source.
208
+ first: _Program
209
+ second: _Program | None
210
+ # handlebars' view after `prepareBlock` swaps inverted blocks; used for whitespace control.
211
+ program: _Program | None
212
+ inverse: _Program | None
213
+ open_strip: _Strip
214
+ inverse_strip: _Strip | None
215
+ close_strip: _Strip | None
216
+
217
+
218
+ _Node = _Content | _Mustache | _Comment | _Block
219
+
220
+
221
+ @dataclass
222
+ class _InverseAndProgram:
223
+ strip: _Strip
224
+ program: _Program
225
+ chain: bool = False
226
+
227
+
228
+ def _helper_name(expr: str) -> str:
229
+ match = re.match(r"[^\s()]+", expr)
230
+ if not match:
231
+ raise TemplateSyntaxError("Block is missing a helper name")
232
+ return match.group(0)
233
+
234
+
235
+ class _Parser:
236
+ def __init__(self, tokens: list[_Token]) -> None:
237
+ self.tokens = tokens
238
+ self.pos = 0
239
+
240
+ def peek(self) -> _Token | None:
241
+ return self.tokens[self.pos] if self.pos < len(self.tokens) else None
242
+
243
+ def next(self) -> _Token:
244
+ token = self.tokens[self.pos]
245
+ self.pos += 1
246
+ return token
247
+
248
+ def parse_root(self) -> _Program:
249
+ program = self.parse_program()
250
+ if self.peek() is not None:
251
+ tag = self.peek()
252
+ assert isinstance(tag, _Tag)
253
+ raise TemplateSyntaxError(f"Unexpected {tag.kind} tag {tag.expr!r}")
254
+ return program
255
+
256
+ def parse_program(self) -> _Program:
257
+ program = _Program()
258
+ while (token := self.peek()) is not None:
259
+ if isinstance(token, str):
260
+ self.next()
261
+ program.body.append(_Content(token, token))
262
+ continue
263
+ if token.kind in ("inverse", "open_chain", "close"):
264
+ break
265
+ self.next()
266
+ if token.kind == "comment":
267
+ program.body.append(_Comment(token.strip))
268
+ elif token.kind in ("open", "open_inverse"):
269
+ program.body.append(self.parse_block(token))
270
+ else:
271
+ program.body.append(_Mustache(token.kind, token.expr, token.strip))
272
+ return program
273
+
274
+ def parse_block(self, open_tag: _Tag) -> _Block:
275
+ program = self.parse_program()
276
+ inverse_and_program = self.parse_inverse()
277
+ close = self.next() if self.peek() is not None else None
278
+ if not isinstance(close, _Tag) or close.kind != "close":
279
+ raise TemplateSyntaxError(f"Unclosed block {open_tag.expr!r}")
280
+ name = _helper_name(open_tag.expr)
281
+ if close.expr != name:
282
+ raise TemplateSyntaxError(f"{name} doesn't match {close.expr}")
283
+ return self.prepare_block(open_tag, program, inverse_and_program, close.strip, open_tag.kind == "open_inverse")
284
+
285
+ def parse_inverse(self) -> _InverseAndProgram | None:
286
+ token = self.peek()
287
+ if not isinstance(token, _Tag):
288
+ return None
289
+ if token.kind == "inverse":
290
+ self.next()
291
+ return _InverseAndProgram(token.strip, self.parse_program())
292
+ if token.kind == "open_chain":
293
+ self.next()
294
+ program = self.parse_program()
295
+ nested = self.parse_inverse()
296
+ # handlebars passes the nested chain as both the inverse and the "close" of the chained block.
297
+ block = self.prepare_block(token, program, nested, nested.strip if nested else None, False)
298
+ return _InverseAndProgram(token.strip, _Program([block], chained=True), chain=True)
299
+ return None
300
+
301
+ @staticmethod
302
+ def prepare_block(
303
+ open_tag: _Tag,
304
+ program: _Program,
305
+ inverse_and_program: _InverseAndProgram | None,
306
+ close_strip: _Strip | None,
307
+ inverted: bool,
308
+ ) -> _Block:
309
+ inverse: _Program | None = None
310
+ inverse_strip: _Strip | None = None
311
+ if inverse_and_program:
312
+ if inverse_and_program.chain:
313
+ chained = inverse_and_program.program.body[0]
314
+ assert isinstance(chained, _Block)
315
+ chained.close_strip = close_strip
316
+ inverse_strip = inverse_and_program.strip
317
+ inverse = inverse_and_program.program
318
+ hb_program: _Program | None = program
319
+ hb_inverse = inverse
320
+ if inverted:
321
+ hb_program, hb_inverse = inverse, program
322
+ return _Block(
323
+ name=_helper_name(open_tag.expr),
324
+ expr=open_tag.expr,
325
+ inverted=inverted,
326
+ first=program,
327
+ second=inverse,
328
+ program=hb_program,
329
+ inverse=hb_inverse,
330
+ open_strip=open_tag.strip,
331
+ inverse_strip=inverse_strip,
332
+ close_strip=close_strip,
333
+ )
334
+
335
+
336
+ # ---------------------------------------------------------------------------
337
+ # WhitespaceControl (port of handlebars/compiler/whitespace-control.js)
338
+ # ---------------------------------------------------------------------------
339
+
340
+ _PREV_WS = re.compile(r"\r?\n" + JS_WS + r"*?\Z")
341
+ _PREV_WS_ROOT = re.compile(r"(?:^|\r?\n)" + JS_WS + r"*?\Z")
342
+ _NEXT_WS = re.compile(JS_WS + r"*?\r?\n")
343
+ _NEXT_WS_ROOT = re.compile(JS_WS + r"*?(?:\r?\n|\Z)")
344
+ _OMIT_RIGHT = re.compile(r"^[ \t]*\r?\n?")
345
+ _OMIT_RIGHT_MULTI = re.compile("^" + JS_WS + "+")
346
+ _OMIT_LEFT = re.compile(r"[ \t]+\Z")
347
+ _OMIT_LEFT_MULTI = re.compile(JS_WS + r"+\Z")
348
+
349
+
350
+ @dataclass
351
+ class _StripInfo:
352
+ open: bool = False
353
+ close: bool = False
354
+ open_standalone: bool = False
355
+ close_standalone: bool = False
356
+ inline_standalone: bool = False
357
+
358
+
359
+ def _is_prev_whitespace(body: list[_Node], i: int | None = None, is_root: bool = False) -> bool:
360
+ if i is None:
361
+ i = len(body)
362
+ prev = body[i - 1] if i - 1 >= 0 else None
363
+ sibling = body[i - 2] if i - 2 >= 0 else None
364
+ if prev is None:
365
+ return is_root
366
+ if isinstance(prev, _Content):
367
+ pattern = _PREV_WS if (sibling is not None or not is_root) else _PREV_WS_ROOT
368
+ return pattern.search(prev.original) is not None
369
+ return False
370
+
371
+
372
+ def _is_next_whitespace(body: list[_Node], i: int | None = None, is_root: bool = False) -> bool:
373
+ if i is None:
374
+ i = -1
375
+ nxt = body[i + 1] if i + 1 < len(body) else None
376
+ sibling = body[i + 2] if i + 2 < len(body) else None
377
+ if nxt is None:
378
+ return is_root
379
+ if isinstance(nxt, _Content):
380
+ pattern = _NEXT_WS if (sibling is not None or not is_root) else _NEXT_WS_ROOT
381
+ return pattern.match(nxt.original) is not None
382
+ return False
383
+
384
+
385
+ def _omit_right(body: list[_Node], i: int | None = None, multiple: bool = False) -> None:
386
+ idx = 0 if i is None else i + 1
387
+ current = body[idx] if idx < len(body) else None
388
+ if not isinstance(current, _Content) or (not multiple and current.right_stripped):
389
+ return
390
+ original = current.value
391
+ current.value = (_OMIT_RIGHT_MULTI if multiple else _OMIT_RIGHT).sub("", current.value, count=1)
392
+ current.right_stripped = current.value != original
393
+
394
+
395
+ def _omit_left(body: list[_Node], i: int | None = None, multiple: bool = False) -> bool:
396
+ idx = len(body) - 1 if i is None else i - 1
397
+ current = body[idx] if idx >= 0 else None
398
+ if not isinstance(current, _Content) or (not multiple and current.left_stripped):
399
+ return False
400
+ original = current.value
401
+ current.value = (_OMIT_LEFT_MULTI if multiple else _OMIT_LEFT).sub("", current.value, count=1)
402
+ current.left_stripped = current.value != original
403
+ return current.left_stripped
404
+
405
+
406
+ class _WhitespaceControl:
407
+ def __init__(self) -> None:
408
+ self.root_seen = False
409
+
410
+ def accept(self, node: _Node | _Program | None) -> _StripInfo | None:
411
+ if isinstance(node, _Program):
412
+ self.program(node)
413
+ return None
414
+ if isinstance(node, _Block):
415
+ return self.block(node)
416
+ if isinstance(node, _Mustache):
417
+ return _StripInfo(open=node.strip.open, close=node.strip.close)
418
+ if isinstance(node, _Comment):
419
+ return _StripInfo(open=node.strip.open, close=node.strip.close, inline_standalone=True)
420
+ return None
421
+
422
+ def program(self, program: _Program) -> None:
423
+ is_root = not self.root_seen
424
+ self.root_seen = True
425
+ body = program.body
426
+ for i, current in enumerate(body):
427
+ strip = self.accept(current)
428
+ if strip is None:
429
+ continue
430
+ prev_ws = _is_prev_whitespace(body, i, is_root)
431
+ next_ws = _is_next_whitespace(body, i, is_root)
432
+ open_standalone = strip.open_standalone and prev_ws
433
+ close_standalone = strip.close_standalone and next_ws
434
+ inline_standalone = strip.inline_standalone and prev_ws and next_ws
435
+
436
+ if strip.close:
437
+ _omit_right(body, i, True)
438
+ if strip.open:
439
+ _omit_left(body, i, True)
440
+ if inline_standalone:
441
+ _omit_right(body, i)
442
+ _omit_left(body, i)
443
+ if open_standalone:
444
+ assert isinstance(current, _Block)
445
+ target = current.program or current.inverse
446
+ assert target is not None
447
+ _omit_right(target.body)
448
+ _omit_left(body, i)
449
+ if close_standalone:
450
+ assert isinstance(current, _Block)
451
+ _omit_right(body, i)
452
+ target = current.inverse or current.program
453
+ assert target is not None
454
+ _omit_left(target.body)
455
+
456
+ def block(self, block: _Block) -> _StripInfo:
457
+ self.accept(block.program)
458
+ self.accept(block.inverse)
459
+
460
+ program = block.program or block.inverse
461
+ assert program is not None
462
+ inverse = block.inverse if block.program else None
463
+ first_inverse = last_inverse = inverse
464
+ if inverse is not None and inverse.chained:
465
+ chained = inverse.body[0]
466
+ assert isinstance(chained, _Block) and chained.program is not None
467
+ first_inverse = chained.program
468
+ while last_inverse is not None and last_inverse.chained:
469
+ tail = last_inverse.body[-1]
470
+ assert isinstance(tail, _Block)
471
+ last_inverse = tail.program
472
+
473
+ close_strip = block.close_strip
474
+ if close_strip is None:
475
+ # `block.closeStrip.open` would throw in handlebars.js; only reachable for malformed chains.
476
+ raise TemplateSyntaxError("Block is missing its close tag")
477
+ strip = _StripInfo(
478
+ open=block.open_strip.open,
479
+ close=close_strip.close,
480
+ open_standalone=_is_next_whitespace(program.body),
481
+ close_standalone=_is_prev_whitespace((first_inverse or program).body),
482
+ )
483
+
484
+ if block.open_strip.close:
485
+ _omit_right(program.body, None, True)
486
+
487
+ if inverse is not None:
488
+ assert first_inverse is not None and last_inverse is not None and block.inverse_strip is not None
489
+ if block.inverse_strip.open:
490
+ _omit_left(program.body, None, True)
491
+ if block.inverse_strip.close:
492
+ _omit_right(first_inverse.body, None, True)
493
+ if close_strip.open:
494
+ _omit_left(last_inverse.body, None, True)
495
+ if _is_prev_whitespace(program.body) and _is_next_whitespace(first_inverse.body):
496
+ _omit_left(program.body)
497
+ _omit_right(first_inverse.body)
498
+ elif close_strip.open:
499
+ _omit_left(program.body, None, True)
500
+
501
+ return strip
502
+
503
+
504
+ # ---------------------------------------------------------------------------
505
+ # Emitting pybars source
506
+ # ---------------------------------------------------------------------------
507
+
508
+
509
+ def _emit_program(program: _Program, out: list[str]) -> None:
510
+ for node in program.body:
511
+ _emit_node(node, out)
512
+
513
+
514
+ def _emit_node(node: _Node, out: list[str]) -> None:
515
+ if isinstance(node, _Content):
516
+ out.append(node.value)
517
+ elif isinstance(node, _Mustache):
518
+ if node.kind == "triple":
519
+ out.append("{{{" + node.expr + "}}}")
520
+ elif node.kind == "amp":
521
+ out.append("{{&" + node.expr + "}}")
522
+ else:
523
+ out.append("{{" + node.expr + "}}")
524
+ elif isinstance(node, _Block):
525
+ out.append(("{{^" if node.inverted else "{{#") + node.expr + "}}")
526
+ _emit_program(node.first, out)
527
+ if node.second is not None:
528
+ out.append("{{else}}")
529
+ # A chained inverse holds a single block, emitted as a nested block.
530
+ _emit_program(node.second, out)
531
+ out.append("{{/" + node.name + "}}")
532
+ # Comments render nothing.
533
+
534
+
535
+ def preprocess(source: str) -> str:
536
+ """Return pybars source whose literal whitespace already matches handlebars.js output."""
537
+ root = _Parser(_tokenize(source)).parse_root()
538
+ _WhitespaceControl().accept(root)
539
+ out: list[str] = []
540
+ _emit_program(root, out)
541
+ return "".join(out)
542
+
543
+
544
+ # ---------------------------------------------------------------------------
545
+ # Runtime: JS-faithful replacements for pybars globals and built-in helpers
546
+ # ---------------------------------------------------------------------------
547
+
548
+ _ESCAPE_MAP = {"&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#x27;", "`": "&#x60;", "=": "&#x3D;"}
549
+ _ESCAPE_RE = re.compile("[&<>\"'`=]")
550
+
551
+
552
+ def escape_expression(value: object) -> str:
553
+ """``Handlebars.Utils.escapeExpression``."""
554
+ if isinstance(value, strlist):
555
+ return str(value)
556
+ if value is None:
557
+ return ""
558
+ text = js_to_string(value)
559
+ return _ESCAPE_RE.sub(lambda m: _ESCAPE_MAP[m.group(0)], text)
560
+
561
+
562
+ def _prepare(value: object, should_escape: bool) -> Any:
563
+ if isinstance(value, strlist):
564
+ return value
565
+ if should_escape:
566
+ return escape_expression(value)
567
+ return "" if value is None else js_to_string(value)
568
+
569
+
570
+ def _property(obj: object, name: str) -> Any:
571
+ if isinstance(obj, Scope):
572
+ return obj.get(name)
573
+ if isinstance(obj, Mapping):
574
+ return obj.get(name)
575
+ if isinstance(obj, (list, tuple, str)):
576
+ if name == "length":
577
+ return len(obj)
578
+ if name.isdigit() and int(name) < len(obj):
579
+ return obj[int(name)]
580
+ return None
581
+ return None
582
+
583
+
584
+ def _resolve(context: Any, *segments: str) -> Any:
585
+ # Same segment conventions as pybars.resolve ('' = this, '@_parent' = ../), JS property access.
586
+ if segments == ("",) and isinstance(context, Scope):
587
+ return context.get("this")
588
+ carryover = False
589
+ for segment in segments:
590
+ if carryover:
591
+ carryover = False
592
+ segment = "@" + segment
593
+ if segment.startswith("@@"):
594
+ segment = segment[1:]
595
+ carryover = True
596
+ if context is None:
597
+ return None
598
+ if segment == "":
599
+ continue
600
+ context = _property(context, segment)
601
+ return context
602
+
603
+
604
+ def _unwrap(value: Any) -> Any:
605
+ return value.get("this") if isinstance(value, Scope) else value
606
+
607
+
608
+ def _helper_if(this: Any, options: Mapping[str, Any], conditional: Any = None, *rest: Any) -> Any:
609
+ if rest:
610
+ raise TemplateSyntaxError("#if requires exactly one argument")
611
+ conditional = _unwrap(conditional)
612
+ if not js_truthy(conditional) or hb_is_empty(conditional):
613
+ return options["inverse"](this)
614
+ return options["fn"](this)
615
+
616
+
617
+ def _helper_unless(this: Any, options: Mapping[str, Any], conditional: Any = None, *rest: Any) -> Any:
618
+ if rest:
619
+ raise TemplateSyntaxError("#unless requires exactly one argument")
620
+ conditional = _unwrap(conditional)
621
+ if not js_truthy(conditional) or hb_is_empty(conditional):
622
+ return options["fn"](this)
623
+ return options["inverse"](this)
624
+
625
+
626
+ def _helper_each(this: Any, options: Mapping[str, Any], context: Any = None) -> Any:
627
+ context = _unwrap(context)
628
+ result = strlist()
629
+ items: list[tuple[Any, Any]]
630
+ if isinstance(context, (list, tuple)):
631
+ items = list(enumerate(context))
632
+ elif isinstance(context, Mapping):
633
+ lookup = {(k if isinstance(k, str) else js_to_string(k)): v for k, v in context.items()}
634
+ items = [(key, lookup[key]) for key in js_object_keys(context)]
635
+ else:
636
+ items = []
637
+ if not items:
638
+ return options["inverse"](this)
639
+ last = len(items) - 1
640
+ for index, (key, value) in enumerate(items):
641
+ scope = Scope(value, this, options["root"], index=index, key=key, first=index == 0, last=index == last)
642
+ result.grow(options["fn"](scope))
643
+ return result
644
+
645
+
646
+ def _helper_missing(this: Any, name: str, *args: Any) -> Any:
647
+ if not args:
648
+ return None
649
+ raise TemplateSyntaxError(f'Missing helper: "{name}"')
650
+
651
+
652
+ def _block_helper_missing(this: Any, options: Mapping[str, Any], context: Any) -> Any:
653
+ # Blocks named after a data property ({{#prop}}): handlebars' blockHelperMissing.
654
+ if context is True:
655
+ return options["fn"](this)
656
+ if context is False or context is None:
657
+ return options["inverse"](this)
658
+ if isinstance(context, (list, tuple)):
659
+ return _helper_each(this, options, context) if context else options["inverse"](this)
660
+ return options["fn"](context)
661
+
662
+
663
+ # ---------------------------------------------------------------------------
664
+ # Custom helpers registered by upstream render.ts
665
+ # ---------------------------------------------------------------------------
666
+
667
+
668
+ def _helper_eq(this: Any, a: Any = None, b: Any = None, *_: Any) -> bool:
669
+ return js_strict_equals(_unwrap(a), _unwrap(b))
670
+
671
+
672
+ def _helper_includes(this: Any, array: Any = None, value: Any = None, *_: Any) -> bool:
673
+ array, value = _unwrap(array), _unwrap(value)
674
+ if not isinstance(array, (list, tuple)):
675
+ return False
676
+ return any(js_same_value_zero(item, value) for item in array)
677
+
678
+
679
+ def _require_str(value: Any, helper: str) -> str:
680
+ if not isinstance(value, str):
681
+ # JS would throw calling String.prototype.replace on a non-string.
682
+ raise TypeError(f"{helper}: expected a string, got {js_to_string(value)!r}")
683
+ return value
684
+
685
+
686
+ def _non_alnum_to_underscore(value: str) -> str:
687
+ # JS regexes replace per UTF-16 code unit, so astral characters become two underscores.
688
+ return "".join(c if c.isascii() and c.isalnum() else "_" * utf16_len(c) for c in value)
689
+
690
+
691
+ def _helper_snake_case(this: Any, value: Any = None, *_: Any) -> str:
692
+ return _non_alnum_to_underscore(_require_str(_unwrap(value), "snakeCase")).lower()
693
+
694
+
695
+ def _helper_path_slug(this: Any, value: Any = None, *_: Any) -> str:
696
+ slug = _non_alnum_to_underscore(_require_str(_unwrap(value), "pathSlug"))
697
+ return re.sub(r"_+", "_", slug.lstrip("_")).lower()
698
+
699
+
700
+ def _helper_safe_json(this: Any, value: Any = None, *_: Any) -> SafeString:
701
+ text = json_stringify(_unwrap(value))
702
+ return SafeString("undefined" if text is None else text)
703
+
704
+
705
+ def _helper_py_json_str(this: Any, value: Any = None, *_: Any) -> SafeString:
706
+ text = json_stringify(_unwrap(value))
707
+ return SafeString("undefined" if text is None else (json_stringify(text) or ""))
708
+
709
+
710
+ def _helper_escape_py_str(this: Any, value: Any = None, *_: Any) -> SafeString:
711
+ value = _unwrap(value)
712
+ text = value if isinstance(value, str) else ""
713
+ return SafeString(text.replace("\\", "\\\\").replace('"""', '\\"\\"\\"'))
714
+
715
+
716
+ def _helper_some(this: Any, array: Any = None, key: Any = None, *_: Any) -> bool:
717
+ array = _unwrap(array)
718
+ if not isinstance(array, (list, tuple)):
719
+ return False
720
+ key = _unwrap(key)
721
+ return any(isinstance(item, Mapping) and key in item and js_truthy(item[key]) for item in array)
722
+
723
+
724
+ def _helper_or(this: Any, *args: Any) -> bool:
725
+ return any(js_truthy(_unwrap(arg)) for arg in args)
726
+
727
+
728
+ HELPERS: dict[str, Callable[..., Any]] = {
729
+ "if": _helper_if,
730
+ "unless": _helper_unless,
731
+ "each": _helper_each,
732
+ "helperMissing": _helper_missing,
733
+ "blockHelperMissing": _block_helper_missing,
734
+ "eq": _helper_eq,
735
+ "includes": _helper_includes,
736
+ "snakeCase": _helper_snake_case,
737
+ "pathSlug": _helper_path_slug,
738
+ "safeJson": _helper_safe_json,
739
+ "pyJsonStr": _helper_py_json_str,
740
+ "escapePyStr": _helper_escape_py_str,
741
+ "some": _helper_some,
742
+ "or": _helper_or,
743
+ }
744
+
745
+
746
+ # ---------------------------------------------------------------------------
747
+ # Compilation
748
+ # ---------------------------------------------------------------------------
749
+
750
+
751
+ class _Compiler(pybars.Compiler):
752
+ def whitespace_control(self, source: str) -> str:
753
+ # Whitespace was already resolved by `preprocess`.
754
+ return source
755
+
756
+
757
+ # pybars' code builder is class-level state, so compilation is serialized.
758
+ _COMPILE_LOCK = threading.Lock()
759
+
760
+ Template = Callable[[Mapping[str, Any]], str]
761
+
762
+
763
+ @lru_cache(maxsize=512)
764
+ def compile_template(source: str) -> Template:
765
+ """Compile Handlebars ``source`` into a function rendering a data mapping to a string."""
766
+ pybars_source = preprocess(source)
767
+ with _COMPILE_LOCK:
768
+ code = _Compiler()._generate_code(pybars_source).full_code
769
+ namespace: dict[str, Any] = {}
770
+ exec(compile(code, "<handlebars-template>", "exec"), namespace) # noqa: S102 - pybars-generated code
771
+ namespace["prepare"] = _prepare
772
+ namespace["resolve"] = _resolve
773
+ render = namespace["render"]
774
+
775
+ def template(data: Mapping[str, Any]) -> str:
776
+ return str(render(data, helpers=HELPERS))
777
+
778
+ return template
779
+
780
+
781
+ def render_template(source: str, data: Mapping[str, Any]) -> str:
782
+ return compile_template(source)(data)