11import types
2- import inspect
32import __builtin__
43import logging
54
6- logger = logging .getLogger (__name__ )
7-
8- """
9- TODO:
10- - Add ModuleHookHelper and remove the deprecated code
11- - Append the old function pointer to the original message object.
12- - Try to avoid multiple hook at the same time.
13- """
14-
15-
16- class ClassHookHelper (object ):
17- def __init__ (self , cls , ** kwargs ):
18- """
19- :param cls: the class object of the class hook
20- :param strategy: 'safe' or 'override' default: safe
21- :param skip_buildin: determine if we skip the build in object default: True
22- :return:
23- """
24- # TODO: Add more strict check for eligible class
25- assert isinstance (cls , (type , types .ClassType )), \
26- "The input object must be a class object"
27- self ._hook_class = cls
28- self ._hook_table = {}
29- self ._strategy = kwargs .get ('strategy' , 'safe' )
30- self ._skip_buildin_class = kwargs .get ('skip_buildin_class' , True )
31-
32- def hook (self , strategy = None , skip_buildin = None ):
33- strategy = strategy or self ._strategy
34- skip_buildin = skip_buildin or self ._skip_buildin_class
35- cls = self ._hook_class
36- base_classes = cls .__bases__
37- for base_class in base_classes :
38- if skip_buildin and base_class .__name__ in __builtin__ .__dict__ :
39- logger .warn ("Skip hooking build-in base class '%s' in sub-class '%s' ..." %
40- (base_class .__name__ , cls .__name__ ))
41- continue
42- for x in cls .__dict__ :
43- if not (x in base_class .__dict__ and strategy == 'safe' ):
44- # instance method
45- if isinstance (cls .__dict__ [x ], (types .FunctionType , classmethod , staticmethod , property )):
46- # build hook table
47- if not (base_class .__name__ in self ._hook_table and
48- isinstance (self ._hook_table [base_class .__name__ ], dict )):
49- self ._hook_table [base_class .__name__ ] = {}
50- self ._hook_table [base_class .__name__ ][x ] = \
51- (base_class .__dict__ [x ] if base_class .__dict__ .has_key (x ) else None , cls .__dict__ [x ])
52- logger .debug ("Wrapping [%s] %s" % (x , str (cls .__dict__ [x ])))
53- setattr (base_class , x , cls .__dict__ [x ])
54- else :
55- logger .debug ("Skip wrapping [%s] %s for security consideration" % (x , str (cls .__dict__ [x ])))
5+ HOOK_TABLE_NAME = '__ZX_PY_HOOK_TABLE__'
566
57- def unhook (self ):
58- cls = self ._hook_class
59- base_classes = cls .__bases__
60- for base_class in base_classes :
61- if base_class .__name__ in self ._hook_table :
62- _sub_hook_table = self ._hook_table [base_class .__name__ ]
63- for x in cls .__dict__ :
64- if x in _sub_hook_table :
65- if _sub_hook_table [x ][1 ] != cls .__dict__ [x ]:
66- logger .error ("Error! This hook is not installed by this helper. [%s] %s != %s " %
67- (x , _sub_hook_table [x ][1 ], str (cls .__dict__ [x ])))
68- continue
69- if _sub_hook_table [x ][0 ] is None :
70- logger .debug ("Delete [%s] %s for unhooking" % (x , str (cls .__dict__ [x ])))
71- delattr (base_class , x )
72- else :
73- logger .debug ("Recover [%s] %s to %s for unhooking" %
74- (x , str (cls .__dict__ [x ]), str (_sub_hook_table [x ][0 ])))
75- setattr (base_class , x , _sub_hook_table [x ][0 ])
76-
77-
78- """
79- Deprecated Code, subject to elimination in the near future.
80- """
7+ logger = logging .getLogger (__name__ )
818
829
8310def register_class_hook (cls , strategy = 'safe' , skip_buildin = True ):
@@ -95,11 +22,23 @@ def register_class_hook(cls, strategy='safe', skip_buildin=True):
9522 logger .warn ("Skip hooking build-in base class '%s' in sub-class '%s' ..." %
9623 (base_class .__name__ , cls .__name__ ))
9724 continue
25+ if HOOK_TABLE_NAME in base_class .__dict__ :
26+ logger .warn ("Skip hooking the base class %s to avoid multi-hook conflict ..." %
27+ base_class .__name__ )
28+ continue
29+ else :
30+ # build HOOK_TABLE
31+ setattr (base_class , HOOK_TABLE_NAME , {})
32+ assert HOOK_TABLE_NAME in base_class .__dict__ , "Unable to append hook table to target class"
9833 for x in cls .__dict__ :
9934 if not (x in base_class .__dict__ and strategy == 'safe' ):
10035 # instance method
10136 if isinstance (cls .__dict__ [x ], (types .FunctionType , classmethod , staticmethod , property )):
10237 logger .debug ("Wrapping [%s] %s" % (x , str (cls .__dict__ [x ])))
38+ # Append hook table
39+ base_class .__dict__ [HOOK_TABLE_NAME ][x ] = base_class .__dict__ [x ] \
40+ if x in base_class .__dict__ else None
41+ # Set hook
10342 setattr (base_class , x , cls .__dict__ [x ])
10443 else :
10544 logger .debug ("Skip wrapping [%s] %s for security consideration" % (x , str (cls .__dict__ [x ])))
@@ -114,43 +53,14 @@ def deregister_class_hook(cls):
11453 "The input object must be a class object"
11554 base_classes = cls .__bases__
11655 for base_class in base_classes :
117- for x in cls .__dict__ :
118- if x in base_class .__dict__ :
119- # instance method
120- if cls .__dict__ [x ] == base_class .__dict__ [x ] and \
121- isinstance (cls .__dict__ [x ], (types .FunctionType , classmethod , staticmethod , property )):
122- logger .debug ("Remove warping [%s] %s" % (x , str (cls .__dict__ [x ])))
123- delattr (base_class , x )
124-
125-
126- def register_module_hook (class_name_list = [], allow_recursive = False , ** kwargs ):
127- """
128- Enumerate all classes in the caller module, hook all or selected classes' base
129- classes with specific module
130- :param class_name_list: given the class list you'd like to hook
131- :return:
132- """
133- try :
134- #
135- # Fetch the caller module
136- #
137- parent_frame = inspect .stack ()[1 ][0 ]
138- caller_module = inspect .getmodule (parent_frame )
139- class_list = [v for k , v in caller_module .__dict__ .iteritems ()
140- if ((k in class_name_list ) or not class_name_list ) and
141- isinstance (v , (type , types .ClassType ))]
142- base_classes_list = reduce (lambda x , y : x + list (y .__bases__ ), class_list , [])
143- if not allow_recursive :
144- class_list = filter (lambda x : x not in base_classes_list , class_list )
145- except Exception , e :
146- logger .error ("Unable to retrieve the caller module : %s" % str (e ))
147- return
148- for c in class_list :
149- try :
150- #
151- # Do the class hook
152- #
153- register_class_hook (c , ** kwargs )
154- except Exception , e :
155- logger .error ("Unable to register class hook" )
56+ if HOOK_TABLE_NAME not in base_class .__dict__ :
57+ logger .warn ("Unable to find Hook Table for class '%s', "
58+ "probably it's not properly hooked." % base_class .__name__ )
15659 continue
60+ _hook_table = base_class .__dict__ [HOOK_TABLE_NAME ]
61+ for x in _hook_table :
62+ if _hook_table [x ]:
63+ setattr (base_class , x , _hook_table [x ])
64+ else :
65+ delattr (base_class , x )
66+ delattr (base_class , HOOK_TABLE_NAME )
0 commit comments