sbidy / pywizlight

A python connector for WiZ devices
MIT License
463 stars 79 forks source link

DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10 #140

Open Teraskull opened 2 years ago

Teraskull commented 2 years ago

Running the example code on Python 3.9:

await asyncio.gather(bulb1.turn_on(PilotBuilder(warm_white=255)), bulb2.turn_on(PilotBuilder(warm_white=255)), loop=loop)

Shows a DeprecationWarning:

DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10

Remove loop=loop for Python 3.10 compatibility.

sbidy commented 2 years ago

According to the documentation, the loop parameter can be removed and should be replaced by asyncio.run - https://docs.python.org/3/library/asyncio-task.html#running-an-asyncio-program

async def turn_bulbs_on(bulb1, bulb2):
  await asyncio.gather(bulb1.turn_on(PilotBuilder(warm_white=255)), bulb2.turn_on(PilotBuilder(warm_white=255)))

def main:
   asyncio.run(async turn_bulbs_on(bulb1, bulb2))

Code is not tested.

To be honest - the example code is a bit out of date 😄

wthueb commented 2 years ago

This issue isn't fixed by 5164976. Try the following:

async def light_on():
    light = wizlight(BULB_IP)
    await light.turn_on()

asyncio.run(light_on())

I haven't looked that deeply into fixing it, as I have a very vague understanding how how WiZ light networking works, but the exception is caused by the pywizlight.wizlight.__del__ function attempting to use the event loop (calling self._async_close), however there is no guarantee that the __del__ function gets called while there's still an event loop. In the above code, light.__del__ gets called after the asyncio.run call returns, thus the event loop is closed already.

The example code produces a deprecation exception as well, since calling asyncio.get_event_loop() is deprecated when it isn't called in an event loop. The fix is not as simple as replacing the example code with loop = asyncio.new_event_loop() since the pywizlight.wizlight.__del__ function still attempts to destroy the event loop after it is already closed (I assume due to the order of which objects are deleted by the interpreter). Also, "Application developers should typically use the high-level asyncio functions, such as asyncio.run(), and should rarely need to reference the loop object or call its methods."

Removing the pywizlight.wizlight.__del__ function appears to avoid all of the issues, however as I said, I'm not sure how the networking with the lights works/if not explicitly closing the transport even matters.

hagemt commented 2 years ago

tl;dr: If you're doing a quick script and want to avoid:

Exception ignored in: <function wizlight.__del__ at ...>
...
RuntimeError: Event loop is closed

One can:

import pywizlight

# in main:
del pywizlight.wizlight.__del__

To avoid the noise.