Repository: projectmesa/mesa

Issue:
Virus on Network example incompatible with networkx >= 2.4
Hello!

After raising the PR #748 ,
https://travis-ci.org/projectmesa/mesa?utm_medium=notification&utm_source=github_status
Travis fails with the error in object, although only the documentation have changed from the previous version. 

Tests are passed in local.

It seems the current master would not pass the tests on travis (?).

Possibly also PR #747 affected by the same issue.

---

Edit: I was wrong **it fails in local** too from a fresh virtualenv. The problem seems to be located in `setup.py`.


Discussion:
Problem addressed in PR above. :bear: :bear: :bear: the master version is currently broken due to networkx upadte to version 2.4.

To avoid having the master again broken after third parties update, pinning dependencies or reconsidering the use of Pipenv should be the next task.
This is currently fixed with setting the requirement for networkx to <2.4

However, I encourage anyone to rather update the example and unpin the requirement. 

Since mesa is a modeling library I think it is a *bad* idea to pin dependencies. A minimum version would be okay, but we shouldn't prevent users from updating there packages and try our best to always work with the latest versions of our dependencies.

In this particular case we should have seen the depreciation warnings long ago on travis. However, with the way the examples are run the warnings don't seem to passed on to py.test. To make things clear: Mesa **does** work with `networkx 2.4`, it is just the example that is failing. I think this is another reason to split out the examples from the main repository. 
> To make things clear: Mesa does work with networkx 2.4, it is just the example that is failing. I think this is another reason to split out the examples from the main repository.

The error happens at mesa/space.py,

>  File "/home/rht/code/venv/lib/python3.7/site-packages/mesa/space.py", line 810, in _place_agent
>    self.G.node[node_id]['agent'].append(agent)
> AttributeError: 'Graph' object has no attribute 'node'

So it is not just the virus example that is failing, it is an error in the Mesa library.

Resolving patch:
diff --git a/examples/virus_on_network/virus_on_network/server.py b/examples/virus_on_network/virus_on_network/server.py
--- a/examples/virus_on_network/virus_on_network/server.py
+++ b/examples/virus_on_network/virus_on_network/server.py
@@ -28,7 +28,7 @@ def edge_width(agent1, agent2):
         return 2
 
     def get_agents(source, target):
-        return G.node[source]['agent'][0], G.node[target]['agent'][0]
+        return G.nodes[source]['agent'][0], G.nodes[target]['agent'][0]
 
     portrayal = dict()
     portrayal['nodes'] = [{'size': 6,
diff --git a/mesa/space.py b/mesa/space.py
--- a/mesa/space.py
+++ b/mesa/space.py
@@ -807,16 +807,16 @@ def move_agent(self, agent, node_id):
     def _place_agent(self, agent, node_id):
         """ Place the agent at the correct node. """
 
-        self.G.node[node_id]['agent'].append(agent)
+        self.G.nodes[node_id]['agent'].append(agent)
 
     def _remove_agent(self, agent, node_id):
         """ Remove an agent from a node. """
 
-        self.G.node[node_id]['agent'].remove(agent)
+        self.G.nodes[node_id]['agent'].remove(agent)
 
     def is_cell_empty(self, node_id):
         """ Returns a bool of the contents of a cell. """
-        return not self.G.node[node_id]['agent']
+        return not self.G.nodes[node_id]['agent']
 
     def get_cell_list_contents(self, cell_list):
         return list(self.iter_cell_list_contents(cell_list))
@@ -825,5 +825,5 @@ def get_all_cell_contents(self):
         return list(self.iter_cell_list_contents(self.G))
 
     def iter_cell_list_contents(self, cell_list):
-        list_of_lists = [self.G.node[node_id]['agent'] for node_id in cell_list if not self.is_cell_empty(node_id)]
+        list_of_lists = [self.G.nodes[node_id]['agent'] for node_id in cell_list if not self.is_cell_empty(node_id)]
         return [item for sublist in list_of_lists for item in sublist]
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -8,7 +8,7 @@
 requires = [
     'click',
     'cookiecutter',
-    'networkx < 2.4',
+    'networkx',
     'numpy',
     'pandas',
     'tornado',


Test patch:
diff --git a/tests/test_space.py b/tests/test_space.py
--- a/tests/test_space.py
+++ b/tests/test_space.py
@@ -335,12 +335,12 @@ def test_move_agent(self):
         _agent = self.agents[agent_number]
 
         assert _agent.pos == initial_pos
-        assert _agent in self.space.G.node[initial_pos]['agent']
-        assert _agent not in self.space.G.node[final_pos]['agent']
+        assert _agent in self.space.G.nodes[initial_pos]['agent']
+        assert _agent not in self.space.G.nodes[final_pos]['agent']
         self.space.move_agent(_agent, final_pos)
         assert _agent.pos == final_pos
-        assert _agent not in self.space.G.node[initial_pos]['agent']
-        assert _agent in self.space.G.node[final_pos]['agent']
+        assert _agent not in self.space.G.nodes[initial_pos]['agent']
+        assert _agent in self.space.G.nodes[final_pos]['agent']
 
     def test_is_cell_empty(self):
         assert not self.space.is_cell_empty(0)
@@ -393,18 +393,18 @@ def test_move_agent(self):
         _agent = self.agents[agent_number]
 
         assert _agent.pos == initial_pos
-        assert _agent in self.space.G.node[initial_pos]['agent']
-        assert _agent not in self.space.G.node[final_pos]['agent']
-        assert len(self.space.G.node[initial_pos]['agent']) == 2
-        assert len(self.space.G.node[final_pos]['agent']) == 1
+        assert _agent in self.space.G.nodes[initial_pos]['agent']
+        assert _agent not in self.space.G.nodes[final_pos]['agent']
+        assert len(self.space.G.nodes[initial_pos]['agent']) == 2
+        assert len(self.space.G.nodes[final_pos]['agent']) == 1
 
         self.space.move_agent(_agent, final_pos)
 
         assert _agent.pos == final_pos
-        assert _agent not in self.space.G.node[initial_pos]['agent']
-        assert _agent in self.space.G.node[final_pos]['agent']
-        assert len(self.space.G.node[initial_pos]['agent']) == 1
-        assert len(self.space.G.node[final_pos]['agent']) == 2
+        assert _agent not in self.space.G.nodes[initial_pos]['agent']
+        assert _agent in self.space.G.nodes[final_pos]['agent']
+        assert len(self.space.G.nodes[initial_pos]['agent']) == 1
+        assert len(self.space.G.nodes[final_pos]['agent']) == 2
 
     def test_is_cell_empty(self):
         assert not self.space.is_cell_empty(0)