From 5534a46c5542dcd6e92b6adadf37a9344d52e285 Mon Sep 17 00:00:00 2001 From: Ben Kurtovic Date: Sun, 4 Oct 2015 03:54:02 -0500 Subject: [PATCH] Add an unload hook for commands and tasks. --- CHANGELOG | 2 ++ README.rst | 6 +++--- docs/customizing.rst | 16 +++++++++++++--- earwigbot/commands/__init__.py | 7 +++++++ earwigbot/managers.py | 15 ++++++++++++++- earwigbot/tasks/__init__.py | 7 +++++++ 6 files changed, 46 insertions(+), 7 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index fec7e5e..f05df7c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,8 @@ v0.2 (unreleased): - Added a new command syntax allowing the caller to redirect replies to another user. Example: "!dictionary >Fred earwig" +- Added unload() hooks to commands and tasks, called when they are killed + during a reload. - Added 'rc' hook type to allow IRC commands to respond to RC watcher events. - Added 'part' hook type as a counterpart to 'join'. - Added !stalk/!watch. diff --git a/README.rst b/README.rst index b8a1324..f0232be 100644 --- a/README.rst +++ b/README.rst @@ -133,8 +133,8 @@ Custom IRC commands ~~~~~~~~~~~~~~~~~~~ Custom commands are subclasses of `earwigbot.commands.Command`_ that override -``Command``'s ``process()`` (and optionally ``check()`` or ``setup()``) -methods. +``Command``'s ``process()`` (and optionally ``check()``, ``setup()``, or +``unload()``) methods. The bot has a wide selection of built-in commands and plugins to act as sample code and/or to give ideas. Start with test_, and then check out chanops_ and @@ -144,7 +144,7 @@ Custom bot tasks ~~~~~~~~~~~~~~~~ Custom tasks are subclasses of `earwigbot.tasks.Task`_ that override ``Task``'s -``run()`` (and optionally ``setup()``) methods. +``run()`` (and optionally ``setup()`` or ``unload()``) methods. See the built-in wikiproject_tagger_ task for a relatively straightforward task, or the afc_statistics_ plugin for a more complicated one. diff --git a/docs/customizing.rst b/docs/customizing.rst index a0c2509..ed5e6f3 100644 --- a/docs/customizing.rst +++ b/docs/customizing.rst @@ -86,8 +86,9 @@ Custom IRC commands Custom commands are subclasses of :py:class:`earwigbot.commands.Command` that override :py:class:`~earwigbot.commands.Command`'s :py:meth:`~earwigbot.commands.Command.process` (and optionally -:py:meth:`~earwigbot.commands.Command.check` or -:py:meth:`~earwigbot.commands.Command.setup`) methods. +:py:meth:`~earwigbot.commands.Command.check`, +:py:meth:`~earwigbot.commands.Command.setup`, or +:py:meth:`~earwigbot.commands.Command.unload`) methods. :py:class:`~earwigbot.commands.Command`'s docstrings should explain what each attribute and method is for and what they should be overridden with, but these @@ -154,6 +155,10 @@ are the basics: `, and :py:meth:`part(chan) `. +- Method :py:meth:`~earwigbot.commands.Command.unload` is called *once* with no + arguments immediately before the command is unloaded, such as when someone + uses ``!reload``. Does nothing by default. + Commands have access to :py:attr:`config.commands[command_name]` for config information, which is a node in :file:`config.yml` like every other attribute of :py:attr:`bot.config`. This can be used to store, for example, API keys or @@ -175,7 +180,8 @@ Custom bot tasks Custom tasks are subclasses of :py:class:`earwigbot.tasks.Task` that override :py:class:`~earwigbot.tasks.Task`'s :py:meth:`~earwigbot.tasks.Task.run` (and optionally -:py:meth:`~earwigbot.tasks.Task.setup`) methods. +:py:meth:`~earwigbot.tasks.Task.setup` or +:py:meth:`~earwigbot.tasks.Task.unload`) methods. :py:class:`~earwigbot.tasks.Task`'s docstrings should explain what each attribute and method is for and what they should be overridden with, but these @@ -220,6 +226,10 @@ are the basics: the task's code goes. For interfacing with MediaWiki sites, read up on the :doc:`Wiki Toolset `. +- Method :py:meth:`~earwigbot.tasks.Task.unload` is called *once* with no + arguments immediately before the task is unloaded. Does nothing by + default. + Tasks have access to :py:attr:`config.tasks[task_name]` for config information, which is a node in :file:`config.yml` like every other attribute of :py:attr:`bot.config`. This can be used to store, for example, edit summaries diff --git a/earwigbot/commands/__init__.py b/earwigbot/commands/__init__.py index 5cd7549..67ba719 100644 --- a/earwigbot/commands/__init__.py +++ b/earwigbot/commands/__init__.py @@ -120,3 +120,10 @@ class Command(object): command's body here. """ pass + + def unload(self): + """Hook called immediately before a command is unloaded. + + Does nothing by default; feel free to override. + """ + pass diff --git a/earwigbot/managers.py b/earwigbot/managers.py index 0bdeb1b..7605139 100644 --- a/earwigbot/managers.py +++ b/earwigbot/managers.py @@ -137,6 +137,19 @@ class _ResourceManager(object): self._load_module(modname, dir) processed.append(modname) + def _unload_resources(self): + """Unload all resources, calling their unload hooks in the process.""" + res_type = self._resource_name[:-1] # e.g. "command" or "task" + for resource in self: + if not hasattr(resource, "unload"): + continue + try: + resource.unload() + except Exception: + e = "Error unloading {0} '{1}'" + self.logger.exception(e.format(res_type, resource.name)) + self._resources.clear() + @property def lock(self): """The resource access/modify lock.""" @@ -146,7 +159,7 @@ class _ResourceManager(object): """Load (or reload) all valid resources into :py:attr:`_resources`.""" name = self._resource_name # e.g. "commands" or "tasks" with self.lock: - self._resources.clear() + self._unload_resources() builtin_dir = path.join(path.dirname(__file__), name) plugins_dir = path.join(self.bot.config.root_dir, name) if getattr(self.bot.config, name).get("disable") is True: diff --git a/earwigbot/tasks/__init__.py b/earwigbot/tasks/__init__.py index 5116f49..48f2d16 100644 --- a/earwigbot/tasks/__init__.py +++ b/earwigbot/tasks/__init__.py @@ -84,6 +84,13 @@ class Task(object): """ pass + def unload(self): + """Hook called immediately before the task is unloaded. + + Does nothing by default; feel free to override. + """ + pass + def make_summary(self, comment): """Make an edit summary by filling in variables in a config value.