From 7651fe616da8949c9feed3a4871119b7b4142dd3 Mon Sep 17 00:00:00 2001 From: Adam Sampson Date: Tue, 21 Dec 2004 18:44:47 +0000 Subject: [PATCH] Basic plugins support. --- MANIFEST.in | 1 + NEWS | 3 +++ PLUGINS | 41 ++++++++++++++++++++++++++++ config | 5 ++++ rawdoglib/plugins.py | 63 ++++++++++++++++++++++++++++++++++++++++++++ rawdoglib/rawdog.py | 8 +++++- 6 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 PLUGINS create mode 100644 rawdoglib/plugins.py diff --git a/MANIFEST.in b/MANIFEST.in index 6f29a25..1dfd445 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,5 @@ include README +include PLUGINS include NEWS include COPYING include COPYING.LGPL diff --git a/NEWS b/NEWS index f39b5ff..6dcb7ab 100644 --- a/NEWS +++ b/NEWS @@ -26,6 +26,9 @@ as ISO-8859-1 (in case it just contains non-UTF-8 junk). Don't print the "state file may be corrupt" error if the user hits Ctrl-C while rawdog's loading it. +Add support for extending rawdog with plugin modules; see the "PLUGINS" +file for more information. + - rawdog 2.1 Fix a character encoding problem with format=text feeds. diff --git a/PLUGINS b/PLUGINS new file mode 100644 index 0000000..34d7581 --- /dev/null +++ b/PLUGINS @@ -0,0 +1,41 @@ +# Writing rawdog plugins + +## Introduction + +As provided, rawdog provides a fairly small set of features. In order to +make it do more complex jobs, rawdog can be extended using plugin +modules written in Python. This document is intended for developers who +want to extend rawdog by writing plugins. + +Extensions work by registering hook functions which are called by +various bits of rawdog's core as it runs. These functions can modify +rawdog's internal state in various interesting ways. An arbitrary number +of functions can be attached to each hook; they are called in the order +they were attached. Hook functions take various arguments depending on +where they're called from, and returns a boolean value indicating +whether further functions attached to the same hook should be called. + +The "plugindirs" config option gives a list of directories to search for +plugins; all Python modules found in those directories will be loaded by +rawdog. In practice, this means that you need to call your file +something ending in ".py" to have it recognised as a plugin. + +## Hooks + +Most hook functions are called with "rawdog" and "config" as their first +two arguments; these are references to the aggregator's Rawdog and +Config objects. + +If you need a hook that doesn't currently exist, please contact me. + +The following hooks are supported: + +### startup(rawdog, config) + +Run when rawdog starts up, after the state file and config file have +been loaded, but before rawdog starts processing command-line arguments. + +### shutdown(rawdog, config) + +Run just before rawdog saves the state file and exits. + diff --git a/config b/config index c7dc90f..22929fb 100644 --- a/config +++ b/config @@ -10,6 +10,11 @@ # assume minutes. # Boolean (yes/no) values in this file are specified as "true" or "false". +# rawdog can be extended using plugin modules written in Python. This +# option specifies the directories to search for plugins to load. If a +# directory does not exist or cannot be read, it will be ignored. +plugindirs plugins + # The maximum number of articles to show on the generated page. # Set this to 0 for no limit. maxarticles 200 diff --git a/rawdoglib/plugins.py b/rawdoglib/plugins.py new file mode 100644 index 0000000..0e3d276 --- /dev/null +++ b/rawdoglib/plugins.py @@ -0,0 +1,63 @@ +# plugins: handle add-on modules for rawdog. +# Copyright 2004 Adam Sampson +# +# rawdog is free software; you can redistribute and/or modify it +# under the terms of that license as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) +# any later version. +# +# rawdog is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with rawdog; see the file COPYING. If not, write to the +# Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA, or see http://www.gnu.org/. + +import os, os.path, imp + +def load_plugins(config): + count = 0 + for dir in config["plugindirs"]: + try: + files = os.listdir(dir) + except OSError: + # Ignore directories that can't be read. + files = [] + + for file in files: + if file == "" or file[0] == ".": + continue + + desc = None + for d in imp.get_suffixes(): + if file.endswith(d[0]): + desc = d + if desc is None: + continue + + fn = os.path.join(dir, file) + config.log("Loading plugin ", fn) + f = open(fn, "r") + mod = imp.load_module("plugin%d" % (count,), f, fn, desc) + count += 1 + f.close() + +attached = {} + +def attach_hook(hookname, func): + """Attach a function to a hook. The function should take the + appropriate arguments for the hook, and should return either True or + False to indicate whether further functions should be processed.""" + attached.setdefault(hookname, []).append(func) + +def call_hook(hookname, *args): + """Call all the functions attached to a hook with the given + arguments, in the order they were added, stopping if a hook function + returns False.""" + for func in attached.get(hookname, []): + if not func(*args): + break + diff --git a/rawdoglib/rawdog.py b/rawdoglib/rawdog.py index 6724e7e..8d86ce4 100644 --- a/rawdoglib/rawdog.py +++ b/rawdoglib/rawdog.py @@ -18,7 +18,7 @@ VERSION = "2.2" STATE_VERSION = 2 -import feedparser, feedfinder +import feedparser, feedfinder, plugins from persister import Persistable, Persister import os, time, sha, getopt, sys, re, urlparse, cgi, socket, urllib2, calendar from StringIO import StringIO @@ -481,6 +481,7 @@ class Config: "feedslist" : [], "feeddefaults" : {}, "defines" : {}, + "plugindirs" : [], "outputfile" : "output.html", "maxarticles" : 200, "maxage" : 0, @@ -553,6 +554,8 @@ class Config: if len(l) != 2: raise ConfigError("Bad line in config: " + line) self["defines"][l[0]] = l[1] + elif l[0] == "plugindirs": + self["plugindirs"] = parse_list(l[1]) elif l[0] == "outputfile": self["outputfile"] = l[1] elif l[0] == "maxarticles": @@ -1116,6 +1119,8 @@ def main(argv): rawdog.sync_from_config(config) + plugins.load_plugins(config) + plugins.call_hook("startup", rawdog, config) stats = Stats() for o, a in optlist: @@ -1143,6 +1148,7 @@ def main(argv): config.reload() rawdog.sync_from_config(config) + plugins.call_hook("shutdown", rawdog, config) if showstats: stats.show() -- 2.35.1