From e7553dcca4dd4378ab7113173334af929ab51498 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Thu, 23 Jan 2020 14:25:10 +0100 Subject: [PATCH] Add subcommand dispatching for CAP/FAIL/WARN/NOTE. --- plugins/ChannelStats/plugin.py | 2 +- src/irclib.py | 53 ++++++++++++++------- test/test_irclib.py | 87 ++++++++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+), 18 deletions(-) diff --git a/plugins/ChannelStats/plugin.py b/plugins/ChannelStats/plugin.py index 0fcefe075..947f7b65a 100644 --- a/plugins/ChannelStats/plugin.py +++ b/plugins/ChannelStats/plugin.py @@ -72,7 +72,7 @@ class ChannelStat(irclib.IrcCommandDispatcher): def addMsg(self, msg): self.msgs += 1 - method = self.dispatchCommand(msg.command) + method = self.dispatchCommand(msg.command, msg.args) if method is not None: method(msg) diff --git a/src/irclib.py b/src/irclib.py index 6489bca34..95b555733 100644 --- a/src/irclib.py +++ b/src/irclib.py @@ -33,6 +33,7 @@ import time import random import base64 import textwrap +import warnings import collections try: @@ -70,9 +71,39 @@ MAX_LINE_SIZE = 512 # Including \r\n class IrcCommandDispatcher(object): """Base class for classes that must dispatch on a command.""" - def dispatchCommand(self, command): + + def dispatchCommand(self, command, args=None): """Given a string 'command', dispatches to doCommand.""" - return getattr(self, 'do' + command.capitalize(), None) + if args is None: + warnings.warn( + "dispatchCommand now takes an 'args' attribute, which is " + "a list of the command's arguments (ie. IrcMsg.args).", + DeprecationWarning) + args = [] + + command = command.upper() + subcommand = None + method = None + + # Dispatch on command + subcommand, if there is a subcommand, and + # a method with the matching name exists + if command in ('FAIL', 'WARN', 'NOTE') and len(args) >= 1: + subcommand = args[0] + elif command in ('CAP',) and len(args) >= 2: + # Note: this only covers the server-to-client format + subcommand = args[1] + + command = command.capitalize() + + if subcommand is not None: + subcommand = subcommand.capitalize() + method = getattr(self, 'do' + command + subcommand, None) + + # If not dispatched on command + subcommand, then dispatch on command + if method is None: + method = getattr(self, 'do' + command, None) + + return method class IrcCallback(IrcCommandDispatcher, log.Firewalled): @@ -141,7 +172,7 @@ class IrcCallback(IrcCommandDispatcher, log.Firewalled): def __call__(self, irc, msg): """Used for handling each message.""" - method = self.dispatchCommand(msg.command) + method = self.dispatchCommand(msg.command, msg.args) if method is not None: method(irc, msg) @@ -429,7 +460,7 @@ class IrcState(IrcCommandDispatcher, log.Firewalled): assert batch in self.batches, \ 'Server references undeclared batch %s' % batch self.batches[batch].messages.append(msg) - method = self.dispatchCommand(msg.command) + method = self.dispatchCommand(msg.command, msg.args) if method is not None: method(irc, msg) @@ -944,7 +975,7 @@ class Irc(IrcCommandDispatcher, log.Firewalled): log.debug('Updating server attribute to %s.', self.server) # Dispatch to specific handlers for commands. - method = self.dispatchCommand(msg.command) + method = self.dispatchCommand(msg.command, msg.args) if method is not None: method(msg) elif self._numericErrorCommandRe.search(msg.command): @@ -1260,18 +1291,6 @@ class Irc(IrcCommandDispatcher, log.Firewalled): self.network, msg.args[1]) self.filterSaslMechanisms(set(msg.args[1].split(','))) - def doCap(self, msg): - subcommand = msg.args[1] - if subcommand == 'ACK': - self.doCapAck(msg) - elif subcommand == 'NAK': - self.doCapNak(msg) - elif subcommand == 'LS': - self.doCapLs(msg) - elif subcommand == 'DEL': - self.doCapDel(msg) - elif subcommand == 'NEW': - self.doCapNew(msg) def doCapAck(self, msg): if len(msg.args) != 3: log.warning('Bad CAP ACK from server: %r', msg) diff --git a/test/test_irclib.py b/test/test_irclib.py index 76d8dbdd5..c367d3d90 100644 --- a/test/test_irclib.py +++ b/test/test_irclib.py @@ -31,6 +31,7 @@ from supybot.test import * import copy import pickle +import warnings import supybot.conf as conf import supybot.irclib as irclib @@ -42,6 +43,92 @@ import supybot.ircutils as ircutils msgs = [] rawmsgs = [] + +class IrcCommandDispatcherTestCase(SupyTestCase): + class DispatchedClass(irclib.IrcCommandDispatcher): + def doPrivmsg(): + pass + def doCap(): + pass + def doFail(): + pass + + class DispatchedClassSub(irclib.IrcCommandDispatcher): + def doPrivmsg(): + pass + def doPrivmsgFoo(): + pass + def doCapLs(): + pass + def doFailFoo(): + pass + + def testCommandDispatch(self): + dispatcher = self.DispatchedClass() + self.assertEqual( + dispatcher.dispatchCommand('privmsg', ['foo']), + dispatcher.doPrivmsg) + self.assertEqual( + dispatcher.dispatchCommand('cap', ['*', 'ls']), + dispatcher.doCap) + self.assertEqual( + dispatcher.dispatchCommand('fail', ['foo', 'bar']), + dispatcher.doFail) + self.assertEqual( + dispatcher.dispatchCommand('foobar', ['*', 'ls']), + None) + + def testSubCommandDispatch(self): + dispatcher = self.DispatchedClassSub() + self.assertEqual( + dispatcher.dispatchCommand('privmsg', ['foo']), + dispatcher.doPrivmsg) + self.assertEqual( + dispatcher.dispatchCommand('cap', ['*', 'ls']), + dispatcher.doCapLs) + self.assertEqual( + dispatcher.dispatchCommand('fail', ['foo', 'bar']), + dispatcher.doFailFoo) + self.assertEqual( + dispatcher.dispatchCommand('foobar', ['*', 'ls']), + None) + + def testCommandDispatchMissingArgs(self): + dispatcher = self.DispatchedClass() + self.assertEqual( + dispatcher.dispatchCommand('privmsg', ['foo']), + dispatcher.doPrivmsg) + self.assertEqual( + dispatcher.dispatchCommand('cap', ['*']), + dispatcher.doCap) + self.assertEqual( + dispatcher.dispatchCommand('fail', []), + dispatcher.doFail) + self.assertEqual( + dispatcher.dispatchCommand('foobar', ['*']), + None) + + def testCommandDispatchLegacy(self): + """Tests the legacy parameters of dispatchCommand, without the "args" + argument.""" + dispatcher = self.DispatchedClass() + with self.assertWarnsRegex(DeprecationWarning, "'args'"): + self.assertEqual( + dispatcher.dispatchCommand('privmsg'), + dispatcher.doPrivmsg) + with self.assertWarnsRegex(DeprecationWarning, "'args'"): + self.assertEqual( + dispatcher.dispatchCommand('cap'), + dispatcher.doCap) + with self.assertWarnsRegex(DeprecationWarning, "'args'"): + self.assertEqual( + dispatcher.dispatchCommand('fail'), + dispatcher.doFail) + with self.assertWarnsRegex(DeprecationWarning, "'args'"): + self.assertEqual( + dispatcher.dispatchCommand('foobar'), + None) + class IrcMsgQueueTestCase(SupyTestCase): mode = ircmsgs.op('#foo', 'jemfinch') msg = ircmsgs.privmsg('#foo', 'hey, you')