konradhalas / dacite

Simple creation of data classes from dictionaries.
MIT License
1.72k stars 107 forks source link

Feature proposal: Ignore optional fields #113

Closed andreas-vester closed 3 years ago

andreas-vester commented 3 years ago

In general, if you have some Optional fields in your dict without providing input data for this field, dacite will automatically attach the None value.

Here's the example right from the docs (https://pypi.org/project/dacite/):

from typing import Optional

@dataclass
class A:
    x: str
    y: Optional[int]

data = {
    'x': 'test',
}

result = from_dict(data_class=A, data=data)

assert result == A(x='test', y=None)

I need to get rid of such an empty Optional field all together. I am looking for something like this: assert result == A(x='test')

konradhalas commented 3 years ago

Hi @KraxelHuber - I think you are looking for the default value for y field, something like:

from typing import Optional

@dataclass
class A:
    x: str
    y: Optional[int] = None

data = {
    'x': 'test',
}

result = from_dict(data_class=A, data=data)

assert result == A(x='test')

Am I right?

andreas-vester commented 3 years ago

@konradhalas No, that's not what I meant. However, you couldn't know as I put in an incorrect assertion. Sorry for that!

What I meant is that result must not have the attribute y if it has not been assigned explicitely.

In fact, I am looking for this test to pass.

from dataclasses import dataclass
from typing import Optional

import pytest
from dacite import from_dict

@dataclass
class A:
    x: str
    y: Optional[int] = None

def test_A():
    data = {'x': 'test'}
    result = from_dict(data_class=A, data=data)

    with pytest.raises(AttributeError):
        result.y

My reasoning is as follows: I may have quite a large number of optional arguments in my dataclass. When I apply from_dict, I want the result to be "clutter-free", i.e. I only want the arguments I explicitely entered to be attributes.

Does that make sense?

konradhalas commented 3 years ago

@KraxelHuber ok, now I get it :)

Unfortunately it's not possible - not because dacite doesn't support it, but because data class doesn't work that way. You can not have a field that appears only whey you assign value.

You can try something like (pseudo code):

class IsRequired(Enum):
    NOT_REQUIRED = "NOT_REQUIRED"

@dataclass()
class X:
    f: Union[int, IsRequired] = IsRequired.NOT_REQUIRED

    def __getattribute__(self, name: str) -> Any:
        value = super().__getattribute__(name)
        if value is IsRequired.NOT_REQUIRED:
            raise AttributeError("...")
        return value

... but it looks very ugly.