Reputation: 553
This question is an extension of a previous question I posted here: link
I use Python 3.6.2. I have a generic class Framework
from which my systems inherit.
I use the decorators on_initialize
, on_event
, and on_finalize
to inform the Framework
where each method must be executed. The argument precedence
of each decorator is used to determine the order of execution for each section (from lower to higher).
# 3 decorators
def on_initialize(precedence=0):
def marker(func):
func._initializer = precedence
return func
return marker
def on_event(precedence=0):
def marker(func):
func._event_handler = precedence
return func
return marker
def on_finalize(precedence=0):
def marker(func):
func._finalizer = precedence
return func
return marker
# Main framework
class Framework:
def __init_subclass__(cls, *args, **kw):
super().__init_subclass__(*args, **kw)
handlers = dict(_initializer=[], _event_handler=[], _finalizer=[])
for name, method in cls.__dict__.items():
for handler_type in handlers:
if hasattr(method, handler_type):
handlers[handler_type].append((getattr(method, handler_type), name))
for handler_type in handlers:
setattr(cls, handler_type,
[handler[1] for handler in sorted(handlers[handler_type])])
def _initialize(self):
for method_name in self._initializer:
getattr(self, method_name)()
def _handle_event(self, event):
for method_name in self._event_handler:
getattr(self, method_name)(event)
def _finalize(self):
for method_name in self._finalizer:
getattr(self, method_name)()
def run(self):
self._initialize()
for event in range(10):
self._handle_event(event)
self._finalize()
class Recorder(Framework):
@on_finalize(precedence=0)
def save_to_db(self):
print('save_to_db')
class TestFramework(Recorder):
@on_initialize(precedence=0)
def get_data(self):
print('get_data')
@on_initialize(precedence=1)
def prepare_data(self):
print('prepare_data')
@on_event(precedence=0)
def process_event(self, event):
print('process_event', event)
@on_finalize(precedence=1)
def generate_report(self):
print('generate_report')
if __name__ == '__main__':
tf = TestFramework()
tf.run()
The results:
> get_data
> prepare_data
> process_event 0
> process_event 1
> process_event 2
> process_event 3
> process_event 4
> process_event 5
> process_event 6
> process_event 7
> process_event 8
> process_event 9
> generate_report
The method save_to_db
from Recorder
is not executed in TestFramework
. I think I'm missing something in __init_subclass__
where I should be iterating through each subclass. Any idea? Many thanks!
Upvotes: 0
Views: 198
Reputation: 1124848
You set a new set of handlers per class, ignoring those of any base class.
Rather than use empty lists in the handlers
dictionary, look for existing lists and start with those:
def __init_subclass__(cls, *args, **kw):
super().__init_subclass__(*args, **kw)
handlers = dict(
_initializer=getattr(cls, '_initializer', []),
_event_handler=getattr(cls, '_event_handler', []),
_finalizer=getattr(cls, '_finalizer', [])))
# ...
You do then have to eliminate any methods that the subclass has overridden!
Alternatively, rather than use only the current class namespace (via cls.__dict__
), use the combined names available in dir()
:
def __init_subclass__(cls, *args, **kw):
super().__init_subclass__(*args, **kw)
handlers = dict(_initializer=[], _event_handler=[], _finalizer=[])
for name in dir(cls):
method = getattr(cls, name)
for handler_type in handlers:
if hasattr(method, handler_type):
handlers[handler_type].append((getattr(method, handler_type), name))
Upvotes: 1