Welcome, guest | Sign In | My Account | Store | Cart

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, ...)
  • 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 !
Python, 89 lines
 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 !

4 comments

Martin Miller 11 years, 8 months ago  # | flag

I think a good name for the recipe class would be Wrapper. I also suggest you follow the PEP 8 -- Style Guide for Python Code recommendation of using 4 spaces per indentation level.

Cyril (author) 11 years, 8 months ago  # | flag

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.

Martin Miller 11 years, 8 months ago  # | flag

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.

Martin Miller 11 years, 8 months ago  # | flag

Another name idea, "OnEvent".

Created by Cyril on Mon, 23 Jul 2012 (MIT)
Python recipes (4591)
Cyril's recipes (1)

Required Modules

Other Information and Tasks