0

Regardless of the fact that I use Kivy and Python3, I am trying to raise an exception in one process and catch it in another. The process Trigger is the one raising the exception, The processes 1 and 2 are catching it. I took a look at the solution using pipe but in my case the processes that are catching can execute different functions, sub-processes and so on. So I gave a try with multiprocessing.Queue(). So far I can raise the exception in Trigger but I can not pass it over to Process2 or Process1. The script ends with no errors at all, seems like there is no communication between the processes. Where is the error?

The code:

#!/usr/bin/python3 python3
# -*- coding: utf-8 -*-

# common modules
import time, os
import signal
import multiprocessing
from multiprocessing import Process
import queue
import subprocess

# set environment variables to avoid "segmentation error" due to raspbian version
os.environ["KIVY_WINDOW"] = "sdl2"
os.environ["KIVY_GL_BACKEND"] = "sdl2"
os.environ["KIVY_IMAGE"] = "sdl2"

from kivy.core.window import Window
Window.show_cursor = True

# kivy modules first, if not Kivy may cause problems
import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager, Screen
kivy.require('2.0.0')

def signal_handler(signal, frame):
    exit(0)

def Trigger(q):
    try:
        for y in range(6):
            print("trigger raise in", y)
            time.sleep(1) # debounce for 100 ms
        raise Exception("trigger on ------------")
    except Exception as e:
        print("exception in trigger:", e)
        e = q.put(e)

def Process1(q):
    # run ref search
    try:
        while True:
            print("process 1")
            time.sleep(1)
    except Exception as e:
        e = q.get()
        print("Function1 exception:", e)
        raise e

# main screen
class MainScreen(Screen):
    def __init__(self, **kwargs):
        self.name="MAIN SCREEN"
        super(Screen, self).__init__(**kwargs)
    
# main app class 
class Process2(App):
    def Function1(self):
        # run ref search
        try:
            for x in range(10):
                print("range 30", x)
                time.sleep(1)
        except Exception as e:
            e = q.get()
            print("exception in function 1:", e)
            raise e

    def Function2(self):
        # run ref search
        try:
            while True:
                print("endless")
                time.sleep(1)
        except Exception as e:
            e = q.get()
            print("exception in function 2:", e)
            raise e

    def build(self):
        sm = Builder.load_string("""
    
ScreenManager:
    canvas:
        Color:
            rgb: 1, 1, 1
        Rectangle:
            size: self.size
    MainScreen:
        name: "MainScreen"
        size_hint: 1, 1
        auto_dismiss: True
        # auto_dismiss: False   # original line
        title: "title"       
        title_align: "center"
        BoxLayout:
            orientation: "vertical"
            spacing: 10
            BoxLayout:
                orientation: "horizontal"
                spacing: 5
                size_hint: 1, .5
                Button:
                    font_size: 30
                    text: "button 1"
                Button: 
                    font_size: 30
                    text: "button 2"
            BoxLayout:
                orientation: "horizontal"
                spacing: 10
                size_hint: 1, .5
""")

        self.Function1()
        self.Function2()


# main ##################################################
if __name__ == '__main__':
    
    #CTRL+C signal handler
    signal.signal(signal.SIGINT, signal_handler)
    signal.signal(signal.SIGTERM, signal_handler)
    
    q = multiprocessing.Queue()
        
    global p1, p2, p0

    # lauch trigger to raise exception
    p0 = Process(target=Trigger, args=(q,))
    p0.daemon = True
    p0.start()
    #launch process 1
    p1 = Process(target=Process1, args=(q,))
    p1.daemon = True
    p1.start()
    #launch Kivy
    p2 = Process(target=Process2().run(), args=(q,))
    p2.start()
    p2.join()
parovelb
  • 353
  • 4
  • 19
  • I would like to add the `Clock.schedule_interval()` of Kivy is not useful for scheduling functions with `while / true` or other loops as a base as the GIL simply cuts off the rest of the executing script. – parovelb Apr 02 '21 at 07:29
  • Does this answer your question? [How to pass stacktrace between processes in Python?](https://stackoverflow.com/questions/56481100/how-to-pass-stacktrace-between-processes-in-python) – Darkonaut Apr 02 '21 at 21:04
  • @Darkonaut unfortunately it does not. You see the triggering condition created in the process `def Trigger(q)` must be able to generate such event that the processes `class Process2(App)` and `def.Process1(q)` are able to detect/catch in any situation. Therefore I guess the try/except wrap should do the job. – parovelb Apr 03 '21 at 08:26
  • Within `Trigger()` you call `e = q.put()` instead of `q.put(e)`, so you're not sending anything. The exception you try to propagate originates from another stack (every thread has its own), so it won't just throw up in another process when you get it from the queue. You need to re-raise it yourself. `p2 = Process(target=Process2().run(), args=(q,))` also looks wrong, since you pass the _result_ from `Process2().run()` as a target-function. – Darkonaut Apr 03 '21 at 09:40
  • Although it is not good news for me, thank you @Darkonaut for the useful notion. I did not know I have to re-rise the exception from another stack, I assumed it get caught once passed in a queue. This puts me back at the beginning of the problem on how to trigger an event that could be detected over all the script without constatly checking the queue. – parovelb Apr 05 '21 at 04:22

0 Answers0