From 4a14cda9728de485862b5d0b8cdbbeada2f1824e Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sat, 13 Jan 2024 12:17:10 +0100 Subject: [PATCH 1/3] Bugfix in agentset to handle addition and removal correctly Fix for #1959 --- mesa/agent.py | 3 ++- tests/test_agent.py | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/mesa/agent.py b/mesa/agent.py index 0b204c2255c..bbe9e944196 100644 --- a/mesa/agent.py +++ b/mesa/agent.py @@ -251,7 +251,8 @@ def do( Returns: AgentSet | list[Any]: The results of the method calls if return_results is True, otherwise the AgentSet itself. """ - res = [getattr(agent, method_name)(*args, **kwargs) for agent in self._agents] + # we iterate over the actual weakref keys and check if weakref is alive before calling the method + res = [getattr(agent(), method_name)(*args, **kwargs) for agent in self._agents.keyrefs() if agent()] return res if return_results else self diff --git a/tests/test_agent.py b/tests/test_agent.py index 3cc8f9adc4e..484c18c94fd 100644 --- a/tests/test_agent.py +++ b/tests/test_agent.py @@ -11,6 +11,21 @@ def get_unique_identifier(self): return self.unique_id +class TestAgentDo(Agent): + + def __init__(self, unique_id, model,): + super().__init__(unique_id, model) + self.agent_set = None + def get_unique_identifier(self): + return self.unique_id + + def do_add(self): + agent = TestAgentDo(self.model.next_id(), self.model) + self.agent_set.add(agent) + + def do_remove(self): + self.agent_set.remove(self) + def test_agent_removal(): model = Model() agent = TestAgent(model.next_id(), model) @@ -164,6 +179,31 @@ def test_agentset_do_method(): with pytest.raises(AttributeError): agentset.do("non_existing_method") + # tests for addition and removal in do + # do iterates, so no error should be raised to change size while iterating + # related to issue #1595 + + #setup + n = 10 + model = Model() + agents = [TestAgentDo(model.next_id(), model) for _ in range(n)] + agentset = AgentSet(agents, model) + for agent in agents: + agent.agent_set = agentset + + agentset.do("do_add") + assert len(agentset) == 2 * n + + #setup + model = Model() + agents = [TestAgentDo(model.next_id(), model) for _ in range(10)] + agentset = AgentSet(agents, model) + for agent in agents: + agent.agent_set = agentset + + agentset.do("do_remove") + assert len(agentset) == 0 + def test_agentset_get_attribute(): model = Model() From 855488e805016411d0ad2147fcb663bd15072716 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sat, 13 Jan 2024 13:24:51 +0100 Subject: [PATCH 2/3] name change --- mesa/agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesa/agent.py b/mesa/agent.py index bbe9e944196..67943147811 100644 --- a/mesa/agent.py +++ b/mesa/agent.py @@ -252,7 +252,7 @@ def do( AgentSet | list[Any]: The results of the method calls if return_results is True, otherwise the AgentSet itself. """ # we iterate over the actual weakref keys and check if weakref is alive before calling the method - res = [getattr(agent(), method_name)(*args, **kwargs) for agent in self._agents.keyrefs() if agent()] + res = [getattr(agentref(), method_name)(*args, **kwargs) for agentref in self._agents.keyrefs() if agentref()] return res if return_results else self From 495d9c3a0aa7c64c5a55ed8efa734f49dce5fa90 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 13 Jan 2024 12:33:52 +0000 Subject: [PATCH 3/3] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/agent.py | 6 +++++- tests/test_agent.py | 13 +++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/mesa/agent.py b/mesa/agent.py index 67943147811..109a9dcdba8 100644 --- a/mesa/agent.py +++ b/mesa/agent.py @@ -252,7 +252,11 @@ def do( AgentSet | list[Any]: The results of the method calls if return_results is True, otherwise the AgentSet itself. """ # we iterate over the actual weakref keys and check if weakref is alive before calling the method - res = [getattr(agentref(), method_name)(*args, **kwargs) for agentref in self._agents.keyrefs() if agentref()] + res = [ + getattr(agentref(), method_name)(*args, **kwargs) + for agentref in self._agents.keyrefs() + if agentref() + ] return res if return_results else self diff --git a/tests/test_agent.py b/tests/test_agent.py index 484c18c94fd..5861038d793 100644 --- a/tests/test_agent.py +++ b/tests/test_agent.py @@ -12,10 +12,14 @@ def get_unique_identifier(self): class TestAgentDo(Agent): - - def __init__(self, unique_id, model,): + def __init__( + self, + unique_id, + model, + ): super().__init__(unique_id, model) self.agent_set = None + def get_unique_identifier(self): return self.unique_id @@ -26,6 +30,7 @@ def do_add(self): def do_remove(self): self.agent_set.remove(self) + def test_agent_removal(): model = Model() agent = TestAgent(model.next_id(), model) @@ -183,7 +188,7 @@ def test_agentset_do_method(): # do iterates, so no error should be raised to change size while iterating # related to issue #1595 - #setup + # setup n = 10 model = Model() agents = [TestAgentDo(model.next_id(), model) for _ in range(n)] @@ -194,7 +199,7 @@ def test_agentset_do_method(): agentset.do("do_add") assert len(agentset) == 2 * n - #setup + # setup model = Model() agents = [TestAgentDo(model.next_id(), model) for _ in range(10)] agentset = AgentSet(agents, model)