Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,9 @@ jobs:
version: ${{ github.ref_name }}

repository-dispatch:
needs: check_release
needs:
- check_release
- publish-pypi
uses: ./.github/workflows/repository-dispatch.yml
secrets: inherit
with:
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/repository-dispatch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ jobs:
repo:
- "opsmill/infrahub-demo-dc-fabric"
- "INFRAHUB_CUSTOMER1_REPOSITORY"
- "INFRAHUB_CUSTOMER2_REPOSITORY"
- "INFRAHUB_CUSTOMER3_REPOSITORY"

steps:
- name: Checkout code
Expand Down
5 changes: 1 addition & 4 deletions .github/workflows/sync-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ on:
- 'docs/docs/**'
- 'docs/sidebars-infrahubctl.ts'
- 'docs/sidebars-python-sdk.ts'
pull_request:
branches:
- stable

jobs:
sync:
Expand Down Expand Up @@ -42,4 +39,4 @@ jobs:
git config user.name github-actions
git config user.email [email protected]
git add .
if ! (git diff --quiet && git diff --staged --quiet); then git commit -m "Sync doc Python SDK from infrahub repo" && git push; fi
if ! (git diff --quiet && git diff --staged --quiet); then git commit -m "Sync doc from Python SDK repo" && git push; fi
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ This project uses [*towncrier*](https://towncrier.readthedocs.io/) and the chang

<!-- towncrier release notes start -->

## [1.12.1](https://github.com/opsmill/infrahub-sdk-python/tree/v1.12.1) - 2025-05-12

### Changed

- Pin click to v8.1.* as a temporary workaround for #406 ([#406](https://github.com/opsmill/infrahub-sdk-python/issues/406))

## [1.12.0](https://github.com/opsmill/infrahub-sdk-python/tree/v1.12.0) - 2025-04-29

### Added
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ At its heart, Infrahub is built on 3 fundamental pillars:

The Infrahub Python SDK greatly simplifies how you can interact with Infrahub programmatically.

More information can be found in the [Infrahub Python SDK Documentation](https://docs.infrahub.app/python-sdk/).
More information can be found in the [Infrahub Python SDK Documentation](https://docs.infrahub.app/python-sdk/introduction).

## Installation

Expand Down
111 changes: 59 additions & 52 deletions docs/docs/python-sdk/guides/store.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,175 +6,182 @@ import TabItem from '@theme/TabItem';

# Using the client store

The client in the SDK contains a store object that is used to store Nodes in a local cache.
The client in the SDK contains a store that is used to store objects in a local cache.

The store is mainly used for the internal working of the SDK. It is used to create relations between nodes that might not yet exist in the database, or to store relations for nodes that were retrieved from the database, amongst other things.
The store is mainly used for the internal working of the SDK. It is used to create relations between objects that might not yet exist in the database, or to store relations for objects that were retrieved from the database, amongst other things.

The store can also be used to store Nodes that we retrieve using the client query methods. This allows to not have to keep references to nodes we retrieved throughout scripts, or avoids situations where we have to redo queries to retrieve nodes.
The store stores objects that we are retrieving from Infrahub using the different query methods. This allows to not have to keep references to objects throughout scripts, or avoids situations where we have to re-execute queries.

## Storing nodes in the store
Objects are stored in the following scenario:

Nodes retrieved from Infrahub using a the SDK client's query methods, such as the `get` `filters` or `all` method, will be automatically stored in the internal store. Nodes stored in the store using this method can be retrieved using their `id` as the key in the store.
- The resulting objects from using the SDK client's `get`, `filters` or `all` methods

<Tabs groupId="async-sync">
<TabItem value="Async" default>

```python
tag = await client.get(kind="BuiltinTag", name__value="RED")
tag_in_store = client.store.get(key=tag.id)
```

</TabItem>
<TabItem value="Sync" default>

```python
tag = client.get(kind="BuiltinTag", name__value="RED")
tag_in_store = client.store.get(key=tag.id)
```

</TabItem>
</Tabs>

This behaviour may not be desirable in all scenarios, therefor you can explicitly disable this behaviour by setting the `populate_store` argument to `False` when calling the query methods.
- The resulting related objects for objects retrieved using the SDK client's query methods, when we use the `prefetch_relationships` argument.

<Tabs groupId="async-sync">
<TabItem value="Async" default>

```python
tag = await client.get(kind="BuiltinTag", name__value="RED", populate_store=False)
device = await client.get(kind="InfraDevice", name__value="atl1-edge1", prefetch_relationships=True)
site = client.store.get(key=device.site.id)
```

</TabItem>
<TabItem value="Sync" default>

```python
tag = client.get(kind="BuiltinTag", name__value="RED", populate_store=False)
device = client.get(kind="InfraDevice", name__value="atl1-edge1", prefetch_relationships=True)
site = client.store.get(key=device.site.id)
```

</TabItem>
</Tabs>

## Manually storing objects in the store

You can store nodes in the object store manually using the `set` method. This has the advantage that you can choose which key you use to reference the node in the store. For example, we could use the name attribute value of the node as the key.
- The related objects of a object's relationship when the `fetch` method is used

<Tabs groupId="async-sync">
<TabItem value="Async" default>

```python
tag = await client.get(kind="BuiltinTag", name__value="RED", populate_store=False)
client.store.set(key=tag.name.value, node=tag)
device = await client.get(kind="InfraDevice", name__value="atl1-edge1")
await device.site.fetch()
site = client.store.get(key=device.site.id)
```

</TabItem>
<TabItem value="Sync" default>

```python
tag = client.get(kind="BuiltinTag", name__value="RED", populate_store=False)
client.store.set(key=tag.name.value, node=tag)
device = client.get(kind="InfraDevice", name__value="atl1-edge1")
device.site.fetch()
site = client.store.get(key=device.site.id)
```

</TabItem>
</Tabs>

## Retrieving object from the store

Nodes can be retrieved from the internal store using the key that was used to store them.
For nodes that are stored by the client's query methods, this will be their `id`.
- Objects that get created using the SDK

<Tabs groupId="async-sync">
<TabItem value="Async" default>

```python
tag = await client.get(kind="BuiltinTag", name__value="RED")
tag = await client.create("BuiltinTag", name="BLACK")
await tag.save()
tag_in_store = client.store.get(key=tag.id)
assert tag == tag_in_store
```

</TabItem>
<TabItem value="Sync" default>

```python
tag = client.get(kind="BuiltinTag", name__value="RED")
tag = client.create("BuiltinTag", name="BLACK")
tag.save()
tag_in_store = client.store.get(key=tag.id)
assert tag == tag_in_store
```

</TabItem>
</Tabs>

For nodes that have been added manually to the store, this will be the key that you specified when storing the node. For example, when you used the name attribute value of the node you can use that name to retrieve the node from the store.
## Retrieving objects from the store

You can retrieve objects from the store using their `id` or `hfid`. When using the `hfid`, we also have to provide the `kind` of the object that we want to retrieve.

<Tabs groupId="async-sync">
<TabItem value="Async" default>

```python
tag = await client.get(kind="BuiltinTag", name__value="RED", populate_store=False)
client.store.set(key=tag.name.value, node=tag)
tag_in_store = client.store.get(key=tag.name.value)
assert tag == tag_in_store
tag = await client.get("BuiltinTag", name__value="BLACK")
tag_in_store = client.store.get(key=tag.id)
tag == tag_in_store

tag = await client.get("BuiltinTag", name__value="BLACK")
tag_in_store = client.store.get(key=tag.hfid, kind="BuiltinTag")
tag == tag_in_store
```

</TabItem>
<TabItem value="Sync" default>

```python
tag = client.get(kind="BuiltinTag", name__value="RED", populate_store=False)
client.store.set(key=tag.name.value, node=tag)
tag_in_store = client.store.get(key=tag.name.value)
assert tag == tag_in_store
tag = client.get("BuiltinTag", name__value="BLACK")
tag_in_store = client.store.get(key=tag.id)
tag == tag_in_store

tag = client.get("BuiltinTag", name__value="BLACK")
tag_in_store = client.store.get(key=tag.hfid, kind="BuiltinTag")
tag == tag_in_store
```

</TabItem>
</Tabs>

## Prefetch relationships
## Manually storing objects in the store

When you are using the internal store, you can use the `prefect_relationships` argument in all the client query methods. All the related nodes, for the relationships that are in scope of the query will be retrieved using this option. This has the advantage that you don't have to `fetch` related node(s) for a relation manually, but it comes at the cost of a more complex query, which might have an impact on the performance of the query.
You can store objects in the store manually using the `set` method. This has the advantage that you can choose a key that you want to use to reference the object in the store, besides the `id` or `hfid`. For example, we could use the name attribute value of the node as the key.

<Tabs groupId="async-sync">
<TabItem value="Async" default>

```python
device = await client.get(kind="TestDevice", name__value="atl1-edge1", populate_store=True, prefetch_relationships=True, include=["interfaces"])
print(device.interfaces.peers[0].peer.name.value)
tag = await client.get(kind="BuiltinTag", name__value="RED", populate_store=False)
client.store.set(key=tag.name.value, node=tag)

tag_in_store = client.store.get(key=tag.name.value)
tag_in_store = client.store.get(key=tag.id)
tag_in_store = client.store.get(key=tag.hfid, kind="BuiltinTag")
```

</TabItem>
<TabItem value="Sync" default>

```python
device = client.get(kind="TestDevice", name__value="atl1-edge1", populate_store=True, prefetch_relationships=True, include=["interfaces"])
print(device.interfaces.peers[0].peer.name.value)
tag = client.get(kind="BuiltinTag", name__value="RED", populate_store=False)
client.store.set(key=tag.name.value, node=tag)

tag_in_store = client.store.get(key=tag.name.value)
tag_in_store = client.store.get(key=tag.id)
tag_in_store = client.store.get(key=tag.hfid, kind="BuiltinTag")
```

</TabItem>
</Tabs>

## Using a custom store
## Disable storing objects in the store using the different query methods

You can use a custom store, outside of the Infrahub SDK client. Storing or retrieving nodes works exactly the same as for the internal store. The advantage is that you are in full control of the contents of the store.
In some scenarios it might not be desirable to automatically store the retrieved objects in the store, when using the SDK client's different query methods. In this case you can set the `populate_store` argument to `False`.

<Tabs groupId="async-sync">
<TabItem value="Async" default>

```python
from infrahub_sdk.store import NodeStore
store = NodeStore()

device = await client.get(kind="TestDevice", name__value="atl1-edge1", populate_store=False)
store.set(key=device.name.value, node=device)
store.get(key=device.name.value)
tag = await client.get(kind="BuiltinTag", name__value="RED", populate_store=False)
```

</TabItem>
<TabItem value="Sync" default>

```python
from infrahub_sdk.store import NodeStoreSync
store = NodeStoreSync()

device = await client.get(kind="TestDevice", name__value="atl1-edge1", populate_store=False)
store.set(key=device.name.value, node=device)
store.get(key=device.name.value)
tag = client.get(kind="BuiltinTag", name__value="RED", populate_store=False)
```

</TabItem>
Expand Down
4 changes: 2 additions & 2 deletions infrahub_sdk/timestamp.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def add(
nanoseconds: int = 0,
disambiguate: Literal["compatible"] = "compatible",
) -> Timestamp:
return Timestamp(
return self.__class__(
self._obj.add(
years=years,
months=months,
Expand Down Expand Up @@ -183,7 +183,7 @@ def subtract(
nanoseconds: int = 0,
disambiguate: Literal["compatible"] = "compatible",
) -> Timestamp:
return Timestamp(
return self.__class__(
self._obj.subtract(
years=years,
months=months,
Expand Down
Loading