Search code examples
pythondecoratorpython-decorators

Create a decorator that saves methods input - returns 'NoneType' object is not callable


Im currently using a server-less solution (ML -engine) , and i need from time to time to save data frames of a process in cloud storage. For that i created the method named save_current_data_frame_to_track and i would like to convert it into a decorator as following:

import numpy as np
import pandas as pd

def save_current_data_frame_to_track(current_data_frame, filename):
    current_data_frame.to_csv(filename + '.csv')
    pass

def save_input_to_track(func):
    def func_wrapper(*args, func):
        for arg in args:
            for key, value in locals():
                if type(value) == 'pandas.core.frame.DataFrame':
                    save_current_data_frame_to_track( value, key)
            return (func)
        return func_wrapper

rp = pd.DataFrame(data={'time_delta_from': [60, 90, 170],
                                'time_delta_to': [30, 60, 120]},
                        index=[1, 2, 3], dtype=np.int32)

@save_input_to_track
def add_1(data):
    data['time_delta_from'] = 1
    return data

add_1(rp)

Which gave me the following error:

 add_1(rp)
TypeError: 'NoneType' object is not callable

Why am i getting this error?


Solution

  • I recommend you to use the wraps, read details about how to get params of the wrapped function here. And looks like your code should be:

    import numpy as np
    import pandas as pd
    from functools import wraps
    
    def save_current_data_frame_to_track(current_data_frame, filename):
        current_data_frame.to_csv(filename + '.csv')
    
    def save_input_to_track(func):
        @wraps(func)
        def func_wrapper(*args, **kwargs):
            for arg in args:
                if isinstance(arg, pd.core.frame.DataFrame):
                    save_current_data_frame_to_track(arg, 'somefile')
            return func(*args)
        return func_wrapper
    
    rp = pd.DataFrame(data={'time_delta_from': [60, 90, 170],
                            'time_delta_to': [30, 60, 120]},
                      index=[1, 2, 3], dtype=np.int32)
    
    @save_input_to_track
    def add_1(data):
        data['time_delta_from'] = 1
        return data
    
    add_1(rp)
    

    for key name as filename, i think the simple solution is use kwarg, here example:

    import numpy as np
    import pandas as pd
    from functools import wraps
    
    def save_current_data_frame_to_track(current_data_frame, filename):
        current_data_frame.to_csv(filename + '.csv')
    
    def save_input_to_track(func):
        @wraps(func)
        def func_wrapper(*args, **kwargs):
            for key, kwarg in kwargs.items():
                if isinstance(kwarg, pd.core.frame.DataFrame):
                    save_current_data_frame_to_track(kwarg, key)
            return func(*args, **kwargs)
        return func_wrapper
    
    rp = pd.DataFrame(data={'time_delta_from': [60, 90, 170],
                            'time_delta_to': [30, 60, 120]},
                      index=[1, 2, 3], dtype=np.int32)
    
    @save_input_to_track
    def add_1(data1, data2):
        data1['time_delta_from'] = 1
        data2['time_delta_from'] = 2
        return data1, data2
    
    add_1(data1=rp, data2=rp)