Thomas Nyberg wrote: > I have a situation in which I want a user to call methods in a certain > order and to force the re-calling of methods "down-stream" if upstream > methods are called again. An example of this sort of thing would be a > pipeline where calling methods again invalidates the results of methods > called afterwards and you'd like to warn the user of that. > > Here is a very simplified example: > > ordered_consistency.py > ----------------------------------------------- > class ConsistencyError(Exception): > pass > > class C: > def __init__(self): > self._a_dirty = self._b_dirty = self._c_dirty = True > > def a(self): > self._a_dirty = self._b_dirty = self._c_dirty = True > print("Calling a()...") > self._a_dirty = False > > def b(self): > if self._a_dirty: > raise ConsistencyError("Re-run a() before calling b()!") > self._b_dirty = self._c_dirty = True > print("Calling b()...") > self._b_dirty = False > > def c(self): > if self._b_dirty: > raise ConsistencyError("Re-run b() before calling c()!") > self._c_dirty = True > print("Calling c()...") > self._c_dirty = False > > def d(self): > if self._c_dirty: > raise ConsistencyError("Re-run c() before calling d()!") > print("Calling d()...") > > c = C() > # This is fine: > c.a() > c.b() > c.c() > c.d() > # This is also fine (with same class instantiation!) > c.c() > c.d() > # This throws an error: > c.b() > c.d() > ----------------------------------------------- > > Here's what you get when calling it: > ----------------------------------------------- > $ python3 ordered_methods.py > Calling a()... > Calling b()... > Calling c()... > Calling d()... > Calling c()... > Calling d()... > Calling b()... > Traceback (most recent call last): > File "ordered_methods.py", line 43, in <module> > c.d() > File "ordered_methods.py", line 29, in d > raise ConsistencyError("Re-run c() before calling d()!") > __main__.ConsistencyError: Re-run c() before calling d()! > ----------------------------------------------- > > My solution seems to work, but has a large amount of repetition and > errors will certainly be introduced the first time anything is changed. > I have the following questions: > > 1) Most importantly, am I being stupid? I.e. is there some obviously > better way to handle this sort of thing? > 2) Is there an out of the box solution somewhere that enforces this > that I've never seen before? > 3) If not, can anyone here see some sort of more obvious way to do this > without all the repetition in dirty bits, error messages, etc.? > > I presume a decorator could be used to do some sort of "order > registration", but I figure I might as well ask before I re-invent a > hexagonal wheel. Thanks a lot for any help!
If the order is linear like it seems to be the following might work: $ cat inorder.py import itertools class ConsistencyError(Exception): pass class InOrder: def __init__(self): self.indices = itertools.count(1) def __call__(self, method): index = next(self.indices) def wrapper(self, *args, **kw): print("index", index, "state", self.state) if index - self.state > 1: raise ConsistencyError result = method(self, *args, **kw) self.state = index return result return wrapper class C: def __init__(self): self.state = 0 inorder = InOrder() @inorder def a(self): print("Calling a()...") @inorder def b(self): print("Calling b()...") @inorder def c(self): print("Calling c()...") @inorder def d(self): print("Calling d()...") $ python3 -i inorder.py >>> >>> c = C() >>> c.a() index 1 state 0 Calling a()... >>> c.b() index 2 state 1 Calling b()... >>> c.a() index 1 state 2 Calling a()... >>> c.c() index 3 state 1 Traceback (most recent call last): File "<stdin>", line 1, in <module> File "inorder.py", line 18, in wrapper raise ConsistencyError __main__.ConsistencyError >>> c.b() index 2 state 1 Calling b()... >>> c.c() index 3 state 2 Calling c()... -- https://mail.python.org/mailman/listinfo/python-list