Repository: projectmesa/mesa
Pull request #1316 (2022-05-15 12:25:42)

add batch_run to simple namespace

codecov[bot]: # [Codecov](https://codecov.io/gh/projectmesa/mesa/pull/1316?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa) Report
> Merging [#1316](https://codecov.io/gh/projectmesa/mesa/pull/1316?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa) (c53a039) into [main](https://codecov.io/gh/projectmesa/mesa/commit/f4f44ad1d5fb5d30651cabc8c8557a97c7f737e6?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa) (f4f44ad) will **increase** coverage by `0.00%`.
> The diff coverage is `100.00%`.

```diff
@@           Coverage Diff           @@
##             main    #1316   +/-   ##
=======================================
  Coverage   89.05%   89.06%           
=======================================
  Files          19       19           
  Lines        1243     1244    +1     
  Branches      244      244           
=======================================
+ Hits         1107     1108    +1     
  Misses        101      101           
  Partials       35       35           
```


| [Impacted Files](https://codecov.io/gh/projectmesa/mesa/pull/1316?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa) | Coverage Δ | |
|---|---|---|
| [mesa/\_\_init\_\_.py](https://codecov.io/gh/projectmesa/mesa/pull/1316/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa#diff-bWVzYS9fX2luaXRfXy5weQ==) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/projectmesa/mesa/pull/1316?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/projectmesa/mesa/pull/1316?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa). Last update [f4f44ad...c53a039](https://codecov.io/gh/projectmesa/mesa/pull/1316?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa).


Patch:
diff --git a/mesa/__init__.py b/mesa/__init__.py
index 62aec15e..fa60756d 100644
--- a/mesa/__init__.py
+++ b/mesa/__init__.py
@@ -13,8 +13,17 @@ import mesa.time as time
 import mesa.space as space
 import mesa.flat.visualization as visualization
 from mesa.datacollection import DataCollector
+from mesa.batchrunner import batch_run  # noqa
 
-__all__ = ["Model", "Agent", "time", "space", "visualization", "DataCollector"]
+__all__ = [
+    "Model",
+    "Agent",
+    "time",
+    "space",
+    "visualization",
+    "DataCollector",
+    "batch_run",
+]
 
 __title__ = "mesa"
 __version__ = "0.9.0"
diff --git a/mesa/flat/__init__.py b/mesa/flat/__init__.py
index 08aefa79..728d8273 100644
--- a/mesa/flat/__init__.py
+++ b/mesa/flat/__init__.py
@@ -3,3 +3,4 @@ from mesa.time import *  # noqa
 from mesa.space import *  # noqa
 from mesa.datacollection import *  # noqa
 from .visualization import *  # noqa
+from mesa.batchrunner import batch_run  # noqa
diff --git a/mesa/model.py b/mesa/model.py
index fddf68ea..6180a455 100644
--- a/mesa/model.py
+++ b/mesa/model.py
@@ -6,6 +6,8 @@ Core Objects: Model
 """
 import random
 
+from mesa.datacollection import DataCollector
+
 # mypy
 from typing import Any, Optional
 
@@ -61,3 +63,22 @@ class Model:
             seed = self._seed
         self.random.seed(seed)
         self._seed = seed
+
+    def initialize_data_collector(
+        self, model_reporters=None, agent_reporters=None, tables=None
+    ) -> None:
+        if not hasattr(self, "schedule") or self.schedule is None:
+            raise RuntimeError(
+                "You must initialize the scheduler (self.schedule) before initializing the data collector."
+            )
+        if self.schedule.get_agent_count() == 0:
+            raise RuntimeError(
+                "You must add agents to the scheduler before initializing the data collector."
+            )
+        self.datacollector = DataCollector(
+            model_reporters=model_reporters,
+            agent_reporters=agent_reporters,
+            tables=tables,
+        )
+        # Collect data for the first time during initialization.
+        self.datacollector.collect(self)


Test patch:
diff --git a/tests/test_datacollector.py b/tests/test_datacollector.py
index 0221b7da..0f2bd4c0 100644
--- a/tests/test_datacollector.py
+++ b/tests/test_datacollector.py
@@ -5,7 +5,6 @@ import unittest
 
 from mesa import Model, Agent
 from mesa.time import BaseScheduler
-from mesa.datacollection import DataCollector
 
 
 class MockAgent(Agent):
@@ -47,7 +46,7 @@ class MockModel(Model):
         for i in range(10):
             a = MockAgent(i, self, val=i)
             self.schedule.add(a)
-        self.datacollector = DataCollector(
+        self.initialize_data_collector(
             {
                 "total_agents": lambda m: m.schedule.get_agent_count(),
                 "model_value": "model_val",
@@ -103,10 +102,11 @@ class TestDataCollector(unittest.TestCase):
         assert "model_calc" in data_collector.model_vars
         assert "model_calc_comp" in data_collector.model_vars
         assert "model_calc_fail" in data_collector.model_vars
-        assert len(data_collector.model_vars["total_agents"]) == 7
-        assert len(data_collector.model_vars["model_value"]) == 7
-        assert len(data_collector.model_vars["model_calc"]) == 7
-        assert len(data_collector.model_vars["model_calc_comp"]) == 7
+        length = 8
+        assert len(data_collector.model_vars["total_agents"]) == length
+        assert len(data_collector.model_vars["model_value"]) == length
+        assert len(data_collector.model_vars["model_calc"]) == length
+        assert len(data_collector.model_vars["model_calc_comp"]) == length
         self.step_assertion(data_collector.model_vars["total_agents"])
         for element in data_collector.model_vars["model_value"]:
             assert element == 100
@@ -123,7 +123,7 @@ class TestDataCollector(unittest.TestCase):
         data_collector = self.model.datacollector
         agent_table = data_collector.get_agent_vars_dataframe()
 
-        assert len(data_collector._agent_records) == 7
+        assert len(data_collector._agent_records) == 8
         for step, records in data_collector._agent_records.items():
             if step < 5:
                 assert len(records) == 10
@@ -165,13 +165,35 @@ class TestDataCollector(unittest.TestCase):
         model_vars = data_collector.get_model_vars_dataframe()
         agent_vars = data_collector.get_agent_vars_dataframe()
         table_df = data_collector.get_table_dataframe("Final_Values")
-        assert model_vars.shape == (7, 5)
-        assert agent_vars.shape == (67, 2)
+        assert model_vars.shape == (8, 5)
+        assert agent_vars.shape == (77, 2)
         assert table_df.shape == (9, 2)
 
         with self.assertRaises(Exception):
             table_df = data_collector.get_table_dataframe("not a real table")
 
 
+class TestDataCollectorInitialization(unittest.TestCase):
+    def setUp(self):
+        self.model = Model()
+
+    def test_initialize_before_scheduler(self):
+        with self.assertRaises(RuntimeError) as cm:
+            self.model.initialize_data_collector()
+        self.assertEqual(
+            str(cm.exception),
+            "You must initialize the scheduler (self.schedule) before initializing the data collector.",
+        )
+
+    def test_initialize_before_agents_added_to_scheduler(self):
+        with self.assertRaises(RuntimeError) as cm:
+            self.model.schedule = BaseScheduler(self)
+            self.model.initialize_data_collector()
+        self.assertEqual(
+            str(cm.exception),
+            "You must add agents to the scheduler before initializing the data collector.",
+        )
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/tests/test_import_namespace.py b/tests/test_import_namespace.py
index 01029185..e405dc8f 100644
--- a/tests/test_import_namespace.py
+++ b/tests/test_import_namespace.py
@@ -27,3 +27,8 @@ def test_import():
     DataCollector
     mesa.DataCollector
     mf.DataCollector
+
+    from mesa.batchrunner import batch_run
+
+    batch_run
+    mesa.batch_run