0

I am attempting to asynchronously stream notifications from Mastodon using Mastodon.py. To run the asynchronous code, I'm attempting to use Trio

I've tried the following:

...
from mastodon import Mastodon, StreamListener
...

def main():
    ...
    access_token = os.getenv("MASTODON_ACCESS_TOKEN")
    api_base_url = os.getenv("MASTODON_API_BASE_URL")

    # Login
    mastodon = Mastodon(
        access_token=access_token,
        api_base_url=api_base_url
    )
    # Show username
    logger.info(f"Logged in as {mastodon.account_verify_credentials()['username']}")
    ...

    logger.info("Starting user stream")
    user = mastodon.stream_user(TheStreamListener())
    logger.info("Started user stream")
    try:
        trio.run(user)
    except KeyboardInterrupt:
        logger.info("Stopping user stream")
        user.close()
        logger.info("Stopped user stream")

class TheStreamListener(StreamListener):
    def on_update(self, status):
        logger.info(f"Got update: {status['content']}")
        ...
    def on_notification(self, notification):
        if notification['type'] == 'mention':
            logger.opt(colors=True).info(f"Got <blue>mention</blue> from {notification['account']['username']}") # noqa E501
        ...
main()

I expected this to log Started user stream after running trio.run(). However, it seems the stream is not asynchronous as the last message logged is Starting user stream. I'm aware there is a run_async parameter for mastodon.stream_user() but have been unable to get it to run asynchronously.

I've tried mastodon.stream_user(TheStreamListener(), run_async=True, reconnect_async=True). However, I'm unsure how I can keep the stream running whilst running other code as the stream exits immediately.

  • mastodon.py's async handling uses a separate thread. It has nothing to do with async Python, as used by asyncio or trio. This is obvious when (if?) you read the documentation. Therefore, this question doesn't make sense. – Matthias Urlichs Aug 13 '23 at 08:43
  • @MatthiasUrlichs I've tried `mastodon.stream_user(TheStreamListener(), run_async=True, reconnect_async=True)`. However, I'm not sure how I can keep the stream running whilst running other code as the stream exits immediately. – slashtechno Aug 13 '23 at 16:02
  • What do you mean by "the stream exits immediately"? A stream is an object. It can't "exit". `stream_user` of course returns immediately, as it runs the real work in a thread, not a Trio (or asyncio) task. – Matthias Urlichs Aug 24 '23 at 17:09
  • @MatthiasUrlichs Ah, okay. so I need to make another Async function that is just used to run the stream? I'm unsure what the best way to keep the stream running in the other thread while continuing to run other code. Whenever the program ends due to the other code completing, the stream, and program, will exit - no? – slashtechno Aug 24 '23 at 19:23
  • No you don't need to make async functions. Whatever for? your code uses threads and callbacks. Yes the program might exit, then again it might not, I wouldn't depend on it. The fix is create a future (the one from `concurrent.futures`, not from `asyncio`) and wait on it when you're done. Set it from your signal handler and maybe from a Mastodon `connection-closed` callback; I didn't look but assume that something like that exists. – Matthias Urlichs Aug 26 '23 at 10:53

1 Answers1

0

I ended up implementing the following code successfully:

def main():
    logger.info("Starting user stream")
    stream = mastodon.stream_user(TheStreamListener(), 
    run_async=True)logger.info("Started user stream")
    trio.run(sleep_or_not, stream)
async def sleep_or_not(stream):
    # Experimenting with running other code while the stream is running
    try:
        async with trio.open_nursery() as nursery:
            nursery.start_soon(trio.sleep_forever)
            nursery.start_soon(print_time)
    except KeyboardInterrupt:
        ...