Skip to main content

Overview

This guide explains how to use Python's type system effectively with the Infrahub SDK, focusing on the use of Protocols for type-safe development.

What is Python Typing

Python typing allows you to specify the expected data types of variables, function arguments, and return values to improve code clarity and catch bugs early.

# Basic type hints
def percentage(num1: int, num2: int) -> float:
return (num1 / num2) * 100

Leveraging Python protocols

The Python SDK for Infrahub has been designed to automatically work with any schemas loaded into Infrahub. Internally, the Python SDK generates dynamic Python representations of your schemas.

While this approach improves code readability, it presents challenges with type checking because each object has a different signature based on your schema.

Without protocols

In the example below, type checkers like Mypy will typically complain about blue_tag.description.value because description is a dynamic parameter generated by the SDK.

# Type checker cannot verify the existence of 'description'
blue_tag = client.get("BuiltinTag", name__value="blue") # blue_tag is of type InfrahubNode or InfrahubNodeSync
blue_tag.description.value = "The blue tag" # Mypy: error: "InfrahubNode" has no attribute "description"
blue_tag.save()

With protocols

To provide strict type checking while maintaining platform extensibility, the Python SDK integrates with Python Protocols.

For all core and internal models, the protocols are included in the SDK under infrahub_sdk.protocols. Whenever you need to specify the kind of object you're working with as a string, you can use the corresponding protocol instead.

from infrahub_sdk.protocols import BuiltinTag

# Type checker can now verify all attributes
blue_tag = client.get(BuiltinTag, name__value="blue") # blue_tag is of type BuiltinTag
blue_tag.description.value = "The blue tag" # No type errors
blue_tag.save()
Python Protocols

Python Protocols, introduced in PEP 544, define a set of method and property signatures that a class must implement to be considered a match, enabling structural subtyping (also known as "duck typing" with static checks). They allow you to specify behavior without requiring inheritance, making code more flexible and type-safe.

More information about Python Protocols can be found here

Generating custom protocols based on your schema

You can generate Python Protocols for your own models using the infrahubctl protocols command. This supports both synchronous and asynchronous Python code.

It's possible to provide the schema from a local directory or from an existing Infrahub Instance.

export INFRAHUB_ADDRESS=https://infrahub.example.com
infrahubctl protocols --out lib/protocols.py --sync

When using a local directory, Protocols for Profiles and Object Templates won't be generated.

Using custom protocols

After generation, you can import and use your custom protocols as describe below.

from lib.protocols import MyOwnObject

# Use your custom protocol
my_object = client.get(MyOwnObject, name__value="example")

if you don't have your own Python module, it's possible to use relative path by having the procotols.py in the same directory as your script/transform/generator