-
Notifications
You must be signed in to change notification settings - Fork 61
Description
Bug report
cytoscapeobj.graph.remove_node(node) removes more nodes than just the node passed as the parameter. This also affects cytoscapeobj.graph.remove_node_by_id(node_id) which uses it.
ipycytoscape/ipycytoscape/cytoscape.py
Lines 287 to 305 in c72f39d
| def remove_node(self, node): | |
| """ | |
| Removes node from the end of the list. Equivalent to Python's remove method. | |
| Parameters | |
| ---------- | |
| node : ipycytoscape.Node | |
| """ | |
| try: | |
| self.nodes.remove(node) | |
| for target in list(self._adj[node.data["id"]]): | |
| self.remove_edge_by_id(node.data["id"], target) | |
| for source in list(self._adj): | |
| for target in list(self._adj[source]): | |
| if target == node.data["id"]: | |
| self.remove_edge_by_id(source, node.data["id"]) | |
| del self._adj[node.data["id"]] | |
| except ValueError: | |
| raise ValueError(f'{node.data["id"]} is not present in the graph.') |
Issues seems to lie specificly in this call:
ipycytoscape/ipycytoscape/cytoscape.py
Line 296 in c72f39d
| self.nodes.remove(node) |
Code for reproduction
-
Execute all existing cells in order. The graph would look like this

-
Observe the list of existing nodes
cytoscapeobj.graph.nodes
_______________________
[Node(data={'id': 'n0'}, position={}),
Node(data={'id': 'n1'}, position={}),
Node(data={'id': 'n2'}, position={}),
Node(data={'id': 'n3'}, position={}),
Node(data={'id': 'n4'}, position={}),
Node(data={'id': 'n5'}, position={}),
Node(data={'id': 'n6'}, position={}),
Node(data={'id': 'n7'}, position={}),
Node(data={'id': 'n8'}, position={}),
Node(data={'id': 'n9'}, position={}),
Node(data={'id': 'n10'}, position={}),
Node(data={'id': 'n11'}, position={}),
Node(data={'id': 'n12'}, position={}),
Node(data={'id': 'n13'}, position={}),
Node(data={'id': 'n14'}, position={}),
Node(data={'id': 'n15'}, position={}),
Node(data={'id': 'n16'}, position={})]
- Remove node with id
n3(the bottom-right child node in the left-most graph)
cytoscapeobj.graph.remove_node_by_id('n3')
The graph now looks like this:

Alternatively, you can remove the node from the list of nodes (which is what cytoscapeobj.graph.remove_node does internally) to the same effect:
cytoscapeobj.graph.nodes.remove(cytoscapeobj.graph.nodes[3])
- Inspect the list of nodes
cytoscapeobj.graph.nodes
_______________________
[Node(data={'id': 'n0'}, position={}),
Node(data={'id': 'n1'}, position={}),
Node(data={'id': 'n2'}, position={}),
Node(data={'id': 'n4'}, position={}, removed=True),
Node(data={'id': 'n5'}, position={}, removed=True),
Node(data={'id': 'n6'}, position={}, removed=True),
Node(data={'id': 'n7'}, position={}, removed=True),
Node(data={'id': 'n8'}, position={}, removed=True),
Node(data={'id': 'n9'}, position={}, removed=True),
Node(data={'id': 'n10'}, position={}, removed=True),
Node(data={'id': 'n11'}, position={}, removed=True),
Node(data={'id': 'n12'}, position={}, removed=True),
Node(data={'id': 'n13'}, position={}, removed=True),
Node(data={'id': 'n14'}, position={}, removed=True),
Node(data={'id': 'n15'}, position={}, removed=True),
Node(data={'id': 'n16'}, position={}, removed=True)]
...
- JS console logs indicate an error immediately after running
cytoscapeobj.graph.remove_node_by_id('n3'):
Uncaught (in promise) Error: Can not create second element with ID `n4`
Uncaught (in promise) Error: Can not create second element with ID `n4`
Uncaught (in promise) Error: Can not create edge `a1b4ae96-fe81-4363-ba59-23298b1a10e3` with nonexistant source `n4`
Is this the intended behavior?
Actual outcome
Nodes with ids "higher" than node requested to be deleted are getting removed=True on them and disappear from the graph.
Expected outcome
Maybe it would make more sense to make this optional, and only delete a single node by default?
Version Info
- runtime environment: Binder (link above)
- ipycytoscape version : 1.3.2
- Python version: 3.7.12
- JupyterLab version: 3.3.1