2

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.

Nikita Vlasenko
  • 4,004
  • 7
  • 47
  • 87
  • Would this help? https://stackoverflow.com/questions/27342149/decorator-execution-order – Kate Melnykova Sep 29 '20 at 01:08
  • For me, code is very reasonable and should work. What is exactly your problem? – Kate Melnykova Sep 29 '20 at 01:08
  • The main problem, I guess, is that I am not fully sure how to pass parameters to both functions (run_batch and command_to_run) at the same time. – Nikita Vlasenko Sep 29 '20 at 13:06
  • 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. – Nikita Vlasenko Sep 29 '20 at 13:23

1 Answers1

1

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').

Glenn D.J.
  • 1,874
  • 1
  • 8
  • 19