paul-nameless / pyfswatch

Python binding to libfswatch
MIT License
10 stars 9 forks source link

signal only works in main thread of the main interpreter #4

Open finnito opened 3 years ago

finnito commented 3 years ago

Kia ora!

Thanks for the great wrapper. I am attempting to use fswatch and tkinter to monitor the filesystem and display a GUI of changes. The trickiness is that monitor.start() and window.mainloop() (from TK) are both blocking calls. The TK mainloop is also not threadsafe, and must be run from the main thread. Therefore, I am trying to use fswatch from a thread.

I am trying to do essentially this, where the boot()function calls monitor.start(), but it then crashes, raising the following exception.

def main():
    # This starts the fswatch monitor
    control_thread = Thread(target=boot, daemon=True)
    control_thread.start()

    # This starts the TK loop
    window.after(0, check_queue)
    window.mainloop()

def boot():
    monitor = Monitor()
    monitor.set_recursive()

    for directory in DIRECTORIES:
        monitor.add_path(directory["path"])

    monitor.set_callback(callback)
    monitor.start()

I'm not super good at this stuff, so any help would be appreciated!

Exception in thread Thread-1:
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/threading.py", line 954, in _bootstrap_inner
    self.run()
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/threading.py", line 892, in run
    self._target(*self._args, **self._kwargs)
  File "/Users/finnlesueur/Git/tooDo/tooDo.py", line 152, in boot
    monitor.start()
  File "/Users/finnlesueur/Git/tooDo/venv/lib/python3.9/site-packages/fswatch/fswatch.py", line 49, in start
    signal.signal(signal.SIGINT, self._handle_signal)
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/signal.py", line 47, in signal
    handler = _signal.signal(_enum_to_int(signalnum), _enum_to_int(handler))
ValueError: signal only works in main thread of the main interpreter
Feuermurmel commented 3 years ago

I have not yet run into this problem myself, but looking at the implementation of Monitor.start(), I think the only way to solve this problem is to remove the call to signal() completely. IMHO, there is no reason to set up a signal handler there. A monitor can be stopped by calling fsw_stop_monitor() from another thread so the signal handler (or an appropriate except KeyboardInterrupt:) can be installed on the main thread.

Edit:

I also ran into the problem. I'm currently using this subclass of Monitor, to solve the problem:

class _Monitor(fswatch.Monitor):
    def start(self):
        fswatch.libfswatch.fsw_start_monitor(self.handle)