shizmob / pydle

An IRCv3-compliant Python 3 IRC library.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Unexpected keyword argument 'loop' under Python 3.10

felixonmars opened this issue · comments

    await client.connect(config["irc"]["server"], tls=True, tls_verify=True)
  File "/usr/lib/python3.10/site-packages/pydle/features/tls.py", line 35, in connect
    return await super().connect(hostname, port, tls=tls, **kwargs)
  File "/usr/lib/python3.10/site-packages/pydle/features/rfc1459/client.py", line 190, in connect
    await super().connect(hostname, port, **kwargs)
  File "/usr/lib/python3.10/site-packages/pydle/client.py", line 124, in connect
    await self._connect(hostname=hostname, port=port, reconnect=reconnect, **kwargs)
  File "/usr/lib/python3.10/site-packages/pydle/features/tls.py", line 54, in _connect
    await self.connection.connect()
  File "/usr/lib/python3.10/site-packages/pydle/connection.py", line 48, in connect
    (self.reader, self.writer) = await asyncio.open_connection(
  File "/usr/lib/python3.10/asyncio/streams.py", line 47, in open_connection
    transport, _ = await loop.create_connection(
TypeError: BaseEventLoop.create_connection() got an unexpected keyword argument 'loop'

Looking through some of the other open issues, it does not appear Pydle supports Python 3.10 at present (Ref #161, #142) as a few places call asyncio.coroutine which was removed in Python 3.10.

I can't say if this issue is related to that or not, but it might be a contributing factor. Can you provide more information about your environment and the context this code is in to assist in diagnosis?

Dug into this a bit.
There are a couple distinct issues

  • If pydle is passed an event loop to use Client(..., loop=some_event_loop), pydle will use it via the now deprecated loop= parameters in asyncio calls.
  • If pydle is not passed an event loop, it will instantiate a new one instead of fetching the current event loop.

The combination of these two issues leads to an easy footgun demonstrated by this sample code (using #165 's new feature, although not relevant to this issue) :

from pydle import Client
import asyncio

client = Client("MyBot", realname='My Bot')


@client.event
async def on_connect(self):
 await self.join('#bottest')


@client.event
async def on_message(self, target, source, message):
 # don't respond to our own messages, as this leads to a positive feedback loop
 if source != self.nickname:
   await self.message(target, message)


async def main():
 await client.connect('localhost', tls=True, tls_verify=False)
 await client.handle_forever()


if __name__ == "__main__":
 asyncio.run(main())

Pydle tries to run on a different event loop than it was called under.

RuntimeError: Task <Task pending name='Task-1' coro=<main() running at test.py:20> cb=[_run_until_complete_cb() at /usr/lib/python3.8/asyncio/base_events.py:184]> got Future <Future pending cb=[_chain_future.<locals>._call_check_cancel() at /usr/lib/python3.8/asyncio/futures.py:360]> attached to a different loop

The currently "correct" version of the above is:

from pydle import Client
import asyncio

loop = asyncio.get_event_loop()
client = Client("MyBot", realname='My Bot', loop=loop)


@client.event
async def on_connect(self):
    await self.join('#bottest')


@client.event
async def on_message(self, target, source, message):
    # don't respond to our own messages, as this leads to a positive feedback loop
    if source != self.nickname:
        await self.message(target, message)


async def main():
    await client.connect('localhost', tls=True, tls_verify=False)
    await client.handle_forever()


if __name__ == "__main__":
    loop.run_until_complete(main())

The real fix here would be to remove Pydle's internal handling of the event loop object entirely, and instead depend upon the fact that pydle's coroutines will be awaited by downstream code - from a valid event loop.
By removing the loop parameter from pydle.Client and all internal logic that uses self.eventloop, we can eliminate this class of issue all together.

Doing so would be a breaking change by semver, as it changes the public api in a backward-incompatible way.

Is this still a problem on 3.11, or is upgrading Python a potential solution?

@Baughn

Pydle is also broken on 3.11. Upgrading is not a fix. See #180 for the current discussion on various potential fixes.

I'll also note for the record that 3.11 removed coroutine as a valid import from asyncio (see: https://docs.python.org/3.11/whatsnew/3.11.html#removed) but Pydle still attempts in some places to import that, so once 3.10 compatibility is confirmed 3.11 will need to have another look.