From c09e480d3742d850ed7487eee8cf3c321a2ab381 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20Fernando=20S=C3=A1nchez?= Date: Thu, 20 Oct 2022 14:12:10 +0200 Subject: [PATCH] black formatting --- examples/events_and_messages/cars.py | 2 +- examples/terrorism/TerroristNetworkModel.py | 4 +- soil/__init__.py | 26 ++++++----- soil/agents/__init__.py | 6 +-- soil/agents/evented.py | 11 +++-- soil/agents/fsm.py | 2 - soil/agents/network_agents.py | 1 + soil/environment.py | 18 +++++--- soil/events.py | 3 ++ soil/time.py | 12 ++--- tests/test_agents.py | 49 +++++++++++---------- tests/test_examples.py | 2 +- tests/test_main.py | 7 ++- tests/test_time.py | 19 ++++---- 14 files changed, 90 insertions(+), 72 deletions(-) diff --git a/examples/events_and_messages/cars.py b/examples/events_and_messages/cars.py index c612f70..00e2632 100644 --- a/examples/events_and_messages/cars.py +++ b/examples/events_and_messages/cars.py @@ -128,7 +128,7 @@ class Driver(Evented, FSM): self.check_passengers() # This will call on_receive behind the scenes, and the agent's status will be updated - self.check_messages() + self.check_messages() yield Delta(30) # Wait at least 30 seconds before checking again try: diff --git a/examples/terrorism/TerroristNetworkModel.py b/examples/terrorism/TerroristNetworkModel.py index fe3034f..2da9d8f 100644 --- a/examples/terrorism/TerroristNetworkModel.py +++ b/examples/terrorism/TerroristNetworkModel.py @@ -258,9 +258,7 @@ class TerroristNetworkModel(TerroristSpreadModel): ) neighbours = set( agent.id - for agent in self.get_neighbors( - agent_class=TerroristNetworkModel - ) + for agent in self.get_neighbors(agent_class=TerroristNetworkModel) ) search = (close_ups | step_neighbours) - neighbours for agent in self.get_agents(search): diff --git a/soil/__init__.py b/soil/__init__.py index 6382d29..3894897 100644 --- a/soil/__init__.py +++ b/soil/__init__.py @@ -47,7 +47,7 @@ def main( "file", type=str, nargs="?", - default=cfg if sim is None else '', + default=cfg if sim is None else "", help="Configuration file for the simulation (e.g., YAML or JSON)", ) parser.add_argument( @@ -169,22 +169,26 @@ def main( sim.exporters = exporters sim.parallel = parallel sim.outdir = output - sims = [sim, ] + sims = [ + sim, + ] else: logger.info("Loading config file: {}".format(args.file)) if not os.path.exists(args.file): logger.error("Please, input a valid file") return - sims = list(simulation.iter_from_config( - args.file, - dry_run=args.dry_run, - exporters=exporters, - parallel=parallel, - outdir=output, - exporter_params=exp_params, - **kwargs, - )) + sims = list( + simulation.iter_from_config( + args.file, + dry_run=args.dry_run, + exporters=exporters, + parallel=parallel, + outdir=output, + exporter_params=exp_params, + **kwargs, + ) + ) for sim in sims: diff --git a/soil/agents/__init__.py b/soil/agents/__init__.py index a9c1fc3..9cf168a 100644 --- a/soil/agents/__init__.py +++ b/soil/agents/__init__.py @@ -197,7 +197,7 @@ class BaseAgent(MesaAgent, MutableMapping, metaclass=MetaAgent): def step(self): if not self.alive: raise time.DeadAgent(self.unique_id) - super().step() + super().step() return time.Delta(self.interval) def log(self, message, *args, level=logging.INFO, **kwargs): @@ -261,7 +261,6 @@ def prob(prob, random): return r < prob - def calculate_distribution(network_agents=None, agent_class=None): """ Calculate the threshold values (thresholds for a uniform distribution) @@ -632,7 +631,8 @@ from .CounterModel import * class Agent(NetworkAgent, EventedAgent): - '''Default agent class, has both network and event capabilities''' + """Default agent class, has both network and event capabilities""" + try: import scipy diff --git a/soil/agents/evented.py b/soil/agents/evented.py index 9b8e144..22e1191 100644 --- a/soil/agents/evented.py +++ b/soil/agents/evented.py @@ -6,7 +6,9 @@ from collections import deque class ReceivedOrTimeout(BaseCond): - def __init__(self, agent, expiration=None, timeout=None, check=True, ignore=False, **kwargs): + def __init__( + self, agent, expiration=None, timeout=None, check=True, ignore=False, **kwargs + ): if expiration is None: if timeout is not None: expiration = agent.now + timeout @@ -23,7 +25,7 @@ class ReceivedOrTimeout(BaseCond): def return_value(self, agent): if not self.ignore and self.expired(agent.now): - raise TimedOut('No messages received') + raise TimedOut("No messages received") if self.check: agent.check_messages() return None @@ -34,7 +36,7 @@ class ReceivedOrTimeout(BaseCond): return (time + delta, self) def __repr__(self): - return f'ReceivedOrTimeout(expires={self.expiration})' + return f"ReceivedOrTimeout(expires={self.expiration})" class EventedAgent(BaseAgent): @@ -55,7 +57,7 @@ class EventedAgent(BaseAgent): def ask(self, msg, timeout=None, **kwargs): ask = Ask(timestamp=self.now, payload=msg, sender=self) self._inbox.append(ask) - expiration = float('inf') if timeout is None else self.now + timeout + expiration = float("inf") if timeout is None else self.now + timeout return ask.replied(expiration=expiration, **kwargs) def check_messages(self): @@ -71,4 +73,5 @@ class EventedAgent(BaseAgent): msg.reply = reply return changed + Evented = EventedAgent diff --git a/soil/agents/fsm.py b/soil/agents/fsm.py index 4b64364..8e3f8d8 100644 --- a/soil/agents/fsm.py +++ b/soil/agents/fsm.py @@ -38,8 +38,6 @@ def state(name=None): self._last_return = None self._last_except = None - - func.id = name or func.__name__ func.is_default = False return func diff --git a/soil/agents/network_agents.py b/soil/agents/network_agents.py index cd57943..090a3a4 100644 --- a/soil/agents/network_agents.py +++ b/soil/agents/network_agents.py @@ -81,4 +81,5 @@ class NetworkAgent(BaseAgent): self.remove_node() return super().die() + NetAgent = NetworkAgent diff --git a/soil/environment.py b/soil/environment.py index ee114e0..aa61d43 100644 --- a/soil/environment.py +++ b/soil/environment.py @@ -313,19 +313,27 @@ class NetworkEnvironment(BaseEnvironment): class EventedEnvironment(BaseEnvironment): - def broadcast(self, msg, sender=None, expiration=None, ttl=None, **kwargs): + def broadcast(self, msg, sender=None, expiration=None, ttl=None, **kwargs): for agent in self.agents(**kwargs): if agent == sender: continue - self.logger.info(f'Telling {repr(agent)}: {msg} ttl={ttl}') + self.logger.info(f"Telling {repr(agent)}: {msg} ttl={ttl}") try: inbox = agent._inbox except AttributeError: - self.logger.info(f'Agent {agent.unique_id} cannot receive events because it does not have an inbox') + self.logger.info( + f"Agent {agent.unique_id} cannot receive events because it does not have an inbox" + ) continue # Allow for AttributeError exceptions in this part of the code - inbox.append(events.Tell(payload=msg, sender=sender, expiration=expiration if ttl is None else self.now+ttl)) + inbox.append( + events.Tell( + payload=msg, + sender=sender, + expiration=expiration if ttl is None else self.now + ttl, + ) + ) class Environment(NetworkEnvironment, EventedEnvironment): - '''Default environment class, has both network and event capabilities''' + """Default environment class, has both network and event capabilities""" diff --git a/soil/events.py b/soil/events.py index 25f471a..82beaff 100644 --- a/soil/events.py +++ b/soil/events.py @@ -3,9 +3,11 @@ from dataclasses import dataclass, field from typing import Any from uuid import uuid4 + class Event: pass + @dataclass class Message: payload: Any @@ -17,6 +19,7 @@ class Message: def expired(self, when): return self.expiration is not None and self.expiration < when + class Reply(Message): source: Message diff --git a/soil/time.py b/soil/time.py index 6c65cc4..6dc39af 100644 --- a/soil/time.py +++ b/soil/time.py @@ -105,15 +105,16 @@ class TimedActivation(BaseScheduler): when = self.time elif isinstance(when, When): when = when.abs() - + self._schedule(agent, None, when) super().add(agent) def _schedule(self, agent, condition=None, when=None): if condition: if not when: - when, condition = condition.schedule_next(when or self.time, - self.step_interval) + when, condition = condition.schedule_next( + when or self.time, self.step_interval + ) else: if when is None: when = self.time + self.step_interval @@ -125,7 +126,6 @@ class TimedActivation(BaseScheduler): self._next[agent.unique_id] = key heappush(self._queue, (key, agent)) - def step(self) -> None: """ Executes agents in order, one at a time. After each step, @@ -170,7 +170,9 @@ class TimedActivation(BaseScheduler): continue if returned: - next_check = returned.schedule_next(self.time, self.step_interval, first=True) + next_check = returned.schedule_next( + self.time, self.step_interval, first=True + ) self._schedule(agent, when=next_check[0], condition=next_check[1]) else: next_check = (self.time + self.step_interval, None) diff --git a/tests/test_agents.py b/tests/test_agents.py index 0cb7f37..76606cf 100644 --- a/tests/test_agents.py +++ b/tests/test_agents.py @@ -14,31 +14,32 @@ class Dead(agents.FSM): class TestAgents(TestCase): def test_die_returns_infinity(self): - '''The last step of a dead agent should return time.INFINITY''' + """The last step of a dead agent should return time.INFINITY""" d = Dead(unique_id=0, model=environment.Environment()) ret = d.step() assert ret == stime.NEVER def test_die_raises_exception(self): - '''A dead agent should raise an exception if it is stepped after death''' + """A dead agent should raise an exception if it is stepped after death""" d = Dead(unique_id=0, model=environment.Environment()) d.step() with pytest.raises(stime.DeadAgent): d.step() - def test_agent_generator(self): - ''' + """ The step function of an agent could be a generator. In that case, the state of the agent will be resumed after every call to step. - ''' + """ a = 0 + class Gen(agents.BaseAgent): def step(self): nonlocal a for i in range(5): yield a += 1 + e = environment.Environment() g = Gen(model=e, unique_id=e.next_id()) e.schedule.add(g) @@ -50,8 +51,9 @@ class TestAgents(TestCase): def test_state_decorator(self): class MyAgent(agents.FSM): run = 0 + @agents.default_state - @agents.state('original') + @agents.state("original") def root(self): self.run += 1 return self.other @@ -66,19 +68,19 @@ class TestAgents(TestCase): assert a.run == 1 a.step() - def test_broadcast(self): - ''' + """ An agent should be able to broadcast messages to every other agent, AND each receiver should be able to process it - ''' + """ + class BCast(agents.Evented): pings_received = 0 def step(self): print(self.model.broadcast) try: - self.model.broadcast('PING') + self.model.broadcast("PING") except Exception as ex: print(ex) while True: @@ -87,7 +89,7 @@ class TestAgents(TestCase): def on_receive(self, msg, sender=None): self.pings_received += 1 - + e = environment.EventedEnvironment() for i in range(10): @@ -96,12 +98,12 @@ class TestAgents(TestCase): pings_received = lambda: [a.pings_received for a in e.agents] assert sorted(pings_received()) == list(range(1, 11)) e.step() - assert all(x==10 for x in pings_received()) + assert all(x == 10 for x in pings_received()) def test_ask_messages(self): - ''' + """ An agent should be able to ask another agent, and wait for a response. - ''' + """ # There are two agents, they try to send pings # This is arguably a very contrived example. In practice, the or @@ -124,24 +126,24 @@ class TestAgents(TestCase): def step(self): target_id = (self.unique_id + 1) % self.count_agents() target = self.model.agents[target_id] - print('starting') + print("starting") while True: - if pongs or not pings: #First agent, or anyone after that + if pongs or not pings: # First agent, or anyone after that pings.append(self.now) - response = yield target.ask('PING') + response = yield target.ask("PING") responses.append(response) else: - print('NOT sending ping') - print('Checking msgs') + print("NOT sending ping") + print("Checking msgs") # Do not block if we have already received a PING if not self.check_messages(): - yield self.received() - print('done') + yield self.received() + print("done") def on_receive(self, msg, sender=None): - if msg == 'PING': + if msg == "PING": pongs.append(self.now) - return 'PONG' + return "PONG" raise Exception("This should never happen") e = environment.EventedEnvironment(schedule_class=stime.OrderedTimedActivation) @@ -149,7 +151,6 @@ class TestAgents(TestCase): e.add_agent(agent_class=Ping) assert e.now == 0 - for i in range(5): e.step() time = i + 1 diff --git a/tests/test_examples.py b/tests/test_examples.py index b2d2750..f589ecb 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -44,7 +44,7 @@ def add_example_tests(): for cfg, path in serialization.load_files( join(EXAMPLES, "**", "*.yml"), ): - if 'soil_output' in path: + if "soil_output" in path: continue p = make_example_test(path=path, cfg=config.Config.from_raw(cfg)) fname = os.path.basename(path) diff --git a/tests/test_main.py b/tests/test_main.py index d100b97..677421a 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -182,8 +182,11 @@ class TestMain(TestCase): n_trials = 50 max_time = 2 - s = simulation.Simulation(model_params={'agents': [{'agent_class': CheckRun}]}, - num_trials=n_trials, max_time=max_time) + s = simulation.Simulation( + model_params={"agents": [{"agent_class": CheckRun}]}, + num_trials=n_trials, + max_time=max_time, + ) runs = list(s.run_simulation(dry_run=True)) over = list(x.now for x in runs if x.now > 2) assert len(runs) == n_trials diff --git a/tests/test_time.py b/tests/test_time.py index 458b734..7fdab0b 100644 --- a/tests/test_time.py +++ b/tests/test_time.py @@ -2,11 +2,12 @@ from unittest import TestCase from soil import time, agents, environment + class TestMain(TestCase): def test_cond(self): - ''' + """ A condition should match a When if the concition is True - ''' + """ t = time.Cond(lambda t: True) f = time.Cond(lambda t: False) @@ -16,17 +17,16 @@ class TestMain(TestCase): assert w is not f def test_cond(self): - ''' + """ Comparing a Cond to a Delta should always return False - ''' + """ c = time.Cond(lambda t: False) d = time.Delta(1) assert c is not d def test_cond_env(self): - ''' - ''' + """ """ times_started = [] times_awakened = [] @@ -35,21 +35,18 @@ class TestMain(TestCase): done = [] class CondAgent(agents.BaseAgent): - def step(self): nonlocal done times_started.append(self.now) while True: times_asleep.append(self.now) - yield time.Cond(lambda agent: agent.now >= 10, - delta=2) + yield time.Cond(lambda agent: agent.now >= 10, delta=2) times_awakened.append(self.now) if self.now >= 10: break done.append(self.now) - env = environment.Environment(agents=[{'agent_class': CondAgent}]) - + env = environment.Environment(agents=[{"agent_class": CondAgent}]) while env.schedule.time < 11: times.append(env.now)