0

Goal:
I have a class with a method that I need to decorate with a function that save data to a file.

If I set a fixed filename in the dataclass it works, but every instance of the dataclass "OG" will have the same file overwritten which is not OK.

I would like to make it variable for each instance.

So the filename will be calculated after class OG init using "field(init=False)" and "post_init" to define a custom filename depending on the "device" attribute:

def write_json(data, filename):
    try:
        with open(filename, 'w') as outfile:
            json.dump(data, outfile)
            return True
    except:
        return False
    
def backup_to_json(filename, write_function):
    def inner(func):
        def wrapper(*args, **kwargs):
            data = func(*args, **kwargs)
            success = write_function(data, filename)
            if success:
                print(f'Backup saved to {filename}')
            else:
                print(f'Failed to save backup to {filename}')
                exit(1)
            return data
        return wrapper
    return inner

@dataclass
class OG:
    device: Devices  # this is an object
    write_function = write_json
    filename: str = field(init=False)

    def __post_init__(self):
        self.filename = str(self.device["name"]) + "_backup.json"
   
    @backup_to_json(filename=filename, write_function=write_function)
    def get_local_context(self):
        return self.device.local_context_data
        
        
dev = {"name": "mydev"}
dev_status, dev_result = session.get_device(**dev)
if dev_status:    
    dev_obj = OG(device=dev_result)
    local_context = dev_obj.get_local_context()        

Expected result:
Each new device should have its own backup file:
dev = {"name": "mydev1"} >> mydev1__backup.json
dev = {"name": "mydev2"} >> mydev2__backup.json

Issue:
I have this error:

Failed to save backup to Field(name='filename',type=<class 'str'>,default=<dataclasses._MISSING_TYPE object at 0x000001F0A613AD30>,default_factory=<dataclasses._MISSING_TYPE object at 0x000001F0A613AD30>,init=False,repr=True,hash=None,compare=True,metadata=mappingproxy({}),_field_type=_FIELD)

It looks like I am getting an object and not the calculated filename string

Any hint?

AJN
  • 1,196
  • 2
  • 19
  • 47
  • because whn you run the decorator, `filename` is a `field` object, which is what `filename: str = field(init=False)` line does – juanpa.arrivillaga Mar 06 '23 at 18:21
  • yes, when using datacalsses, if one attribut depends on another, I have to use "field(init=False)" to make the filename not initialized at init but later when "__post_init__" is called and there I can refer to other attributes. – AJN Mar 06 '23 at 22:21
  • You are asking how to access instance state in a method decorator. – juanpa.arrivillaga Mar 07 '23 at 02:14
  • https://stackoverflow.com/users/214143/jason-aller the post you mention does not answer the question, because it doesn't handle the case of a decorator that uses an attribute of the class it decorates. So i vote to reopen the question – AJN Mar 12 '23 at 16:17
  • 1
    It *does* answer it. In your `wrapper` functions, `args[0]` will be the instance, so you can use `args[0].filename` – juanpa.arrivillaga Mar 12 '23 at 21:22
  • @juanpa.arrivillaga Can you post your answer I will mark it as the "solution" – AJN Apr 01 '23 at 14:18

0 Answers0