Repository: projectmesa/mesa
Pull request #1105 (2021-12-14 12:49:30)

Pyupgrade 3.6: Update syntax with Python 3.6+ features
Updated Python code with Python 3.6+ features:
- utf-8 encoding is now the default ([PEP 3120](https://www.python.org/dev/peps/pep-3120/), Python 3.0+)
- Replace list comprehensions by Generator Expressions ([PEP 289](https://www.python.org/dev/peps/pep-0289/), Python 2.4+)
- Replace yield loop by yield from ([PEP 380](https://www.python.org/dev/peps/pep-0380/), Python 3.3+)

Used [pyupgrade](https://github.com/asottile/pyupgrade) 2.29.1 with `--py36-plus`.

codecov[bot]: # [Codecov](https://codecov.io/gh/projectmesa/mesa/pull/1105?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa) Report
> Merging [#1105](https://codecov.io/gh/projectmesa/mesa/pull/1105?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa) (8a0d87e) into [main](https://codecov.io/gh/projectmesa/mesa/commit/d2243971e6101e812422c0853eb7ae1a1671ffbc?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa) (d224397) will **decrease** coverage by `0.03%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/projectmesa/mesa/pull/1105/graphs/tree.svg?width=650&height=150&src=pr&token=OUHxz83iDi&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa)](https://codecov.io/gh/projectmesa/mesa/pull/1105?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa)

```diff
@@            Coverage Diff             @@
##             main    #1105      +/-   ##
==========================================
- Coverage   88.92%   88.88%   -0.04%     
==========================================
  Files          19       19              
  Lines        1183     1179       -4     
  Branches      239      235       -4     
==========================================
- Hits         1052     1048       -4     
  Misses         97       97              
  Partials       34       34              
```


| [Impacted Files](https://codecov.io/gh/projectmesa/mesa/pull/1105?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/1105/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% <ø> (ø)` | |
| [mesa/time.py](https://codecov.io/gh/projectmesa/mesa/pull/1105/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa#diff-bWVzYS90aW1lLnB5) | `95.52% <ø> (ø)` | |
| [mesa/visualization/modules/\_\_init\_\_.py](https://codecov.io/gh/projectmesa/mesa/pull/1105/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa#diff-bWVzYS92aXN1YWxpemF0aW9uL21vZHVsZXMvX19pbml0X18ucHk=) | `100.00% <ø> (ø)` | |
| [mesa/space.py](https://codecov.io/gh/projectmesa/mesa/pull/1105/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa#diff-bWVzYS9zcGFjZS5weQ==) | `95.89% <100.00%> (-0.03%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/projectmesa/mesa/pull/1105?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/1105?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa). Last update [d224397...8a0d87e](https://codecov.io/gh/projectmesa/mesa/pull/1105?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).

rht: LGTM

Patch:
diff --git a/.github/workflows/update-pipfile.yml b/.github/workflows/update-pipfile.yml
index 0545b61d..050d81ca 100644
--- a/.github/workflows/update-pipfile.yml
+++ b/.github/workflows/update-pipfile.yml
@@ -14,7 +14,7 @@ jobs:
     - uses: actions/checkout@v2
     - uses: actions/setup-python@v2
       with:
-        python-version: 3.7
+        python-version: '3.9'
     - run: pip install -U pip
     - run: pip install -U wheel
     - run: pip install -U pipenv
diff --git a/Pipfile b/Pipfile
index 4a6aded9..da535315 100644
--- a/Pipfile
+++ b/Pipfile
@@ -11,4 +11,4 @@ pytest-cov = "*"
 mesa = "*"
 
 [requires]
-python_version = "3.7"
+python_version = "3.9"
diff --git a/Pipfile.lock b/Pipfile.lock
index 50b49d84..e9050878 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,11 +1,11 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "9cc59726bf39a0888cf25205723f38f7d2a17ec55aceb1942b30af5fbea8151e"
+            "sha256": "3d8caf3ab53cebc0f6b41160cf246e4fbb7332bca299c7297c66f4c72b72d979"
         },
         "pipfile-spec": 6,
         "requires": {
-            "python_version": "3.7"
+            "python_version": "3.9"
         },
         "sources": [
             {
@@ -78,14 +78,6 @@
             "markers": "python_version >= '3'",
             "version": "==3.3"
         },
-        "importlib-metadata": {
-            "hashes": [
-                "sha256:53ccfd5c134223e497627b9815d5030edf77d2ed573922f7a0b8f8bb81a1c100",
-                "sha256:75bdec14c397f528724c1bfd9709d660b33a4d2e77387a3358f20b848bb5e5fb"
-            ],
-            "markers": "python_version < '3.8'",
-            "version": "==4.8.2"
-        },
         "jinja2": {
             "hashes": [
                 "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8",
@@ -367,14 +359,6 @@
             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
             "version": "==4.62.3"
         },
-        "typing-extensions": {
-            "hashes": [
-                "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e",
-                "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"
-            ],
-            "markers": "python_version < '3.8'",
-            "version": "==4.0.1"
-        },
         "urllib3": {
             "hashes": [
                 "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece",
@@ -382,14 +366,6 @@
             ],
             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
             "version": "==1.26.7"
-        },
-        "zipp": {
-            "hashes": [
-                "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832",
-                "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"
-            ],
-            "markers": "python_version >= '3.6'",
-            "version": "==3.6.0"
         }
     },
     "develop": {
@@ -457,14 +433,6 @@
             "markers": "python_version >= '3.6'",
             "version": "==6.2"
         },
-        "importlib-metadata": {
-            "hashes": [
-                "sha256:53ccfd5c134223e497627b9815d5030edf77d2ed573922f7a0b8f8bb81a1c100",
-                "sha256:75bdec14c397f528724c1bfd9709d660b33a4d2e77387a3358f20b848bb5e5fb"
-            ],
-            "markers": "python_version < '3.8'",
-            "version": "==4.8.2"
-        },
         "iniconfig": {
             "hashes": [
                 "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3",
@@ -534,22 +502,6 @@
                 "sha256:f04066f68f5554911363063a30b108d2b5a5b1a010aa8b6132af78489fe3aade"
             ],
             "version": "==1.2.2"
-        },
-        "typing-extensions": {
-            "hashes": [
-                "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e",
-                "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"
-            ],
-            "markers": "python_version < '3.8'",
-            "version": "==4.0.1"
-        },
-        "zipp": {
-            "hashes": [
-                "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832",
-                "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"
-            ],
-            "markers": "python_version >= '3.6'",
-            "version": "==3.6.0"
         }
     }
 }
diff --git a/README.rst b/README.rst
index e82eb84c..dd2c581a 100644
--- a/README.rst
+++ b/README.rst
@@ -49,6 +49,12 @@ You can also use `pip` to install the github version:
 .. code-block:: bash
 
     $ pip install -e git+https://github.com/projectmesa/mesa#egg=mesa
+    
+Or any other (development) branch on this repo or your own fork:
+
+.. code-block:: bash
+
+    $ pip install -e git+https://github.com/YOUR_FORK/mesa@YOUR_BRANCH
 
 Take a look at the `examples <https://github.com/projectmesa/mesa/tree/main/examples>`_ folder for sample models demonstrating Mesa features.
 
diff --git a/docs/conf.py b/docs/conf.py
index 6357dce5..d682ce1a 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -1,5 +1,4 @@
 #!/usr/bin/env python3
-# -*- coding: utf-8 -*-
 #
 # Mesa documentation build configuration file, created by
 # sphinx-quickstart on Sun Jan  4 23:34:09 2015.
diff --git a/examples/bank_reserves/batch_run.py b/examples/bank_reserves/batch_run.py
index d6cbaea8..e028f414 100644
--- a/examples/bank_reserves/batch_run.py
+++ b/examples/bank_reserves/batch_run.py
@@ -186,15 +186,6 @@ br_params = {
     "reserve_percent": 5,
 }
 
-"""br = BatchRunner(
-    BankReservesModel,
-    br_params,
-    iterations=2,
-    max_steps=1000,
-    nr_processes=None,
-    # model_reporters={"Data Collector": lambda m: m.datacollector},
-)"""
-
 if __name__ == "__main__":
     data = batch_run(
         BankReservesModel,
@@ -204,8 +195,20 @@ if __name__ == "__main__":
     )
     br_df = pd.DataFrame(data)
     br_df.to_csv("BankReservesModel_Data.csv")
-    # br.run_all()
+
+    # The commented out code below is the equivalent code as above, but done
+    # via the legacy BatchRunner class. This is a good example to look at if
+    # you want to migrate your code to use `batch_run()` from `BatchRunner`.
     """
+    br = BatchRunner(
+        BankReservesModel,
+        br_params,
+        iterations=2,
+        max_steps=1000,
+        nr_processes=None,
+        # model_reporters={"Data Collector": lambda m: m.datacollector},
+    )
+    br.run_all()
     br_df = br.get_model_vars_dataframe()
     br_step_data = pd.DataFrame()
     for i in range(len(br_df["Data Collector"])):
diff --git a/examples/sugarscape_cg/sugarscape_cg/agents.py b/examples/sugarscape_cg/sugarscape_cg/agents.py
index c9e82f29..3374cc53 100644
--- a/examples/sugarscape_cg/sugarscape_cg/agents.py
+++ b/examples/sugarscape_cg/sugarscape_cg/agents.py
@@ -47,12 +47,12 @@ class SsAgent(Agent):
         ]
         neighbors.append(self.pos)
         # Look for location with the most sugar
-        max_sugar = max([self.get_sugar(pos).amount for pos in neighbors])
+        max_sugar = max(self.get_sugar(pos).amount for pos in neighbors)
         candidates = [
             pos for pos in neighbors if self.get_sugar(pos).amount == max_sugar
         ]
         # Narrow down to the nearest ones
-        min_dist = min([get_distance(self.pos, pos) for pos in candidates])
+        min_dist = min(get_distance(self.pos, pos) for pos in candidates)
         final_candidates = [
             pos for pos in candidates if get_distance(self.pos, pos) == min_dist
         ]
diff --git a/examples/virus_on_network/virus_on_network/model.py b/examples/virus_on_network/virus_on_network/model.py
index c249e2b5..abeb8ca9 100644
--- a/examples/virus_on_network/virus_on_network/model.py
+++ b/examples/virus_on_network/virus_on_network/model.py
@@ -15,7 +15,7 @@ class State(Enum):
 
 
 def number_state(model, state):
-    return sum([1 for a in model.grid.get_all_cell_contents() if a.state is state])
+    return sum(1 for a in model.grid.get_all_cell_contents() if a.state is state)
 
 
 def number_infected(model):
diff --git a/mesa/__init__.py b/mesa/__init__.py
index 2364709b..5f04fce6 100644
--- a/mesa/__init__.py
+++ b/mesa/__init__.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """
 Mesa Agent-Based Modeling Framework
 
diff --git a/mesa/batchrunner.py b/mesa/batchrunner.py
index e81e1321..c6293ea5 100644
--- a/mesa/batchrunner.py
+++ b/mesa/batchrunner.py
@@ -172,10 +172,15 @@ def _model_run_func(
     for step in steps:
         model_data, all_agents_data = _collect_data(model, step)
 
-        stepdata = [
-            {**{"Step": step}, **kwargs, **model_data, **agent_data}
-            for agent_data in all_agents_data
-        ]
+        # If there are agent_reporters, then create an entry for each agent
+        if all_agents_data:
+            stepdata = [
+                {**{"Step": step}, **kwargs, **model_data, **agent_data}
+                for agent_data in all_agents_data
+            ]
+        # If there is only model data, then create a single entry for the step
+        else:
+            stepdata = [{**{"Step": step}, **kwargs, **model_data}]
         data.extend(stepdata)
 
     return tuple(kwargs.values()), data
diff --git a/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/setup.py b/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/setup.py
index 770fbbc6..64023f8c 100644
--- a/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/setup.py
+++ b/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/setup.py
@@ -1,5 +1,4 @@
 #!/usr/bin/env python
-# -*- coding: utf-8 -*-
 from setuptools import setup, find_packages
 
 requires = ["mesa"]
diff --git a/mesa/space.py b/mesa/space.py
index 90d3fc05..873fa4cc 100644
--- a/mesa/space.py
+++ b/mesa/space.py
@@ -623,8 +623,7 @@ class HexGrid(Grid):
         if not include_center and pos in coordinates:
             coordinates.remove(pos)
 
-        for i in coordinates:
-            yield i
+        yield from coordinates
 
     def neighbor_iter(self, pos: Coordinate) -> Iterator[GridContent]:
         """Iterate over position neighbors.
diff --git a/mesa/time.py b/mesa/time.py
index dde633f0..0caaea82 100644
--- a/mesa/time.py
+++ b/mesa/time.py
@@ -62,9 +62,7 @@ class BaseScheduler:
 
         if agent.unique_id in self._agents:
             raise Exception(
-                "Agent with unique id {0} already added to scheduler".format(
-                    repr(agent.unique_id)
-                )
+                f"Agent with unique id {repr(agent.unique_id)} already added to scheduler"
             )
 
         self._agents[agent.unique_id] = agent
diff --git a/mesa/visualization/modules/__init__.py b/mesa/visualization/modules/__init__.py
index df013b69..250b9705 100644
--- a/mesa/visualization/modules/__init__.py
+++ b/mesa/visualization/modules/__init__.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """
 Container for all built-in visualization modules.
 """
diff --git a/setup.py b/setup.py
index 61bd7a6b..fb50a7d1 100644
--- a/setup.py
+++ b/setup.py
@@ -1,5 +1,4 @@
 #!/usr/bin/env python
-# -*- coding: utf-8 -*-
 import re
 
 from setuptools import setup, find_packages
@@ -13,7 +12,7 @@ extras_require = {
 }
 
 version = ""
-with open("mesa/__init__.py", "r") as fd:
+with open("mesa/__init__.py") as fd:
     version = re.search(
         r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', fd.read(), re.MULTILINE
     ).group(1)


Test patch:
diff --git a/tests/test_batch_run.py b/tests/test_batch_run.py
index d963d3ba..7d24e88f 100644
--- a/tests/test_batch_run.py
+++ b/tests/test_batch_run.py
@@ -47,6 +47,7 @@ class MockModel(Model):
         variable_agent_param=None,
         fixed_model_param=None,
         schedule=None,
+        enable_agent_reporters=True,
         **kwargs
     ):
         super().__init__()
@@ -55,9 +56,13 @@ class MockModel(Model):
         self.variable_agent_param = variable_agent_param
         self.fixed_model_param = fixed_model_param
         self.n_agents = 3
+        if enable_agent_reporters:
+            agent_reporters = {"agent_id": "unique_id", "agent_local": "local"}
+        else:
+            agent_reporters = None
         self.datacollector = DataCollector(
             model_reporters={"reported_model_param": self.get_local_model_param},
-            agent_reporters={"agent_id": "unique_id", "agent_local": "local"},
+            agent_reporters=agent_reporters,
         )
         self.running = True
         self.init_agents()
@@ -121,5 +126,19 @@ def test_batch_run_with_params():
     )
 
 
+def test_batch_run_no_agent_reporters():
+    result = batch_run(MockModel, {"enable_agent_reporters": False})
+    print(result)
+    assert result == [
+        {
+            "RunId": 0,
+            "iteration": 0,
+            "Step": 1000,
+            "enable_agent_reporters": False,
+            "reported_model_param": 42,
+        }
+    ]
+
+
 def test_batch_run_single_core():
     batch_run(MockModel, {}, nr_processes=1, iterations=10)
diff --git a/tests/test_datacollector.py b/tests/test_datacollector.py
index d2c9d2b8..0221b7da 100644
--- a/tests/test_datacollector.py
+++ b/tests/test_datacollector.py
@@ -110,7 +110,7 @@ class TestDataCollector(unittest.TestCase):
         self.step_assertion(data_collector.model_vars["total_agents"])
         for element in data_collector.model_vars["model_value"]:
             assert element == 100
-        self.step_assertion((data_collector.model_vars["model_calc"]))
+        self.step_assertion(data_collector.model_vars["model_calc"])
         for element in data_collector.model_vars["model_calc_comp"]:
             assert element == 75
         for element in data_collector.model_vars["model_calc_fail"]:
diff --git a/tests/test_examples.py b/tests/test_examples.py
index b1ee0516..7ca71b72 100644
--- a/tests/test_examples.py
+++ b/tests/test_examples.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 import sys
 import os.path
 import unittest