Skip to content

Commit 0840079

Browse files
committed
updates
1 parent f7afffd commit 0840079

File tree

6 files changed

+97
-32
lines changed

6 files changed

+97
-32
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Refactor InfrahubNode to avoid the creation of a dynamic Python class for each object defined

infrahub_sdk/ctl/generator.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,20 +74,20 @@ async def run(
7474
targets = await client.get(
7575
kind="CoreGroup", branch=branch, include=["members"], name__value=generator_config.targets
7676
)
77-
await targets.members.fetch()
77+
await targets._get_relationship_many(name="members").fetch()
7878

79-
if not targets.members.peers:
79+
if not targets._get_relationship_many(name="members").peers:
8080
console.print(
8181
f"[red]No members found within '{generator_config.targets}', not running generator '{generator_name}'"
8282
)
8383
return
8484

85-
for member in targets.members.peers:
85+
for member in targets._get_relationship_many(name="members").peers:
8686
check_parameter = {}
8787
if identifier:
8888
attribute = getattr(member.peer, identifier)
8989
check_parameter = {identifier: attribute.value}
90-
params = {"name": member.peer.name.value}
90+
params = {"name": member.peer._get_attribute(name="name").value}
9191
generator = generator_class(
9292
query=generator_config.query,
9393
client=client,

infrahub_sdk/node/node.py

Lines changed: 82 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
from __future__ import annotations
22

3+
from collections.abc import Iterable
34
from copy import copy
45
from typing import TYPE_CHECKING, Any
56

67
from ..constants import InfrahubClientMode
7-
from ..exceptions import FeatureNotSupportedError, NodeNotFoundError, SchemaNotFoundError
8+
from ..exceptions import FeatureNotSupportedError, NodeNotFoundError, ResourceNotDefinedError, SchemaNotFoundError
89
from ..graphql import Mutation, Query
910
from ..schema import GenericSchemaAPI, RelationshipCardinality, RelationshipKind
1011
from ..utils import compare_lists, generate_short_id, get_flat_value
@@ -42,8 +43,6 @@ def __init__(self, schema: MainSchemaTypesAPI, branch: str, data: dict | None =
4243
self._branch = branch
4344
self._existing: bool = True
4445
self._attribute_data: dict[str, Attribute] = {}
45-
self._relationship_cardinality_many_data: dict[str, RelationshipManager | RelationshipManagerSync] = {}
46-
self._relationship_cardinality_one_data: dict[str, RelatedNode | RelatedNodeSync] = {}
4746

4847
# Generate a unique ID only to be used inside the SDK
4948
# The format if this ID is purposely different from the ID used by the API
@@ -142,18 +141,6 @@ def _init_attributes(self, data: dict | None = None) -> None:
142141
name=attr_schema.name, schema=attr_schema, data=attr_data
143142
)
144143

145-
def __getattr__(
146-
self, name: str
147-
) -> Attribute | RelationshipManager | RelationshipManagerSync | RelatedNode | RelatedNodeSync:
148-
if "_attribute_data" in self.__dict__ and name in self._attribute_data:
149-
return self._attribute_data[name]
150-
if "_relationship_cardinality_many_data" in self.__dict__ and name in self._relationship_cardinality_many_data:
151-
return self._relationship_cardinality_many_data[name]
152-
if "_relationship_cardinality_one_data" in self.__dict__ and name in self._relationship_cardinality_one_data:
153-
return self._relationship_cardinality_one_data[name]
154-
155-
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")
156-
157144
def __setattr__(self, name: str, value: Any) -> None:
158145
"""Set values for attributes that exist or revert to normal behaviour"""
159146
if "_attribute_data" in self.__dict__ and name in self._attribute_data:
@@ -463,6 +450,12 @@ def _relationship_mutation(self, action: str, relation_to_update: str, related_n
463450
}}
464451
"""
465452

453+
def _get_attribute(self, name: str) -> Attribute:
454+
if name in self._attribute_data:
455+
return self._attribute_data[name]
456+
457+
raise ResourceNotDefinedError(message=f"The node doesn't have an attribute for {name}")
458+
466459

467460
class InfrahubNode(InfrahubNodeBase):
468461
"""Represents a Infrahub node in an asynchronous context."""
@@ -486,6 +479,9 @@ def __init__(
486479
if isinstance(data, dict) and isinstance(data.get("node"), dict):
487480
data = data.get("node")
488481

482+
self._relationship_cardinality_many_data: dict[str, RelationshipManager] = {}
483+
self._relationship_cardinality_one_data: dict[str, RelatedNode] = {}
484+
489485
super().__init__(schema=schema, branch=branch or client.default_branch, data=data)
490486

491487
@classmethod
@@ -523,6 +519,16 @@ def _init_relationships(self, data: dict | None = None) -> None:
523519
data=rel_data,
524520
)
525521

522+
def __getattr__(self, name: str) -> Attribute | RelationshipManager | RelatedNode:
523+
if "_attribute_data" in self.__dict__ and name in self._attribute_data:
524+
return self._attribute_data[name]
525+
if "_relationship_cardinality_many_data" in self.__dict__ and name in self._relationship_cardinality_many_data:
526+
return self._relationship_cardinality_many_data[name]
527+
if "_relationship_cardinality_one_data" in self.__dict__ and name in self._relationship_cardinality_one_data:
528+
return self._relationship_cardinality_one_data[name]
529+
530+
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")
531+
526532
def __setattr__(self, name: str, value: Any) -> None:
527533
"""Set values for relationship names that exist or revert to normal behaviour"""
528534
if "_relationship_cardinality_one_data" in self.__dict__ and name in self._relationship_cardinality_one_data:
@@ -552,14 +558,14 @@ async def artifact_generate(self, name: str) -> None:
552558
self._validate_artifact_support(ARTIFACT_GENERATE_FEATURE_NOT_SUPPORTED_MESSAGE)
553559

554560
artifact = await self._client.get(kind="CoreArtifact", name__value=name, object__ids=[self.id])
555-
await artifact.definition.fetch() # type: ignore[attr-defined]
556-
await artifact.definition.peer.generate([artifact.id]) # type: ignore[attr-defined]
561+
await artifact._get_relationship_one(name="definition").fetch()
562+
await artifact._get_relationship_one(name="definition").peer.generate([artifact.id])
557563

558564
async def artifact_fetch(self, name: str) -> str | dict[str, Any]:
559565
self._validate_artifact_support(ARTIFACT_GENERATE_FEATURE_NOT_SUPPORTED_MESSAGE)
560566

561567
artifact = await self._client.get(kind="CoreArtifact", name__value=name, object__ids=[self.id])
562-
content = await self._client.object_store.get(identifier=artifact.storage_id.value) # type: ignore[attr-defined]
568+
content = await self._client.object_store.get(identifier=artifact._get_attribute(name="storage_id").value)
563569
return content
564570

565571
async def delete(self, timeout: int | None = None, request_context: RequestContext | None = None) -> None:
@@ -1002,6 +1008,27 @@ async def get_pool_resources_utilization(self) -> list[dict[str, Any]]:
10021008
return [edge["node"] for edge in response[graphql_query_name]["edges"]]
10031009
return []
10041010

1011+
def _get_relationship_many(self, name: str) -> RelationshipManager:
1012+
if name in self._relationship_cardinality_many_data:
1013+
return self._relationship_cardinality_many_data[name]
1014+
1015+
raise ResourceNotDefinedError(message=f"The node doesn't have a cardinality=many relationship for {name}")
1016+
1017+
def _get_relationship_one(self, name: str) -> RelatedNode:
1018+
if name in self._relationship_cardinality_one_data:
1019+
return self._relationship_cardinality_one_data[name]
1020+
1021+
raise ResourceNotDefinedError(message=f"The node doesn't have a cardinality=one relationship for {name}")
1022+
1023+
def __dir__(self) -> Iterable[str]:
1024+
base = list(super().__dir__())
1025+
return sorted(
1026+
base
1027+
+ list(self._attribute_data.keys())
1028+
+ list(self._relationship_cardinality_many_data.keys())
1029+
+ list(self._relationship_cardinality_one_data.keys())
1030+
)
1031+
10051032

10061033
class InfrahubNodeSync(InfrahubNodeBase):
10071034
"""Represents a Infrahub node in a synchronous context."""
@@ -1025,6 +1052,9 @@ def __init__(
10251052
if isinstance(data, dict) and isinstance(data.get("node"), dict):
10261053
data = data.get("node")
10271054

1055+
self._relationship_cardinality_many_data: dict[str, RelationshipManagerSync] = {}
1056+
self._relationship_cardinality_one_data: dict[str, RelatedNodeSync] = {}
1057+
10281058
super().__init__(schema=schema, branch=branch or client.default_branch, data=data)
10291059

10301060
@classmethod
@@ -1063,6 +1093,16 @@ def _init_relationships(self, data: dict | None = None) -> None:
10631093
data=rel_data,
10641094
)
10651095

1096+
def __getattr__(self, name: str) -> Attribute | RelationshipManagerSync | RelatedNodeSync:
1097+
if "_attribute_data" in self.__dict__ and name in self._attribute_data:
1098+
return self._attribute_data[name]
1099+
if "_relationship_cardinality_many_data" in self.__dict__ and name in self._relationship_cardinality_many_data:
1100+
return self._relationship_cardinality_many_data[name]
1101+
if "_relationship_cardinality_one_data" in self.__dict__ and name in self._relationship_cardinality_one_data:
1102+
return self._relationship_cardinality_one_data[name]
1103+
1104+
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")
1105+
10661106
def __setattr__(self, name: str, value: Any) -> None:
10671107
"""Set values for relationship names that exist or revert to normal behaviour"""
10681108
if "_relationship_cardinality_one_data" in self.__dict__ and name in self._relationship_cardinality_one_data:
@@ -1090,13 +1130,13 @@ def generate(self, nodes: list[str] | None = None) -> None:
10901130
def artifact_generate(self, name: str) -> None:
10911131
self._validate_artifact_support(ARTIFACT_GENERATE_FEATURE_NOT_SUPPORTED_MESSAGE)
10921132
artifact = self._client.get(kind="CoreArtifact", name__value=name, object__ids=[self.id])
1093-
artifact.definition.fetch() # type: ignore[attr-defined]
1094-
artifact.definition.peer.generate([artifact.id]) # type: ignore[attr-defined]
1133+
artifact._get_relationship_one(name="definition").fetch()
1134+
artifact._get_relationship_one(name="definition").peer.generate([artifact.id])
10951135

10961136
def artifact_fetch(self, name: str) -> str | dict[str, Any]:
10971137
self._validate_artifact_support(ARTIFACT_FETCH_FEATURE_NOT_SUPPORTED_MESSAGE)
10981138
artifact = self._client.get(kind="CoreArtifact", name__value=name, object__ids=[self.id])
1099-
content = self._client.object_store.get(identifier=artifact.storage_id.value) # type: ignore[attr-defined]
1139+
content = self._client.object_store.get(identifier=artifact._get_attribute(name="storage_id").value)
11001140
return content
11011141

11021142
def delete(self, timeout: int | None = None, request_context: RequestContext | None = None) -> None:
@@ -1538,3 +1578,24 @@ def get_pool_resources_utilization(self) -> list[dict[str, Any]]:
15381578
if response[graphql_query_name].get("count", 0):
15391579
return [edge["node"] for edge in response[graphql_query_name]["edges"]]
15401580
return []
1581+
1582+
def _get_relationship_many(self, name: str) -> RelationshipManager | RelationshipManagerSync:
1583+
if name in self._relationship_cardinality_many_data:
1584+
return self._relationship_cardinality_many_data[name]
1585+
1586+
raise ResourceNotDefinedError(message=f"The node doesn't have a cardinality=many relationship for {name}")
1587+
1588+
def _get_relationship_one(self, name: str) -> RelatedNode | RelatedNodeSync:
1589+
if name in self._relationship_cardinality_one_data:
1590+
return self._relationship_cardinality_one_data[name]
1591+
1592+
raise ResourceNotDefinedError(message=f"The node doesn't have a cardinality=one relationship for {name}")
1593+
1594+
def __dir__(self) -> Iterable[str]:
1595+
base = list(super().__dir__())
1596+
return sorted(
1597+
base
1598+
+ list(self._attribute_data.keys())
1599+
+ list(self._relationship_cardinality_many_data.keys())
1600+
+ list(self._relationship_cardinality_one_data.keys())
1601+
)

infrahub_sdk/query_groups.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

3-
from typing import TYPE_CHECKING, Any, Sequence
3+
from collections.abc import Sequence
4+
from typing import TYPE_CHECKING, Any
45

56
from .constants import InfrahubClientMode
67
from .exceptions import NodeNotFoundError
@@ -101,7 +102,7 @@ async def get_group(self, store_peers: bool = False) -> InfrahubNode | None:
101102
if not store_peers:
102103
return group
103104

104-
self.previous_members = group._relationship_cardinality_many_data["members"].peers
105+
self.previous_members = group._get_relationship_many(name="members").peers
105106
return group
106107

107108
async def delete_unused(self) -> None:
@@ -195,7 +196,7 @@ def get_group(self, store_peers: bool = False) -> InfrahubNodeSync | None:
195196
if not store_peers:
196197
return group
197198

198-
self.previous_members = group._relationship_cardinality_many_data["members"].peers
199+
self.previous_members = group._get_relationship_many(name="members").peers
199200
return group
200201

201202
def delete_unused(self) -> None:

pyproject.toml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,6 @@ disallow_untyped_defs = true
113113
module = "infrahub_sdk.ctl.check"
114114
disable_error_code = ["call-overload"]
115115

116-
[[tool.mypy.overrides]]
117-
module = "infrahub_sdk.ctl.generator"
118-
disable_error_code = ["attr-defined"]
119-
120116
[[tool.mypy.overrides]]
121117
module = "infrahub_sdk.ctl.schema"
122118
disable_error_code = ["arg-type", "attr-defined", "misc", "union-attr"]

tests/unit/sdk/test_node.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,12 @@ async def test_init_node_data_user_with_relationships(client, location_schema: N
187187
assert isinstance(node.primary_tag, RelatedNodeBase)
188188
assert node.primary_tag.id == "pppppppp"
189189

190+
keys = dir(node)
191+
assert "name" in keys
192+
assert "type" in keys
193+
assert "tags" in keys
194+
assert "get_kind" in keys
195+
190196

191197
@pytest.mark.parametrize("property_test", property_tests)
192198
@pytest.mark.parametrize("client_type", client_types)

0 commit comments

Comments
 (0)