Page 1 of 1

PyO3 modules may only be initialized once per interpreter process

Posted: Friday 02 June 2023 16:41
by pipiche
We have a plugin which use the cryptophraphy 41.0.1 module, when you stop and then start the plugin (without restarting Domoticz), you get this error

I found a pointer to that GitHub issue, https://github.com/PyO3/pyo3/issues/2644 but not an expert in the embedded python library
Jun 02 16:35:43 rasp domoticz[21346]: 2023-06-02 16:35:43.632 Error: Zigpy-Elelabs: Call to function 'onStart' failed, exception details:
Jun 02 16:35:43 rasp domoticz[21346]: 2023-06-02 16:35:43.646 Error: Zigpy-Elelabs: Traceback (most recent call last):
Jun 02 16:35:43 rasp domoticz[21346]: 2023-06-02 16:35:43.646 Error: Zigpy-Elelabs: File "/var/lib/domoticz/plugins/Domoticz-Zigbee/plugin.py", line 1537, in onStart
Jun 02 16:35:43 rasp domoticz[21346]: 2023-06-02 16:35:43.646 Error: Zigpy-Elelabs: _plugin.onStart()
Jun 02 16:35:43 rasp domoticz[21346]: 2023-06-02 16:35:43.646 Error: Zigpy-Elelabs: File "/var/lib/domoticz/plugins/Domoticz-Zigbee/plugin.py", line 602, in onStart
Jun 02 16:35:43 rasp domoticz[21346]: 2023-06-02 16:35:43.646 Error: Zigpy-Elelabs: from zigpy.config import (CONF_DEVICE, CONF_DEVICE_PATH,
Jun 02 16:35:43 rasp domoticz[21346]: 2023-06-02 16:35:43.646 Error: Zigpy-Elelabs: File "/usr/local/lib/python3.10/site-packages/zigpy/config/__init__.py", line 32, in <module>
Jun 02 16:35:43 rasp domoticz[21346]: 2023-06-02 16:35:43.646 Error: Zigpy-Elelabs: from zigpy.config.validators import (
Jun 02 16:35:43 rasp domoticz[21346]: 2023-06-02 16:35:43.646 Error: Zigpy-Elelabs: File "/usr/local/lib/python3.10/site-packages/zigpy/config/validators.py", line 9, in <module>
Jun 02 16:35:43 rasp domoticz[21346]: 2023-06-02 16:35:43.646 Error: Zigpy-Elelabs: import zigpy.zdo.types as zdo_t
Jun 02 16:35:43 rasp domoticz[21346]: 2023-06-02 16:35:43.647 Error: Zigpy-Elelabs: File "/usr/local/lib/python3.10/site-packages/zigpy/zdo/__init__.py", line 10, in <module>
Jun 02 16:35:43 rasp domoticz[21346]: 2023-06-02 16:35:43.647 Error: Zigpy-Elelabs: import zigpy.util
Jun 02 16:35:43 rasp domoticz[21346]: 2023-06-02 16:35:43.647 Error: Zigpy-Elelabs: File "/usr/local/lib/python3.10/site-packages/zigpy/util.py", line 14, in <module>
Jun 02 16:35:43 rasp domoticz[21346]: 2023-06-02 16:35:43.647 Error: Zigpy-Elelabs: from cryptography.hazmat.primitives.ciphers import Cipher
Jun 02 16:35:43 rasp domoticz[21346]: 2023-06-02 16:35:43.647 Error: Zigpy-Elelabs: File "/usr/local/lib/python3.10/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py", line 11, in <module>
Jun 02 16:35:43 rasp domoticz[21346]: 2023-06-02 16:35:43.647 Error: Zigpy-Elelabs: from cryptography.hazmat.primitives.ciphers.base import (
Jun 02 16:35:43 rasp domoticz[21346]: 2023-06-02 16:35:43.647 Error: Zigpy-Elelabs: File "/usr/local/lib/python3.10/site-packages/cryptography/hazmat/primitives/ciphers/base.py", line 10, in <module>
Jun 02 16:35:43 rasp domoticz[21346]: 2023-06-02 16:35:43.647 Error: Zigpy-Elelabs: from cryptography.exceptions import (
Jun 02 16:35:43 rasp domoticz[21346]: 2023-06-02 16:35:43.647 Error: Zigpy-Elelabs: File "/usr/local/lib/python3.10/site-packages/cryptography/exceptions.py", line 9, in <module>
Jun 02 16:35:43 rasp domoticz[21346]: 2023-06-02 16:35:43.647 Error: Zigpy-Elelabs: from cryptography.hazmat.bindings._rust import exceptions as rust_exceptions
Jun 02 16:35:43 rasp domoticz[21346]: 2023-06-02 16:35:43.647 Error: Zigpy-Elelabs: ImportError: PyO3 modules may only be initialized once per interpreter process

Re: PyO3 modules may only be initialized once per interpreter process[/quote]

Posted: Monday 05 June 2023 16:58
by pipiche
@dnpwwo, what do you think ? Should I create an Issue on the domoticz GitHub ?

Here is a quick way to test it

(1) install the latest cryptopgrahy python3 module

Code: Select all

sudo python3 -m pip install cryptography==41.0.01 --upgrade
(2) Install the python3 plugin to duplicate the problem (see after)
(3) restart domoticz
(4) add "Multiple Reload" plugin
(5) Use update to generate a stop/start and look after the domoticz log

You'll get this error message
Jun 05 16:54:27 rasp domoticz[15544]: 2023-06-05 16:54:27.956 Error: Test MultipleReload: (MultipleReload) failed to load 'plugin.py', Python Path used was '/var/lib/domoticz/plugins/MulitpleReload/:/usr/lib/python310.zip:/usr/lib/python3.10:/usr/lib/python3.10/lib-dynload:/usr/local/lib/python3.10/site-packages:/usr/lib/python3.10/site-packages'.
Jun 05 16:54:27 rasp domoticz[15544]: 2023-06-05 16:54:27.970 Error: Test MultipleReload: Traceback (most recent call last):
Jun 05 16:54:27 rasp domoticz[15544]: 2023-06-05 16:54:27.970 Error: Test MultipleReload: File "/var/lib/domoticz/plugins/MulitpleReload/plugin.py", line 17, in <module>
Jun 05 16:54:27 rasp domoticz[15544]: 2023-06-05 16:54:27.970 Error: Test MultipleReload: from cryptography.hazmat.primitives.ciphers import Cipher
Jun 05 16:54:27 rasp domoticz[15544]: 2023-06-05 16:54:27.970 Error: Test MultipleReload: File "/usr/local/lib/python3.10/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py", line 11, in <module>
Jun 05 16:54:27 rasp domoticz[15544]: 2023-06-05 16:54:27.970 Error: Test MultipleReload: from cryptography.hazmat.primitives.ciphers.base import (
Jun 05 16:54:27 rasp domoticz[15544]: 2023-06-05 16:54:27.970 Error: Test MultipleReload: File "/usr/local/lib/python3.10/site-packages/cryptography/hazmat/primitives/ciphers/base.py", line 10, in <module>
Jun 05 16:54:27 rasp domoticz[15544]: 2023-06-05 16:54:27.970 Error: Test MultipleReload: from cryptography.exceptions import (
Jun 05 16:54:27 rasp domoticz[15544]: 2023-06-05 16:54:27.970 Error: Test MultipleReload: File "/usr/local/lib/python3.10/site-packages/cryptography/exceptions.py", line 9, in <module>
Jun 05 16:54:27 rasp domoticz[15544]: 2023-06-05 16:54:27.970 Error: Test MultipleReload: from cryptography.hazmat.bindings._rust import exceptions as rust_exceptions
Jun 05 16:54:27 rasp domoticz[15544]: 2023-06-05 16:54:27.970 Error: Test MultipleReload: ImportError: PyO3 modules may only be initialized once per interpreter process
Here after is the plugin.py code.

Code: Select all

"""
<plugin key="MultipleReload" name="Multiple Reload" author="pipcihe" version="0.1" externallink="">
    <params>
        <param field="Mode5" label="Debug mode" width="250px">
            <options>
                <option label="Off" value="False" default="true"/>
                <option label="On" value="True"/>
            </options>
        </param>

    </params>
</plugin>
"""

import Domoticz

from cryptography.hazmat.primitives.ciphers import Cipher

class BasePlugin:

    def __init__(self):
        self.plugin_ready = False
        return

    def onStart(self):
        Domoticz.Debug('onStart called')
        if Parameters["Mode5"]=="True":
            Domoticz.Debugging(2)
            self.debug=True
        else:
            self.debug=False

    def onHeartbeat(self):
        Domoticz.Debug('onHeartbeat called')

    def onCommand(self, Unit, Command, Level, Hue):
        Domoticz.Debug('onCommand called')

    def onStop(self):
        Domoticz.Debug('onStop called')

global _plugin
_plugin = BasePlugin()

def onStart():
    global _plugin
    _plugin.onStart()

def onStop():
    global _plugin
    _plugin.onStop()

def onHeartbeat():
    global _plugin
    _plugin.onHeartbeat()

def onCommand(Unit, Command, Level, Hue):
    global _plugin
    _plugin.onCommand(Unit, Command, Level, Hue)

Re: PyO3 modules may only be initialized once per interpreter process[/quote]

Posted: Monday 05 June 2023 19:37
by pipiche
In fact I wonder why we do an Py_Initialize() everytime we restart a plugin.
Shall we not do the Py_Initialize() only the first time and then rely on it

So it is not needed to Py_FinalizeEx()

And probably save some memory leaks

Re: PyO3 modules may only be initialized once per interpreter process[/quote]

Posted: Thursday 08 June 2023 14:00
by zak45
got the same problem with the Broadlink plugin !

Re: PyO3 modules may only be initialized once per interpreter process[/quote]

Posted: Tuesday 13 June 2023 12:46
by pipiche
should we report it as an issue in Domoticz github ?

Re: PyO3 modules may only be initialized once per interpreter process[/quote]

Posted: Tuesday 13 June 2023 12:53
by pipiche
zak45 wrote: Thursday 08 June 2023 14:00 got the same problem with the Broadlink plugin !
https://github.com/domoticz/domoticz/issues/5717

Re: PyO3 modules may only be initialized once per interpreter process

Posted: Tuesday 13 June 2023 13:27
by gizmocuz
I think the idea is to totally remove the plugin from memory and start it again (will also flush memory/close connections, graceful sign-out etc)
This happens also with normal hardware.
So, by the looks of it, this is not happening. I wonder if it is still in memory when the hardware is being disabled/deleted.
Best to fire up visual studio, compile, set breakpoints and debug

Re: PyO3 modules may only be initialized once per interpreter process

Posted: Saturday 08 July 2023 0:12
by Dnpwwo
I suspect this is not solvable because it is an issue with the library and not Python (or the framework).

There is no 'un-import' function in Python to force a complete cleanup, I believe Python holds libraries in an internal cache even if nothing is referencing them anymore.

Importing them a second time should be fine but some libraries don't handle this properly because they hold values globally inside themselves (which seems to be the case here). A defect should be raised against the library itself.

Re: PyO3 modules may only be initialized once per interpreter process

Posted: Monday 10 July 2023 22:06
by JBN
This post is mainly for others that might run into the same problems that I experienced.

I made a plugin for Reolink cameras using reolink_aio and it requires asyncio. I managed to get that working using the pattern from @pipiche as described here viewtopic.php?p=288731 and solution https://github.com/zigbeefordomoticz/Do ... d639d1f84d.

However, I tried to figure out why I got "'NoneType' object is not callable" from reolink_aio when restarting the module (perhaps not the normal behavior but something I do when debugging the plugin). After some searching the NoneType error happens when reolink_aio calls datetime.strptime (as an example when getting the camera host and issue a new subscribe webhook). Thus, it seems that strptime also suffers from problematic caching and is reported here - https://github.com/python/cpython/issues/71587. No fix has been published as of yet.

I have a work-around in place, that seems to solve it, for datetime in the meanwhile:

Code: Select all

import sys
sys.modules["_datetime"] = None
import datetime

Re: PyO3 modules may only be initialized once per interpreter process

Posted: Monday 10 July 2023 22:07
by pipiche
By doing that, you are not using the c compiled version but the pure python version


Envoyé de mon iPhone en utilisant Tapatalk

Re: PyO3 modules may only be initialized once per interpreter process

Posted: Monday 10 July 2023 22:21
by JBN
Thanks for the comment and your work. I assume that primarly makes the plugin slower when using datetime functions. I might be able to change the upstream reolink_aio module so it works but at least now it seems work.

Re: PyO3 modules may only be initialized once per interpreter process

Posted: Saturday 15 July 2023 9:35
by pipiche
Dnpwwo wrote: Saturday 08 July 2023 0:12 I suspect this is not solvable because it is an issue with the library and not Python (or the framework).

There is no 'un-import' function in Python to force a complete cleanup, I believe Python holds libraries in an internal cache even if nothing is referencing them anymore.

Importing them a second time should be fine but some libraries don't handle this properly because they hold values globally inside themselves (which seems to be the case here). A defect should be raised against the library itself.
https://github.com/pyca/cryptography/issues/9016