> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ubiquex.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Python Component

> Author a Pulumi ComponentResource in Python for full programmatic control.

Python is the dominant language in data engineering, ML infrastructure, and much of the cloud operations world. Teams already running Python-heavy Pulumi stacks can author ubx components in Python without switching languages. The structure mirrors TypeScript exactly: a class that extends `pulumi.ComponentResource`, a constructor that creates child resources, and public `pulumi.Output` properties for the values consumers will reference. The call site in `.iac` is unchanged from tutorial 18 or 19 — the same `component` block, the same `source` path pointing at a local directory, the same `component.name.output` references. Python or TypeScript or Go, the authoring language is never visible to `.iac` consumers.

## What you'll learn

* How to structure a Python `ComponentResource` for use with ubx
* The idiomatic Python pattern: `ComponentResource` + `register_outputs()`
* The identical call site across all authoring languages

***

## Why this matters

<Info>
  Python components run through Pulumi's standard Python SDK — no ubx-specific runtime, no special packaging. A Python component that works standalone in a Pulumi Python project works identically when consumed via ubx.
</Info>

***

## The source code

<CodeGroup>
  ```hcl main.iac theme={"theme":"css-variables"}
  # Forward-looking syntax — identical call site regardless of component language.
  # ubx v1 local source resolution only supports .iac sources today (UBX-125).
  component "database" {
    source         = "./component"
    identifier     = "myapp-${input.env}"
    instance_class = "db.t3.micro"
  }
  ```

  ```python component/__main__.py theme={"theme":"css-variables"}
  # Real Python ComponentResource — full Pulumi SDK available.
  from typing import Optional
  import pulumi
  import pulumi_aws as aws


  class RdsDatabaseArgs:
      def __init__(
          self,
          identifier: str,
          instance_class: str = "db.t3.micro",
          engine_version: str = "15",
      ):
          self.identifier = identifier
          self.instance_class = instance_class
          self.engine_version = engine_version


  class RdsDatabase(pulumi.ComponentResource):
      endpoint: pulumi.Output[str]
      identifier: pulumi.Output[str]

      def __init__(
          self,
          name: str,
          args: RdsDatabaseArgs,
          opts: Optional[pulumi.ResourceOptions] = None,
      ):
          super().__init__("ubx:component:RdsDatabase", name, {}, opts)

          db = aws.rds.Instance(
              f"{name}-db",
              identifier=args.identifier,
              allocated_storage=20,
              engine="postgres",
              engine_version=args.engine_version,
              instance_class=args.instance_class,
              username="admin",
              password="changeme",
              skip_final_snapshot=True,
              opts=pulumi.ResourceOptions(parent=self),
          )

          self.endpoint = db.endpoint
          self.identifier = db.identifier
          self.register_outputs({
              "endpoint": self.endpoint,
              "identifier": self.identifier,
          })
  ```

  ```hcl inputs.iac theme={"theme":"css-variables"}
  input "env" {
    type    = "string"
    default = "dev"
  }
  ```

  ```hcl outputs.iac theme={"theme":"css-variables"}
  output "db_endpoint" {
    value = component.database.endpoint
  }

  output "db_identifier" {
    value = component.database.identifier
  }
  ```

  ```hcl ubx.iac theme={"theme":"css-variables"}
  ubx {
    name    = "python-component"
    runtime = "python"
    stacks  = ["dev", "staging", "prod"]
  }

  backend "local" {
    path = ".ubx/state"
  }
  ```
</CodeGroup>

***

## How it works

<Steps>
  <Step title="The Python component follows standard Pulumi patterns">
    `super().__init__("ubx:component:RdsDatabase", name, {}, opts)` registers the component in Pulumi's resource graph. Child resources are created with `opts=pulumi.ResourceOptions(parent=self)`. `register_outputs()` exposes outputs to callers.
  </Step>

  <Step title="Typed args via a dataclass-style args class">
    Python doesn't have TypeScript's interface syntax, so a dedicated `RdsDatabaseArgs` class provides typed, documented inputs. Defaults live in `__init__`, making the interface clear without requiring keyword-only arguments on the constructor.
  </Step>

  <Step title="ubx v1 local resolution: .iac sources only">
    Identical to the TypeScript case — `source = "./component"` pointing at a Python directory fails `ubx validate` today with `no .iac files found`. This is a forward-looking example showing the correct component structure for when UBX-125 ships.
  </Step>
</Steps>

***

## Identical call site across languages

Compare these three component blocks — all three call the same interface with different implementations behind `source`:

```hcl theme={"theme":"css-variables"}
# .iac component (works today — tutorial 18)
component "database" { source = "./components/rds"  identifier = "myapp-dev" instance_class = "db.t3.micro" }

# TypeScript component (forward-looking — tutorial 19)
component "database" { source = "./component"        identifier = "myapp-dev" instance_class = "db.t3.micro" }

# Python component (forward-looking — this tutorial)
component "database" { source = "./component"        identifier = "myapp-dev" instance_class = "db.t3.micro" }
```

The call site is syntactically identical. Swapping authoring languages requires no changes to consuming stacks.

***

## Common mistakes

<Warning>
  ubx v1 does not resolve local Python component sources. `source = "./component"` pointing at a directory with only `.py` files fails `ubx validate` with `internal error: local component "./component": no .iac files found`. Use a `.iac` component (tutorial 18) for local components that need to validate today.
</Warning>

<Tip>
  The `"ubx:component:RdsDatabase"` type URN string passed to `super().__init__()` appears in Pulumi state and in `pulumi stack output --show-urns`. Use a consistent naming scheme: `"ubx:component:YourComponentName"` — same pattern across TypeScript, Python, and Go implementations.
</Tip>

***

## Run it

```bash theme={"theme":"css-variables"}
# ubx validate currently fails for this example (UBX-125 — forward-looking syntax)
ubx validate  # expected: internal error: no .iac files found
```

***

## What you learned

<Check>A Python component extends `pulumi.ComponentResource` with `register_outputs()` for outputs</Check>
<Check>The `.iac` call site is identical regardless of authoring language</Check>
<Check>Local Python component resolution is forward-looking in ubx v1 (tracked as UBX-125)</Check>

***

## Next steps

<CardGroup cols={2}>
  <Card title="Go component" href="/v1/tutorials/21-go-component">
    Author the same component in Go
  </Card>

  <Card title="Local .iac component" href="/v1/tutorials/18-local-iac-component">
    The fully-supported local component path today
  </Card>
</CardGroup>

***

<Info>
  Full runnable example: [github.com/ubiquex/ubx-examples/20-python-component](https://github.com/ubiquex/ubx-examples/tree/main/20-python-component)
</Info>
