Mesa 2.2.3__tar.gz → 2.2.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of Mesa might be problematic. Click here for more details.
- {mesa-2.2.3 → mesa-2.2.4}/HISTORY.md +16 -0
- {mesa-2.2.3 → mesa-2.2.4}/PKG-INFO +1 -1
- {mesa-2.2.3 → mesa-2.2.4}/mesa/__init__.py +1 -1
- {mesa-2.2.3 → mesa-2.2.4}/mesa/agent.py +1 -0
- {mesa-2.2.3 → mesa-2.2.4}/mesa/datacollection.py +9 -8
- {mesa-2.2.3 → mesa-2.2.4}/mesa/experimental/jupyter_viz.py +1 -1
- {mesa-2.2.3 → mesa-2.2.4}/mesa/model.py +11 -1
- {mesa-2.2.3 → mesa-2.2.4}/mesa/time.py +10 -3
- {mesa-2.2.3 → mesa-2.2.4}/tests/test_agent.py +13 -0
- {mesa-2.2.3 → mesa-2.2.4}/tests/test_time.py +22 -0
- {mesa-2.2.3 → mesa-2.2.4}/.codespellignore +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/.coveragerc +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/.github/ISSUE_TEMPLATE/asking-help.md +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/.github/ISSUE_TEMPLATE/bug-report.md +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/.github/ISSUE_TEMPLATE/feature-request.md +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/.github/dependabot.yml +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/.github/release.yml +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/.github/workflows/build_lint.yml +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/.github/workflows/codespell.yml +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/.github/workflows/release.yml +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/.gitignore +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/.pre-commit-config.yaml +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/.readthedocs.yml +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/CITATION.bib +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/CODE_OF_CONDUCT.md +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/CONTRIBUTING.rst +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/Dockerfile +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/LICENSE +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/README.md +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docker-compose.yml +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/Makefile +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/README.md +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/apis/api_main.rst +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/apis/batchrunner.rst +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/apis/datacollection.rst +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/apis/init.rst +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/apis/space.rst +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/apis/time.rst +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/apis/visualization.rst +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/best-practices.rst +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/conf.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/howto.rst +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/images/Mesa_Screenshot.png +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/images/mesa_logo.ico +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/images/mesa_logo.png +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/images/tutorial/br_ginis.png +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/images/tutorial/dc_endwealth.png +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/images/tutorial/dc_gini.png +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/images/tutorial/dc_oneagent.png +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/images/tutorial/first_hist.png +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/images/tutorial/multirun_hist.png +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/images/tutorial/numpy_grid.png +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/images/tutorial/viz_chart.png +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/images/tutorial/viz_empty.png +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/images/tutorial/viz_greycircles.png +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/images/tutorial/viz_histogram.png +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/images/tutorial/viz_redcircles.png +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/index.rst +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/make.bat +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/mesa.rst +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/mesa.visualization.modules.rst +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/mesa.visualization.rst +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/modular-visualization.rst +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/overview.rst +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/packages.rst +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/tutorials/MoneyModel.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/tutorials/adv_tutorial_legacy.ipynb +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/tutorials/files/viz_chart.png +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/tutorials/files/viz_empty.png +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/tutorials/files/viz_greycircles.png +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/tutorials/files/viz_histogram.png +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/tutorials/files/viz_redcircles.png +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/tutorials/files/viz_slider.png +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/tutorials/intro_tutorial.ipynb +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/docs/tutorials/visualization_tutorial.ipynb +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/mesa/batchrunner.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/mesa/cookiecutter-mesa/cookiecutter.json +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/mesa/cookiecutter-mesa/hooks/post_gen_project.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/README.md +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/run.pytemplate +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/setup.pytemplate +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/__init__.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/model.pytemplate +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/server.pytemplate +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/mesa/experimental/__init__.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/mesa/experimental/components/matplotlib.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/mesa/flat/__init__.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/mesa/flat/visualization.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/mesa/main.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/mesa/space.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/mesa/visualization/ModularVisualization.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/mesa/visualization/TextVisualization.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/mesa/visualization/UserParam.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/mesa/visualization/__init__.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/mesa/visualization/modules.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/mypy.ini +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/pyproject.toml +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/tests/__init__.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/tests/read_requirements.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/tests/test_batch_run.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/tests/test_datacollector.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/tests/test_end_to_end_viz.sh +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/tests/test_examples.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/tests/test_grid.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/tests/test_import_namespace.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/tests/test_jupyter_viz.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/tests/test_lifespan.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/tests/test_main.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/tests/test_model.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/tests/test_scaffold.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/tests/test_space.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/tests/test_tornado.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/tests/test_usersettableparam.py +0 -0
- {mesa-2.2.3 → mesa-2.2.4}/tests/test_visualization.py +0 -0
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
title: Release History
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
+
# 2.2.4 (2024-01-26)
|
|
6
|
+
|
|
7
|
+
## Highlights
|
|
8
|
+
Mesa v2.2.4 is a small but important bugfix release for the 2.2 release series. It fixes an essential bug in where agents weren't shuffled in the `RandomActivation` scheduler (effectively making it sequential activation)([#2007](https://github.com/projectmesa/mesa/pull/2007)). It also fixes a small behaviour change in `RandomActivationByType.agents_by_type()` ([#1996](https://github.com/projectmesa/mesa/pull/1996)). Furthermore, this release adds an internal clock to the `Model`, which allows to use a Mesa model without a scheduler (using the `AgentSet` API)([#1942](https://github.com/projectmesa/mesa/pull/1942)).
|
|
9
|
+
|
|
10
|
+
Updating from previous 2.2 releases is highly recommended, especially when using the `RandomActivation` scheduler.
|
|
11
|
+
|
|
12
|
+
## What's Changed
|
|
13
|
+
### 🛠 Enhancements made
|
|
14
|
+
* refactor: Remove dependence on model.schedule, add clock to Model by @rht in https://github.com/projectmesa/mesa/pull/1942
|
|
15
|
+
### 🐛 Bugs fixed
|
|
16
|
+
* Fix AgentSet inplace shuffle (and thus RandomActivation), add tests by @EwoutH in https://github.com/projectmesa/mesa/pull/2007
|
|
17
|
+
* fix: Reverse dict key and value for agents_by_type by @rht in https://github.com/projectmesa/mesa/pull/1996
|
|
18
|
+
|
|
19
|
+
**Full Changelog**: https://github.com/projectmesa/mesa/compare/v2.2.3...v2.2.4
|
|
20
|
+
|
|
5
21
|
# 2.2.3 (2024-01-22)
|
|
6
22
|
|
|
7
23
|
## Highlights
|
|
@@ -25,7 +25,7 @@ __all__ = [
|
|
|
25
25
|
]
|
|
26
26
|
|
|
27
27
|
__title__ = "mesa"
|
|
28
|
-
__version__ = "2.2.
|
|
28
|
+
__version__ = "2.2.4"
|
|
29
29
|
__license__ = "Apache 2.0"
|
|
30
30
|
_this_year = datetime.datetime.now(tz=datetime.timezone.utc).date().year
|
|
31
31
|
__copyright__ = f"Copyright {_this_year} Project Mesa Team"
|
|
@@ -14,8 +14,7 @@ name.
|
|
|
14
14
|
|
|
15
15
|
When the collect() method is called, each model-level function is called, with
|
|
16
16
|
the model as the argument, and the results associated with the relevant
|
|
17
|
-
variable. Then the agent-level functions are called on each agent
|
|
18
|
-
scheduler.
|
|
17
|
+
variable. Then the agent-level functions are called on each agent.
|
|
19
18
|
|
|
20
19
|
Additionally, other objects can write directly to tables by passing in an
|
|
21
20
|
appropriate dictionary object for a table row.
|
|
@@ -30,8 +29,7 @@ The DataCollector then stores the data it collects in dictionaries:
|
|
|
30
29
|
Finally, DataCollector can create a pandas DataFrame from each collection.
|
|
31
30
|
|
|
32
31
|
The default DataCollector here makes several assumptions:
|
|
33
|
-
* The model has
|
|
34
|
-
* The schedule has an agent list called agents
|
|
32
|
+
* The model has an agent list called agents
|
|
35
33
|
* For collecting agent-level variables, agents must have a unique_id
|
|
36
34
|
"""
|
|
37
35
|
import contextlib
|
|
@@ -67,7 +65,7 @@ class DataCollector:
|
|
|
67
65
|
|
|
68
66
|
Model reporters can take four types of arguments:
|
|
69
67
|
1. Lambda function:
|
|
70
|
-
{"agent_count": lambda m: m.
|
|
68
|
+
{"agent_count": lambda m: len(m.agents)}
|
|
71
69
|
2. Method of a class/instance:
|
|
72
70
|
{"agent_count": self.get_agent_count} # self here is a class instance
|
|
73
71
|
{"agent_count": Model.get_agent_count} # Model here is a class
|
|
@@ -180,11 +178,14 @@ class DataCollector:
|
|
|
180
178
|
rep_funcs = self.agent_reporters.values()
|
|
181
179
|
|
|
182
180
|
def get_reports(agent):
|
|
183
|
-
_prefix = (agent.model.
|
|
181
|
+
_prefix = (agent.model._steps, agent.unique_id)
|
|
184
182
|
reports = tuple(rep(agent) for rep in rep_funcs)
|
|
185
183
|
return _prefix + reports
|
|
186
184
|
|
|
187
|
-
agent_records = map(
|
|
185
|
+
agent_records = map(
|
|
186
|
+
get_reports,
|
|
187
|
+
model.schedule.agents if hasattr(model, "schedule") else model.agents,
|
|
188
|
+
)
|
|
188
189
|
return agent_records
|
|
189
190
|
|
|
190
191
|
def collect(self, model):
|
|
@@ -207,7 +208,7 @@ class DataCollector:
|
|
|
207
208
|
|
|
208
209
|
if self.agent_reporters:
|
|
209
210
|
agent_records = self._record_agents(model)
|
|
210
|
-
self._agent_records[model.
|
|
211
|
+
self._agent_records[model._steps] = list(agent_records)
|
|
211
212
|
|
|
212
213
|
def add_table_row(self, table_name, row, ignore_missing=False):
|
|
213
214
|
"""Add a row dictionary to a specific table.
|
|
@@ -178,7 +178,7 @@ def ModelController(model, play_interval, current_step, reset_counter):
|
|
|
178
178
|
def do_step():
|
|
179
179
|
model.step()
|
|
180
180
|
previous_step.value = current_step.value
|
|
181
|
-
current_step.value
|
|
181
|
+
current_step.value = model._steps
|
|
182
182
|
|
|
183
183
|
def do_play():
|
|
184
184
|
model.running = True
|
|
@@ -13,11 +13,13 @@ import warnings
|
|
|
13
13
|
from collections import defaultdict
|
|
14
14
|
|
|
15
15
|
# mypy
|
|
16
|
-
from typing import Any
|
|
16
|
+
from typing import Any, Union
|
|
17
17
|
|
|
18
18
|
from mesa.agent import Agent, AgentSet
|
|
19
19
|
from mesa.datacollection import DataCollector
|
|
20
20
|
|
|
21
|
+
TimeT = Union[float, int]
|
|
22
|
+
|
|
21
23
|
|
|
22
24
|
class Model:
|
|
23
25
|
"""Base class for models in the Mesa ABM library.
|
|
@@ -68,6 +70,9 @@ class Model:
|
|
|
68
70
|
self.current_id = 0
|
|
69
71
|
self.agents_: defaultdict[type, dict] = defaultdict(dict)
|
|
70
72
|
|
|
73
|
+
self._steps: int = 0
|
|
74
|
+
self._time: TimeT = 0 # the model's clock
|
|
75
|
+
|
|
71
76
|
# Warning flags for current experimental features. These make sure a warning is only printed once per model.
|
|
72
77
|
self.agentset_experimental_warning_given = False
|
|
73
78
|
|
|
@@ -112,6 +117,11 @@ class Model:
|
|
|
112
117
|
def step(self) -> None:
|
|
113
118
|
"""A single step. Fill in here."""
|
|
114
119
|
|
|
120
|
+
def _advance_time(self, deltat: TimeT = 1):
|
|
121
|
+
"""Increment the model's steps counter and clock."""
|
|
122
|
+
self._steps += 1
|
|
123
|
+
self._time += deltat
|
|
124
|
+
|
|
115
125
|
def next_id(self) -> int:
|
|
116
126
|
"""Return the next unique ID for agents, increment current_id"""
|
|
117
127
|
self.current_id += 1
|
|
@@ -73,6 +73,8 @@ class BaseScheduler:
|
|
|
73
73
|
self.model = model
|
|
74
74
|
self.steps = 0
|
|
75
75
|
self.time: TimeT = 0
|
|
76
|
+
self._original_step = self.step
|
|
77
|
+
self.step = self._wrapped_step
|
|
76
78
|
|
|
77
79
|
if agents is None:
|
|
78
80
|
agents = []
|
|
@@ -115,6 +117,11 @@ class BaseScheduler:
|
|
|
115
117
|
self.steps += 1
|
|
116
118
|
self.time += 1
|
|
117
119
|
|
|
120
|
+
def _wrapped_step(self):
|
|
121
|
+
"""Wrapper for the step method to include time and step updating."""
|
|
122
|
+
self._original_step()
|
|
123
|
+
self.model._advance_time()
|
|
124
|
+
|
|
118
125
|
def get_agent_count(self) -> int:
|
|
119
126
|
"""Returns the current number of agents in the queue."""
|
|
120
127
|
return len(self._agents)
|
|
@@ -143,8 +150,8 @@ class BaseScheduler:
|
|
|
143
150
|
|
|
144
151
|
def do_each(self, method, shuffle=False):
|
|
145
152
|
if shuffle:
|
|
146
|
-
self.
|
|
147
|
-
self.
|
|
153
|
+
self._agents.shuffle(inplace=True)
|
|
154
|
+
self._agents.do(method)
|
|
148
155
|
|
|
149
156
|
|
|
150
157
|
class RandomActivation(BaseScheduler):
|
|
@@ -299,7 +306,7 @@ class RandomActivationByType(BaseScheduler):
|
|
|
299
306
|
|
|
300
307
|
agentsbytype = defaultdict(dict)
|
|
301
308
|
for k, v in self._agents_by_type.items():
|
|
302
|
-
agentsbytype[k] = {agent: agent
|
|
309
|
+
agentsbytype[k] = {agent.unique_id: agent for agent in v}
|
|
303
310
|
|
|
304
311
|
return agentsbytype
|
|
305
312
|
|
|
@@ -251,3 +251,16 @@ def test_agentset_select_by_type():
|
|
|
251
251
|
# Test with no type specified (should select all agents)
|
|
252
252
|
all_agents = agentset.select()
|
|
253
253
|
assert len(all_agents) == len(mixed_agents)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def test_agentset_shuffle():
|
|
257
|
+
model = Model()
|
|
258
|
+
test_agents = [TestAgent(model.next_id(), model) for _ in range(12)]
|
|
259
|
+
|
|
260
|
+
agentset = AgentSet(test_agents, model=model)
|
|
261
|
+
agentset = agentset.shuffle()
|
|
262
|
+
assert not all(a1 == a2 for a1, a2 in zip(test_agents, agentset))
|
|
263
|
+
|
|
264
|
+
agentset = AgentSet(test_agents, model=model)
|
|
265
|
+
agentset.shuffle(inplace=True)
|
|
266
|
+
assert not all(a1 == a2 for a1, a2 in zip(test_agents, agentset))
|
|
@@ -224,6 +224,28 @@ class TestRandomActivation(TestCase):
|
|
|
224
224
|
agent_ids = {agent.unique_id for agent in model.agents}
|
|
225
225
|
assert all(entry in agent_ids for entry in keys)
|
|
226
226
|
|
|
227
|
+
def test_not_sequential(self):
|
|
228
|
+
model = MockModel(activation=RANDOM)
|
|
229
|
+
# Create 10 agents
|
|
230
|
+
for _ in range(10):
|
|
231
|
+
model.schedule.add(MockAgent(model.next_id(), model))
|
|
232
|
+
# Run 3 steps
|
|
233
|
+
for _ in range(3):
|
|
234
|
+
model.step()
|
|
235
|
+
# Filter out non-integer elements from the log
|
|
236
|
+
filtered_log = [item for item in model.log if isinstance(item, int)]
|
|
237
|
+
|
|
238
|
+
# Check that there are no 18 consecutive agents id's in the filtered log
|
|
239
|
+
total_agents = 10
|
|
240
|
+
assert not any(
|
|
241
|
+
all(
|
|
242
|
+
(filtered_log[(i + j) % total_agents] - filtered_log[i]) % total_agents
|
|
243
|
+
== j % total_agents
|
|
244
|
+
for j in range(18)
|
|
245
|
+
)
|
|
246
|
+
for i in range(len(filtered_log))
|
|
247
|
+
), f"Agents are activated sequentially:\n{filtered_log}"
|
|
248
|
+
|
|
227
249
|
|
|
228
250
|
class TestSimultaneousActivation(TestCase):
|
|
229
251
|
"""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|