numberoverzero / bottom

asyncio-based rfc2812-compliant IRC Client
http://bottom-docs.readthedocs.io
MIT License
76 stars 23 forks source link

Example fails to run on python 3.10 #60

Open precision opened 2 years ago

precision commented 2 years ago

It appears that the asyncio API has changed in python 3.10, causing Bottom to exception.

Python 3.10.1 (main, Dec 11 2021, 17:22:55) [GCC 11.1.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import bottom
>>> import asyncio
>>> host = 'chat.freenode.net'
>>> port = 6697
>>> ssl = True
>>> NICK = "bottom-bot"
>>> CHANNEL = "#bottom-dev"
>>> bot = bottom.Client(host=host, port=port, ssl=ssl)
>>> bot.loop.create_task(bot.connect())
<Task pending name='Task-1' coro=<RawClient.connect() running at /home/uriah/git/urwbot/.venv/lib/python3.10/site-packages/bottom/client.py:69>>
>>> bot.loop.run_forever()
Task exception was never retrieved
future: <Task finished name='Task-2' coro=<process() done, defined at /home/uriah/git/urwbot/.venv/lib/python3.10/site-packages/bottom/client.py:12> exception=TypeError('As of 3.10, the *loop* parameter was removed from Event() since it is no longer necessary')>
Traceback (most recent call last):
  File "/home/uriah/git/urwbot/.venv/lib/python3.10/site-packages/bottom/client.py", line 28, in process
    await next_handler(message)
  File "/home/uriah/git/urwbot/.venv/lib/python3.10/site-packages/bottom/client.py", line 26, in next_handler
    await handler(next_handler, message)
  File "/home/uriah/git/urwbot/.venv/lib/python3.10/site-packages/bottom/client.py", line 191, in handler
    client.trigger(event, **kwargs)
  File "/home/uriah/git/urwbot/.venv/lib/python3.10/site-packages/bottom/client.py", line 102, in trigger
    async_event = self._events[event]
  File "/home/uriah/git/urwbot/.venv/lib/python3.10/site-packages/bottom/client.py", line 53, in <lambda>
    lambda: asyncio.Event(loop=self.loop))
  File "/usr/lib/python3.10/asyncio/locks.py", line 167, in __init__
    super().__init__(loop=loop)
  File "/usr/lib/python3.10/asyncio/mixins.py", line 17, in __init__
    raise TypeError(
TypeError: As of 3.10, the *loop* parameter was removed from Event() since it is no longer necessary
Task exception was never retrieved
future: <Task finished name='Task-3' coro=<process() done, defined at /home/uriah/git/urwbot/.venv/lib/python3.10/site-packages/bottom/client.py:12> exception=TypeError('As of 3.10, the *loop* parameter was removed from Event() since it is no longer necessary')>
Traceback (most recent call last):
  File "/home/uriah/git/urwbot/.venv/lib/python3.10/site-packages/bottom/client.py", line 28, in process
    await next_handler(message)
  File "/home/uriah/git/urwbot/.venv/lib/python3.10/site-packages/bottom/client.py", line 26, in next_handler
    await handler(next_handler, message)
  File "/home/uriah/git/urwbot/.venv/lib/python3.10/site-packages/bottom/client.py", line 191, in handler
    client.trigger(event, **kwargs)
  File "/home/uriah/git/urwbot/.venv/lib/python3.10/site-packages/bottom/client.py", line 102, in trigger
    async_event = self._events[event]
  File "/home/uriah/git/urwbot/.venv/lib/python3.10/site-packages/bottom/client.py", line 53, in <lambda>
    lambda: asyncio.Event(loop=self.loop))
  File "/usr/lib/python3.10/asyncio/locks.py", line 167, in __init__
    super().__init__(loop=loop)
  File "/usr/lib/python3.10/asyncio/mixins.py", line 17, in __init__
    raise TypeError(
TypeError: As of 3.10, the *loop* parameter was removed from Event() since it is no longer necessary
Task exception was never retrieved
future: <Task finished name='Task-4' coro=<process() done, defined at /home/uriah/git/urwbot/.venv/lib/python3.10/site-packages/bottom/client.py:12> exception=TypeError('As of 3.10, the *loop* parameter was removed from Event() since it is no longer necessary')>
Traceback (most recent call last):
  File "/home/uriah/git/urwbot/.venv/lib/python3.10/site-packages/bottom/client.py", line 28, in process
    await next_handler(message)
  File "/home/uriah/git/urwbot/.venv/lib/python3.10/site-packages/bottom/client.py", line 26, in next_handler
    await handler(next_handler, message)
  File "/home/uriah/git/urwbot/.venv/lib/python3.10/site-packages/bottom/client.py", line 191, in handler
    client.trigger(event, **kwargs)
  File "/home/uriah/git/urwbot/.venv/lib/python3.10/site-packages/bottom/client.py", line 102, in trigger
    async_event = self._events[event]
  File "/home/uriah/git/urwbot/.venv/lib/python3.10/site-packages/bottom/client.py", line 53, in <lambda>
    lambda: asyncio.Event(loop=self.loop))
  File "/usr/lib/python3.10/asyncio/locks.py", line 167, in __init__
    super().__init__(loop=loop)
  File "/usr/lib/python3.10/asyncio/mixins.py", line 17, in __init__
    raise TypeError(
TypeError: As of 3.10, the *loop* parameter was removed from Event() since it is no longer necessary
^CTraceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.10/asyncio/base_events.py", line 595, in run_forever
    self._run_once()
  File "/usr/lib/python3.10/asyncio/base_events.py", line 1845, in _run_once
    event_list = self._selector.select(timeout)
  File "/usr/lib/python3.10/selectors.py", line 469, in select
    fd_event_list = self._selector.poll(timeout, max_ev)
KeyboardInterrupt
numberoverzero commented 2 years ago

Thanks for opening an issue, sorry this is failing (and for so long!)

I'll get started on a fix to remove the loop parameter and reference, this will be a major version bump.

precision commented 2 years ago

Thank you for taking a look. I knew that there might be some breakages by playing with 3.10 this early and just wanted to let you know.

BakasuraRCE commented 1 year ago

Hey @numberoverzero!

First of all, let me say thanks, your project is so cool, I'm a big fan of this type of methodology (keep it simple and consistent). The mode of injecting events with decorators is just perfect, you see, FastAPI uses it, too the mode of detecting sync and async and call events with create_task for beginners is nice(would be nice to have a config to allow just await and keep to the user the control of background tasks with create_task).

ATM in the scene, no other project is using coroutines by default, and really don't understand why. I really wait to your future releases :)

Here is my grain of sand to make it work quickly in Python 3.10:

Wrap the Client class with this:

class Python310Client(Client):
    def __init__(self, host: str, port: int, *, encoding: str = "utf-8", ssl: bool = True, loop: Optional[asyncio.AbstractEventLoop] = None) -> None:
        """Fix 3.10 error"""
        super().__init__(host, port, encoding=encoding, ssl=ssl, loop=loop)
        self._events = collections.defaultdict(lambda: asyncio.Event())

And for the connect event(also any wait call), remove the loop parameter:

    @bot.on('client_connect')
    async def connect(**kwargs):
        bot.send('NICK', nick=nick)
        bot.send('USER', user=nick, realname=nick)

        # Don't try to join channels until the server has
        # sent the MOTD, or signaled that there's no MOTD.
        done, pending = await asyncio.wait(
            [bot.wait('RPL_ENDOFMOTD'),
             bot.wait('ERR_NOMOTD')],
            return_when=asyncio.FIRST_COMPLETED
        )

        # Cancel whichever waiter's event didn't come in.
        for future in pending:
            future.cancel()

        bot.send('join', channel=channel)