-
Notifications
You must be signed in to change notification settings - Fork 20
Description
Would a PR containing a listener class be acceptable? This would not have to remove old functionality, it could be purely additive.
Why?
- In my opinion, the most important reason, is allowing guaranteed cleanup. Consider a listener which uses
subprocess.POpen. If a user were to force quit (for example aSIGQUITon linux) the process, or a native component of a GUI library were to segfault, python may not clean up the child process it was listening to. Proving an API that promises to do so would be greatly appreciated. For example, a user could add a segfault handler to kill child subprocesses beforeos._exit(1). Right now I'm using a somewhat reimplemented version ofdarkdetectdue to the lack of this functionality. - Right now, the listeners are uninterruptible. For some listeners, like the Windows listener, this may make sense, but for others that poll processes, it is avoidable as these processes can be killed. In this case,
NotImpelementedError()s are acceptable. - Allowing subclassing will allow editing of functionality
Possible API + Example
class BaseListener:
def __init__(self, callback):
self.set_callback(callback)
def set_callback(self, callback):
self._callback = callback
def listen(self):
raise NotImpelementedError()
def stop(self):
raise NotImpelementedError()In _linux_detect.py:
class Listener(BaseListener):
def __init__(self, callback):
super().__init__(callback)
self._proc = None
def listen(self):
self._proc = subprocess.Popen(
('gsettings', 'monitor', 'org.gnome.desktop.interface', 'gtk-theme'),
stdout=subprocess.PIPE,
universal_newlines=True,
)
with self._proc:
for line in p.stdout:
self._callback('Dark' if '-dark' in line.strip().removeprefix("gtk-theme: '").removesuffix("'").lower() else 'Light')
def stop(self):
if self._proc is not None:
self._proc.kill()
self._proc.wait()
self._proc = None
# For those who want to use the old API
def listener(callback):
Listener(callback).listen()This would allow users to ensure that cleanup has happened properly, interrupt a listener, or subclass to add / edit functionality. For listeners that subclass, really the important bit is having the subprocess saved into something that users can kill if the need should arise, but this class struct has other benefits as mentioned above.
Possible Reasons to Subclass
These are other possible benefits of allowing subclassing:
- A user wants to use an alternative listener for a specific OS. Perhaps their linux distro doesn't use gnome; but they still want the macOS and Windows listeners to work as is.
- A user wants to edit the windows listener so that interrupts are possible, they can override the listen function and perhaps poll once per second if they desire
- They want to easily swap out the callback function for some reason; they can use the base classes'
set_callback, or have it set via a subclass method - A user may want to subclass
Listenerand their own class as part of a mixin design pattern or some other pattern.
In the examples above, darkdetect still only provides an API that users can use, so unlike in #29 we don't need to do threading stuff. We simply provide a listener object for the user to use; and to support the existing API we simply just have the standard listener(callback) function invoke Listener(callback).listen().
If I were to make a PR to add such functionality for each supported OS, would it be considered or rejected?