|  | 
|  | 1 | +--- | 
|  | 2 | +title: Strict Typing in Python | 
|  | 3 | +--- | 
|  | 4 | + | 
|  | 5 | +import Tabs from '@theme/Tabs'; | 
|  | 6 | +import TabItem from '@theme/TabItem'; | 
|  | 7 | + | 
|  | 8 | +# Overview | 
|  | 9 | + | 
|  | 10 | +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. | 
|  | 11 | + | 
|  | 12 | +:::note What is Python Typing | 
|  | 13 | + | 
|  | 14 | +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. | 
|  | 15 | + | 
|  | 16 | +```python | 
|  | 17 | +# Basic type hints | 
|  | 18 | +def percentage(num1: int, num2: int) -> float: | 
|  | 19 | +    return (num1 / num2) * 100 | 
|  | 20 | +``` | 
|  | 21 | + | 
|  | 22 | +::: | 
|  | 23 | + | 
|  | 24 | +## Leveraging Python protocols | 
|  | 25 | + | 
|  | 26 | +The Python SDK for Infrahub has been designed to automatically work with any schemas loaded into Infrahub. | 
|  | 27 | +Internally, the Python SDK generates dynamic Python representations of your schemas. | 
|  | 28 | + | 
|  | 29 | +While this approach improves code readability, it presents challenges with type checking because each object has a different signature based on your schema. | 
|  | 30 | + | 
|  | 31 | +### Without protocols | 
|  | 32 | + | 
|  | 33 | +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. | 
|  | 34 | + | 
|  | 35 | +```python | 
|  | 36 | +# Type checker cannot verify the existence of 'description' | 
|  | 37 | +blue_tag = client.get("BuiltinTag", name__value="blue")  # blue_tag is of type InfrahubNode or InfrahubNodeSync | 
|  | 38 | +blue_tag.description.value = "The blue tag"  # Mypy: error: "InfrahubNode" has no attribute "description" | 
|  | 39 | +blue_tag.save() | 
|  | 40 | +``` | 
|  | 41 | + | 
|  | 42 | +### With protocols | 
|  | 43 | + | 
|  | 44 | +To provide strict type checking while maintaining platform extensibility, the Python SDK integrates with Python Protocols. | 
|  | 45 | + | 
|  | 46 | +For all core and internal models, the protocols are included in the SDK under `infrahub_sdk.protocols`. | 
|  | 47 | +Whenever you need to specify the kind of object you're working with as a string, you can use the corresponding protocol instead. | 
|  | 48 | + | 
|  | 49 | +```python | 
|  | 50 | +from infrahub_sdk.protocols import BuiltinTag | 
|  | 51 | + | 
|  | 52 | +# Type checker can now verify all attributes | 
|  | 53 | +blue_tag = client.get(BuiltinTag, name__value="blue")  # blue_tag is of type BuiltinTag | 
|  | 54 | +blue_tag.description.value = "The blue tag"  # No type errors | 
|  | 55 | +blue_tag.save() | 
|  | 56 | +``` | 
|  | 57 | + | 
|  | 58 | +:::note Python Protocols | 
|  | 59 | + | 
|  | 60 | +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. | 
|  | 61 | + | 
|  | 62 | +More information about Python Protocols can be found [here](https://typing.python.org/en/latest/spec/protocol.html) | 
|  | 63 | + | 
|  | 64 | +::: | 
|  | 65 | + | 
|  | 66 | +## Generating custom protocols based on your schema | 
|  | 67 | + | 
|  | 68 | +You can generate Python Protocols for your own models using the `infrahubctl protocols` command. This supports both synchronous and asynchronous Python code. | 
|  | 69 | + | 
|  | 70 | +It's possible to provide the schema from a local directory or from an existing Infrahub Instance. | 
|  | 71 | + | 
|  | 72 | +<Tabs groupId="local-remote"> | 
|  | 73 | +  <TabItem value="Existing Infrahub Instance" default> | 
|  | 74 | + | 
|  | 75 | +  ```shell | 
|  | 76 | +  export INFRAHUB_ADDRESS=https://infrahub.example.com | 
|  | 77 | +  infrahubctl protocols --out lib/protocols.py --sync | 
|  | 78 | +  ``` | 
|  | 79 | + | 
|  | 80 | +  </TabItem> | 
|  | 81 | +  <TabItem value="Local Directory" default> | 
|  | 82 | + | 
|  | 83 | +  ```shell | 
|  | 84 | +  infrahubctl protocols --schemas schemas/tag.schema.yml --out lib/protocols.py | 
|  | 85 | +  ``` | 
|  | 86 | + | 
|  | 87 | +  </TabItem> | 
|  | 88 | +</Tabs> | 
|  | 89 | + | 
|  | 90 | +> When using a local directory, Protocols for Profiles and Object Templates won't be generated. | 
|  | 91 | +
 | 
|  | 92 | +## Using custom protocols | 
|  | 93 | + | 
|  | 94 | +After generation, you can import and use your custom protocols as describe below. | 
|  | 95 | + | 
|  | 96 | +```python | 
|  | 97 | +from lib.protocols import MyOwnObject | 
|  | 98 | + | 
|  | 99 | +# Use your custom protocol | 
|  | 100 | +my_object = client.get(MyOwnObject, name__value="example") | 
|  | 101 | +``` | 
|  | 102 | + | 
|  | 103 | +> 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 | 
0 commit comments