Skip to content
Permalink
Browse files

Updated documentation. Added more type controls to Message attributes.

  • Loading branch information...
javipalanca committed Mar 22, 2019
1 parent a66f924 commit 5021de40650b6f1e9657de6c3b80ecf470a6e971
Showing with 293 additions and 48 deletions.
  1. +9 −9 docs/agents.rst
  2. +60 −14 docs/behaviours.rst
  3. +2 −2 docs/presence.rst
  4. +16 −11 docs/usage.rst
  5. +42 −0 examples/join.py
  6. +43 −12 spade/message.py
  7. +121 −0 tests/test_message.py
@@ -7,7 +7,7 @@ Using templates

Templates is the method used by SPADE to dispatch received messages to the behaviour that is waiting for that message.
When adding a behaviour you can set a template for that behaviour, which allows the agent to deliver a message received
by the agent to that registered behaviour. A :class:`Template` instance has the same attributes of a :class:`Message` and all the
by the agent to that registered behaviour. A ``Template`` instance has the same attributes of a ``Message`` and all the
attributes defined in the template must be equal in the message for this to match.

The attributes that can be set in a template are:
@@ -109,9 +109,9 @@ Here is a self-explaining example::
self.exit_code = "Job Finished!"

# stop agent from behaviour
self.agent.stop()
await self.agent.stop()

def setup(self):
async def setup(self):
print("SenderAgent started")
self.b = self.InformBehav()
self.add_behaviour(self.b)
@@ -162,9 +162,9 @@ Ok, we have sent a message but now we need someone to receive that message. Show
print("Message sent!")

# stop agent from behaviour
self.agent.stop()
await self.agent.stop()

def setup(self):
async def setup(self):
print("SenderAgent started")
b = self.InformBehav()
self.add_behaviour(b)
@@ -181,9 +181,9 @@ Ok, we have sent a message but now we need someone to receive that message. Show
print("Did not received any message after 10 seconds")

# stop agent from behaviour
self.agent.stop()
await self.agent.stop()

def setup(self):
async def setup(self):
print("ReceiverAgent started")
b = self.RecvBehav()
template = Template()
@@ -194,8 +194,8 @@ Ok, we have sent a message but now we need someone to receive that message. Show

if __name__ == "__main__":
receiveragent = ReceiverAgent("receiver@your_xmpp_server", "receiver_password")
receiveragent.start()
time.sleep(2) # wait for receiver agent to be prepared. In next sections we'll use presence notification.
future = receiveragent.start()
future.result() # wait for receiver agent to be prepared.
senderagent = SenderAgent("sender@your_xmpp_server", "sender_password")
senderagent.start()

@@ -38,12 +38,12 @@ Let's see an example::

async def on_end(self):
# stop agent from behaviour
self.agent.stop()
await self.agent.stop()

async def on_start(self):
self.counter = 0

def setup(self):
async def setup(self):
print(f"PeriodicSenderAgent started at {datetime.datetime.now().time()}")
start_at = datetime.datetime.now() + datetime.timedelta(seconds=5)
b = self.InformBehav(period=2, start_at=start_at)
@@ -62,18 +62,18 @@ Let's see an example::
self.kill()

async def on_end(self):
self.agent.stop()
await self.agent.stop()

def setup(self):
async def setup(self):
print("ReceiverAgent started")
b = self.RecvBehav()
self.add_behaviour(b)


if __name__ == "__main__":
receiveragent = ReceiverAgent("receiver@your_xmpp_server", "receiver_password")
receiveragent.start()
time.sleep(1) # wait for receiver agent to be prepared. In next sections we'll use presence notification.
future = receiveragent.start()
future.result() # wait for receiver agent to be prepared.
senderagent = PeriodicSenderAgent("sender@your_xmpp_server", "sender_password")
senderagent.start()

@@ -146,9 +146,9 @@ Let's see an example::
await self.send(msg)

async def on_end(self):
self.agent.stop()
await self.agent.stop()

def setup(self):
async def setup(self):
print(f"TimeoutSenderAgent started at {datetime.datetime.now().time()}")
start_at = datetime.datetime.now() + datetime.timedelta(seconds=5)
b = self.InformBehav(start_at=start_at)
@@ -166,17 +166,17 @@ Let's see an example::
self.kill()

async def on_end(self):
self.agent.stop()
await self.agent.stop()

def setup(self):
async def setup(self):
b = self.RecvBehav()
self.add_behaviour(b)


if __name__ == "__main__":
receiveragent = ReceiverAgent("receiver@your_xmpp_server", "receiver_password")
receiveragent.start()
time.sleep(1) # wait for receiver agent to be prepared. In next sections we'll use presence notification.
future = receiveragent.start()
future.result() # wait for receiver agent to be prepared.
senderagent = TimeoutSenderAgent("sender@your_xmpp_server", "sender_password")
senderagent.start()

@@ -253,7 +253,7 @@ transit to::

async def on_end(self):
print(f"FSM finished at state {self.current_state}")
self.agent.stop()
await self.agent.stop()


class StateOne(State):
@@ -280,7 +280,7 @@ transit to::


class FSMAgent(Agent):
def setup(self):
async def setup(self):
fsm = ExampleFSMBehaviour()
fsm.add_state(name=STATE_ONE, state=StateOne(), initial=True)
fsm.add_state(name=STATE_TWO, state=StateTwo())
@@ -313,3 +313,49 @@ The output of this example is::
FSM finished at state STATE_THREE
Agent finished


Waiting a Behaviour
-------------------

Sometimes you may need to wait for a behaviour to finish. In order to make this easy, behaviours provide a method called
``join``. Using this method you can wait for a behaviour to be finished. Be careful, since this is a blocking operation.
In order to make it usable inside and outside coroutines, this is also a morphing method (like ``start`` and ``stop``)
which behaves different depending on the context. It returns a coroutine or a future depending on whether it is called
from a coroutine or a synchronous method. Example::

import asyncio
from spade.agent import Agent
from spade.behaviour import OneShotBehaviour
from spade import quit_spade


class DummyAgent(Agent):
class LongBehav(OneShotBehaviour):
async def run(self):
await asyncio.sleep(5)
print("Long Behaviour has finished")

class WaitingBehav(OneShotBehaviour):
async def run(self):
await self.agent.behav.join() # this join must be awaited
print("Waiting Behaviour has finished")

async def setup(self):
print("Agent starting . . .")
self.behav = self.LongBehav()
self.add_behaviour(self.behav)
self.behav2 = self.WaitingBehav()
self.add_behaviour(self.behav2)


if __name__ == "__main__":
dummy = DummyAgent("your_jid@your_xmpp_server", "your_password")
future = dummy.start()
future.result()

dummy.behav2.join() # this join must not be awaited

print("Stopping agent.")
dummy.stop()

quit_spade()
@@ -190,7 +190,7 @@ This is an example that shows in a practical way the presence module::


class Agent1(Agent):
def setup(self):
async def setup(self):
print("Agent {} running".format(self.name))
self.add_behaviour(self.Behav1())

@@ -216,7 +216,7 @@ This is an example that shows in a practical way the presence module::


class Agent2(Agent):
def setup(self):
async def setup(self):
print("Agent {} running".format(self.name))
self.add_behaviour(self.Behav2())

@@ -25,7 +25,7 @@ To create an agent in a project you just need to: ::
from spade import agent

class DummyAgent(agent.Agent):
def setup(self):
async def setup(self):
print("Hello World! I'm agent {}".format(str(self.jid)))

dummy = DummyAgent("your_jid@your_xmpp_server", "your_password")
@@ -76,7 +76,7 @@ Example::
self.counter += 1
await asyncio.sleep(1)

def setup(self):
async def setup(self):
print("Agent starting . . .")
b = self.MyBehav()
self.add_behaviour(b)
@@ -113,7 +113,7 @@ waits for a second (to iterate again).
call async methods inside the ``run()`` method, like the ``await asyncio.sleep(1)``, which sleeps during one second
without blocking the event loop.

Now look at the ``setup()`` method of the agent. There, we make an instance of MyBehav and add it to the current agent
Now look at the ``setup()`` coroutine of the agent. There, we make an instance of MyBehav and add it to the current agent
by means of the ``add_behaviour()`` method. The first parameter of this method is the behaviour we want to add, and
there is also a second optional parameter which is the template associated to that behaviour, but we will talk later
about templates.
@@ -165,7 +165,7 @@ An example of how to kill a behaviour::
async def on_end(self):
print("Behaviour finished with exit code {}.".format(self.exit_code))

def setup(self):
async def setup(self):
print("Agent starting . . .")
self.my_behav = self.MyBehav()
self.add_behaviour(self.my_behav)
@@ -212,19 +212,24 @@ Creating an agent from within another agent

There is a common use case where you may need to create an agent from within another agent, that is, from within another
agent's behaviour. This is a *special* case because you can't create a new event loop when you have a loop already
running. For this special case you can use the ``loop`` argument in the ``Agent`` constructor to share an event loop
between more than one agent. There is also a coroutine that allows you to start the agent from a behaviour. This is the
``async_start`` coroutine, which accepts the same arguments as the ``start`` method. Example::
running. For this special case you can use the ``start`` method as usual. But in this case ```start`` behaves as a
coroutine, so it MUST be called with an ``await`` statement in order to work properly. Example::

class CreateBehav(OneShotBehaviour):
async def run(self):
agent2 = Agent("agent2@fake_server", "fake_password", loop=self.agent.loop)
await agent2.async_start(auto_register=False)
agent2 = Agent("agent2@fake_server", "fake_password")
# This start is inside an async def, so it must be awaited
await agent2.start(auto_register=False)

agent1 = Agent("agent1@fake_server", "fake_password")
agent1.add_behaviour(CreateBehav())
# This start is in a synchronous piece of code, so it must NOT be awaited
agent1.start(auto_register=False)


.. warning:: If you call the ``start`` method (instead of the ``async_start`` coroutine) from within a behaviour, you'll
get an error.
.. warning:: Remember to call ``start`` with an ``await`` whenever you are inside an asyncronous method (another coroutine).
Otherwise, call ``start`` as usual (without the ``await`` statement).


.. note:: The ``stop`` method behaves just like ``start``. They change depending on the context.
They return a coroutine or a future depending on whether they are called from a coroutine or a synchronous method.
@@ -0,0 +1,42 @@
import asyncio
import getpass

from spade.agent import Agent
from spade.behaviour import OneShotBehaviour
from spade import quit_spade


class DummyAgent(Agent):
class LongBehav(OneShotBehaviour):
async def run(self):
await asyncio.sleep(5)
print("Long Behaviour has finished")

class WaitingBehav(OneShotBehaviour):
async def run(self):
await self.agent.behav.join() # this join must be awaited
print("Waiting Behaviour has finished")

async def setup(self):
print("Agent starting . . .")
self.behav = self.LongBehav()
self.add_behaviour(self.behav)
self.behav2 = self.WaitingBehav()
self.add_behaviour(self.behav2)


if __name__ == "__main__":

jid = input("JID> ")
passwd = getpass.getpass()

dummy = DummyAgent(jid, passwd)
future = dummy.start()
future.result()

dummy.behav2.join() # this join must not be awaited

print("Stopping agent.")
dummy.stop()

quit_spade()

0 comments on commit 5021de4

Please sign in to comment.
You can’t perform that action at this time.