From 13b589c64fb32978cd3411837c6391e7c3cb725e Mon Sep 17 00:00:00 2001 From: Iddo Date: Wed, 29 Jul 2026 12:13:05 +0200 Subject: [PATCH 1/2] docs: expand Python typing guide with real-world protocol examples Add a generators/transforms usage example (multi-kind filters with a typed list) and guidance to regenerate protocols on schema change and treat the generated file as a build artifact. Rewrite the overview opener as a capability lead. --- docs/docs/python-sdk/guides/python-typing.mdx | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/docs/docs/python-sdk/guides/python-typing.mdx b/docs/docs/python-sdk/guides/python-typing.mdx index 77780177b..5c0fce4b6 100644 --- a/docs/docs/python-sdk/guides/python-typing.mdx +++ b/docs/docs/python-sdk/guides/python-typing.mdx @@ -7,7 +7,7 @@ import TabItem from '@theme/TabItem'; # 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. +Use Python's type system with the Infrahub SDK to catch schema mismatches while you write code, not when it runs. This guide shows how to type your SDK calls with Protocols, generate typed classes from your own schema, and generate Pydantic models from your GraphQL queries. :::note What is Python Typing @@ -102,6 +102,32 @@ 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 `protocols.py` in the same directory as your script/transform/generator +### Using protocols in generators and transforms + +Protocols make the most difference in generators, transforms, and checks, where you traverse relationships and set attributes across many object kinds. Import the generated class, pass it as the `kind` argument, and use it as a type hint: + +```python +from .protocols import NetworkDevice + +# `kind` is the generated class, not a string, so attributes are type-checked +leaf_switches: list[NetworkDevice] = await client.filters( + kind=NetworkDevice, role__value="leaf", include=["interfaces"] +) + +for switch in leaf_switches: + switch.name.value # autocompleted, and checked against the schema +``` + +### Keep protocols in sync with your schema + +The generated file is a build artifact: regenerate it whenever your schema changes, and commit the result. When the schema changes and you regenerate, your type checker reports every line that no longer matches the schema, so you find the mismatch while you write code instead of at runtime. + +Regenerate with the same command you used to create the file: + +```shell +infrahubctl protocols --out lib/protocols.py +``` + ## Generating Pydantic models from GraphQL queries When working with GraphQL queries, you can generate type-safe Pydantic models that correspond to your query return types. This provides excellent type safety and IDE support for your GraphQL operations. From 581d30ea9fc33dfc3b45e58ca3da087d2af23c90 Mon Sep 17 00:00:00 2001 From: Iddo Date: Wed, 29 Jul 2026 20:22:03 +0200 Subject: [PATCH 2/2] docs: drop redundant type annotation in filters() protocol example --- docs/docs/python-sdk/guides/python-typing.mdx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/docs/python-sdk/guides/python-typing.mdx b/docs/docs/python-sdk/guides/python-typing.mdx index 5c0fce4b6..24506d4f6 100644 --- a/docs/docs/python-sdk/guides/python-typing.mdx +++ b/docs/docs/python-sdk/guides/python-typing.mdx @@ -109,10 +109,9 @@ Protocols make the most difference in generators, transforms, and checks, where ```python from .protocols import NetworkDevice -# `kind` is the generated class, not a string, so attributes are type-checked -leaf_switches: list[NetworkDevice] = await client.filters( - kind=NetworkDevice, role__value="leaf", include=["interfaces"] -) +# Passing the generated class as `kind` makes `filters()` return `list[NetworkDevice]`, +# so `switch` is fully typed without annotating the variable yourself +leaf_switches = await client.filters(kind=NetworkDevice, role__value="leaf", include=["interfaces"]) for switch in leaf_switches: switch.name.value # autocompleted, and checked against the schema