Write a Python Transformation
A Python Transformation processes Infrahub data through user-written Python code, producing JSON output (or any structured data you serialize). Use this when Jinja templating isn't enough — for example, conditional logic, external API calls, or complex aggregation. The steps below cover how to write one.
For conceptual background, see Transformations. For a step-by-step walkthrough with a running example, see Build a Python Transformation in the Academy tutorials.
Assumes a working Infrahub instance, a connected Git repository, and
infrahubctlconfigured locally. See Installation and Connect a repository if you're starting fresh.
Write the GraphQL query
Create a .gql file with the data your transform needs in the Git repository. Use variables for per-target scoping.
query MyQuery($device_name: String!) {
InfraDevice(name__value: $device_name) {
edges {
node {
name { value }
# ... other fields
}
}
}
}
For reusable query fragments shared across Transformations or Generators, see GraphQL fragments.
Implement the Transformation class
Create a Python file under transforms/ (or wherever fits your repository structure):
from infrahub_sdk.transforms import InfrahubTransform
class YourTransform(InfrahubTransform):
query = "<your_query>"
async def transform(self, data):
# Process the query response into your output format
device = data["InfraDevice"]["edges"][0]["node"]
return {
"name": device["name"]["value"],
# ... build your structured output
}
The class must inherit from InfrahubTransform and implement an async transform() method that accepts a dict (the GraphQL response) and returns a dict (the JSON output). Set query to the query name you'll register in .infrahub.yml.
Whether you read from the data dictionary or self.nodes depends on convert_query_response in the Python Transform definition.
Access local files (optional)
If your transform needs to read templates or other files from the repository, use self.root_directory:
async def transform(self, data):
templates_path = f"{self.root_directory}/templates"
# ... load and use template files
Register in .infrahub.yml
Add the Transformation definition to your repository's .infrahub.yml:
python_transforms:
- name: <your_transform>
description: "<one-line description>"
file_path: "transforms/<your_transform>.py"
class_name: YourTransform
convert_query_response: <false|true>
queries:
- name: <your_query>
file_path: "queries/<your_query>.gql"
For full .infrahub.yml syntax, see infrahub.yml configuration.
Test locally
infrahubctl transform <your_transform> <var_name>=<value>
Use --branch=<branch_name> to render against a specific branch. Output prints to the terminal as JSON.
For debugging, add breakpoint() inside transform() to inspect data at runtime.
Deploy to Infrahub
Commit your changes to the repository and push them up to your remote. The Transformation registers with Infrahub when the repository syncs.
Render via the API
Once deployed, render on demand:
GET https://<host>/api/transform/python/<your_transform>?branch=<branch_name>&<var>=<value>
The endpoint is branch-aware. Pass query variables as URL parameters.
Generate as an artifact
To cache transform output and tie it to a specific target object, define an artifact that uses this Transformation. See Use artifacts.
Related
- Transformations — concept and architecture
- Write a Jinja2 transformation — for template-based output
- GraphQL fragments — share query fragments across Transformations
- Use artifacts — cache Transformation output
- infrahub.yml configuration