Not a very new recipe, but a short one and (I hope) useful :
- wrapping any function "f" with two decorators "enter_event" and "exit_event" that will trigger calling of user functions when, ... hum ... evidently, just before entering and just after exiting the "f" function.
Typical usages :
- debugging on a function by function basis :
- emit a trace in log file to see when functions are called and check sequences correctness (very usefull when programming by events)
- feed a profile analyzer (by fine tuning which functions are enabled)
- feed a code coverage analyzer ( " )
- kind of validator on function calling :
- implement programming by contracts :
- check that parameters values of "f" function will not have an unexpected value or be of an unexpected type
- this allow to increase code robustness by narrowing
- implement invariants (eg. check that returned value is always in the excepted range, ...)
- insure that a function follow specifications by meta-checking that has always predictable results (eg. return the fixed expected value for each possible input value, ...)
- implement programming by contracts :
- minimum modification of existing code
- in the same thinking line as the "monkey patching" concept
Notes on usage :
- recipe works on functions and any kind of methods (methods, class methods, and static methods)
- the usage order of "@enter_event" and "@exit_event" decorators doesn't matter : the result will be the same
- PLEASE VOTE FOR THIS RECIPE if you like it !
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | #!/usr/bin/python
# -*- coding: utf-8 -*-
# Recipe
# ----------------------------------------------------------------------------
class recipe:
@staticmethod
def enter_event(notified_f):
def wrapper(f):
def caller(obj, *args, **kargs):
notified_f(obj, *args, **kargs)
ret = f(obj, *args, **kargs)
return ret
return caller
return wrapper
@staticmethod
def exit_event(notified_f):
def wrapper(f):
def caller(obj, *args, **kargs):
# Start of diff between enter_event
ret = f(obj, *args, **kargs)
notified_f(obj, *args, **kargs)
# End of diff between enter_event
return ret
return caller
return wrapper
# Tests
# ----------------------------------------------------------------------------
class c:
def notify_entering(self, *args, **kargs):
print ' - function notify_entering() is triggered :'
print ' - self : [%s]' % self
print ' - args : %s' % repr(args)
print ' - kargs : %s' % repr(kargs)
print
def notify_exiting(self, *args, **kargs):
print ' - function notify_exiting() is triggered :'
print ' - self : [%s]' % self
print ' - args : %s' % repr(args)
print ' - kargs : %s' % repr(kargs)
print
# Method
@recipe.enter_event(notify_entering)
@recipe.exit_event(notify_exiting)
def f(self, x):
print ' - inside o.f() ...'
print ' - self = [%s]' % self
print ' - x = [%s]' % x
print
# Class method
@classmethod
@recipe.enter_event(notify_entering)
@recipe.exit_event(notify_exiting)
def fclass(cls, x):
print ' - inside o.fclass() ...'
print ' - cls = [%s]' % cls
print ' - x = [%s]' % x
print
# Static method
@staticmethod
@recipe.enter_event(notify_entering)
@recipe.exit_event(notify_exiting)
def fstatic(x):
print ' - inside o.fstatic() ...'
print ' - x = [%s]' % x
print
if __name__ == '__main__':
o = c()
print '-' * 78
print '- calling o.f(123) ...'
o.f(123)
print '-' * 78
print '- calling o.fclass(234) ...'
o.fclass(234)
print '-' * 78
print '- calling o.fstatic(345) ...'
o.fstatic(345)
|
I was not pleased at all about the first name I gave to the recipe (that's why it has not beeing named yet)
I really need to find a good real name for this recipe (any suggestions are welcome)
Please note, that recipe content, features and coverage is likely to by improved (and it will be at a regular basis)
I will be pleased to discuss about this subject (by email : mchk0123@gmail.com, or through comments below)
Feel free to send any comments, ideas, suggestions of improvement, ...
PLEASE VOTE FOR THIS RECIPE if you like it !
I think a good name for the
recipe
class would beWrapper
. I also suggest you follow the PEP 8 -- Style Guide for Python Code recommendation of using 4 spaces per indentation level.Thanks for your suggests, indeed I was accustomed with tabs because it's lazier for code navigation ; but I forgot the portability way was with spaces. I made the update.
Regarding "Wrapper" as recipe's name, I found it a little too abstract. Thinking pythonicly wrapper is a synonym of decorator (every recipe on the form of a decorator can be qualified of "wrapper").
Moreover it doesn't capture enough the specific semantic of triggering advertised functions upon entering and exiting.By the way, perhaps it's a good start and just only need an additional adjective after it ? I don't known.
Thanks for fixing the tabs -- looks better now (IMHO) and as you said is more portable this way. As for a classname, seems like "Trigger" or "EventTrigger" would capture the essence of what it does well.
Another name idea, "OnEvent".