.iac component format (tutorial 18) is great for portability — it compiles to whatever runtime the consuming stack uses. But sometimes you need the full Python SDK: pulumi_awsx for opinionated VPC patterns, custom retry logic, dynamic attribute construction, or a provider that hasn’t been mapped to the ubx type system yet. A local Python component gives you all of that while keeping the call site in .iac identical to any other component block. Drop a __main__.py and a requirements.txt into a subdirectory, and ubx auto-detects the Python runtime, merges the pip dependencies into the stack’s requirements.txt, and emits a native Python ComponentResource class in the generated __main__.py. The consumer never knows what language the component is written in.
What you’ll learn
- The directory structure for a local Python component
- How ubx auto-detects the Python runtime from file presence
- How
requirements.txtfrom the component is merged into the stack automatically - How
schema.jsonvalidates inputs at the call site before any code runs - How to call a local Python component from a
.iacstack
Why this matters
pulumi_awsx provides high-level VPC, ECS, and EKS constructs that create dozens of child resources behind a single call. These constructs are only available in the Pulumi SDKs — they can’t be expressed as raw unit blocks. A local Python component lets you wrap an awsx.ec2.Vpc call and expose its outputs as typed values consumed by other blocks in your stack.The source code
How it works
ubx auto-detects the Python runtime from file presence
When
source = "./components/vpc" points at a directory containing __main__.py and requirements.txt, ubx identifies it as a Python component. No explicit runtime declaration is needed in the component directory — the presence of these files is the signal. If only .iac files are present, ubx falls back to the native IR pipeline.requirements.txt is merged into the stack's dependencies
ubx reads
components/vpc/requirements.txt and merges its entries into the stack-level requirements.txt written alongside __main__.py in .ubx/. Duplicate package names are de-duplicated; the stack’s own constraint wins on version conflict. The result: running pip install -r .ubx/requirements.txt installs everything the stack and all its components need.schema.json validates inputs before compilation
When
schema.json is present, ubx validate checks that all inputs declared as required (no default) are provided at the call site, and that all component.vpc.X output references in the consuming stack exist in the schema’s outputs map. Type mismatches in inputs and references to undeclared outputs become compile errors rather than runtime surprises.ubx inlines the component class in the generated __main__.py
The compiler reads
components/vpc/__main__.py, wraps it, and emits it at the top of the generated stack __main__.py alongside the call site instantiation. No packaging, no pip install of the component, no import from an external module — the class is self-contained in the build artifact.Plan output
What ubx generates
Common mistakes
When to use each component type
| Approach | Use when |
|---|---|
Native .iac component | You want portability across TypeScript, Python, and Go runtimes. No external SDK calls needed. |
| Local Python component | You need pulumi_awsx, pulumi_kubernetes high-level constructs, or any Python library not available in raw .iac. Stack runtime must be python. |
Registry component (registry.ubiquex.io/…) | You want versioning, semantic releases, and sharing across repositories via the Strata registry. |
Run it
What you learned
A local Python component is auto-detected from
__main__.py + requirements.txt — no explicit runtime declaration neededrequirements.txt from each component directory is merged into the stack’s requirements.txt automaticallyschema.json turns missing inputs and bad output references into compile errors via ubx validateThe component class is inlined in the generated
__main__.py — no packaging or publishing step requiredNext steps
Local .iac component
The portable, runtime-agnostic alternative
Multi-stack dependency resolution
Use component outputs across stacks with @name.output
Full runnable example: github.com/ubiquex/ubx-examples/60-local-python-component

