diff --git a/ChangeLog b/ChangeLog index 93a94a495..9a17782a8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ + * Changed Bugzilla to use the registry, rather than a custom + flatfile database format. + * Added the KeepAlive plugin, to send useless keepalive messages to someone every some period. It's mostly just because we noticed that MozBot had one, and we couldn't allow ourselves to diff --git a/plugins/Bugzilla.py b/plugins/Bugzilla.py index ef761953f..8b744097e 100644 --- a/plugins/Bugzilla.py +++ b/plugins/Bugzilla.py @@ -46,13 +46,13 @@ import xml.dom.minidom as minidom from itertools import imap, ifilter from htmlentitydefs import entitydefs as entities -import supybot.registry as registry import supybot.conf as conf import supybot.utils as utils import supybot.plugins as plugins import supybot.ircutils as ircutils import supybot.privmsgs as privmsgs +import supybot.registry as registry import supybot.webutils as webutils import supybot.callbacks as callbacks import supybot.structures as structures @@ -68,33 +68,16 @@ priorityKeys = ['p1', 'p2', 'p3', 'p4', 'p5', 'Low', 'Normal', 'High', severityKeys = ['enhancement', 'trivial', 'minor', 'normal', 'major', 'critical', 'blocker'] -dbfilename = os.path.join(conf.supybot.directories.data(), 'Bugzilla.db') - -def makeDb(filename): - if os.path.exists(filename): - d = structures.PersistentDictionary(filename) - else: - d = structures.PersistentDictionary(filename) - d['gcc'] = ['http://gcc.gnu.org/bugzilla', 'GCC'] - d['rh'] = ['http://bugzilla.redhat.com/bugzilla', 'Red Hat'] - d['gnome'] = ['http://bugzilla.gnome.org/bugzilla', 'Gnome'] - d['mozilla'] = ['http://bugzilla.mozilla.org', 'Mozilla'] - d['ximian'] = ['http://bugzilla.ximian.com/bugzilla', 'Ximian Gnome'] - d.flush() - return d - - class BugzillaError(Exception): """A bugzilla error""" pass - def configure(advanced): from supybot.questions import output, expect, anything, yn conf.registerPlugin('Bugzilla', True) output("""The Bugzilla plugin has the functionality to watch for URLs that match a specific pattern (we call this a snarfer). When - supybot sees such a URL, he will parse the web page for + a supybot sees such a URL, it will parse the web page for information and reply with the results.""") if yn('Do you want this bug snarfer enabled by default?', default=False): conf.supybot.plugins.Bugzilla.bugSnarfer.setValue(True) @@ -111,6 +94,24 @@ conf.registerChannelValue(conf.supybot.plugins.Bugzilla, 'replyNoBugzilla', to use when notifying the user that there is no information about that bugzilla site.""")) +class Bugzillae(registry.SpaceSeparatedListOfStrings): + List = ircutils.IrcSet + +conf.registerGlobalValue(conf.supybot.plugins.Bugzilla, 'bugzillae', + Bugzillae([], """Determines what bugzillas will be added to the bot when it + starts.""")) + +def registerBugzilla(name, url='', description=''): + conf.supybot.plugins.Bugzilla.bugzillae().add(name) + group = conf.registerGroup(conf.supybot.plugins.Bugzilla.bugzillae, name) + URL = conf.registerGlobalValue(group, 'url', registry.String(url, '')) + DESC = conf.registerGlobalValue(group, 'description', + registry.String(description, '')) + if url: + URL.setValue(url) + if description: + DESC.setValue(description) + class Bugzilla(callbacks.PrivmsgCommandAndRegexp): """Show a link to a bug report with a brief description""" threaded = True @@ -120,7 +121,11 @@ class Bugzilla(callbacks.PrivmsgCommandAndRegexp): callbacks.PrivmsgCommandAndRegexp.__init__(self) self.entre = re.compile('&(\S*?);') # Schema: {name, [url, description]} - self.db = makeDb(dbfilename) + self.db = ircutils.IrcDict() + for name in self.registryValue('bugzillae'): + registerBugzilla(name) + group = self.registryValue('bugzillae.%s' % name, value=False) + self.db[name] = [group.url(), group.description()] self.shorthand = utils.abbrev(self.db.keys()) def keywords2query(self, keywords): @@ -139,9 +144,6 @@ class Bugzilla(callbacks.PrivmsgCommandAndRegexp): query.append('ctype=csv') return query - def die(self): - self.db.close() - def add(self, irc, msg, args): """ @@ -155,6 +157,7 @@ class Bugzilla(callbacks.PrivmsgCommandAndRegexp): if url[-1] == '/': url = url[:-1] self.db[name] = [url, description] + registerBugzilla(name, url, description) self.shorthand = utils.abbrev(self.db.keys()) irc.replySuccess() @@ -168,6 +171,7 @@ class Bugzilla(callbacks.PrivmsgCommandAndRegexp): try: name = self.shorthand[name] del self.db[name] + self.registryValue('bugzillae').remove(name) self.shorthand = utils.abbrev(self.db.keys()) irc.replySuccess() except KeyError: diff --git a/src/structures.py b/src/structures.py index 2555d5cf3..28995fbe7 100644 --- a/src/structures.py +++ b/src/structures.py @@ -33,8 +33,6 @@ Data structures for Python. """ -## from __future__ import generators - __revision__ = "$Id$" import supybot.fix as fix @@ -42,6 +40,7 @@ import supybot.fix as fix import types import pprint import string +import threading from itertools import imap class RingBuffer(object): @@ -153,10 +152,7 @@ class RingBuffer(object): self.L[idx] = elt def __repr__(self): - if self.full: - return 'RingBuffer(%r, %r)' % (self.maxSize, list(self)) - else: - return 'RingBuffer(%r, %r)' % (self.maxSize, list(self)) + return 'RingBuffer(%r, %r)' % (self.maxSize, list(self)) def __getstate__(self): return (self.maxSize, self.full, self.i, self.L) @@ -353,30 +349,29 @@ class TwoWayDictionary(dict): dict.__delitem__(self, value) -class PersistentDictionary(dict): - _trans = string.maketrans('\r\n', ' ') - _locals = locals - _globals = globals - def __init__(self, filename, globals=None, locals=None): - mylocals = locals - myglobals = globals - if mylocals is None: - mylocals = self._locals() - if myglobals is None: - myglobals = self._globals() - self.filename = filename - try: - fd = file(filename, 'r') - s = fd.read() - fd.close() - s.translate(self._trans) - super(self.__class__, self).__init__(eval(s, myglobals, mylocals)) - except IOError: - pass +class WorkQueue(object): + def __init__(self, q=None, n=1): + if q is None: + q = Queue.Queue() + self.q = q + self.numberOfThreads = n + self.threads = [] + for _ in xrange(n): + t = threading.Thread(target=self.target) + self.threads.append(t) + t.start() + + def target(self): + while 1: + f = self.q.get() + if f is None: + self.q.put(None) + break + else: + f() - def close(self): - fd = file(self.filename, 'w') - fd.write(pprint.pformat(self)) - fd.close() + def enqueue(self, f): + self.q.put(f) - flush = close + +# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: diff --git a/test/test_Bugzilla.py b/test/test_Bugzilla.py index ae1c30d59..1e9f6050d 100644 --- a/test/test_Bugzilla.py +++ b/test/test_Bugzilla.py @@ -35,6 +35,7 @@ if network: class BugzillaTest(PluginTestCase, PluginDocumentation): plugins = ('Bugzilla',) def testBug(self): + self.assertNotError('add gcc http://gcc.gnu.org/bugzilla gcc') self.assertNotError('bug gcc 5') def testAddRemove(self): @@ -44,10 +45,12 @@ if network: self.assertError('bug xiph 413') def testSearch(self): + self.assertNotError('add gcc http://gcc.gnu.org/bugzilla gcc') self.assertNotError('search gcc alpha') self.assertNotError('search --keywords=fixed gcc alpha') def testConfigBugzillaSnarfer(self): + self.assertNotError('add gcc http://gcc.gnu.org/bugzilla gcc') conf.supybot.plugins.bugzilla.bugSnarfer.setValue(False) self.assertNoResponse( 'http://gcc.gnu.org/bugzilla/show_bug.cgi?id=5') diff --git a/test/test_structures.py b/test/test_structures.py index 68188ba41..1dead838f 100644 --- a/test/test_structures.py +++ b/test/test_structures.py @@ -576,30 +576,5 @@ class TwoWayDictionaryTestCase(SupyTestCase): self.failIf('foo' in d) -class PersistentDictionaryTestCase(SupyTestCase): - def test(self): - d = PersistentDictionary('test.dict') - d['foo'] = 'bar' - d[1] = 2 - d.close() - d2 = PersistentDictionary('test.dict') - self.failUnless('foo' in d) - self.assertEqual(d['foo'], 'bar') - self.failUnless(1 in d) - self.assertEqual(d[1], 2) - self.failUnless('foo' in d2) - self.assertEqual(d2['foo'], 'bar') - self.failUnless(1 in d2) - self.assertEqual(d2[1], 2) - - def testFlush(self): - d = PersistentDictionary('test.dict') - d[1] = 2 - d.flush() - d2 = PersistentDictionary('test.dict') - self.failUnless(1 in d2) - self.assertEqual(d2[1], 2) - - # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: