Arthur
Arthur

Reputation: 3473

Method injecting not working

I am trying to inject my method instead of another method but it works only with methods with small count of arguments.

My injector:

def inject(target_function, new_function):
     @wraps(target_function)
     def _inject(*args, **kwargs):
         return new_function(target_function, *args, **kwargs)

     return _inject  

# decorator injection.
def inject_to(target_object, target_function_name):

     def _inject_to(new_function):
         target_function = getattr(target_object, target_function_name)
         setattr(target_object, target_function_name, inject(target_function, new_function))
         return new_function

     return _inject_to

Original method, that I want to replace with my method:

@macros.macro
class Logger:
    __qualname__ = 'Logger'
    suppress = False

    def exception(self, message, *args, exc=None, log_current_callstack=True, level=150, owner=None, trigger_breakpoint=False):
        if Logger.suppress:
            return

My method:

@injector.inject_to(Logger, 'exception')
def exception(original, self, *args, **kwargs):

    f.writeLine("Logger.exception !!!")
    f.writeLine("original" + str(original))
    f.writeLine("self" + str(self))
    f.writeLine("args" + str(*args))
    f.writeLine("kwargs" + str(**kwargs))

This injector works fine with functions like def update_progress(self):, but for Logger.exception it doesn't work.

Interesting moment - when my method was injected, it doesn't work and original method Logger.exception doesn't executed too.

So, can I ask you two questions?

  1. How to correctly replace Logger.exception with my method?
  2. How to correctly call original Logger.exception in my method, how to correctly pass arguments to the original function?

Upvotes: 1

Views: 314

Answers (1)

Mad Physicist
Mad Physicist

Reputation: 114330

The fundamental problem you have is that you assign the output of inject to a method attribute. The result of inject always accepts the original function as its first argument, while a method is always invoked with self as the first parameter. There is no amount of decorators in the world that will change this for you, so you will have to work around it.

Whatever inject_to assigns in the line setattr(target_object, target_function_name, ...) must accept self as a first parameter. Just rewrite _inject to accept self first. Perhaps allow both versions and add a boolean parameter to inject. Also change the order of the arguments to your version of exception:

def inject(target_function, new_function, method=False):
    if method:
        @wraps(target_function)
        def _inject(self, *args, **kwargs):
            return new_function(self, target_function, *args, **kwargs)
    else:
        @wraps(target_function)
        def _inject(*args, **kwargs):
            return new_function(target_function, *args, **kwargs)

    return _inject

...
setattr(target_object, target_function_name, inject(target_function, new_function, method=True))
...

@injector.inject_to(Logger, 'exception')
def exception(self, original, *args, **kwargs):
    ...

Upvotes: 3

Related Questions