This module can only be loaded into one interpreter per process

Python and python framework

Moderator: leecollings

Post Reply
wizzard72
Posts: 116
Joined: Friday 20 December 2013 7:45
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

This module can only be loaded into one interpreter per process

Post 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?
Hansbit
Posts: 36
Joined: Monday 17 February 2014 15:10
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

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

Post 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?
kimhav
Posts: 154
Joined: Tuesday 01 October 2013 8:31
Target OS: Raspberry Pi / ODroid
Domoticz version: 2023.2
Location: Sweden
Contact:

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

Post 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'.
User avatar
waltervl
Posts: 5735
Joined: Monday 28 January 2019 18:48
Target OS: Linux
Domoticz version: 2024.7
Location: NL
Contact:

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

Post 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
Domoticz running on Udoo X86 (on Ubuntu)
Devices/plugins: ZigbeeforDomoticz (with Xiaomi, Ikea, Tuya devices), Nefit Easy, Midea Airco, Omnik Solar, Goodwe Solar
Post Reply

Who is online

Users browsing this forum: Google [Bot] and 1 guest