Files
XtendR/xtendr/xtendrbase.py
T
2026-07-27 20:54:00 +02:00

60 lines
2.0 KiB
Python

from abc import ABC, abstractmethod
class XtendRBase(ABC):
"""Abstract base class for all plugins.
Lifecycle: pre_load() -> run() -> stop() -> unload(). XtendRSystem calls
unload() exactly once, right before it drops its own references to the
plugin instance and removes its module from sys.modules. This is the
plugin's only chance to release anything it holds that XtendRSystem
doesn't know about and can't clean up on its own, for example:
- GObject/GTK signal handler ids from .connect() (disconnect them, or
the GObject side keeps a reference to the bound method, which keeps
the plugin instance -- and everything it references -- alive).
- Widgets the plugin inserted into the app's widget tree (remove them
from their parent; a parented widget is kept alive by GTK regardless
of what Python does with its own references).
- GLib.timeout_add / idle_add source ids (GLib.source_remove them).
- Any threads it started that aren't daemon threads, or open files/
sockets/subprocesses.
unload() has a default no-op implementation so existing plugins that
don't hold any such resources keep working unchanged; override it only
when there's something to release.
Example:
>>> class TestPlugin(XtendRBase):
... def run(self):
... print("Running TestPlugin")
... def stop(self):
... print("Stopping TestPlugin")
... def unload(self):
... print("Unloading TestPlugin")
>>> plugin = TestPlugin()
>>> plugin.run()
Running TestPlugin
>>> plugin.stop()
Stopping TestPlugin
>>> plugin.unload()
Unloading TestPlugin
"""
@abstractmethod
def run(self, *args, **kwargs):
pass
@abstractmethod
def stop(self):
pass
@abstractmethod
def pre_load(self, *args):
pass
def unload(self):
"""Release any resources the plugin holds. Called once by
XtendRSystem.detach(), after stop(). Optional to override;
default is a no-op."""
pass