Nikita Vlasenko
Nikita Vlasenko

Reputation: 4352

Implement python decorator for passing down the function to another function

I want to design a python decorator that takes a function which is decorated and passes it down to another function. Here is code explanation of what I am trying to do:

def run_decorator(run_batch):                                                                                                
    # inner function can access the outer local                                                                            
    # functions like in this case "func"                                                                                   
    def check(command_to_run):                                                                                             
        @functools.wraps(command_to_run)                                                                                             
        def wrapper(*args, **kwargs):
            batch_json_path = kwargs['batch_json_path']                                                                    
            batch_name = kwargs['batch_name']                                                                              
            folder_path = kwargs['folder_path']                                                                            
            if batch_json_path is not None:                                                                                
                if batch_present(batch_json_path, batch_name):
                    run_batch(batch_json_path, command_to_run, batch_name)
         return wrapper
    return check

def run_batch(batch_abs_path, command_to_run, batch_name=None):
    with open(batch_abs_path) as json_file:
        variant = ...
        tag_data = ...
        command_to_run(variant, batch_name, tag_data)

@run_decorator(run_batch=run_batch)                                                                                                         
def load_tag_for_variant(variant, batch_name, tag_data):

Is such behavior possible to achieve? Any suggestions would be greatly appreciated.

Upvotes: 2

Views: 224

Answers (1)

Glenn D.J.
Glenn D.J.

Reputation: 1965

You indicated in a comment: I need to pass to the decorator 3 parameters: batch_json_path, batch_name and folder_path which are not the parameters of the load_tag_for_variant function, but should somehow be fed into the decorator / load_tag_for_variant at the moment program runs..

I understand from that, that you want to pass in those arguments at decoration time. To do this you should pass them to the run_decorator function.

def run_decorator(run_batch, batch_json_path, batch_name, folder_path):                                                                                                                                                                               
    def check(command_to_run):                                                                                             
        @functools.wraps(command_to_run)                                                                                             
        def wrapper(*args, **kwargs):                                                                        
            if batch_json_path is not None:                                                                                
                if batch_present(batch_json_path, batch_name):
                    run_batch(batch_json_path, batch_name, command_to_run, *args, **kwargs)
         return wrapper
    return check
    
@run_decorator(run_batch, batch_json_path="a_path", batch_name="a_name", folder_path="a_path")                                                                                                         
def load_tag_for_variant(variant, batch_name, tag_data):
    ...

You also want to pass the args (variant, batch_name, tag_data) and kwargs with which you call the decorated function on to the original function (load_tag_for_variant/command_to_run). With the example function you have this can be done like this:

def run_batch(batch_abs_path, batch_name, command_to_run, *args, **kwargs):
    command_to_run(*args, **kwargs)
    ...

Given that you have batch_name in there twice, I'm not sure whether you want this defined at decoration time or at function invocation. If you just want to get it from the kwargs for instance, you can do kwargs.get('batch_name').

Upvotes: 1

Related Questions