Skip to content

Commit 0e0ddb6

Browse files
fix: don't change graph on incorrect call to tmp_remove_nodes (#266)
* fix: don't change graph on incorrect call to tmp_remove_nodes Signed-off-by: Vincent Koppen <vincent.koppen@alliander.com> * cleanup Signed-off-by: Vincent Koppen <vincent.koppen@alliander.com> * keep as is Signed-off-by: Vincent Koppen <vincent.koppen@alliander.com> --------- Signed-off-by: Vincent Koppen <vincent.koppen@alliander.com>
1 parent e76602a commit 0e0ddb6

2 files changed

Lines changed: 50 additions & 31 deletions

File tree

src/power_grid_model_ds/_core/model/graphs/models/base.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -221,16 +221,21 @@ def tmp_remove_nodes(self, nodes: list[int]) -> Generator:
221221
considering certain nodes.
222222
"""
223223
edge_list = []
224-
for node in nodes:
225-
edge_list += list(self.in_branches(node))
226-
self.delete_node(node)
224+
node_list = []
227225

228-
yield
226+
try:
227+
for node in nodes:
228+
edge_list += list(self.in_branches(node))
229+
230+
self.delete_node(node)
231+
node_list.append(node)
229232

230-
for node in nodes:
231-
self.add_node(int(node)) # convert to int to avoid type issues when input is e.g. a numpy array
232-
for source, target in edge_list:
233-
self.add_branch(source, target)
233+
yield
234+
finally:
235+
for node in node_list:
236+
self.add_node(int(node)) # convert to int to avoid type issues when input is e.g. a numpy array
237+
for source, target in edge_list:
238+
self.add_branch(source, target)
234239

235240
@contextmanager
236241
def tmp_remove_branches(self, branches: list[tuple[int, int]]) -> Generator:

tests/unit/model/graphs/test_graph_model.py

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -143,39 +143,53 @@ def test_graph_in_branches(self, graph: BaseGraphModel):
143143
assert list(graph.in_branches(2)) == [(1, 2), (1, 2), (1, 2)]
144144

145145

146-
def test_tmp_remove_nodes(graph_with_2_routes: BaseGraphModel) -> None:
147-
graph = graph_with_2_routes
146+
class TestTmpRemoveNodes:
147+
def test_tmp_remove_nodes(self, graph_with_2_routes: BaseGraphModel) -> None:
148+
graph = graph_with_2_routes
149+
150+
assert graph.nr_branches == 4
148151

149-
assert graph.nr_branches == 4
152+
# add parallel branches to test whether they are restored correctly
153+
graph.add_branch(1, 5)
154+
graph.add_branch(5, 1)
150155

151-
# add parallel branches to test whether they are restored correctly
152-
graph.add_branch(1, 5)
153-
graph.add_branch(5, 1)
156+
assert graph.nr_nodes == 5
157+
assert graph.nr_branches == 6
154158

155-
assert graph.nr_nodes == 5
156-
assert graph.nr_branches == 6
159+
before_sets = [frozenset(branch) for branch in graph.all_branches]
160+
counter_before = Counter(before_sets)
157161

158-
before_sets = [frozenset(branch) for branch in graph.all_branches]
159-
counter_before = Counter(before_sets)
162+
with graph.tmp_remove_nodes([1, 2]):
163+
assert graph.nr_nodes == 3
164+
assert list(graph.all_branches) == [(5, 4)]
160165

161-
with graph.tmp_remove_nodes([1, 2]):
162-
assert graph.nr_nodes == 3
163-
assert list(graph.all_branches) == [(5, 4)]
166+
assert graph.nr_nodes == 5
167+
assert graph.nr_branches == 6
164168

165-
assert graph.nr_nodes == 5
166-
assert graph.nr_branches == 6
169+
after_sets = [frozenset(branch) for branch in graph.all_branches]
170+
counter_after = Counter(after_sets)
171+
assert counter_before == counter_after
167172

168-
after_sets = [frozenset(branch) for branch in graph.all_branches]
169-
counter_after = Counter(after_sets)
170-
assert counter_before == counter_after
173+
def test_tmp_remove_nodes_array_input(self, graph_with_2_routes: BaseGraphModel) -> None:
174+
with graph_with_2_routes.tmp_remove_nodes(np.array([1, 2])): # type: ignore[arg-type]
175+
pass
176+
177+
# check that the external ids are still all integers instead of e.g. np.int
178+
assert all([isinstance(e_id, int) for e_id in graph_with_2_routes.external_ids])
171179

180+
def test_invalid_tmp_remove_nodes(self, graph_with_2_routes: BaseGraphModel) -> None:
181+
original_graph = deepcopy(graph_with_2_routes)
182+
assert graph_with_2_routes.nr_nodes == 5
183+
assert graph_with_2_routes.nr_branches == 4
172184

173-
def test_tmp_remove_nodes_array_input(graph_with_2_routes: BaseGraphModel) -> None:
174-
with graph_with_2_routes.tmp_remove_nodes(np.array([1, 2])): # type: ignore[arg-type]
175-
pass
185+
# When we remove node 1 and then an non-existing node that crashes the process
186+
with pytest.raises(MissingNodeError), graph_with_2_routes.tmp_remove_nodes([1, 99]):
187+
pass
176188

177-
# check that the external ids are still all integers instead of e.g. np.int
178-
assert all([isinstance(e_id, int) for e_id in graph_with_2_routes.external_ids])
189+
# The remaining graph object should still contain the same nodes and edges.
190+
assert graph_with_2_routes.nr_nodes == 5
191+
assert graph_with_2_routes.nr_branches == 4
192+
assert graph_with_2_routes == original_graph
179193

180194

181195
class TestTmpRemoveBranches:

0 commit comments

Comments
 (0)