Page 1 of 1

This module can only be loaded into one interpreter per process

Posted: Monday 13 June 2022 22:29
by wizzard72
I'm in a process of creating a plugin for Unifi Protect. There is a python library called pyunifiprotect (https://github.com/briis/pyunifiprotect) and is used by Home Assistant. When I create a simple plugin it always fails on the importing part.

Code: Select all

# Unifi-Protect plugin
#
# Author: Wizzard72
# Versions:
#   0.0.1: First ...
"""
<plugin key="UnifiProtect" name="Unifi Protect" author="Wizzard72" version="0.0.1" wikilink="">
    <description>
        <h2>Unifi Protect plugin</h2><br/>
    </description>
    <params>
        <param field="Address" label="IP Address / DNS name of the Unifi Controller" width="200px" required="true" default="127.0.0.1"/>
        <param field="Port" label="Port" width="30px" required="true" default="8443"/>
        <param field="Username" label="Username" width="200px" required="true" default="[email protected]"/>
        <param field="Password" label="Password" width="600px" required="true" default="password" password="true"/>
        <param field="Mode1" label="Site Name" width="200px" required="true" default="default"/>
        <param field="Mode6" label="Debug" width="75px">
            <options>
                <option label="None" value="0"  default="true" />
                <option label="Python Only" value="2"/>
                <option label="Basic Debugging" value="62"/>
                <option label="Basic+Messages" value="126"/>
                <option label="Connections Only" value="16"/>
                <option label="Connections+Queue" value="144"/>
                <option label="All" value="-1"/>
            </options>
        </param>
    </params>
</plugin>
"""

import os
import sys

module_paths = [x[0] for x in os.walk( os.path.join(os.path.dirname(__file__), '.', '.env/lib/') ) if x[0].endswith('site-packages') ]
for mp in module_paths:
    sys.path.append(mp)

import Domoticz
# For documentation: sudo pip3 install asyncio
# For documentation: sudo pip3 install pyunifiprotect
import queue
import threading
import time
import sys
import asyncio
from pyunifiprotect import ProtectApiClient

class BasePlugin:
    _versionCheck = None
    _ProtectConn = None

    def __init__(self):
        return

    def onStart(self):
        strName = "onStart: "
        Domoticz.Debug(strName+"called")

        if (Parameters["Mode6"] != "0"):
            Domoticz.Debugging(int(Parameters["Mode6"]))
        else:
            Domoticz.Debugging(0)

        # check if version of domoticz is 2020.2 or higher
        try:
            if int(Parameters["DomoticzVersion"].split('.')[0]) < 2020:  # check domoticz major version
                Domoticz.Error(
                    "Domoticz version required by this plugin is 2020.2 (you are running version {}).".format(
                        Parameters["DomoticzVersion"]))
                Domoticz.Error("Plugin is therefore disabled")
                self.setVersionCheck(False, "onStart")
            else:
                self.setVersionCheck(True, "onStart")
        except Exception as err:
            Domoticz.Error("Domoticz version check returned an error: {}. Plugin is therefore disabled".format(err))
            self.setVersionCheck(False, "onStart")
        if not self._versionCheck:
            return

        self.login()
        Domoticz.Heartbeat(5)

    def onStop(self):
        strName = "onStop: "
        Domoticz.Debug(strName+"Pluggin is stopping.")

    def onConnect(self, Connection, Status, Description):
        strName = "onConnect: "
        Domoticz.Debug(strName+"called")
        Domoticz.Debug(strName+"Connection = "+str(Connection))
        Domoticz.Debug(strName+"Status = "+str(Status))
        Domoticz.Debug(strName+"Description = "+str(Description))
    def onMessage(self, Connection, Data):
        strName = "onMessage: "
        Domoticz.Debug(strName+"called")
        DumpHTTPResponseToLog(Data)
        Domoticz.Debug(strName+"Data = " +str(Data))
        strData = Data["Data"].decode("utf-8", "ignore")
        status = int(Data["Status"])

    def onCommand(self, Unit, Command, Level, Hue):
        strName = "onCommand: "
        Domoticz.Log(strName+"called for Unit " + str(Unit) + ": Parameter '" + str(Command) + "', Level: " + str(Level))

    def onNotification(self, Name, Subject, Text, Status, Priority, Sound, ImageFile):
        strName = "onNotification: "
        Domoticz.Debug(strName+"called")
        Domoticz.Log(strName+"Notification: " + Name + "," + Subject + "," + Text + "," + Status + "," + str(Priority) + "," + Sound + "," + ImageFile)

    def onDisconnect(self, Connection):
        strName = "onDisconnect: "
        Domoticz.Debug(strName+"called")

    def onHeartbeat(self):
        strName = "onHeartbeat: "
        Domoticz.Debug(strName+"called")

    def setVersionCheck(self, value, note):
        strName = "setVersionCheck - "
        if value is True:
            if self._versionCheck is not False:
                self._versionCheck = True
                Domoticz.Log(strName+"Plugin allowed to start (triggered by: "+note+")")
        elif value is False:
            self._versionCheck = False
            Domoticz.Error(strName+"Plugin NOT allowed to start (triggered by: "+note+")")

    def login(self):
        strName = "login: "
        _protectConn = ProtectApiClient(Parameters["Address"], Parameters["Port"], Parameters["Username"], Parameters["Password"], verify_ssl=True)
        await protectConn.update()


global _plugin
_plugin = BasePlugin()

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

def onConnect(Connection, Status, Description):
    global _plugin
    _plugin.onConnect(Connection, Status, Description)

def onMessage(Connection, Data):
    global _plugin
    _plugin.onMessage(Connection, Data)

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

def onNotification(Name, Subject, Text, Status, Priority, Sound, ImageFile):
    global _plugin
    _plugin.onNotification(Name, Subject, Text, Status, Priority, Sound, ImageFile)

def onDisconnect(Connection):
    global _plugin
    _plugin.onDisconnect(Connection)

def onHeartbeat():
    global _plugin
    _plugin.onHeartbeat()
It always fails on: from pyunifiprotect import ProtectApiClient

Error in Domoticz Log:

Code: Select all

2022-06-13 22:27:16.048 Status: Protect: Started.
2022-06-13 22:27:16.048 Status: Protect: Entering work loop.
2022-06-13 22:27:16.572 Error: Protect: (UnifiProtect) failed to load 'plugin.py', Python Path used was '/home/paul/domoticz/plugins/Domoticz-Unifi-Protect/:/usr/lib/python39.zip:/usr/lib/python3.9:/usr/lib/python3.9/lib-dynload:/usr/local/lib/python3.9/dist-packages:/usr/lib/python3/dist-packages:/usr/lib/python3.9/dist-packages'.
2022-06-13 22:27:16.576 Error: Protect: Traceback (most recent call last):
2022-06-13 22:27:16.576 Error: Protect: File "/home/paul/domoticz/plugins/Domoticz-Unifi-Protect/plugin.py", line 47, in <module>
2022-06-13 22:27:16.576 Error: Protect: from pyunifiprotect import ProtectApiClient
2022-06-13 22:27:16.576 Error: Protect: File "/usr/local/lib/python3.9/dist-packages/pyunifiprotect/__init__.py", line 2, in <module>
2022-06-13 22:27:16.576 Error: Protect: from pyunifiprotect.api import ProtectApiClient
2022-06-13 22:27:16.576 Error: Protect: File "/usr/local/lib/python3.9/dist-packages/pyunifiprotect/api.py", line 21, in <module>
2022-06-13 22:27:16.576 Error: Protect: from pyunifiprotect.data import (
2022-06-13 22:27:16.576 Error: Protect: File "/usr/local/lib/python3.9/dist-packages/pyunifiprotect/data/__init__.py", line 1, in <module>
2022-06-13 22:27:16.576 Error: Protect: from pyunifiprotect.data.base import (
2022-06-13 22:27:16.576 Error: Protect: File "/usr/local/lib/python3.9/dist-packages/pyunifiprotect/data/base.py", line 21, in <module>
2022-06-13 22:27:16.576 Error: Protect: from pydantic import BaseModel
2022-06-13 22:27:16.576 Error: Protect: ImportError: Interpreter change detected - this module can only be loaded into one interpreter per process.
Does anyone have an idea how to solve this?

Re: This module can only be loaded into one interpreter per process

Posted: Wednesday 29 March 2023 18:27
by Hansbit
Same error here. Wrote a small test plugin to reproduce. Starting Domoticz and/or starting plugin works ok, but when enabling/disabling the plugin (or press the 'update' button in Hardware section )is keeps giving the error:

Code: Select all

2023-03-29 18:11:10.071 Error: Test: ImportError: Interpreter change detected - this module can only be loaded into one interpreter per process.
Ok, the error explains well; the module can only be loaded once, but why terminate with error? Is there a way to determine if the module is loaded and skip my re-loading? Will the 1st loaded module be accessible from the restarted plugin?

Sample code to reproduce the error:

Code: Select all

# Sample plugin which reproduces the error "ImportError: Interpreter change detected - this module can only be loaded into one interpreter per process." occurs when restarting (enable / disable or press 'update') this plugin.
# 
# Author: H4nsie

"""
<plugin key="TEST" name="Test for lib" author="H4nsie" version="0.0.0" wikilink="http://www.domoticz.com/" externallink="https://github.com/">
    <description>
        <h2>TEST PLUGIN</h2><br/>
        <ul style="list-style-type:square">
            <li>Just a test</li>
         </ul>
    </description>    
</plugin>
"""

import Domoticz
from lxml import objectify
Results in:
2023-03-29 18:11:09.869 Status: Test: Stop directive received.
2023-03-29 18:11:09.884 Status: Test: Exiting work loop.
2023-03-29 18:11:09.969 Status: Test: Stopping threads.
2023-03-29 18:11:09.969 Status: Test: Stopped.
2023-03-29 18:11:09.969 Status: Test: Entering work loop.
2023-03-29 18:11:09.969 Status: Test: Started.
2023-03-29 18:11:10.045 Error: Test: (TEST) failed to load 'plugin.py', Python Path used was '/opt/domoticz/userdata/plugins/TEST/:/usr/lib/python37.zip:/usr/lib/python3.7:/usr/lib/python3.7/lib-dynload:/usr/local/lib/python3.7/dist-packages:/usr/lib/python3/dist-packages:/usr/lib/python3.7/dist-packages'.
2023-03-29 18:11:10.071 Error: Test: Traceback (most recent call last):
2023-03-29 18:11:10.071 Error: Test: File "/opt/domoticz/userdata/plugins/TEST/plugin.py", line 17, in <module>
2023-03-29 18:11:10.071 Error: Test: from lxml import objectify
2023-03-29 18:11:10.071 Error: Test: ImportError: Interpreter change detected - this module can only be loaded into one interpreter per process.

Anybody knows?

Re: This module can only be loaded into one interpreter per process

Posted: Tuesday 30 January 2024 19:13
by kimhav
How did it go this plugin - was the issue ever solved? While it would be nice to get snapshots from the unifi cams it would be more of a primary interest to be able to collect alerts when the unifi protect alerts of 'intruders'.

Re: This module can only be loaded into one interpreter per process

Posted: Tuesday 30 January 2024 20:02
by waltervl
The original issue "This module can only be loaded into one interpreter per process" seems to be happening for some python modules and is discussed in this GitHub issue in more detail:
https://github.com/domoticz/domoticz/issues/5717