Ayuto

Python detours

Dec 7th, 2013
222
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 2.93 KB | None | 0 0
  1. class HookType:
  2.     '''
  3.    Pre  - Calls the callback before the orignal function
  4.    Post - Calls the callback after the orignal function
  5.    '''
  6.  
  7.     Pre, Post = range(2)
  8.  
  9.  
  10. class Override(object):
  11.     '''
  12.    Return an object of this class to use a custom return value.
  13.  
  14.    EXAMPLES:
  15.  
  16.    Override()     -> None
  17.    Override(1)    -> 1
  18.    Override(1, 2) -> (1, 2)
  19.    '''
  20.  
  21.     def __init__(self, *args):
  22.         self.args = args
  23.  
  24.     def getReturnValue(self):
  25.         return self.args[0] if len(self.args) == 1 else self.args or None
  26.  
  27.  
  28. class Detour(object):
  29.     '''
  30.    Detours a function by overriding it with a Detour instance.
  31.  
  32.    EXAMPLE:
  33.  
  34.    def myfunc(x, y):
  35.        print 'Original function:', (x, y)
  36.        return x + y
  37.  
  38.    def mycallback(args, kw):
  39.        print 'Detour callback:', (args, kw)
  40.  
  41.        # Set the first parameter to 10
  42.        args[0] = 10
  43.  
  44.    myfunc = Detour.detourFunction(myfunc, HookType.Pre, mycallback)
  45.    print myfunc(1, 2), type(myfunc)
  46.  
  47.    myfunc = Detour.undetourFunction(myfunc, HookType.Pre, mycallback)
  48.    print myfunc(1, 2), type(myfunc)
  49.  
  50.    OUTPUT:
  51.    >>> Detour callback: ([1, 2], {})
  52.    >>> Original function: (10, 2)
  53.    >>> 12 <class 'Detour'>
  54.    >>> Original function: (1, 2)
  55.    >>> 3 <type 'function'>
  56.    '''
  57.  
  58.     def __new__(cls, func, hooktype, callback):
  59.         # Function already detoured?
  60.         if isinstance(func, Detour):
  61.             func.callbacks[hooktype].add(callback)
  62.             return func
  63.  
  64.         # Create a detour
  65.         self = super(Detour, cls).__new__(cls, func, hooktype, callback)
  66.         self.trampoline = func
  67.         self.callbacks  = {HookType.Pre: set(), HookType.Post: set()}
  68.         return self
  69.  
  70.     def __init__(self, func, hooktype, callback):
  71.         self.callbacks[hooktype].add(callback)
  72.  
  73.     def __call__(self, *args, **kw):
  74.         # Convert "args" into a list, so we can modify the parameters
  75.         args = list(args)
  76.         ret  = None
  77.  
  78.         # Do pre-calls
  79.         for callback in self.callbacks[HookType.Pre]:
  80.             temp = callback(args, kw)
  81.             if isinstance(temp, Override):
  82.                 ret = temp
  83.  
  84.         # Was an Override instance returned?
  85.         if ret is not None:
  86.             return ret.getReturnValue()
  87.  
  88.         # Call the trampoline
  89.         ret = self.trampoline(*args, **kw)
  90.  
  91.         # Do post-calls
  92.         for callback in self.callbacks[HookType.Post]:
  93.             temp = callback(args, kw, ret)
  94.             if isinstance(temp, Override):
  95.                 ret = temp
  96.  
  97.         return ret.getReturnValue() if isinstance(ret, Override) else ret
  98.  
  99.     @classmethod
  100.     def detourFunction(cls, func, hooktype, callback):
  101.         return cls(func, hooktype, callback)
  102.  
  103.     @staticmethod
  104.     def undetourFunction(func, hooktype, callback):
  105.         func.callbacks[hooktype].discard(callback)
  106.         return func if any(func.callbacks.values()) else func.trampoline
Advertisement
Add Comment
Please, Sign In to add comment