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 deprecatedloop=
parameters inasyncio
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?
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.