From adea7c7f78f8e5874995869ee0ab3c8f423d5258 Mon Sep 17 00:00:00 2001 From: Patrick Ogenstad Date: Thu, 22 Jan 2026 17:06:31 +0100 Subject: [PATCH] Use format specifiers instead of percent format --- infrahub_sdk/groups.py | 23 ++++++++++------------- infrahub_sdk/utils.py | 2 +- pyproject.toml | 1 - tests/unit/sdk/test_query_analyzer.py | 25 +++++++++++-------------- tests/unit/sdk/test_utils.py | 24 ++++++++++++++++++++++++ 5 files changed, 46 insertions(+), 29 deletions(-) diff --git a/infrahub_sdk/groups.py b/infrahub_sdk/groups.py index 9c39003d..11ee9a15 100644 --- a/infrahub_sdk/groups.py +++ b/infrahub_sdk/groups.py @@ -6,21 +6,18 @@ async def group_add_subscriber( client: InfrahubClient, group: InfrahubNode, subscribers: list[str], branch: str ) -> dict: subscribers_str = ["{ id: " + f'"{subscriber}"' + " }" for subscriber in subscribers] - query = """ - mutation { + query = f""" + mutation {{ RelationshipAdd( - data: { - id: "%s", + data: {{ + id: "{group.id}", name: "subscribers", - nodes: [ %s ] - } - ) { + nodes: [ {", ".join(subscribers_str)} ] + }} + ) {{ ok - } - } - """ % ( - group.id, - ", ".join(subscribers_str), - ) + }} + }} + """ return await client.execute_graphql(query=query, branch_name=branch, tracker="mutation-relationshipadd") diff --git a/infrahub_sdk/utils.py b/infrahub_sdk/utils.py index 8556b012..d67aaacb 100644 --- a/infrahub_sdk/utils.py +++ b/infrahub_sdk/utils.py @@ -156,7 +156,7 @@ def deep_merge_dict(dicta: dict, dictb: dict, path: list | None = None) -> dict: elif a_val == b_val or (a_val is not None and b_val is None): continue else: - raise ValueError("Conflict at %s" % ".".join(path + [str(key)])) + raise ValueError(f"Conflict at {'.'.join(path + [str(key)])}") else: dicta[key] = dictb[key] return dicta diff --git a/pyproject.toml b/pyproject.toml index c417a909..55ff62c2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -262,7 +262,6 @@ ignore = [ "SIM108", # Use ternary operator `key_str = f"{value[ALIAS_KEY]}: {key}" if ALIAS_KEY in value and value[ALIAS_KEY] else key` instead of `if`-`else`-block "SIM110", # Use `return any(getattr(item, resource_field) == resource_id for item in getattr(self, RESOURCE_MAP[resource_type]))` instead of `for` loop "TC003", # Move standard library import `collections.abc.Iterable` into a type-checking block - "UP031", # Use format specifiers instead of percent format ] diff --git a/tests/unit/sdk/test_query_analyzer.py b/tests/unit/sdk/test_query_analyzer.py index a4deefb2..4c4cdc0f 100644 --- a/tests/unit/sdk/test_query_analyzer.py +++ b/tests/unit/sdk/test_query_analyzer.py @@ -152,22 +152,19 @@ async def test_get_variables(query_01: str, query_04: str, query_05: str, query_ [("[ID]", False), ("[ID]!", True), ("[ID!]", False), ("[ID!]!", True)], ) async def test_get_nested_variables(var_type: str, var_required: bool) -> None: - query = ( - """ - query ($ids: %s){ - TestPerson(ids: $ids) { - edges { - node { - name { + query = f""" + query ($ids: {var_type}){{ + TestPerson(ids: $ids) {{ + edges {{ + node {{ + name {{ value - } - } - } - } - } + }} + }} + }} + }} + }} """ - % var_type - ) gqa = GraphQLQueryAnalyzer(query=query) assert [var.model_dump() for var in gqa.variables] == [ diff --git a/tests/unit/sdk/test_utils.py b/tests/unit/sdk/test_utils.py index eae23150..8044ba2c 100644 --- a/tests/unit/sdk/test_utils.py +++ b/tests/unit/sdk/test_utils.py @@ -119,6 +119,30 @@ def test_deep_merge_dict() -> None: assert deep_merge_dict(f, g) == {"keyA": "foo", "keyB": "bar"} +def test_deep_merge_dict_conflict_scalar_values() -> None: + """Test that conflicting scalar values raise ValueError.""" + a = {"key": 1} + b = {"key": 2} + with pytest.raises(ValueError, match="Conflict at key"): + deep_merge_dict(a, b) + + +def test_deep_merge_dict_conflict_nested() -> None: + """Test that nested conflicts include full path in error message.""" + a = {"level1": {"level2": {"key": "value_a"}}} + b = {"level1": {"level2": {"key": "value_b"}}} + with pytest.raises(ValueError, match=r"Conflict at level1\.level2\.key"): + deep_merge_dict(a, b) + + +def test_deep_merge_dict_conflict_type_mismatch() -> None: + """Test that type mismatches (non-dict/list) raise ValueError.""" + a = {"key": "string"} + b = {"key": 123} + with pytest.raises(ValueError, match="Conflict at key"): + deep_merge_dict(a, b) + + def test_str_to_bool() -> None: assert str_to_bool(True) is True assert str_to_bool(False) is False