Skip to content
This repository has been archived by the owner on Aug 31, 2021. It is now read-only.

Passing data between greenlets #24

Open
wants to merge 16 commits into
base: master
Choose a base branch
from

Conversation

rossjrw
Copy link

@rossjrw rossjrw commented Feb 14, 2020

pyaib's eponymous asynchronous nature makes it difficult - if not outright impossible - to pass data between greenlets, or pause a greenlet to wait for some event.

Not good enough for me - I had a situation where I needed a keyword command to request a fresh NAMES response from the server and compare that to the cached NAMES response in my database. How could I possibly do that? In a function decorated with @keyword, I can't stop it in the middle and wait for @observe('IRC_MSG_353'). They would both have to be handled by different functions - different, completely separate, asynchronous functions.

This PR introduces a new concept: Signals, which are pretty much a fork of Events. Signals are messages broadcasted from one greenlet to all others. They have a name/ID and can carry data with them.

This PR introduces a new decorator and two new functions:

  • pyaib.signals.emit_signal: a non-blocking function that emits a signal. Takes arguments irc_context and the name/ID of the signal to be broadcasted. Has an optional data kwarg that specifies an object to be sent along with the signal, which all recipient greenlets will recieve.
  • pyaib.components.awaits_signal: a decorator for plugin functions. The function is called when the signal is received. (Added for consistency with Events; not sure I see a use case)
  • pyaib.signals.await_signal: a function that blocks until the signal is recieved. Takes arguments irc_context and the name/ID of the signal to wait for. Has an optional timeout kwarg which is a float specifying the number of seconds to wait for before raising TimeoutError. If not specified, it will wait forever. Returns whatever data was transmitted along with the signal, defaulting to None.

Example usage (a truncated examples/plugins/signals.py):

from pyiab.plugins import plugin_class
from pyaib.components import observe, awaits_signal
from pyaib.signals import emit_signal, await_signal
import re

@plugin_class('names')
class Names:
    @keyword('names')
    def get_list_of_names(self, irc_c, message, trigger, args, kwargs):
        irc_c.RAW("NAMES %s" % message.channel)
        try:
            response = await_signal(irc_c, 'NAMES_RESPONSE', timeout=10.0)
        except TimeoutError:
            message.reply("The request timed out.")
            return
        channel = response[0]
        names = response[1]
        assert channel == message.channel
        message.reply("List of channel members: %s" % ", ".join(names))

    @observe('IRC_MSG_353') # 353 indicates a NAMES response
    def recieve_names(self, irc_c, message):
        response = re.split(r"\s:?", message.args.strip())[2:]
        channel = response[0]
        names = response[1:]
        emit_signal(irc_c, 'NAMES_RESPONSE', data=(channel, names))

The development process can be found here: rossjrw#1

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants