ワンクリックで
aspire-polyglot-python-apphost
Python AppHost patterns for Aspire 13.4.6+ polyglot orchestration
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Python AppHost patterns for Aspire 13.4.6+ polyglot orchestration
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
| name | aspire-polyglot-python-apphost |
| description | Python AppHost patterns for Aspire 13.4.6+ polyglot orchestration |
| domain | aspire, python, orchestration |
| confidence | high |
| source | earned (13.4.6 migration) |
Aspire 13.4.6 introduced a breaking change in the Python SDK: create_builder() now returns a context manager (DistributedApplicationBuilder) instead of a plain builder object. This change affects ALL Python AppHost projects.
The builder's internal _handle starts as None and is ONLY populated inside __enter__, which invokes the Aspire.Hosting/createBuilder RPC to the .NET host. Without the context manager, all builder capability calls fail with TypeError: Type mismatch for parameter 'builder': expected unknown, got unknown.
ALWAYS wrap the builder lifecycle in a with statement:
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent / ".aspire/modules"))
from aspire_app import create_builder
with create_builder() as builder:
# All builder configuration MUST be inside the with block
cache = builder.add_redis("cache")
db = builder.add_postgres("pg").add_database("mydb")
app = builder.add_dockerfile("myapp", "./src")
app.with_reference(cache)
app.with_reference(db)
app.with_otlp_exporter()
app.with_external_http_endpoints()
app.with_http_endpoint(target_port=8080, env="PORT")
app_instance = builder.build()
app_instance.run()
The Python guest runtime bootstraps its venv via uv pip sync pylock.apphost.toml. This file MUST exist at the sample root.
Location: samples/{sample-name}/pylock.apphost.toml (same directory as apphost.py)
Minimum valid content (for apphosts with no dependencies):
# This file was autogenerated by uv via the following command:
# uv pip compile requirements.txt --format pylock.toml -o pylock.apphost.toml
lock-version = "1.0"
created-by = "uv"
requires-python = ">=3.11"
Python version constraint: MUST be ">=3.11" to match .aspire/modules/pyproject.toml (which is generated at runtime). Do NOT pin to a specific patch version like ">=3.13.3".
When to use an empty lock: When the root requirements.txt is empty or contains only comments, and aspire_app.py has no external dependencies (it's pure stdlib), an empty PEP 751 lock is valid.
The generated Python bindings live at .aspire/modules/aspire_app.py (13.4.6+ layout).
Standard sys.path setup:
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent / ".aspire/modules"))
from aspire_app import create_builder
Legacy layout (.modules/ — pre-13.4.6) is deprecated. Always use .aspire/modules/.
Redis cache:
cache = builder.add_redis("cache")
app.with_reference(cache) # Injects ConnectionStrings__cache
PostgreSQL database:
postgres = builder.add_postgres("pg")
db = postgres.add_database("mydb")
app.with_reference(db) # Injects ConnectionStrings__mydb
Dockerfile-based app:
app = builder.add_dockerfile("app-name", "./src")
app.with_otlp_exporter() # Enable OpenTelemetry
app.with_external_http_endpoints() # Expose ports externally
app.with_http_endpoint(target_port=8080, env="PORT")
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent / ".aspire/modules"))
from aspire_app import create_builder
with create_builder() as builder:
cache = builder.add_redis("cache")
wiki = builder.add_dockerfile("wiki", "./src")
wiki.with_reference(cache)
wiki.with_otlp_exporter()
wiki.with_external_http_endpoints()
wiki.with_http_endpoint(target_port=8080, env="PORT")
app = builder.build()
app.run()
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent / ".aspire/modules"))
from aspire_app import create_builder
with create_builder() as builder:
# PostgreSQL server + database resource
postgres = builder.add_postgres("pg")
pollsdb = postgres.add_database("pollsdb")
polls = builder.add_dockerfile("polls", "./src")
polls.with_reference(pollsdb)
polls.with_otlp_exporter()
polls.with_external_http_endpoints()
polls.with_http_endpoint(target_port=8080, env="PORT")
app = builder.build()
app.run()
from aspire_app import create_builder
builder = create_builder() # ❌ NO — _handle stays None
cache = builder.add_redis("cache") # ❌ TypeError: expected unknown, got unknown
app = builder.build()
app.run()
Why this fails: __enter__ never runs, so _handle is None, and the .NET host rejects all builder calls.
samples/myapp/
├── apphost.py
├── aspire.config.json
└── src/
└── ...
# ❌ NO pylock.apphost.toml — runtime bootstrap fails
Why this fails: The Python guest runtime calls uv pip sync pylock.apphost.toml to set up its venv. Without this file, the bootstrap fails.
requires-python = ">=3.13.3" # ❌ NO — too restrictive
Why this fails: .aspire/modules/pyproject.toml (generated at runtime) specifies requires-python = ">=3.11". Pinning to a specific patch version creates a version conflict.
Correct:
requires-python = ">=3.11" # ✅ YES — matches generated bindings
sys.path.insert(0, str(Path(__file__).parent / ".modules")) # ❌ OLD
from aspire import create_builder # ❌ OLD module name
Why this fails: The 13.4.6 binding layout moved from .modules/aspire.py → .aspire/modules/aspire_app.py.
Correct:
sys.path.insert(0, str(Path(__file__).parent / ".aspire/modules")) # ✅
from aspire_app import create_builder # ✅
Syntax check:
python3 -c "import ast; ast.parse(open('apphost.py').read()); print('OK')"
Runtime launch (only via coordinator on shared Docker host):
cd samples/myapp
aspire run
# Expect: dashboardUrl in output, no TypeError
App containers can start BEFORE their datastores are reachable on cold launches (when images must pull or containers must initialize). This causes DNS resolution failures or connection errors.
Example failure (django-htmx-polls on cold launch):
django.db.utils.OperationalError: could not translate host name "pg.dev.internal" to address
Root cause: .with_reference(db_resource) only injects the connection string — it does NOT establish startup ordering.
ALWAYS pair .with_reference() with .wait_for() when your app depends on a datastore:
# PostgreSQL example (django-htmx-polls)
postgres = builder.add_postgres("pg")
pollsdb = postgres.add_database("pollsdb")
polls = builder.add_dockerfile("polls", "./src")
polls.with_reference(pollsdb) # Injects ConnectionStrings__pollsdb
polls.wait_for(pollsdb) # Waits for Postgres to be Healthy before starting
polls.with_otlp_exporter()
polls.with_external_http_endpoints()
polls.with_http_endpoint(target_port=8080, env="PORT")
# Redis example (flask-markdown-wiki)
cache = builder.add_redis("cache")
wiki = builder.add_dockerfile("wiki", "./src")
wiki.with_reference(cache) # Injects ConnectionStrings__cache
wiki.wait_for(cache) # Waits for Redis to be Healthy before starting
wiki.with_otlp_exporter()
wiki.with_external_http_endpoints()
wiki.with_http_endpoint(target_port=8080, env="PORT")
Use .wait_for() when:
manage.py migrate, Alembic, Flyway)Applies to:
add_postgres + add_database)add_redis)add_mysql + add_database)add_sql_server + add_database)db = builder.add_<datastore>("<name>") # or add_database() for SQL datastores
app = builder.add_dockerfile("app", "./src")
app.with_reference(db) # ✅ Injects connection string
app.wait_for(db) # ✅ Waits for datastore to be Healthy
Chain order:
with_reference(db) — Injects connection stringwait_for(db) — Establishes startup orderingwith_otlp_exporter() — Enables observabilitywith_external_http_endpoints() — Exposes portswith_http_endpoint() — Configures specific port.squad/decisions/inbox/banner-python-waitfor-coldstart.md — cold-start fix decision.squad/decisions/inbox/banner-python-apphost-13.4.6-migration.md — migration decision.squad/agents/banner/history.md — historical context.aspire/modules/aspire_app.py (generated at runtime)Go AppHost patterns for Aspire 13.4.6+ polyglot orchestration
How to structure Java apphosts for Aspire 13.4.6 polyglot: default-package convention, binding imports, launcher requirements, and startup ordering