Fix some problems in Infobot.py's sqlite implementation and genericize

some other errors to better fit in with db abstraction
This commit is contained in:
James Vega 2004-08-17 20:01:23 +00:00
parent 376e67def3
commit 72f36efc18
3 changed files with 86 additions and 35 deletions

View File

@ -43,6 +43,7 @@ import re
import random import random
import cPickle as pickle import cPickle as pickle
import supybot.dbi as dbi
import supybot.conf as conf import supybot.conf as conf
import supybot.utils as utils import supybot.utils as utils
import supybot.ircmsgs as ircmsgs import supybot.ircmsgs as ircmsgs
@ -106,7 +107,10 @@ class PickleInfobotDB(object):
self._is = utils.InsensitivePreservingDict() self._is = utils.InsensitivePreservingDict()
self._are = utils.InsensitivePreservingDict() self._are = utils.InsensitivePreservingDict()
else: else:
try:
(self._is, self._are) = pickle.load(fd) (self._is, self._are) = pickle.load(fd)
except cPickle.UnpicklingError, e:
raise dbi.InvalidDBError, str(e)
self._changes = 0 self._changes = 0
self._responses = 0 self._responses = 0
@ -119,7 +123,10 @@ class PickleInfobotDB(object):
self.flush() self.flush()
def changeIs(self, factoid, replacer): def changeIs(self, factoid, replacer):
try:
old = self._is[factoid] old = self._is[factoid]
except KeyError:
raise dbi.NoRecordError
if replacer is not None: if replacer is not None:
self._is[factoid] = replacer(old) self._is[factoid] = replacer(old)
self.flush() self.flush()
@ -136,7 +143,10 @@ class PickleInfobotDB(object):
self._changes += 1 self._changes += 1
def delIs(self, factoid): def delIs(self, factoid):
try:
del self._is[factoid] del self._is[factoid]
except KeyError:
raise dbi.NoRecordError
self.flush() self.flush()
self._changes += 1 self._changes += 1
@ -144,7 +154,10 @@ class PickleInfobotDB(object):
return factoid in self._is return factoid in self._is
def changeAre(self, factoid, replacer): def changeAre(self, factoid, replacer):
try:
old = self._are[factoid] old = self._are[factoid]
except KeyError:
raise dbi.NoRecordError
if replacer is not None: if replacer is not None:
self._are[factoid] = replacer(old) self._are[factoid] = replacer(old)
self._changes += 1 self._changes += 1
@ -164,7 +177,10 @@ class PickleInfobotDB(object):
self._changes += 1 self._changes += 1
def delAre(self, factoid): def delAre(self, factoid):
try:
del self._are[factoid] del self._are[factoid]
except KeyError:
raise dbi.NoRecordError
self.flush() self.flush()
self._changes += 1 self._changes += 1
@ -186,21 +202,24 @@ class SqliteInfobotDB(object):
self._responses = 0 self._responses = 0
def _getDb(self): def _getDb(self):
try:
if os.path.exists(filename): if os.path.exists(filename):
return sqlite.connect(filename) return sqlite.connect(filename)
#else: #else:
db = sqlite.connect(filename) db = sqlite.connect(filename)
cursor = db.cursor() cursor = db.cursor()
cursor.execute("""CREATE TABLE isFacts ( cursor.execute("""CREATE TABLE isFacts (
key TEXT PRIMARY KEY, key TEXT UNIQUE ON CONFLICT REPLACE,
value TEXT value TEXT
);""") );""")
cursor.execute("""CREATE TABLE areFacts ( cursor.execute("""CREATE TABLE areFacts (
key TEXT PRIMARY KEY, key TEXT UNIQUE ON CONFLICT REPLACE,
value TEXT value TEXT
);""") );""")
db.commit() db.commit()
return db return db
except sqlite.DatabaseError, e:
raise dbi.InvalidDBError, str(e)
def close(self): def close(self):
pass pass
@ -209,6 +228,8 @@ class SqliteInfobotDB(object):
db = self._getDb() db = self._getDb()
cursor = db.cursor() cursor = db.cursor()
cursor.execute("""SELECT value FROM isFacts WHERE key=%s""", factoid) cursor.execute("""SELECT value FROM isFacts WHERE key=%s""", factoid)
if cursor.rowcount == 0:
raise dbi.NoRecordError
old = cursor.fetchone()[0] old = cursor.fetchone()[0]
if replacer is not None: if replacer is not None:
cursor.execute("""UPDATE isFacts SET value=%s WHERE key=%s""", cursor.execute("""UPDATE isFacts SET value=%s WHERE key=%s""",
@ -235,6 +256,8 @@ class SqliteInfobotDB(object):
db = self._getDb() db = self._getDb()
cursor = db.cursor() cursor = db.cursor()
cursor.execute("""DELETE FROM isFacts WHERE key=%s""", factoid) cursor.execute("""DELETE FROM isFacts WHERE key=%s""", factoid)
if cursor.rowcount == 0:
raise dbi.NoRecordError
db.commit() db.commit()
self._changes += 1 self._changes += 1
@ -248,6 +271,8 @@ class SqliteInfobotDB(object):
db = self._getDb() db = self._getDb()
cursor = db.cursor() cursor = db.cursor()
cursor.execute("""SELECT value FROM areFacts WHERE key=%s""", factoid) cursor.execute("""SELECT value FROM areFacts WHERE key=%s""", factoid)
if cursor.rowcount == 0:
raise dbi.NoRecordError
old = cursor.fetchone()[0] old = cursor.fetchone()[0]
if replacer is not None: if replacer is not None:
cursor.execute("""UPDATE areFacts SET value=%s WHERE key=%s""", cursor.execute("""UPDATE areFacts SET value=%s WHERE key=%s""",
@ -274,6 +299,8 @@ class SqliteInfobotDB(object):
db = self._getDb() db = self._getDb()
cursor = db.cursor() cursor = db.cursor()
cursor.execute("""DELETE FROM areFacts WHERE key=%s""", factoid) cursor.execute("""DELETE FROM areFacts WHERE key=%s""", factoid)
if cursor.rowcount == 0:
raise dbi.NoRecordError
db.commit() db.commit()
self._changes += 1 self._changes += 1
@ -320,6 +347,12 @@ class Infobot(callbacks.PrivmsgCommandAndRegexp):
def die(self): def die(self):
self.db.close() self.db.close()
def _error(self, s):
if self.addressed:
self.irc.error(s)
else:
self.log.warning(s)
def reply(self, s, irc=None, msg=None, action=False): def reply(self, s, irc=None, msg=None, action=False):
if self.replied: if self.replied:
self.log.debug('Already replied, not replying again.') self.log.debug('Already replied, not replying again.')
@ -355,12 +388,16 @@ class Infobot(callbacks.PrivmsgCommandAndRegexp):
assert self.msg is not None assert self.msg is not None
msg = self.msg msg = self.msg
isAre = None isAre = None
try:
if self.db.hasIs(key): if self.db.hasIs(key):
isAre = 'is' isAre = 'is'
value = self.db.getIs(key) value = self.db.getIs(key)
elif self.db.hasAre(key): elif self.db.hasAre(key):
isAre = 'are' isAre = 'are'
value = self.db.getAre(key) value = self.db.getAre(key)
except dbi.InvalidDBError:
self._error('Unable to access db: %s' % e)
return
if isAre is None: if isAre is None:
if self.addressed: if self.addressed:
if dunno: if dunno:
@ -388,7 +425,7 @@ class Infobot(callbacks.PrivmsgCommandAndRegexp):
s = s.strip() # After stripFormatting for formatted spaces. s = s.strip() # After stripFormatting for formatted spaces.
s = utils.normalizeWhitespace(s) s = utils.normalizeWhitespace(s)
contractions = [('what\'s', 'what is'), ('where\'s', 'where is'), contractions = [('what\'s', 'what is'), ('where\'s', 'where is'),
('who\'s', 'who is'),] ('who\'s', 'who is'), ('wtf\'s', 'wtf is'),]
for (contraction, replacement) in contractions: for (contraction, replacement) in contractions:
if s.startswith(contraction): if s.startswith(contraction):
s = replacement + s[len(contraction):] s = replacement + s[len(contraction):]
@ -467,7 +504,7 @@ class Infobot(callbacks.PrivmsgCommandAndRegexp):
try: try:
method(fact) method(fact)
deleted = True deleted = True
except KeyError: except dbi.NoRecordError:
pass pass
if deleted: if deleted:
self.confirm() self.confirm()
@ -492,7 +529,7 @@ class Infobot(callbacks.PrivmsgCommandAndRegexp):
try: try:
method(fact, r) method(fact, r)
changed = True changed = True
except KeyError: except dbi.NoRecordError:
pass pass
if changed: if changed:
self.confirm() self.confirm()
@ -501,7 +538,7 @@ class Infobot(callbacks.PrivmsgCommandAndRegexp):
irc.reply('I\'ve never heard of %s, %s!' % (fact, msg.nick)) irc.reply('I\'ve never heard of %s, %s!' % (fact, msg.nick))
def doUnknown(self, irc, msg, match): def doUnknown(self, irc, msg, match):
r"^(.+?)\?[?!. ]*$" r"^(.+?)\s*\?[?!. ]*$"
key = match.group(1) key = match.group(1)
if self.addressed or self.registryValue('answerUnaddressedQuestions'): if self.addressed or self.registryValue('answerUnaddressedQuestions'):
self.factoid(key) # Does the dunno'ing for us itself. self.factoid(key) # Does the dunno'ing for us itself.
@ -513,7 +550,7 @@ class Infobot(callbacks.PrivmsgCommandAndRegexp):
r"^(.+)\s+(?<!\\)(was|is|am|were|are)\s+(also\s+)?(.+?)[?!. ]*$" r"^(.+)\s+(?<!\\)(was|is|am|were|are)\s+(also\s+)?(.+?)[?!. ]*$"
(key, isAre, also, value) = match.groups() (key, isAre, also, value) = match.groups()
key = key.replace('\\', '') key = key.replace('\\', '')
if key.lower() in ('where', 'what', 'who'): if key.lower() in ('where', 'what', 'who', 'wtf'):
# It's a question. # It's a question.
if self.addressed or \ if self.addressed or \
self.registryValue('answerUnaddressedQuestions'): self.registryValue('answerUnaddressedQuestions'):

View File

@ -51,6 +51,9 @@ class Error(Exception):
class NoRecordError(KeyError): class NoRecordError(KeyError):
pass pass
class InvalidDBError(Exception):
pass
class MappingInterface(object): class MappingInterface(object):
"""This is a class to represent the underlying representation of a map """This is a class to represent the underlying representation of a map
from integer keys to strings.""" from integer keys to strings."""

View File

@ -34,12 +34,12 @@ from testsupport import *
import supybot.plugins.Infobot import supybot.plugins.Infobot
confirms = supybot.plugins.Infobot.confirms confirms = supybot.plugins.Infobot.confirms
dunnos = supybot.plugins.Infobot.dunnos dunnos = supybot.plugins.Infobot.dunnos
ibot = conf.supybot.plugins.Infobot
class InfobotTestCase(ChannelPluginTestCase): class InfobotTestCase(ChannelPluginTestCase):
plugins = ('Infobot',) plugins = ('Infobot',)
_endRe = re.compile(r'!|, \S+\.|\.') _endRe = re.compile(r'!|, \S+\.|\.')
def testIsSnarf(self): def testIsSnarf(self):
ibot = conf.supybot.plugins.Infobot
learn = ibot.snarfUnaddressedDefinitions() learn = ibot.snarfUnaddressedDefinitions()
answer = ibot.answerUnaddressedQuestions() answer = ibot.answerUnaddressedQuestions()
try: try:
@ -57,7 +57,6 @@ class InfobotTestCase(ChannelPluginTestCase):
ibot.answerUnaddressedQuestions.setValue(answer) ibot.answerUnaddressedQuestions.setValue(answer)
def testAreSnarf(self): def testAreSnarf(self):
ibot = conf.supybot.plugins.Infobot
learn = ibot.snarfUnaddressedDefinitions() learn = ibot.snarfUnaddressedDefinitions()
answer = ibot.answerUnaddressedQuestions() answer = ibot.answerUnaddressedQuestions()
try: try:
@ -72,7 +71,6 @@ class InfobotTestCase(ChannelPluginTestCase):
ibot.answerUnaddressedQuestions.setValue(answer) ibot.answerUnaddressedQuestions.setValue(answer)
def testIsResponses(self): def testIsResponses(self):
ibot = conf.supybot.plugins.Infobot
learn = ibot.snarfUnaddressedDefinitions() learn = ibot.snarfUnaddressedDefinitions()
answer = ibot.answerUnaddressedQuestions() answer = ibot.answerUnaddressedQuestions()
try: try:
@ -89,7 +87,6 @@ class InfobotTestCase(ChannelPluginTestCase):
ibot.answerUnaddressedQuestions.setValue(answer) ibot.answerUnaddressedQuestions.setValue(answer)
def testAnswerUnaddressed(self): def testAnswerUnaddressed(self):
ibot = conf.supybot.plugins.Infobot
answer = ibot.answerUnaddressedQuestions() answer = ibot.answerUnaddressedQuestions()
try: try:
ibot.answerUnaddressedQuestions.setValue(True) ibot.answerUnaddressedQuestions.setValue(True)
@ -100,4 +97,18 @@ class InfobotTestCase(ChannelPluginTestCase):
finally: finally:
ibot.answerUnaddressedQuestions.setValue(answer) ibot.answerUnaddressedQuestions.setValue(answer)
def testReplaceFactoid(self):
answer = ibot.answerUnaddressedQuestions()
learn = ibot.snarfUnaddressedDefinitions()
try:
ibot.answerUnaddressedQuestions.setValue(True)
ibot.snarfUnaddressedDefinitions.setValue(True)
self.assertSnarfNoResponse('forums are good')
self.assertSnarfRegexp('forums?', 'good')
self.assertNotError('no, forums are evil')
self.assertSnarfRegexp('forums?', 'evil')
finally:
ibot.answerUnaddressedQuestions.setValue(answer)
ibot.snarfUnaddressedDefinitions.setValue(learn)
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: