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.
- agentcore/__init__.py +1 -0
- agentcore/agent_inspector/NOTICE +9 -0
- agentcore/agent_inspector/favicon.svg +60 -0
- agentcore/agent_inspector/index.css +1 -0
- agentcore/agent_inspector/index.html +14 -0
- agentcore/agent_inspector/index.js +289 -0
- agentcore/assets/AGENTS.md +60 -0
- agentcore/assets/README.md +108 -0
- agentcore/assets/agents/AGENTS.md +218 -0
- agentcore/assets/cdk/.prettierrc +8 -0
- agentcore/assets/cdk/README.md +26 -0
- agentcore/assets/cdk/bin/cdk.ts +216 -0
- agentcore/assets/cdk/cdk.json +88 -0
- agentcore/assets/cdk/gitignore.template +9 -0
- agentcore/assets/cdk/jest.config.js +9 -0
- agentcore/assets/cdk/lib/cdk-stack.ts +291 -0
- agentcore/assets/cdk/npmignore.template +6 -0
- agentcore/assets/cdk/package.json +30 -0
- agentcore/assets/cdk/test/cdk.test.ts +31 -0
- agentcore/assets/cdk/tsconfig.json +28 -0
- agentcore/assets/container/python/Dockerfile +47 -0
- agentcore/assets/container/python/dockerignore.template +27 -0
- agentcore/assets/container/typescript/Dockerfile +25 -0
- agentcore/assets/container/typescript/dockerignore.template +24 -0
- agentcore/assets/datasets/predefined-v1.jsonl +3 -0
- agentcore/assets/datasets/simulated-v1.jsonl +3 -0
- agentcore/assets/evaluators/autoevals-lambda/execution-role-policy.json +15 -0
- agentcore/assets/evaluators/autoevals-lambda/lambda_function.py +37 -0
- agentcore/assets/evaluators/autoevals-lambda/pyproject.toml +22 -0
- agentcore/assets/evaluators/deepeval-lambda/execution-role-policy.json +15 -0
- agentcore/assets/evaluators/deepeval-lambda/lambda_function.py +29 -0
- agentcore/assets/evaluators/deepeval-lambda/pyproject.toml +19 -0
- agentcore/assets/evaluators/python-lambda/execution-role-policy.json +10 -0
- agentcore/assets/evaluators/python-lambda/lambda_function.py +19 -0
- agentcore/assets/evaluators/python-lambda/pyproject.toml +15 -0
- agentcore/assets/harness/invoke.py.template +74 -0
- agentcore/assets/mcp/python/README.md +27 -0
- agentcore/assets/mcp/python/pyproject.toml +22 -0
- agentcore/assets/mcp/python/server.py +117 -0
- agentcore/assets/mcp/python-lambda/README.md +22 -0
- agentcore/assets/mcp/python-lambda/handler.py +144 -0
- agentcore/assets/mcp/python-lambda/pyproject.toml +15 -0
- agentcore/assets/python/a2a/googleadk/base/README.md +22 -0
- agentcore/assets/python/a2a/googleadk/base/gitignore.template +41 -0
- agentcore/assets/python/a2a/googleadk/base/main.py +124 -0
- agentcore/assets/python/a2a/googleadk/base/model/__init__.py +1 -0
- agentcore/assets/python/a2a/googleadk/base/model/load.py +41 -0
- agentcore/assets/python/a2a/googleadk/base/pyproject.toml +22 -0
- agentcore/assets/python/a2a/langchain_langgraph/base/README.md +22 -0
- agentcore/assets/python/a2a/langchain_langgraph/base/gitignore.template +41 -0
- agentcore/assets/python/a2a/langchain_langgraph/base/main.py +144 -0
- agentcore/assets/python/a2a/langchain_langgraph/base/model/__init__.py +1 -0
- agentcore/assets/python/a2a/langchain_langgraph/base/model/load.py +123 -0
- agentcore/assets/python/a2a/langchain_langgraph/base/pyproject.toml +26 -0
- agentcore/assets/python/a2a/strands/base/README.md +22 -0
- agentcore/assets/python/a2a/strands/base/gitignore.template +41 -0
- agentcore/assets/python/a2a/strands/base/main.py +113 -0
- agentcore/assets/python/a2a/strands/base/model/__init__.py +1 -0
- agentcore/assets/python/a2a/strands/base/model/load.py +123 -0
- agentcore/assets/python/a2a/strands/base/pyproject.toml +23 -0
- agentcore/assets/python/a2a/strands/capabilities/memory/__init__.py +1 -0
- agentcore/assets/python/a2a/strands/capabilities/memory/session.py +46 -0
- agentcore/assets/python/agui/googleadk/base/README.md +30 -0
- agentcore/assets/python/agui/googleadk/base/gitignore.template +41 -0
- agentcore/assets/python/agui/googleadk/base/main.py +91 -0
- agentcore/assets/python/agui/googleadk/base/model/__init__.py +1 -0
- agentcore/assets/python/agui/googleadk/base/model/load.py +41 -0
- agentcore/assets/python/agui/googleadk/base/pyproject.toml +25 -0
- agentcore/assets/python/agui/langchain_langgraph/base/README.md +22 -0
- agentcore/assets/python/agui/langchain_langgraph/base/gitignore.template +41 -0
- agentcore/assets/python/agui/langchain_langgraph/base/main.py +130 -0
- agentcore/assets/python/agui/langchain_langgraph/base/model/__init__.py +1 -0
- agentcore/assets/python/agui/langchain_langgraph/base/model/load.py +123 -0
- agentcore/assets/python/agui/langchain_langgraph/base/pyproject.toml +31 -0
- agentcore/assets/python/agui/strands/base/README.md +22 -0
- agentcore/assets/python/agui/strands/base/gitignore.template +41 -0
- agentcore/assets/python/agui/strands/base/main.py +105 -0
- agentcore/assets/python/agui/strands/base/model/__init__.py +1 -0
- agentcore/assets/python/agui/strands/base/model/load.py +123 -0
- agentcore/assets/python/agui/strands/base/pyproject.toml +27 -0
- agentcore/assets/python/agui/strands/capabilities/memory/__init__.py +1 -0
- agentcore/assets/python/agui/strands/capabilities/memory/session.py +46 -0
- agentcore/assets/python/http/autogen/base/README.md +45 -0
- agentcore/assets/python/http/autogen/base/gitignore.template +41 -0
- agentcore/assets/python/http/autogen/base/main.py +145 -0
- agentcore/assets/python/http/autogen/base/mcp_client/__init__.py +1 -0
- agentcore/assets/python/http/autogen/base/mcp_client/client.py +28 -0
- agentcore/assets/python/http/autogen/base/model/__init__.py +1 -0
- agentcore/assets/python/http/autogen/base/model/load.py +136 -0
- agentcore/assets/python/http/autogen/base/pyproject.toml +34 -0
- agentcore/assets/python/http/bma/base/Dockerfile +41 -0
- agentcore/assets/python/http/bma/base/README.md +178 -0
- agentcore/assets/python/http/bma/base/client.py +200 -0
- agentcore/assets/python/http/bma/base/lifecycle/server.py +641 -0
- agentcore/assets/python/http/bma/base/otel/collector.yaml +69 -0
- agentcore/assets/python/http/bma/base/plugins/acr-report/.codex-plugin/plugin.json +6 -0
- agentcore/assets/python/http/bma/base/plugins/acr-report/skills/acr-report/SKILL.md +16 -0
- agentcore/assets/python/http/bma/base/policies/bma-acr-policy.json +11 -0
- agentcore/assets/python/http/bma/base/policies/bma-session-policy.json +35 -0
- agentcore/assets/python/http/bma/base/policies/bma-session-trust.json +20 -0
- agentcore/assets/python/http/bma/base/pyproject.toml +21 -0
- agentcore/assets/python/http/googleadk/base/README.md +45 -0
- agentcore/assets/python/http/googleadk/base/gitignore.template +41 -0
- agentcore/assets/python/http/googleadk/base/main.py +207 -0
- agentcore/assets/python/http/googleadk/base/mcp_client/__init__.py +1 -0
- agentcore/assets/python/http/googleadk/base/mcp_client/client.py +75 -0
- agentcore/assets/python/http/googleadk/base/model/__init__.py +1 -0
- agentcore/assets/python/http/googleadk/base/model/load.py +41 -0
- agentcore/assets/python/http/googleadk/base/pyproject.toml +24 -0
- agentcore/assets/python/http/langchain_langgraph/base/README.md +45 -0
- agentcore/assets/python/http/langchain_langgraph/base/gitignore.template +41 -0
- agentcore/assets/python/http/langchain_langgraph/base/main.py +227 -0
- agentcore/assets/python/http/langchain_langgraph/base/mcp_client/__init__.py +1 -0
- agentcore/assets/python/http/langchain_langgraph/base/mcp_client/client.py +79 -0
- agentcore/assets/python/http/langchain_langgraph/base/model/__init__.py +1 -0
- agentcore/assets/python/http/langchain_langgraph/base/model/load.py +123 -0
- agentcore/assets/python/http/langchain_langgraph/base/pyproject.toml +38 -0
- agentcore/assets/python/http/openaiagents/base/README.md +45 -0
- agentcore/assets/python/http/openaiagents/base/gitignore.template +41 -0
- agentcore/assets/python/http/openaiagents/base/main.py +200 -0
- agentcore/assets/python/http/openaiagents/base/mcp_client/__init__.py +1 -0
- agentcore/assets/python/http/openaiagents/base/mcp_client/client.py +74 -0
- agentcore/assets/python/http/openaiagents/base/model/__init__.py +1 -0
- agentcore/assets/python/http/openaiagents/base/model/load.py +37 -0
- agentcore/assets/python/http/openaiagents/base/pyproject.toml +21 -0
- agentcore/assets/python/http/strands/base/README.md +46 -0
- agentcore/assets/python/http/strands/base/gitignore.template +41 -0
- agentcore/assets/python/http/strands/base/main.py +714 -0
- agentcore/assets/python/http/strands/base/mcp_client/__init__.py +1 -0
- agentcore/assets/python/http/strands/base/mcp_client/client.py +116 -0
- agentcore/assets/python/http/strands/base/model/__init__.py +1 -0
- agentcore/assets/python/http/strands/base/model/load.py +239 -0
- agentcore/assets/python/http/strands/base/model/mantle_compat.py +21 -0
- agentcore/assets/python/http/strands/base/pyproject.toml +31 -0
- agentcore/assets/python/http/strands/base/skills/fetcher.py +279 -0
- agentcore/assets/python/http/strands/capabilities/execution-limits/hooks/execution_limits.py +54 -0
- agentcore/assets/python/http/strands/capabilities/memory/__init__.py +1 -0
- agentcore/assets/python/http/strands/capabilities/memory/session.py +47 -0
- agentcore/assets/python/http/strands/capabilities/payments/__init__.py +4 -0
- agentcore/assets/python/http/strands/capabilities/payments/payments.py +142 -0
- agentcore/assets/python/mcp/standalone/base/README.md +36 -0
- agentcore/assets/python/mcp/standalone/base/gitignore.template +41 -0
- agentcore/assets/python/mcp/standalone/base/main.py +83 -0
- agentcore/assets/python/mcp/standalone/base/pyproject.toml +16 -0
- agentcore/assets/typescript/.gitkeep +0 -0
- agentcore/assets/typescript/http/strands/base/README.md +42 -0
- agentcore/assets/typescript/http/strands/base/gitignore.template +22 -0
- agentcore/assets/typescript/http/strands/base/main.ts +149 -0
- agentcore/assets/typescript/http/strands/base/mcp_client/client.ts +11 -0
- agentcore/assets/typescript/http/strands/base/model/load.ts +102 -0
- agentcore/assets/typescript/http/strands/base/package.json +38 -0
- agentcore/assets/typescript/http/strands/base/tsconfig.json +19 -0
- agentcore/assets/typescript/http/strands/capabilities/memory/memory.ts +52 -0
- agentcore/assets/typescript/http/vercelai/base/README.md +42 -0
- agentcore/assets/typescript/http/vercelai/base/gitignore.template +22 -0
- agentcore/assets/typescript/http/vercelai/base/main.ts +70 -0
- agentcore/assets/typescript/http/vercelai/base/model/load.ts +121 -0
- agentcore/assets/typescript/http/vercelai/base/package.json +35 -0
- agentcore/assets/typescript/http/vercelai/base/tsconfig.json +19 -0
- agentcore/aws/__init__.py +263 -0
- agentcore/aws/_aws_errors.py +72 -0
- agentcore/aws/_http.py +176 -0
- agentcore/aws/_util.py +37 -0
- agentcore/aws/account.py +165 -0
- agentcore/aws/agentcore.py +971 -0
- agentcore/aws/agentcore_ab_tests.py +230 -0
- agentcore/aws/agentcore_batch_evaluation.py +227 -0
- agentcore/aws/agentcore_config_bundles.py +220 -0
- agentcore/aws/agentcore_control.py +994 -0
- agentcore/aws/agentcore_datasets.py +222 -0
- agentcore/aws/agentcore_harness.py +330 -0
- agentcore/aws/agentcore_payments.py +430 -0
- agentcore/aws/agentcore_recommendation.py +140 -0
- agentcore/aws/agui_parser.py +131 -0
- agentcore/aws/agui_types.py +342 -0
- agentcore/aws/api_client.py +87 -0
- agentcore/aws/arn.py +18 -0
- agentcore/aws/aws_context.py +23 -0
- agentcore/aws/bedrock.py +44 -0
- agentcore/aws/bedrock_agent.py +129 -0
- agentcore/aws/bedrock_import.py +252 -0
- agentcore/aws/bedrock_import_types.py +111 -0
- agentcore/aws/cloudwatch.py +136 -0
- agentcore/aws/connect_shell.py +266 -0
- agentcore/aws/iam.py +41 -0
- agentcore/aws/partition.py +58 -0
- agentcore/aws/policy_generation.py +116 -0
- agentcore/aws/region.py +54 -0
- agentcore/aws/retry.py +27 -0
- agentcore/aws/shell_framer.py +136 -0
- agentcore/aws/stage_endpoint.py +39 -0
- agentcore/aws/target_region.py +54 -0
- agentcore/aws/transaction_search.py +97 -0
- agentcore/cdk/__init__.py +10 -0
- agentcore/cdk/constants.py +13 -0
- agentcore/cdk/local_cdk_project.py +48 -0
- agentcore/cdk/toolkit.py +706 -0
- agentcore/cli/__init__.py +0 -0
- agentcore/cli/commands/__init__.py +0 -0
- agentcore/cli/commands/_jobs_common.py +39 -0
- agentcore/cli/commands/_run_wizards.py +639 -0
- agentcore/cli/commands/add/__init__.py +37 -0
- agentcore/cli/commands/add/_commander.py +52 -0
- agentcore/cli/commands/add/_common.py +47 -0
- agentcore/cli/commands/add/agent_wizard.py +528 -0
- agentcore/cli/commands/add/auth_options.py +154 -0
- agentcore/cli/commands/add/skill_action.py +105 -0
- agentcore/cli/commands/add/skill_command.py +87 -0
- agentcore/cli/commands/add/tool_action.py +160 -0
- agentcore/cli/commands/add/tool_command.py +117 -0
- agentcore/cli/commands/add/validate.py +333 -0
- agentcore/cli/commands/capacity_provider/__init__.py +86 -0
- agentcore/cli/commands/capacity_provider/action.py +114 -0
- agentcore/cli/commands/capacity_provider/constants.py +22 -0
- agentcore/cli/commands/config.py +91 -0
- agentcore/cli/commands/config_bundle.py +251 -0
- agentcore/cli/commands/create/__init__.py +643 -0
- agentcore/cli/commands/create/action.py +406 -0
- agentcore/cli/commands/create/dependencies.py +91 -0
- agentcore/cli/commands/create/harness_validate.py +82 -0
- agentcore/cli/commands/create/validate.py +195 -0
- agentcore/cli/commands/create/wizard.py +127 -0
- agentcore/cli/commands/dataset.py +119 -0
- agentcore/cli/commands/deploy/__init__.py +31 -0
- agentcore/cli/commands/deploy/_guards.py +25 -0
- agentcore/cli/commands/deploy/actions.py +840 -0
- agentcore/cli/commands/deploy/command.py +349 -0
- agentcore/cli/commands/deploy/progress.py +115 -0
- agentcore/cli/commands/deploy/types.py +68 -0
- agentcore/cli/commands/deploy/utils.py +43 -0
- agentcore/cli/commands/deploy/validate.py +21 -0
- agentcore/cli/commands/dev/__init__.py +5 -0
- agentcore/cli/commands/dev/_deps.py +50 -0
- agentcore/cli/commands/dev/browser_mode.py +270 -0
- agentcore/cli/commands/dev/command.py +450 -0
- agentcore/cli/commands/dev/terminal_mode.py +379 -0
- agentcore/cli/commands/exec/__init__.py +29 -0
- agentcore/cli/commands/exec/action.py +442 -0
- agentcore/cli/commands/exec/command.py +206 -0
- agentcore/cli/commands/exec/types.py +35 -0
- agentcore/cli/commands/export/__init__.py +114 -0
- agentcore/cli/commands/export/constants.py +21 -0
- agentcore/cli/commands/export/fetch_harness_spec.py +234 -0
- agentcore/cli/commands/export/harness_action.py +385 -0
- agentcore/cli/commands/export/harness_mapper.py +868 -0
- agentcore/cli/commands/export/harness_resolver.py +95 -0
- agentcore/cli/commands/export/types.py +90 -0
- agentcore/cli/commands/export/wizard.py +97 -0
- agentcore/cli/commands/feedback/__init__.py +69 -0
- agentcore/cli/commands/feedback/action.py +30 -0
- agentcore/cli/commands/feedback/consent_prompt.py +35 -0
- agentcore/cli/commands/feedback/wizard.py +49 -0
- agentcore/cli/commands/fetch/__init__.py +69 -0
- agentcore/cli/commands/fetch/action.py +62 -0
- agentcore/cli/commands/help.py +65 -0
- agentcore/cli/commands/import_/__init__.py +167 -0
- agentcore/cli/commands/import_/_js.py +72 -0
- agentcore/cli/commands/import_/actions.py +457 -0
- agentcore/cli/commands/import_/constants.py +45 -0
- agentcore/cli/commands/import_/import_evaluator.py +140 -0
- agentcore/cli/commands/import_/import_gateway.py +520 -0
- agentcore/cli/commands/import_/import_memory.py +128 -0
- agentcore/cli/commands/import_/import_online_eval.py +165 -0
- agentcore/cli/commands/import_/import_pipeline.py +145 -0
- agentcore/cli/commands/import_/import_runtime.py +214 -0
- agentcore/cli/commands/import_/import_utils.py +410 -0
- agentcore/cli/commands/import_/phase1_update.py +110 -0
- agentcore/cli/commands/import_/phase2_import.py +206 -0
- agentcore/cli/commands/import_/resource_import.py +228 -0
- agentcore/cli/commands/import_/template_utils.py +148 -0
- agentcore/cli/commands/import_/types.py +150 -0
- agentcore/cli/commands/import_/yaml_parser.py +265 -0
- agentcore/cli/commands/init.py +196 -0
- agentcore/cli/commands/invoke/__init__.py +32 -0
- agentcore/cli/commands/invoke/_deps.py +73 -0
- agentcore/cli/commands/invoke/action.py +840 -0
- agentcore/cli/commands/invoke/command.py +490 -0
- agentcore/cli/commands/invoke/constants.py +40 -0
- agentcore/cli/commands/invoke/guards.py +23 -0
- agentcore/cli/commands/invoke/interactive.py +604 -0
- agentcore/cli/commands/invoke/resolve.py +149 -0
- agentcore/cli/commands/invoke/resolve_prompt.py +60 -0
- agentcore/cli/commands/invoke/types.py +61 -0
- agentcore/cli/commands/invoke/validate.py +33 -0
- agentcore/cli/commands/job_actions.py +239 -0
- agentcore/cli/commands/job_history.py +380 -0
- agentcore/cli/commands/logs/__init__.py +53 -0
- agentcore/cli/commands/logs/action.py +159 -0
- agentcore/cli/commands/logs/filter_pattern.py +17 -0
- agentcore/cli/commands/logs/guards.py +51 -0
- agentcore/cli/commands/logs_evals.py +42 -0
- agentcore/cli/commands/package.py +118 -0
- agentcore/cli/commands/remove/__init__.py +172 -0
- agentcore/cli/commands/remove/skill_command.py +131 -0
- agentcore/cli/commands/remove/tool_command.py +59 -0
- agentcore/cli/commands/run.py +808 -0
- agentcore/cli/commands/run_ingest.py +97 -0
- agentcore/cli/commands/shared/__init__.py +0 -0
- agentcore/cli/commands/shared/arn_utils.py +9 -0
- agentcore/cli/commands/shared/filesystem_utils.py +421 -0
- agentcore/cli/commands/shared/header_utils.py +87 -0
- agentcore/cli/commands/shared/indexed_key_parser.py +76 -0
- agentcore/cli/commands/shared/lifecycle_utils.py +72 -0
- agentcore/cli/commands/shared/region_utils.py +20 -0
- agentcore/cli/commands/shared/vpc_utils.py +99 -0
- agentcore/cli/commands/status/__init__.py +30 -0
- agentcore/cli/commands/status/_guards.py +25 -0
- agentcore/cli/commands/status/action.py +741 -0
- agentcore/cli/commands/status/command.py +321 -0
- agentcore/cli/commands/status/constants.py +36 -0
- agentcore/cli/commands/status/format_knowledge_base.py +111 -0
- agentcore/cli/commands/telemetry.py +79 -0
- agentcore/cli/commands/traces/__init__.py +155 -0
- agentcore/cli/commands/traces/action.py +123 -0
- agentcore/cli/commands/update/__init__.py +63 -0
- agentcore/cli/commands/update/action.py +164 -0
- agentcore/cli/commands/validate.py +165 -0
- agentcore/cli/constants.py +96 -0
- agentcore/cli/errors.py +118 -0
- agentcore/cli/external_requirements/__init__.py +67 -0
- agentcore/cli/external_requirements/checks.py +280 -0
- agentcore/cli/external_requirements/detect.py +55 -0
- agentcore/cli/external_requirements/versions.py +57 -0
- agentcore/cli/groups.py +23 -0
- agentcore/cli/interactive.py +85 -0
- agentcore/cli/main.py +137 -0
- agentcore/cli/naming.py +11 -0
- agentcore/cli/notices.py +42 -0
- agentcore/cli/project.py +34 -0
- agentcore/cli/run_command.py +27 -0
- agentcore/cli/tree.py +135 -0
- agentcore/cli/update_notifier.py +123 -0
- agentcore/cli/variadic.py +50 -0
- agentcore/cloudformation/__init__.py +84 -0
- agentcore/cloudformation/bootstrap.py +64 -0
- agentcore/cloudformation/logical_ids.py +37 -0
- agentcore/cloudformation/outputs.py +566 -0
- agentcore/cloudformation/stack_discovery.py +75 -0
- agentcore/cloudformation/stack_events.py +107 -0
- agentcore/cloudformation/stack_status.py +98 -0
- agentcore/cloudformation/types.py +44 -0
- agentcore/lib/__init__.py +68 -0
- agentcore/lib/constants.py +77 -0
- agentcore/lib/dependency_management/__init__.py +23 -0
- agentcore/lib/dependency_management/messages.py +92 -0
- agentcore/lib/dependency_management/plan.py +98 -0
- agentcore/lib/dependency_management/semver.py +137 -0
- agentcore/lib/dependency_management/sync.py +139 -0
- agentcore/lib/dependency_management/types.py +56 -0
- agentcore/lib/errors/__init__.py +5 -0
- agentcore/lib/errors/types.py +352 -0
- agentcore/lib/errors/validation.py +121 -0
- agentcore/lib/packaging/__init__.py +81 -0
- agentcore/lib/packaging/build_args.py +23 -0
- agentcore/lib/packaging/build_context.py +26 -0
- agentcore/lib/packaging/build_context_dockerignore.py +45 -0
- agentcore/lib/packaging/container.py +95 -0
- agentcore/lib/packaging/helpers.py +289 -0
- agentcore/lib/packaging/node.py +153 -0
- agentcore/lib/packaging/python.py +135 -0
- agentcore/lib/packaging/types.py +69 -0
- agentcore/lib/packaging/uv.py +45 -0
- agentcore/lib/result.py +104 -0
- agentcore/lib/schemas/__init__.py +1 -0
- agentcore/lib/schemas/io/__init__.py +27 -0
- agentcore/lib/schemas/io/config_io.py +458 -0
- agentcore/lib/schemas/io/global_config.py +239 -0
- agentcore/lib/schemas/io/path_resolver.py +132 -0
- agentcore/lib/secrets/__init__.py +18 -0
- agentcore/lib/secrets/cipher.py +70 -0
- agentcore/lib/secrets/key_provider.py +163 -0
- agentcore/lib/secrets/sensitive_keys.py +55 -0
- agentcore/lib/time_constants.py +4 -0
- agentcore/lib/utils/__init__.py +47 -0
- agentcore/lib/utils/aws_account.py +15 -0
- agentcore/lib/utils/credentials.py +68 -0
- agentcore/lib/utils/dotenv.py +32 -0
- agentcore/lib/utils/env.py +105 -0
- agentcore/lib/utils/json_format.py +29 -0
- agentcore/lib/utils/json_rpc.py +22 -0
- agentcore/lib/utils/platform.py +33 -0
- agentcore/lib/utils/polling.py +86 -0
- agentcore/lib/utils/subprocess.py +135 -0
- agentcore/lib/utils/time_parser.py +48 -0
- agentcore/lib/utils/validation.py +106 -0
- agentcore/logging/__init__.py +10 -0
- agentcore/logging/_common.py +77 -0
- agentcore/logging/cdk_io_host.py +36 -0
- agentcore/logging/create_logger.py +107 -0
- agentcore/logging/dev_logger.py +63 -0
- agentcore/logging/exec_logger.py +159 -0
- agentcore/logging/invoke_logger.py +154 -0
- agentcore/logging/remove_logger.py +95 -0
- agentcore/operations/__init__.py +0 -0
- agentcore/operations/agent/__init__.py +0 -0
- agentcore/operations/agent/config_bundle_defaults.py +31 -0
- agentcore/operations/agent/generate/__init__.py +28 -0
- agentcore/operations/agent/generate/schema_mapper.py +319 -0
- agentcore/operations/agent/generate/types.py +172 -0
- agentcore/operations/agent/generate/write_agent_to_project.py +67 -0
- agentcore/operations/agent/import_/__init__.py +31 -0
- agentcore/operations/agent/import_/base_translator.py +449 -0
- agentcore/operations/agent/import_/constants.py +23 -0
- agentcore/operations/agent/import_/execute.py +127 -0
- agentcore/operations/agent/import_/langgraph_translator.py +294 -0
- agentcore/operations/agent/import_/pyproject_generator.py +73 -0
- agentcore/operations/agent/import_/strands_translator.py +289 -0
- agentcore/operations/config_bundle/__init__.py +1 -0
- agentcore/operations/config_bundle/bundle_name_variants.py +9 -0
- agentcore/operations/config_bundle/diff_versions.py +52 -0
- agentcore/operations/config_bundle/resolve_bundle.py +65 -0
- agentcore/operations/connectors/__init__.py +3 -0
- agentcore/operations/connectors/translators.py +63 -0
- agentcore/operations/dataset/__init__.py +21 -0
- agentcore/operations/dataset/delete.py +7 -0
- agentcore/operations/dataset/publish.py +16 -0
- agentcore/operations/dataset/pull.py +21 -0
- agentcore/operations/dataset/push.py +201 -0
- agentcore/operations/dataset/resolve_dataset.py +65 -0
- agentcore/operations/dataset/status.py +18 -0
- agentcore/operations/dataset/wait.py +25 -0
- agentcore/operations/deploy/__init__.py +152 -0
- agentcore/operations/deploy/backfill_vpc_id.py +90 -0
- agentcore/operations/deploy/change_detection.py +84 -0
- agentcore/operations/deploy/constants.py +58 -0
- agentcore/operations/deploy/dependency_sync.py +105 -0
- agentcore/operations/deploy/ensure_target.py +30 -0
- agentcore/operations/deploy/gateway_status.py +44 -0
- agentcore/operations/deploy/managed_memory_notice.py +36 -0
- agentcore/operations/deploy/payment_connector_authorization.py +106 -0
- agentcore/operations/deploy/post_deploy_datasets.py +96 -0
- agentcore/operations/deploy/post_deploy_knowledge_bases.py +104 -0
- agentcore/operations/deploy/post_deploy_observability.py +28 -0
- agentcore/operations/deploy/post_deploy_online_evals.py +60 -0
- agentcore/operations/deploy/pre_deploy_identity.py +505 -0
- agentcore/operations/deploy/preflight.py +385 -0
- agentcore/operations/deploy/region_override.py +45 -0
- agentcore/operations/deploy/teardown.py +153 -0
- agentcore/operations/dev/__init__.py +61 -0
- agentcore/operations/dev/_http.py +79 -0
- agentcore/operations/dev/_server.py +165 -0
- agentcore/operations/dev/codezip_dev_server.py +151 -0
- agentcore/operations/dev/config.py +128 -0
- agentcore/operations/dev/constants.py +9 -0
- agentcore/operations/dev/container_dev_server.py +237 -0
- agentcore/operations/dev/container_runtime.py +37 -0
- agentcore/operations/dev/dev_server.py +244 -0
- agentcore/operations/dev/gateway_env.py +25 -0
- agentcore/operations/dev/invoke.py +214 -0
- agentcore/operations/dev/invoke_a2a.py +204 -0
- agentcore/operations/dev/invoke_agui.py +88 -0
- agentcore/operations/dev/invoke_mcp.py +145 -0
- agentcore/operations/dev/invoke_types.py +30 -0
- agentcore/operations/dev/load_dev_env.py +26 -0
- agentcore/operations/dev/memory_env.py +19 -0
- agentcore/operations/dev/otel/__init__.py +5 -0
- agentcore/operations/dev/otel/collector.py +234 -0
- agentcore/operations/dev/otel/transforms.py +331 -0
- agentcore/operations/dev/payment_env.py +50 -0
- agentcore/operations/dev/server.py +28 -0
- agentcore/operations/dev/sse_transform.py +30 -0
- agentcore/operations/dev/utils.py +152 -0
- agentcore/operations/dev/web_ui/__init__.py +31 -0
- agentcore/operations/dev/web_ui/constants.py +25 -0
- agentcore/operations/dev/web_ui/handlers/__init__.py +36 -0
- agentcore/operations/dev/web_ui/handlers/_common.py +36 -0
- agentcore/operations/dev/web_ui/handlers/_deployed.py +37 -0
- agentcore/operations/dev/web_ui/handlers/a2a_proxy.py +37 -0
- agentcore/operations/dev/web_ui/handlers/cloudwatch_traces.py +71 -0
- agentcore/operations/dev/web_ui/handlers/harness_invocation.py +89 -0
- agentcore/operations/dev/web_ui/handlers/harness_utils.py +95 -0
- agentcore/operations/dev/web_ui/handlers/invocations.py +327 -0
- agentcore/operations/dev/web_ui/handlers/mcp_proxy.py +119 -0
- agentcore/operations/dev/web_ui/handlers/memory.py +84 -0
- agentcore/operations/dev/web_ui/handlers/resources.py +351 -0
- agentcore/operations/dev/web_ui/handlers/start.py +182 -0
- agentcore/operations/dev/web_ui/handlers/status.py +25 -0
- agentcore/operations/dev/web_ui/handlers/traces.py +50 -0
- agentcore/operations/dev/web_ui/run_web_ui.py +60 -0
- agentcore/operations/dev/web_ui/web_server.py +297 -0
- agentcore/operations/eval/__init__.py +8 -0
- agentcore/operations/eval/list_eval_runs.py +18 -0
- agentcore/operations/eval/logs_eval.py +119 -0
- agentcore/operations/eval/pause_resume.py +90 -0
- agentcore/operations/eval/run_eval.py +354 -0
- agentcore/operations/eval/shared/__init__.py +0 -0
- agentcore/operations/eval/shared/dataset_loader.py +68 -0
- agentcore/operations/eval/shared/dataset_session_provider.py +122 -0
- agentcore/operations/eval/shared/evaluator_runner.py +89 -0
- agentcore/operations/eval/shared/scenario_executor.py +71 -0
- agentcore/operations/eval/shared/span_collector.py +255 -0
- agentcore/operations/eval/storage.py +67 -0
- agentcore/operations/feedback/__init__.py +21 -0
- agentcore/operations/feedback/aperture_client.py +98 -0
- agentcore/operations/feedback/build_payload.py +97 -0
- agentcore/operations/feedback/constants.py +26 -0
- agentcore/operations/feedback/submit_feedback.py +150 -0
- agentcore/operations/fetch_access/__init__.py +36 -0
- agentcore/operations/fetch_access/_target.py +17 -0
- agentcore/operations/fetch_access/fetch_gateway_token.py +75 -0
- agentcore/operations/fetch_access/fetch_harness_token.py +72 -0
- agentcore/operations/fetch_access/fetch_runtime_token.py +76 -0
- agentcore/operations/fetch_access/list_agents.py +18 -0
- agentcore/operations/fetch_access/list_gateways.py +26 -0
- agentcore/operations/fetch_access/list_harnesses.py +23 -0
- agentcore/operations/fetch_access/oauth_token.py +162 -0
- agentcore/operations/fetch_access/types.py +34 -0
- agentcore/operations/harness/__init__.py +1 -0
- agentcore/operations/harness/orphan.py +84 -0
- agentcore/operations/harness/skill_utils.py +56 -0
- agentcore/operations/identity/__init__.py +33 -0
- agentcore/operations/identity/api_key_credential_provider.py +68 -0
- agentcore/operations/identity/oauth2_credential_provider.py +95 -0
- agentcore/operations/ingest/__init__.py +184 -0
- agentcore/operations/init/__init__.py +3 -0
- agentcore/operations/init/files.py +59 -0
- agentcore/operations/insights/__init__.py +21 -0
- agentcore/operations/insights/insights_storage.py +67 -0
- agentcore/operations/insights/run_insights.py +148 -0
- agentcore/operations/invoke/__init__.py +5 -0
- agentcore/operations/invoke/resolve_agent_context.py +85 -0
- agentcore/operations/jobs/__init__.py +38 -0
- agentcore/operations/jobs/ab_test/__init__.py +0 -0
- agentcore/operations/jobs/ab_test/build_options.py +126 -0
- agentcore/operations/jobs/ab_test/format.py +121 -0
- agentcore/operations/jobs/ab_test/handler.py +412 -0
- agentcore/operations/jobs/ab_test/promote.py +142 -0
- agentcore/operations/jobs/ab_test/resolve.py +221 -0
- agentcore/operations/jobs/batch_evaluation/__init__.py +0 -0
- agentcore/operations/jobs/batch_evaluation/build_source.py +68 -0
- agentcore/operations/jobs/batch_evaluation/dataset_phase1.py +95 -0
- agentcore/operations/jobs/batch_evaluation/format.py +58 -0
- agentcore/operations/jobs/batch_evaluation/handler.py +203 -0
- agentcore/operations/jobs/insights/__init__.py +0 -0
- agentcore/operations/jobs/insights/format.py +65 -0
- agentcore/operations/jobs/insights/handler.py +178 -0
- agentcore/operations/jobs/recommendation/__init__.py +0 -0
- agentcore/operations/jobs/recommendation/apply_to_bundle.py +76 -0
- agentcore/operations/jobs/recommendation/build_config.py +271 -0
- agentcore/operations/jobs/recommendation/fetch_session_spans.py +93 -0
- agentcore/operations/jobs/recommendation/format.py +77 -0
- agentcore/operations/jobs/recommendation/handler.py +331 -0
- agentcore/operations/jobs/shared/__init__.py +0 -0
- agentcore/operations/jobs/shared/constants.py +85 -0
- agentcore/operations/jobs/shared/engine.py +187 -0
- agentcore/operations/jobs/shared/format.py +104 -0
- agentcore/operations/jobs/shared/region.py +34 -0
- agentcore/operations/jobs/shared/resolve_agent_state.py +30 -0
- agentcore/operations/jobs/shared/storage.py +117 -0
- agentcore/operations/jobs/shared/wait.py +30 -0
- agentcore/operations/knowledge_base/__init__.py +33 -0
- agentcore/operations/knowledge_base/connector_config.py +105 -0
- agentcore/operations/knowledge_base/hydrate_data_sources.py +59 -0
- agentcore/operations/mcp/__init__.py +4 -0
- agentcore/operations/memory/__init__.py +13 -0
- agentcore/operations/memory/_records.py +72 -0
- agentcore/operations/memory/list_memory_records.py +45 -0
- agentcore/operations/memory/retrieve_memory_records.py +52 -0
- agentcore/operations/node/__init__.py +15 -0
- agentcore/operations/node/setup.py +36 -0
- agentcore/operations/python/__init__.py +17 -0
- agentcore/operations/python/setup.py +47 -0
- agentcore/operations/resolve_agent.py +159 -0
- agentcore/operations/session/__init__.py +86 -0
- agentcore/operations/traces/__init__.py +29 -0
- agentcore/operations/traces/get_trace.py +202 -0
- agentcore/operations/traces/insights_query.py +74 -0
- agentcore/operations/traces/list_traces.py +46 -0
- agentcore/operations/traces/trace_url.py +25 -0
- agentcore/operations/traces/types.py +55 -0
- agentcore/primitives/__init__.py +0 -0
- agentcore/primitives/_gateway_target_wizard.py +293 -0
- agentcore/primitives/_gateway_validation.py +442 -0
- agentcore/primitives/_harness_cli.py +557 -0
- agentcore/primitives/_harness_validation.py +163 -0
- agentcore/primitives/_harness_wizard.py +726 -0
- agentcore/primitives/_knowledge_base_wizard.py +259 -0
- agentcore/primitives/_payment_wizard.py +331 -0
- agentcore/primitives/_policy_form.py +160 -0
- agentcore/primitives/agent.py +817 -0
- agentcore/primitives/auth_utils.py +77 -0
- agentcore/primitives/base.py +178 -0
- agentcore/primitives/capacity_provider.py +501 -0
- agentcore/primitives/config_bundle.py +329 -0
- agentcore/primitives/credential.py +387 -0
- agentcore/primitives/credential_utils.py +45 -0
- agentcore/primitives/dataset.py +211 -0
- agentcore/primitives/deployed_gateways.py +34 -0
- agentcore/primitives/evaluator.py +909 -0
- agentcore/primitives/gateway.py +496 -0
- agentcore/primitives/gateway_target.py +988 -0
- agentcore/primitives/harness.py +706 -0
- agentcore/primitives/knowledge_base.py +477 -0
- agentcore/primitives/memory.py +387 -0
- agentcore/primitives/online_eval_config.py +365 -0
- agentcore/primitives/online_insights.py +275 -0
- agentcore/primitives/payment_connector.py +427 -0
- agentcore/primitives/payment_eligible.py +20 -0
- agentcore/primitives/payment_manager.py +605 -0
- agentcore/primitives/payment_validation.py +68 -0
- agentcore/primitives/policy.py +622 -0
- agentcore/primitives/policy_engine.py +358 -0
- agentcore/primitives/registry.py +56 -0
- agentcore/primitives/runtime_endpoint.py +361 -0
- agentcore/primitives/secret_flag_warning.py +17 -0
- agentcore/schema/__init__.py +14 -0
- agentcore/schema/_zod.py +395 -0
- agentcore/schema/constants.py +306 -0
- agentcore/schema/schemas/__init__.py +40 -0
- agentcore/schema/schemas/agent_env.py +545 -0
- agentcore/schema/schemas/agentcore_project.py +530 -0
- agentcore/schema/schemas/auth.py +286 -0
- agentcore/schema/schemas/aws_targets.py +72 -0
- agentcore/schema/schemas/connections.py +187 -0
- agentcore/schema/schemas/deployed_state.py +322 -0
- agentcore/schema/schemas/mcp.py +650 -0
- agentcore/schema/schemas/mcp_defs.py +54 -0
- agentcore/schema/schemas/primitives/__init__.py +52 -0
- agentcore/schema/schemas/primitives/ab_test.py +162 -0
- agentcore/schema/schemas/primitives/capacity_provider.py +214 -0
- agentcore/schema/schemas/primitives/config_bundle.py +48 -0
- agentcore/schema/schemas/primitives/dataset.py +55 -0
- agentcore/schema/schemas/primitives/evaluator.py +171 -0
- agentcore/schema/schemas/primitives/harness.py +710 -0
- agentcore/schema/schemas/primitives/knowledge_base.py +90 -0
- agentcore/schema/schemas/primitives/memory.py +113 -0
- agentcore/schema/schemas/primitives/online_eval_config.py +77 -0
- agentcore/schema/schemas/primitives/payment.py +114 -0
- agentcore/schema/schemas/primitives/policy.py +83 -0
- agentcore/schema/schemas/primitives/tags.py +50 -0
- agentcore/schema/schemas/zod_util.py +5 -0
- agentcore/schema/types/__init__.py +3 -0
- agentcore/schema/types/path.py +16 -0
- agentcore/templates/__init__.py +115 -0
- agentcore/templates/_handlebars.py +782 -0
- agentcore/templates/_js.py +164 -0
- agentcore/templates/base_renderer.py +107 -0
- agentcore/templates/cdk_renderer.py +129 -0
- agentcore/templates/evaluator_renderer.py +39 -0
- agentcore/templates/gateway_target_renderer.py +69 -0
- agentcore/templates/llm_context/README.md +16 -0
- agentcore/templates/llm_context/agentcore.ts +685 -0
- agentcore/templates/llm_context/aws-targets.ts +51 -0
- agentcore/templates/profiles.py +151 -0
- agentcore/templates/render.py +92 -0
- agentcore/templates/renderers.py +83 -0
- agentcore/templates/schema_assets.py +23 -0
- agentcore/templates/template_root.py +28 -0
- agentcore/templates/types.py +192 -0
- agentcore_cli-0.31.1.dist-info/METADATA +84 -0
- agentcore_cli-0.31.1.dist-info/RECORD +655 -0
- agentcore_cli-0.31.1.dist-info/WHEEL +4 -0
- agentcore_cli-0.31.1.dist-info/entry_points.txt +2 -0
- agentcore_cli-0.31.1.dist-info/licenses/LICENSE +175 -0
- 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 = {"&": "&", "<": "<", ">": ">", '"': """, "'": "'", "`": "`", "=": "="}
|
|
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)
|