I've come up with a base class you can inherit from to enforce method call order (tested in Python 2.7 and 3.6):
from types import FunctionType, MethodType
class Sequenced(object):
def __init__(self):
_methods = [name for name, obj in self.__class__.__dict__.items()
if type(obj) == FunctionType]
assert set(self._method_sequence).issubset(set(_methods))
self._sequence_pos = 0
def __getattribute__(self, name):
attr = object.__getattribute__(self, name)
if type(attr) == MethodType:
if attr.__name__ in self._method_sequence:
if self._sequence_pos >= len(self._method_sequence):
raise RuntimeError("All sequenced methods already called.")
if attr.__name__ != self._method_sequence[self._sequence_pos]:
raise RuntimeError("{0} method call expected.".format(
self._method_sequence[self._sequence_pos]))
self._sequence_pos += 1
def func_wrapper(*args, **kwargs):
return attr(*args, **kwargs)
return func_wrapper
else:
return attr
Be warned, I don't fully understand how this works (I've managed to stop __getattribute__
from causing infinite recursion, but I don't understand what caused it in the first place, and I don't understand why I have to use FunctionType
in one place and MethodType
in another). It passed my minimal testing (Python 2.7 and 3.6), but you'll want to make sure you test it as well.
All you need to do with your class is make it inherit from Sequenced
, and modify its __init__
method like so:
class MyClass(Sequenced):
def __init__(self):
self._method_sequence = [
"generate_query",
"send_query",
"receive_response"
]
super(MyClass, self).__init__()
def generate_query(self):
# Generate the query
pass
def send_query(self):
# send the query over the network
pass
def receive_response(self):
# read the response from buffer
pass
The base class allows your class to contain other methods (sequencing is only enforced on methods within the _method_sequence
list). _method_sequence
can contain duplicates if you want methods to be called more than once.
In the case of a sequenced method being called out of sequence, or attempting to use a sequenced method after already having gone through the list, RuntimeError
is raised.
It's possible to modify the base class so that sequence methods can be called repeatedly once available, but I'll leave that as an exercise to the reader.