pythonpython-attrs

Python attrs nested objects convertes


I'm using attrs lib to parse my configuration file, and I'm looking for a better way to parse nested data objects.

Example :

from attrs import define, field, validators
from typing import Dict

class HoconParser:
    @classmethod
    def from_hocon(cls, file_path):
        from pyhocon import ConfigFactory

        return cls(
            **ConfigFactory.parse_file(file_path)
        )
    
    @classmethod
    def from_dictstrobj(cls, dictstrobj):
        
        if dictstrobj:
            return {
                key: cls(**value) for key, value in dictstrobj.items()
            }
        return {}
@define
class ClassOne(HoconParser):
    id: str = field(validator=validators.instance_of(str))
    name: str = field(validator=validators.instance_of(str))
    
@define
class ClassTwo(HoconParser):
    id: str = field(validator=validators.instance_of(str))
    name: str = field(validator=validators.instance_of(str))
    test: Dict[str, ClassOne] = field(
        converter=ClassOne.from_dictstrobj,
        validator=validators.deep_mapping(
            key_validator=validators.instance_of(str),
            value_validator=validators.instance_of(ClassOne)
        )
    )
    
a = ClassTwo.from_hocon("test.conf")

Basically, ClassTwo has an attribute which is a Dict[str, ClassOne].

In order to make this work, I had to make a specific function named from_dictstrobj and use it as a converter for test attribute.

Is there any better way to do this ?


Solution

  • I'm a major contributor to attrs and the author of cattrs.

    You might want to use the cattrs library here. I'm assuming ConfigFactory.parse_file(file_path) produces a dictionary?

    cattrs can recursively transform this dictionary into a graph of objects.

    Here's the modified code:

    from cattrs import structure
    
    class HoconParser:
        @classmethod
        def from_hocon(cls, file_path):
            from pyhocon import ConfigFactory
    
            return structure(ConfigFactory.parse_file(file_path), cls)
    

    Hope this helps!