Esempio n. 1
0
def test_filter():
    trace = TraceStore(10)
    for i in range(5):
        trace.append(i)

    result = [x[1] for x in trace.filter()]
    assert result == [0, 1, 2, 3, 4]
Esempio n. 2
0
def test_filter_limit():
    trace = TraceStore(10)
    for i in range(5):
        trace.append(i)

    result = [x[1] for x in trace.filter(limit=3)]
    assert result == [2, 3, 4]
Esempio n. 3
0
    def __init__(self, jid, password, verify_security=False, loop=None):
        self.jid = aioxmpp.JID.fromstr(jid)
        self.password = password
        self.verify_security = verify_security

        self.behaviours = []
        self._values = {}

        self.traces = TraceStore(size=1000)

        if loop:
            self.loop = loop
        else:
            self.loop = asyncio.new_event_loop()
        self.aiothread = AioThread(self, self.loop)
        self._alive = Event()

        # obtain an instance of the service
        self.message_dispatcher = self.client.summon(SimpleMessageDispatcher)

        # Presence service
        self.presence = PresenceManager(self)

        # Web service
        self.web = WebApp(agent=self)
Esempio n. 4
0
    def __init__(self, jid, password, verify_security=False):
        """
        Creates an agent

        Args:
          jid (str): The identifier of the agent in the form username@server
          password (str): The password to connect to the server
          verify_security (bool): Wether to verify or not the SSL certificates
        """
        self.jid = aioxmpp.JID.fromstr(jid)
        self.password = password
        self.verify_security = verify_security

        self.behaviours = []
        self._values = {}

        self.conn_coro = None
        self.stream = None
        self.client = None
        self.message_dispatcher = None
        self.presence = None
        self.loop = None

        self.container = Container()
        self.container.register(self)

        self.loop = self.container.loop

        # Web service
        self.web = WebApp(agent=self)

        self.traces = TraceStore(size=1000)

        self._alive = Event()
Esempio n. 5
0
def test_append_with_category():
    trace = TraceStore(10)
    trace.append("EVENT", "CATEGORY")
    assert len(trace.store) == 1
    assert trace.store[0][1] == "EVENT"
    assert type(trace.store[0][0]) == datetime.datetime
    assert trace.store[0][2] == "CATEGORY"
Esempio n. 6
0
def test_append():
    trace = TraceStore(10)
    trace.append("EVENT")
    assert len(trace.store) == 1
    assert trace.store[0][1] == "EVENT"
    assert type(trace.store[0][0]) == datetime.datetime
    assert trace.store[0][2] is None
Esempio n. 7
0
def test_filter_category_limit():
    cycle = itertools.cycle(range(3))
    trace = TraceStore(10)
    for i in range(5):
        trace.append(1, str(next(cycle)))

    result = [x[2] for x in trace.filter(category="0", limit=1)]
    assert result == ["0"]
Esempio n. 8
0
def test_append_max_size():
    trace = TraceStore(2)
    for i in range(5):
        trace.append(i)

    assert len(trace.store) == 2
    assert trace.store[0][1] == 4
    assert trace.store[1][1] == 3
Esempio n. 9
0
def test_all():
    trace = TraceStore(10)
    for i in range(5):
        trace.append(i)
    all = trace.all()
    events = [e[1] for e in all]

    assert events == [0, 1, 2, 3, 4]
Esempio n. 10
0
def test_all_limit():
    trace = TraceStore(10)
    for i in range(5):
        trace.append(i)
    all = trace.all(limit=2)
    events = [e[1] for e in all]

    assert events == [3, 4]
Esempio n. 11
0
def test_filter_to_limit():
    cycle = itertools.cycle(range(3))
    trace = TraceStore(10)
    for i in range(5):
        msg = Message(body=str(i), to="{}@server".format(next(cycle)), sender="sender@server")
        trace.append(msg)

    result = [x[1].body for x in trace.filter(to="0@server", limit=1)]
    assert result == ["3"]
Esempio n. 12
0
def test_filter_to_and_category_limit():
    cycle = itertools.cycle(range(3))
    trace = TraceStore(10)
    for i in range(5):
        c = str(next(cycle))
        msg = Message(body=str(c), to="{}@server".format(c), sender="sender@server")
        trace.append(msg, c)

    result = [(x[1].body, x[2]) for x in trace.filter(to="1@server", category="1", limit=1)]
    assert result == [("1", "1")]
Esempio n. 13
0
    def __init__(self,
                 jid,
                 password,
                 verify_security=False,
                 use_container=True,
                 loop=None):
        """
        Creates an agent

        Args:
          jid (str): The identifier of the agent in the form username@server
          password (str): The password to connect to the server
          verify_security (bool): Wether to verify or not the SSL certificates
          loop (an asyncio event loop): the event loop if it was already created (optional)
        """
        self.jid = aioxmpp.JID.fromstr(jid)
        self.password = password
        self.verify_security = verify_security

        self.behaviours = []
        self._values = {}

        if use_container:
            self.container = Container()
            self.container.register(self)
        else:
            self.container = None

        self.traces = TraceStore(size=1000)

        if loop:
            self.loop = loop
            self.external_loop = True
        else:
            self.loop = asyncio.new_event_loop()
            self.external_loop = False
        asyncio.set_event_loop(self.loop)

        self.aiothread = AioThread(self, self.loop)
        self._alive = Event()

        # obtain an instance of the service
        self.message_dispatcher = self.client.summon(SimpleMessageDispatcher)

        # Presence service
        self.presence = PresenceManager(self)

        # Web service
        self.web = WebApp(agent=self)
Esempio n. 14
0
    def __init__(self, jid, password, verify_security=False):
        """
        Creates an agent

        Args:
          jid (str): The identifier of the agent in the form username@server
          password (str): The password to connect to the server
          verify_security (bool): Wether to verify or not the SSL certificates
        """
        self.jid = aioxmpp.JID.fromstr(jid)
        self.password = password
        self.verify_security = verify_security

        self.behaviours = []
        self._values = {}

        self.conn_coro = None
        self.stream = None
        self.client = None
        self.message_dispatcher = None
        self.presence = None
        self.loop = None

        self.container = Container()
        self.container.register(self)

        self.loop = self.container.loop

        # Web service
        self.web = WebApp(agent=self)

        self.traces = TraceStore(size=1000)

        self._alive = Event()
Esempio n. 15
0
    def __init__(self, colour, jid, password, verify_security=False):
        self.jid = aioxmpp.JID.fromstr(jid)
        self.password = password
        self.verify_security = verify_security

        self.behaviours = []
        self._values = {}

        self.conn_coro = None
        self.stream = None
        self.client = None
        self.message_dispatcher = None
        self.presence = None
        self.loop = None

        self.container = Container()
        self.container.register(self)

        self.loop = self.container.loop

        # Web service
        self.web = WebApp(agent=self)
        self.traces = TraceStore(size=1000)
        self._alive = Event()

        self.colour = colour
        self.finished = False
        # initialize four pawns with
        # id (first leter from colour and index (from 1 to 4))
        self.pawns = [Pawn(i, self.colour, self.__getitem__() + str(i))
                      for i in range(1, 5)]
Esempio n. 16
0
def test_append_to_top():
    trace = TraceStore(10)
    for i in range(5):
        trace.append(i)
    trace.append("EVENT")
    assert len(trace.store) == 6
    assert trace.store[0][1] == "EVENT"
Esempio n. 17
0
def test_reset():
    trace = TraceStore(10)
    for i in range(5):
        trace.append(i)
    assert len(trace.store) == 5

    trace.reset()
    assert len(trace.store) == 0
Esempio n. 18
0
def test_received():
    Event = namedtuple("Event", ["value", "sent"])
    trace = TraceStore(10)
    for i in range(5):
        trace.append(Event(i, True))

    empty_received = trace.received()
    assert len(empty_received) == 0

    for i in range(5, 10):
        trace.append(Event(i, False))

    received = [r[1].value for r in trace.received()]
    assert len(received) == 5
    assert received == [5, 6, 7, 8, 9]

    limit_received = [r[1].value for r in trace.received(limit=3)]
    assert len(limit_received) == 3
    assert limit_received == [7, 8, 9]
Esempio n. 19
0
    def __init__(self, jid, password, verify_security=False):

        self.jid = aioxmpp.JID.fromstr(jid)
        self.password = password
        self.verify_security = verify_security

        self.behaviours = []
        self._values = {}

        self.conn_coro = None
        self.stream = None
        self.client = None
        self.message_dispatcher = None
        self.presence = None
        self.loop = None

        self.container = Container()
        self.container.register(self)

        self.loop = self.container.loop

        # Web service
        self.web = WebApp(agent=self)

        self.traces = TraceStore(size=1000)

        self._alive = Event()

        self.players = deque()
        self.standing = []
        self.board = Board()
        # is game finished
        self.finished = False
        # last rolled value from die (dice)
        self.rolled_value = None
        # player who last rolled die
        self.curr_player = None
        # curr_player's possible pawn to move
        self.allowed_pawns = []
        # curr_player's chosen pawn to move
        self.picked_pawn = None
        # used for nicer print
        self.prompted_for_pawn = False
        # chosen index from allowed pawn
        self.index = None
        # jog pawn if any
        self.jog_pawns = []
        # agents that represent players
        self.playerAgents = []
Esempio n. 20
0
class Agent(object):
    def __init__(self, jid, password, verify_security=False, loop=None):
        """
        Creates an agent

        Args:
          jid (str): The identifier of the agent in the form username@server
          password (str): The password to connect to the server
          verify_security (bool): Wether to verify or not the SSL certificates
          loop (an asyncio event loop): the event loop if it was already created (optional)
        """
        self.jid = aioxmpp.JID.fromstr(jid)
        self.password = password
        self.verify_security = verify_security

        self.behaviours = []
        self._values = {}

        self.traces = TraceStore(size=1000)

        if loop:
            self.loop = loop
            self.external_loop = True
        else:
            self.loop = asyncio.new_event_loop()
            self.external_loop = False

        self.aiothread = AioThread(self, self.loop)
        self._alive = Event()

        # obtain an instance of the service
        self.message_dispatcher = self.client.summon(SimpleMessageDispatcher)

        # Presence service
        self.presence = PresenceManager(self)

        # Web service
        self.web = WebApp(agent=self)

    def start(self, auto_register=True):
        """
        Starts the agent. This fires some actions:

            * if auto_register: register the agent in the server
            * runs the event loop
            * connects the agent to the server
            * runs the registered behaviours

        Args:
          auto_register (bool, optional): register the agent in the server (Default value = True)

        """
        if auto_register:
            self.register()

        self.aiothread.connect()

        self._start()

    async def async_start(self, auto_register=True):
        """
        Starts the agent from a coroutine. This fires some actions:

            * if auto_register: register the agent in the server
            * runs the event loop
            * connects the agent to the server
            * runs the registered behaviours

        Args:
          auto_register (bool, optional): register the agent in the server (Default value = True)

        """
        if auto_register:
            await self.async_register()

        await self.aiothread.async_connect()

        self._start()

    def _start(self):
        self.aiothread.start()
        self._alive.set()

        # register a message callback here
        self.message_dispatcher.register_callback(
            aioxmpp.MessageType.CHAT,
            None,
            self._message_received,
        )
        self.setup()

    def register(self):  # pragma: no cover
        """ Register the agent in the XMPP server. """

        metadata = aioxmpp.make_security_layer(
            None, no_verify=not self.verify_security)
        query = ibr.Query(self.jid.localpart, self.password)
        _, stream, features = self.loop.run_until_complete(
            aioxmpp.node.connect_xmlstream(self.jid, metadata))
        self.loop.run_until_complete(ibr.register(stream, query))

    async def async_register(self):  # pragma: no cover
        """ Register the agent in the XMPP server from a coroutine. """
        metadata = aioxmpp.make_security_layer(
            None, no_verify=not self.verify_security)
        query = ibr.Query(self.jid.localpart, self.password)
        _, stream, features = await aioxmpp.node.connect_xmlstream(
            self.jid, metadata)
        await ibr.register(stream, query)

    def setup(self):
        """
        Setup agent before startup.
        This method may be overloaded.
        """
        pass

    @property
    def name(self):
        """ Returns the name of the agent (the string before the '@') """
        return self.jid.localpart

    @property
    def client(self):
        """ Returns the client that is connected to the xmpp server """
        return self.aiothread.client

    @property
    def stream(self):
        """ Returns the stream of the connection """
        return self.aiothread.stream

    @property
    def avatar(self):
        """
        Generates a unique avatar for the agent based on its JID.
        Uses Gravatar service with MonsterID option.

        Returns:
          str: the url of the agent's avatar

        """
        return self.build_avatar_url(self.jid.bare())

    @staticmethod
    def build_avatar_url(jid):
        """
        Static method to build a gravatar url with the agent's JID

        Args:
          jid (aioxmpp.JID): an XMPP identifier

        Returns:
          str: an URL for the gravatar

        """
        digest = md5(str(jid).encode("utf-8")).hexdigest()
        return "http://www.gravatar.com/avatar/{md5}?d=monsterid".format(
            md5=digest)

    def submit(self, coro):
        """
        Runs a coroutine in the event loop of the agent.
        this call is not blocking.

        Args:
          coro (coroutine): the coroutine to be run

        Returns:
            asyncio.Future: the future of the coroutine execution

        """
        return asyncio.run_coroutine_threadsafe(coro, loop=self.loop)

    def add_behaviour(self, behaviour, template=None):
        """
        Adds and starts a behaviour to the agent.
        If template is not None it is used to match
        new messages and deliver them to the behaviour.

        Args:
          behaviour (spade.behaviour.CyclicBehaviour): the behaviour to be started
          template (spade.template.Template, optional): the template to match messages with (Default value = None)

        """
        behaviour.set_agent(self)
        behaviour.set_template(template)
        self.behaviours.append(behaviour)
        behaviour.start()

    def remove_behaviour(self, behaviour):
        """
        Removes a behaviour from the agent.
        The behaviour is first killed.

        Args:
          behaviour (spade.behaviour.CyclicBehaviour): the behaviour instance to be removed

        """
        if not self.has_behaviour(behaviour):
            raise ValueError("This behaviour is not registered")
        index = self.behaviours.index(behaviour)
        self.behaviours[index].kill()
        self.behaviours.pop(index)

    def has_behaviour(self, behaviour):
        """
        Checks if a behaviour is added to an agent.

        Args:
          behaviour (spade.behaviour.CyclicBehaviour): the behaviour instance to check

        Returns:
          bool: a boolean that indicates wether the behaviour is inside the agent.

        """
        return behaviour in self.behaviours

    def stop(self):
        """ Stops an agent and kills all its behaviours. """
        self.presence.set_unavailable()
        for behav in self.behaviours:
            behav.kill()
        if self.web.server:
            self.web.server.close()
            self.submit(self.web.app.shutdown())
            self.submit(self.web.handler.shutdown(60.0))
            self.submit(self.web.app.cleanup())
        self.aiothread.finalize()
        self._alive.clear()

    def is_alive(self):
        """
        Checks if the agent is alive.

        Returns:
          bool: wheter the agent is alive or not

        """
        return self._alive.is_set()

    def set(self, name, value):
        """
        Stores a knowledge item in the agent knowledge base.

        Args:
          name (str): name of the item
          value (object): value of the item

        """
        self._values[name] = value

    def get(self, name):
        """
        Recovers a knowledge item from the agent's knowledge base.

        Args:
          name(str): name of the item

        Returns:
          object: the object retrieved or None

        """
        if name in self._values:
            return self._values[name]
        else:
            return None

    def _message_received(self, msg):
        """
        Callback run when an XMPP Message is reveived.
        This callback delivers the message to every behaviour
        that is waiting for it using their templates match.
        the aioxmpp.Message is converted to spade.message.Message

        Args:
          msg (aioxmpp.Messagge): the message just received.

        Returns:
            list(asyncio.Future): a list of futures of the append of the message at each matched behaviour.

        """
        logger.debug(f"Got message: {msg}")

        msg = Message.from_node(msg)
        futures = []
        matched = False
        for behaviour in (x for x in self.behaviours if x.match(msg)):
            futures.append(self.submit(behaviour.enqueue(msg)))
            logger.debug(f"Message enqueued to behaviour: {behaviour}")
            self.traces.append(msg, category=str(behaviour))
            matched = True
        if not matched:
            logger.warning(f"No behaviour matched for message: {msg}")
            self.traces.append(msg)
        return futures
Esempio n. 21
0
class Agent(object):
    def __init__(self, jid, password, verify_security=False, loop=None):
        self.jid = aioxmpp.JID.fromstr(jid)
        self.password = password
        self.verify_security = verify_security

        self.behaviours = []
        self._values = {}

        self.traces = TraceStore(size=1000)

        if loop:
            self.loop = loop
        else:
            self.loop = asyncio.new_event_loop()
        self.aiothread = AioThread(self, self.loop)
        self._alive = Event()

        # obtain an instance of the service
        self.message_dispatcher = self.client.summon(SimpleMessageDispatcher)

        # Presence service
        self.presence = PresenceManager(self)

        # Web service
        self.web = WebApp(agent=self)

    def start(self, auto_register=False):
        if auto_register:
            self.register()

        self.aiothread.connect()
        self.aiothread.start()
        self._alive.set()
        self.aiothread.event.wait()

        # register a message callback here
        self.message_dispatcher.register_callback(
            aioxmpp.MessageType.CHAT,
            None,
            self._message_received,
        )

        self.setup()

    def register(self):  # pragma: no cover
        metadata = aioxmpp.make_security_layer(None, no_verify=not self.verify_security)
        _, stream, features = self.loop.run_until_complete(aioxmpp.node.connect_xmlstream(self.jid, metadata))
        query = ibr.Query(self.jid.localpart, self.password)
        self.loop.run_until_complete(ibr.register(stream, query))

    def setup(self):
        """
        setup agent before startup.
        this method may be overloaded.
        """
        pass

    @property
    def name(self):
        return self.jid.localpart

    @property
    def client(self):
        return self.aiothread.client

    @property
    def stream(self):
        return self.aiothread.stream

    @property
    def avatar(self):
        """
        Generates a unique avatar for the agent based on its JID.
        Uses Gravatar service with MonsterID option.
        :return: the url of the agent's avatar
        :rtype: :class:`str`
        """
        return self.build_avatar_url(self.jid.bare())

    @staticmethod
    def build_avatar_url(jid):
        digest = md5(str(jid).encode("utf-8")).hexdigest()
        return "http://www.gravatar.com/avatar/{md5}?d=monsterid".format(md5=digest)

    def submit(self, coro):
        """
        runs a coroutine in the event loop of the agent.
        this call is not blocking.
        :param coro: the coroutine to be run
        :type coro: coroutine
        """
        return asyncio.run_coroutine_threadsafe(coro, loop=self.loop)

    def add_behaviour(self, behaviour, template=None):
        """
        Adds and starts a behaviour to the agent.
        If template is not None it is used to match
        new messages and deliver them to the behaviour.
        :param behaviour: the behaviour to be started
        :type behaviour: :class:`spade.behaviour.CyclicBehaviour`
        :param template: the template to match messages with
        :type template: :class:`spade.template.Template`
        """
        behaviour.set_agent(self)
        behaviour.set_template(template)
        self.behaviours.append(behaviour)
        behaviour.start()

    def remove_behaviour(self, behaviour):
        """
        Removes a behaviour from the agent.
        The behaviour is first killed.
        :param behaviour: the behaviour instance to be removed
        :type behaviour: :class:`spade.behaviour.CyclicBehaviour`
        """
        if not self.has_behaviour(behaviour):
            raise ValueError("This behaviour is not registered")
        index = self.behaviours.index(behaviour)
        self.behaviours[index].kill()
        self.behaviours.pop(index)

    def has_behaviour(self, behaviour):
        """
        Checks if a behaviour is added to an agent.
        :param behaviour: the behaviour instance to check
        :type behaviour: :class:`spade.behaviour.CyclicBehaviour`
        :return: a boolean that indicates wether the behaviour is inside the agent.
        :rtype: bool
        """
        return behaviour in self.behaviours

    def stop(self):
        """
        Stops an agent and kills all its behaviours.
        """
        for behav in self.behaviours:
            behav.kill()
        if self.web.server:
            self.web.server.close()
            self.submit(self.web.app.shutdown())
            self.submit(self.web.handler.shutdown(60.0))
            self.submit(self.web.app.cleanup())
        self.aiothread.finalize()
        self._alive.clear()

    def is_alive(self):
        """
        checks if the agent is alive
        :return: wheter the agent is alive or not
        :rtype: :class:`bool`
        """
        return self._alive.is_set()

    def set(self, name, value):
        """
        Stores a knowledge item in the agent knowledge base.
        :param name: name of the item
        :type name: :class:`str`
        :param value: value of the item
        :type value: :class:`object`
        """
        self._values[name] = value

    def get(self, name):
        """
        Recovers a knowledge item from the agent's knowledge base.
        :param name: name of the item
        :type name: :class:`str`
        :return: the object retrieved or None
        :rtype: :class:`object`
        """
        if name in self._values:
            return self._values[name]
        else:
            return None

    def _message_received(self, msg):
        """
        Callback run when an XMPP Message is reveived.
        This callback delivers the message to every behaviour
        that is waiting for it using their templates match.
        the aioxmpp.Message is converted to spade.message.Message
        :param msg: the message just received.
        :type msg: aioxmpp.Messagge
        """
        logger.debug(f"Got message: {msg}")

        msg = Message.from_node(msg)
        futures = []
        matched = False
        for behaviour in (x for x in self.behaviours if x.match(msg)):
            futures.append(self.submit(behaviour.enqueue(msg)))
            logger.debug(f"Message enqueued to behaviour: {behaviour}")
            self.traces.append(msg, category=str(behaviour))
            matched = True
        if not matched:
            logger.warning(f"No behaviour matched for message: {msg}")
            self.traces.append(msg)
        return futures
Esempio n. 22
0
class Agent(object):
    def __init__(self, jid, password, verify_security=False):
        """
        Creates an agent

        Args:
          jid (str): The identifier of the agent in the form username@server
          password (str): The password to connect to the server
          verify_security (bool): Wether to verify or not the SSL certificates
        """
        self.jid = aioxmpp.JID.fromstr(jid)
        self.password = password
        self.verify_security = verify_security

        self.behaviours = []
        self._values = {}

        self.conn_coro = None
        self.stream = None
        self.client = None
        self.message_dispatcher = None
        self.presence = None
        self.loop = None

        self.container = Container()
        self.container.register(self)

        self.loop = self.container.loop

        # Web service
        self.web = WebApp(agent=self)

        self.traces = TraceStore(size=1000)

        self._alive = Event()

    def set_loop(self, loop):
        self.loop = loop

    def set_container(self, container):
        """
        Sets the container to which the agent is attached

        Args:
            container (spade.container.Container): the container to be attached to
        """
        self.container = container

    def start(self, auto_register=True):
        """
        Tells the container to start this agent.
        It returns a coroutine or a future depending on whether it is called from a coroutine or a synchronous method.

        Args:
            auto_register (bool): register the agent in the server (Default value = True)
        """
        return self.container.start_agent(agent=self,
                                          auto_register=auto_register)

    async def _async_start(self, auto_register=True):
        """
        Starts the agent from a coroutine. This fires some actions:

            * if auto_register: register the agent in the server
            * runs the event loop
            * connects the agent to the server
            * runs the registered behaviours

        Args:
          auto_register (bool, optional): register the agent in the server (Default value = True)

        """

        if auto_register:
            await self._async_register()
        self.client = aioxmpp.PresenceManagedClient(
            self.jid,
            aioxmpp.make_security_layer(self.password,
                                        no_verify=not self.verify_security),
            loop=self.loop,
            logger=logging.getLogger(self.jid.localpart))

        # obtain an instance of the service
        self.message_dispatcher = self.client.summon(SimpleMessageDispatcher)

        # Presence service
        self.presence = PresenceManager(self)

        await self._async_connect()

        # register a message callback here
        self.message_dispatcher.register_callback(
            aioxmpp.MessageType.CHAT,
            None,
            self._message_received,
        )
        await self.setup()
        self._alive.set()
        for behaviour in self.behaviours:
            if not behaviour.is_running:
                behaviour.start()

    async def _async_connect(self):  # pragma: no cover
        """ connect and authenticate to the XMPP server. Async mode. """
        try:
            self.conn_coro = self.client.connected()
            aenter = type(self.conn_coro).__aenter__(self.conn_coro)
            self.stream = await aenter
            logger.info(f"Agent {str(self.jid)} connected and authenticated.")
        except aiosasl.AuthenticationFailure:
            raise AuthenticationFailure(
                "Could not authenticate the agent. Check user and password or use auto_register=True"
            )

    async def _async_register(self):  # pragma: no cover
        """ Register the agent in the XMPP server from a coroutine. """
        metadata = aioxmpp.make_security_layer(
            None, no_verify=not self.verify_security)
        query = ibr.Query(self.jid.localpart, self.password)
        _, stream, features = await aioxmpp.node.connect_xmlstream(
            self.jid, metadata, loop=self.loop)
        await ibr.register(stream, query)

    async def setup(self):
        """
        Setup agent before startup.
        This coroutine may be overloaded.
        """
        await asyncio.sleep(0)

    @property
    def name(self):
        """ Returns the name of the agent (the string before the '@') """
        return self.jid.localpart

    @property
    def avatar(self):
        """
        Generates a unique avatar for the agent based on its JID.
        Uses Gravatar service with MonsterID option.

        Returns:
          str: the url of the agent's avatar

        """
        return self.build_avatar_url(self.jid.bare())

    @staticmethod
    def build_avatar_url(jid):
        """
        Static method to build a gravatar url with the agent's JID

        Args:
          jid (aioxmpp.JID): an XMPP identifier

        Returns:
          str: an URL for the gravatar

        """
        digest = md5(str(jid).encode("utf-8")).hexdigest()
        return "http://www.gravatar.com/avatar/{md5}?d=monsterid".format(
            md5=digest)

    def submit(self, coro):
        """
        Runs a coroutine in the event loop of the agent.
        this call is not blocking.

        Args:
          coro (coroutine): the coroutine to be run

        Returns:
            asyncio.Future: the future of the coroutine execution

        """
        return asyncio.run_coroutine_threadsafe(coro, loop=self.loop)

    def add_behaviour(self, behaviour, template=None):
        """
        Adds and starts a behaviour to the agent.
        If template is not None it is used to match
        new messages and deliver them to the behaviour.

        Args:
          behaviour (spade.behaviour.CyclicBehaviour): the behaviour to be started
          template (spade.template.Template, optional): the template to match messages with (Default value = None)

        """
        behaviour.set_agent(self)
        if issubclass(type(behaviour), FSMBehaviour):
            for _, state in behaviour.get_states().items():
                state.set_agent(self)
        behaviour.set_template(template)
        self.behaviours.append(behaviour)
        if self.is_alive():
            behaviour.start()

    def remove_behaviour(self, behaviour):
        """
        Removes a behaviour from the agent.
        The behaviour is first killed.

        Args:
          behaviour (spade.behaviour.CyclicBehaviour): the behaviour instance to be removed

        """
        if not self.has_behaviour(behaviour):
            raise ValueError("This behaviour is not registered")
        index = self.behaviours.index(behaviour)
        self.behaviours[index].kill()
        self.behaviours.pop(index)

    def has_behaviour(self, behaviour):
        """
        Checks if a behaviour is added to an agent.

        Args:
          behaviour (spade.behaviour.CyclicBehaviour): the behaviour instance to check

        Returns:
          bool: a boolean that indicates wether the behaviour is inside the agent.

        """
        return behaviour in self.behaviours

    def stop(self):
        """
        Tells the container to start this agent.
        It returns a coroutine or a future depending on whether it is called from a coroutine or a synchronous method.
        """
        return self.container.stop_agent(self)

    async def _async_stop(self):
        """ Stops an agent and kills all its behaviours. """
        if self.presence:
            self.presence.set_unavailable()
        for behav in self.behaviours:
            behav.kill()
        if self.web.is_started():
            await self.web.runner.cleanup()
        """ Discconnect from XMPP server. """
        if self.is_alive():
            # Disconnect from XMPP server
            self.client.stop()
            aexit = self.conn_coro.__aexit__(*sys.exc_info())
            await aexit
            logger.info("Client disconnected.")

        self._alive.clear()

    def is_alive(self):
        """
        Checks if the agent is alive.

        Returns:
          bool: wheter the agent is alive or not

        """
        return self._alive.is_set()

    def set(self, name, value):
        """
        Stores a knowledge item in the agent knowledge base.

        Args:
          name (str): name of the item
          value (object): value of the item

        """
        self._values[name] = value

    def get(self, name):
        """
        Recovers a knowledge item from the agent's knowledge base.

        Args:
          name(str): name of the item

        Returns:
          object: the object retrieved or None

        """
        if name in self._values:
            return self._values[name]
        else:
            return None

    def _message_received(self, msg):
        """
        Callback run when an XMPP Message is reveived.
        This callback delivers the message to every behaviour
        that is waiting for it. First, the aioxmpp.Message is
        converted to spade.message.Message

        Args:
          msg (aioxmpp.Messagge): the message just received.

        Returns:
            list(asyncio.Future): a list of futures of the append of the message at each matched behaviour.

        """

        msg = Message.from_node(msg)
        return self.dispatch(msg)

    def dispatch(self, msg):
        """
        Dispatch the message to every behaviour that is waiting for
        it using their templates match.

        Args:
          msg (spade.message.Messagge): the message to dispatch.

        Returns:
            list(asyncio.Future): a list of futures of the append of the message at each matched behaviour.

        """
        logger.debug(f"Got message: {msg}")
        futures = []
        matched = False
        for behaviour in (x for x in self.behaviours if x.match(msg)):
            futures.append(self.submit(behaviour.enqueue(msg)))
            logger.debug(f"Message enqueued to behaviour: {behaviour}")
            self.traces.append(msg, category=str(behaviour))
            matched = True
        if not matched:
            logger.warning(f"No behaviour matched for message: {msg}")
            self.traces.append(msg)
        return futures
Esempio n. 23
0
class Agent(object):
    def __init__(self, jid, password, verify_security=False):
        """
        Creates an agent

        Args:
          jid (str): The identifier of the agent in the form username@server
          password (str): The password to connect to the server
          verify_security (bool): Wether to verify or not the SSL certificates
        """
        self.jid = aioxmpp.JID.fromstr(jid)
        self.password = password
        self.verify_security = verify_security

        self.behaviours = []
        self._values = {}

        self.conn_coro = None
        self.stream = None
        self.client = None
        self.message_dispatcher = None
        self.presence = None
        self.loop = None

        self.container = Container()
        self.container.register(self)

        self.loop = self.container.loop

        # Web service
        self.web = WebApp(agent=self)

        self.traces = TraceStore(size=1000)

        self._alive = Event()

    def set_loop(self, loop):
        self.loop = loop

    def set_container(self, container):
        """
        Sets the container to which the agent is attached

        Args:
            container (spade.container.Container): the container to be attached to
        """
        self.container = container

    def start(self, auto_register=True):
        """
        Tells the container to start this agent.
        It returns a coroutine or a future depending on whether it is called from a coroutine or a synchronous method.

        Args:
            auto_register (bool): register the agent in the server (Default value = True)
        """
        return self.container.start_agent(agent=self, auto_register=auto_register)

    async def _async_start(self, auto_register=True):
        """
        Starts the agent from a coroutine. This fires some actions:

            * if auto_register: register the agent in the server
            * runs the event loop
            * connects the agent to the server
            * runs the registered behaviours

        Args:
          auto_register (bool, optional): register the agent in the server (Default value = True)

        """

        if auto_register:
            await self._async_register()
        self.client = aioxmpp.PresenceManagedClient(self.jid,
                                                    aioxmpp.make_security_layer(self.password,
                                                                                no_verify=not self.verify_security),
                                                    loop=self.loop,
                                                    logger=logging.getLogger(self.jid.localpart))

        # obtain an instance of the service
        self.message_dispatcher = self.client.summon(SimpleMessageDispatcher)

        # Presence service
        self.presence = PresenceManager(self)

        await self._async_connect()

        # register a message callback here
        self.message_dispatcher.register_callback(
            aioxmpp.MessageType.CHAT,
            None,
            self._message_received,
        )
        await self.setup()
        self._alive.set()
        for behaviour in self.behaviours:
            if not behaviour.is_running:
                behaviour.start()

    async def _async_connect(self):  # pragma: no cover
        """ connect and authenticate to the XMPP server. Async mode. """
        try:
            self.conn_coro = self.client.connected()
            aenter = type(self.conn_coro).__aenter__(self.conn_coro)
            self.stream = await aenter
            logger.info(f"Agent {str(self.jid)} connected and authenticated.")
        except aiosasl.AuthenticationFailure:
            raise AuthenticationFailure(
                "Could not authenticate the agent. Check user and password or use auto_register=True")

    async def _async_register(self):  # pragma: no cover
        """ Register the agent in the XMPP server from a coroutine. """
        metadata = aioxmpp.make_security_layer(None, no_verify=not self.verify_security)
        query = ibr.Query(self.jid.localpart, self.password)
        _, stream, features = await aioxmpp.node.connect_xmlstream(self.jid, metadata, loop=self.loop)
        await ibr.register(stream, query)

    async def setup(self):
        """
        Setup agent before startup.
        This coroutine may be overloaded.
        """
        await asyncio.sleep(0)

    @property
    def name(self):
        """ Returns the name of the agent (the string before the '@') """
        return self.jid.localpart

    @property
    def avatar(self):
        """
        Generates a unique avatar for the agent based on its JID.
        Uses Gravatar service with MonsterID option.

        Returns:
          str: the url of the agent's avatar

        """
        return self.build_avatar_url(self.jid.bare())

    @staticmethod
    def build_avatar_url(jid):
        """
        Static method to build a gravatar url with the agent's JID

        Args:
          jid (aioxmpp.JID): an XMPP identifier

        Returns:
          str: an URL for the gravatar

        """
        digest = md5(str(jid).encode("utf-8")).hexdigest()
        return "http://www.gravatar.com/avatar/{md5}?d=monsterid".format(md5=digest)

    def submit(self, coro):
        """
        Runs a coroutine in the event loop of the agent.
        this call is not blocking.

        Args:
          coro (coroutine): the coroutine to be run

        Returns:
            asyncio.Future: the future of the coroutine execution

        """
        return asyncio.run_coroutine_threadsafe(coro, loop=self.loop)

    def add_behaviour(self, behaviour, template=None):
        """
        Adds and starts a behaviour to the agent.
        If template is not None it is used to match
        new messages and deliver them to the behaviour.

        Args:
          behaviour (spade.behaviour.CyclicBehaviour): the behaviour to be started
          template (spade.template.Template, optional): the template to match messages with (Default value = None)

        """
        behaviour.set_agent(self)
        if issubclass(type(behaviour), FSMBehaviour):
            for _, state in behaviour.get_states().items():
                state.set_agent(self)
        behaviour.set_template(template)
        self.behaviours.append(behaviour)
        if self.is_alive():
            behaviour.start()

    def remove_behaviour(self, behaviour):
        """
        Removes a behaviour from the agent.
        The behaviour is first killed.

        Args:
          behaviour (spade.behaviour.CyclicBehaviour): the behaviour instance to be removed

        """
        if not self.has_behaviour(behaviour):
            raise ValueError("This behaviour is not registered")
        index = self.behaviours.index(behaviour)
        self.behaviours[index].kill()
        self.behaviours.pop(index)

    def has_behaviour(self, behaviour):
        """
        Checks if a behaviour is added to an agent.

        Args:
          behaviour (spade.behaviour.CyclicBehaviour): the behaviour instance to check

        Returns:
          bool: a boolean that indicates wether the behaviour is inside the agent.

        """
        return behaviour in self.behaviours

    def stop(self):
        """
        Tells the container to start this agent.
        It returns a coroutine or a future depending on whether it is called from a coroutine or a synchronous method.
        """
        return self.container.stop_agent(self)

    async def _async_stop(self):
        """ Stops an agent and kills all its behaviours. """
        if self.presence:
            self.presence.set_unavailable()
        for behav in self.behaviours:
            behav.kill()
        if self.web.is_started():
            await self.web.runner.cleanup()

        """ Discconnect from XMPP server. """
        if self.is_alive():
            # Disconnect from XMPP server
            self.client.stop()
            aexit = self.conn_coro.__aexit__(*sys.exc_info())
            await aexit
            logger.info("Client disconnected.")

        self._alive.clear()

    def is_alive(self):
        """
        Checks if the agent is alive.

        Returns:
          bool: wheter the agent is alive or not

        """
        return self._alive.is_set()

    def set(self, name, value):
        """
        Stores a knowledge item in the agent knowledge base.

        Args:
          name (str): name of the item
          value (object): value of the item

        """
        self._values[name] = value

    def get(self, name):
        """
        Recovers a knowledge item from the agent's knowledge base.

        Args:
          name(str): name of the item

        Returns:
          object: the object retrieved or None

        """
        if name in self._values:
            return self._values[name]
        else:
            return None

    def _message_received(self, msg):
        """
        Callback run when an XMPP Message is reveived.
        This callback delivers the message to every behaviour
        that is waiting for it. First, the aioxmpp.Message is
        converted to spade.message.Message

        Args:
          msg (aioxmpp.Messagge): the message just received.

        Returns:
            list(asyncio.Future): a list of futures of the append of the message at each matched behaviour.

        """

        msg = Message.from_node(msg)
        return self.dispatch(msg)

    def dispatch(self, msg):
        """
        Dispatch the message to every behaviour that is waiting for
        it using their templates match.

        Args:
          msg (spade.message.Messagge): the message to dispatch.

        Returns:
            list(asyncio.Future): a list of futures of the append of the message at each matched behaviour.

        """
        logger.debug(f"Got message: {msg}")
        futures = []
        matched = False
        for behaviour in (x for x in self.behaviours if x.match(msg)):
            futures.append(self.submit(behaviour.enqueue(msg)))
            logger.debug(f"Message enqueued to behaviour: {behaviour}")
            self.traces.append(msg, category=str(behaviour))
            matched = True
        if not matched:
            logger.warning(f"No behaviour matched for message: {msg}")
            self.traces.append(msg)
        return futures
Esempio n. 24
0
def test_len():
    trace = TraceStore(10)
    for i in range(5):
        trace.append(i)
    assert trace.len() == 5
Esempio n. 25
0
def test_init():
    trace = TraceStore(10)

    assert trace.size == 10
    assert trace.store == []