원클릭으로
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 직업 분류 기준
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
| 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)