Repository: ultrabug/py3status

Issue:
vnstat module
Requires program "vnstat" installed and configured properly.



Discussion:
Hey @ShadowPrince thanks for this new contribution !

I'm in a road trip far from home and it looks like I can't merge using the github app. I'll check again later.

Please bear with me :)

I'm back, thanks a lot for your patience and contribution !


Resolving patch:
diff --git a/py3status/__init__.py b/py3status/__init__.py
--- a/py3status/__init__.py
+++ b/py3status/__init__.py
@@ -28,21 +28,21 @@ def main():
 
     try:
         locale.setlocale(locale.LC_ALL, "")
-    except locale.Error:
-        print("No locale available")
+    except locale.Error as err:
+        print(f"No locale available ({err})")
         sys.exit(2)
 
     py3 = None
     try:
         py3 = Py3statusWrapper(options)
         py3.setup()
-    except (BrokenPipeError, KeyboardInterrupt):
+    except (BrokenPipeError, KeyboardInterrupt) as err:
         if py3:
-            py3.notify_user("Setup interrupted")
+            py3.notify_user(f"Setup interrupted ({err})")
         sys.exit(0)
-    except Exception:
+    except Exception as err:
         if py3:
-            py3.report_exception("Setup error")
+            py3.report_exception(f"Setup error ({err})")
         else:
             # we cannot report this Exception
             raise
@@ -52,8 +52,8 @@ def main():
         py3.run()
     except (BrokenPipeError, KeyboardInterrupt):
         pass
-    except Exception:
-        py3.report_exception("Runtime error")
+    except Exception as err:
+        py3.report_exception(f"Runtime error ({err})")
         sys.exit(3)
     finally:
         py3.stop()
diff --git a/py3status/core.py b/py3status/core.py
--- a/py3status/core.py
+++ b/py3status/core.py
@@ -6,7 +6,7 @@
 from json import dumps
 from pathlib import Path
 from pprint import pformat
-from signal import signal, SIGTERM, SIGUSR1, SIGTSTP, SIGCONT
+from signal import signal, Signals, SIGTERM, SIGUSR1, SIGTSTP, SIGCONT
 from subprocess import Popen
 from threading import Event, Thread
 from syslog import syslog, LOG_ERR, LOG_INFO, LOG_WARNING
@@ -260,6 +260,7 @@ def __init__(self, options):
         self.output_modules = {}
         self.py3_modules = []
         self.running = True
+        self.stop_signal = SIGTSTP
         self.update_queue = deque()
         self.update_request = Event()
 
@@ -527,14 +528,6 @@ def setup(self):
         Setup py3status and spawn i3status/events/modules threads.
         """
 
-        # SIGTSTP will be received from i3bar indicating that all output should
-        # stop and we should consider py3status suspended.  It is however
-        # important that any processes using i3 ipc should continue to receive
-        # those events otherwise it can lead to a stall in i3.
-        signal(SIGTSTP, self.i3bar_stop)
-        # SIGCONT indicates output should be resumed.
-        signal(SIGCONT, self.i3bar_start)
-
         # log py3status and python versions
         self.log("=" * 8)
         msg = "Starting py3status version {version} python {python_version}"
@@ -641,6 +634,35 @@ def setup(self):
             sys.stdout = Path("/dev/null").open("w")
             sys.stderr = Path("/dev/null").open("w")
 
+        # make sure we honor custom i3bar protocol stop/resume signals
+        # while providing users a way to opt out from that feature
+        # using the 0 value as specified by the i3bar protocol
+        custom_stop_signal = (
+            self.config["py3_config"].get("py3status", {}).get("stop_signal")
+        )
+        if custom_stop_signal is not None:
+            try:
+                # 0 is a special value for i3bar protocol, use it as-is
+                if custom_stop_signal == 0:
+                    self.stop_signal = custom_stop_signal
+                else:
+                    self.stop_signal = Signals(custom_stop_signal)
+            except ValueError:
+                error = (
+                    f"py3status.stop_signal '{custom_stop_signal}' is invalid "
+                    f"and should be a number between 0 (disable) and 31"
+                )
+                self.log(error, level="error")
+                raise Exception(error)
+
+        # SIGTSTP can be received and indicates that all output should
+        # stop and we should consider py3status suspended.  It is however
+        # important that any processes using i3 ipc should continue to receive
+        # those events otherwise it can lead to a stall in i3.
+        signal(SIGTSTP, self.i3bar_stop)
+        # SIGCONT indicates output should be resumed.
+        signal(SIGCONT, self.i3bar_start)
+
         # get the list of py3status configured modules
         self.py3_modules = self.config["py3_config"]["py3_modules"]
 
@@ -952,16 +974,16 @@ def process_module_output(self, module):
 
     def i3bar_stop(self, signum, frame):
         if time.time() - self.i3bar_inhibit_stp > 1:
-            self.log("received SIGTSTP")
+            self.log(f"received stop_signal {Signals(signum).name}")
             self.i3bar_running = False
             # i3status should be stopped
             self.i3status_thread.suspend_i3status()
             self.sleep_modules()
         else:
-            self.log("received inhibited SIGTSTP")
+            self.log(f"inhibited stop_signal {Signals(signum).name}")
 
     def i3bar_start(self, signum, frame):
-        self.log("received SIGCONT")
+        self.log(f"received resume signal {Signals(signum).name}")
         self.i3bar_inhibit_stp = time.time()
         self.i3bar_running = True
         self.wake_modules()
@@ -1016,7 +1038,7 @@ def run(self):
         header = {
             "version": 1,
             "click_events": self.config["click_events"],
-            "stop_signal": SIGTSTP,
+            "stop_signal": self.stop_signal or 0,
         }
         write(dumps(header))
         write("\n[[]\n")


Test patch: