Search code examples
pythonjsonpython-attrs

cattrs : can I use a json converter in a class method to instantiate an object from a json file?


I have been an extensive user of attrs for two years, but I have discovered cattrs only recently so my question may sound naive.

In my codes, I often use a class method to instantiate an attrs object from a json file. For example :

from cattrs.preconf.json import make_converter
from attrs import field, define, validators

from enum import StrEnum, auto
from pathlib import Path

class Colors(StrEnum):
    BLUE = auto()
    RED = auto()
    GREEN = auto()


@define
class A:
    color: Colors = field(validator=validators.instance_of(Colors))
    value: int = field(validator=validators.instance_of(int))

    @classmethod
    def from_json_file(cls, path: Path):
        with open(path, 'r') as f:
            data = json.load(f)
        return cls(color=Colors(data["color"]), value=data["value"])

I was wondering if I could use the power of ¢attrs and the json converter in the following way :

    @classmethod
    def from_json_file(cls, path: Path):
        json_converter = make_converter()
        return json_converter.loads(path.read_text(), cls)

The code works as expected but does it respect the principle of cattrs to separate the class logic from the un/structuring rules ?


Solution

  • No. Creating a converter instance within the class method violates cattrs' principle of separation between class logic and un/structuring rules. The conversion configuration should be maintained externally.

    converter = make_converter()
    converter.register_structure_hook(Colors, lambda v, _: Colors(v))
    
    @define
    class A:
        color: Colors = field(validator=validators.instance_of(Colors))
        value: int = field(validator=validators.instance_of(int))
    
    def load_a_from_json(path: Path) -> A:
        return converter.loads(path.read_text(), A)