There's a pattern that I learned from Twisted Python code.
class SMTP:
def lookupMethod(self, command):
return getattr(self, 'do_' + command.upper(), None)
def do_HELO(self, rest):
return 'Howdy ' + rest
def do_QUIT(self, rest):
return 'Bye'
SMTP().lookupMethod('HELO')('foo.bar.com') # => 'Howdy foo.bar.com'
SMTP().lookupMethod('QUIT')('') # => 'Bye'
You can use it any time you need to dispatch on a token and execute extended piece of code. In a state machine you would have state_
methods, and dispatch on self.state
. This switch can be cleanly extended by inheriting from base class and defining your own do_
methods. Often times you won't even have do_
methods in the base class.
Edit: how exactly is that used
In case of SMTP you will receive HELO
from the wire. The relevant code (from twisted/mail/smtp.py
, modified for our case) looks like this
class SMTP:
# ...
def do_UNKNOWN(self, rest):
raise NotImplementedError, 'received unknown command'
def state_COMMAND(self, line):
line = line.strip()
parts = line.split(None, 1)
if parts:
method = self.lookupMethod(parts[0]) or self.do_UNKNOWN
if len(parts) == 2:
return method(parts[1])
else:
return method('')
else:
raise SyntaxError, 'bad syntax'
SMTP().state_COMMAND(' HELO foo.bar.com ') # => Howdy foo.bar.com
You'll receive ' HELO foo.bar.com '
(or you might get 'QUIT'
or 'RCPT TO: foo'
). This is tokenized into parts
as ['HELO', 'foo.bar.com']
. The actual method lookup name is taken from parts[0]
.
(The original method is also called state_COMMAND
, because it uses the same pattern to implement a state machine, i.e. getattr(self, 'state_' + self.mode)
)