Omit `None` values in TOML instead of throwing TypeError
jonaslb opened this issue · 4 comments
Is your feature request related to a problem? Please describe.
Example:
@dataclass
class MyExampleConfig(DataClassTOMLMixin):
username: str | None = None
MyExampleConfig().to_toml()
Gives the following exception:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-9-68a219996b72> in <module>
----> 1 MyExampleConfig().to_toml()
~/.local/lib/python3.11/site-packages/mashumaro/core/meta/builder.py in to_toml(self, encoder)
~/.local/lib/python3.11/site-packages/tomli_w/_writer.py in dumps(__obj, multiline_strings)
37 def dumps(__obj: dict[str, Any], *, multiline_strings: bool = False) -> str:
38 ctx = Context(multiline_strings, {})
---> 39 return "".join(gen_table_chunks(__obj, ctx, name=""))
40
41
~/.local/lib/python3.11/site-packages/tomli_w/_writer.py in gen_table_chunks(table, ctx, name, inside_aot)
71 yielded = True
72 for k, v in literals:
---> 73 yield f"{format_key_part(k)} = {format_literal(v, ctx)}\n"
74
75 for k, v, in_aot in tables:
~/.local/lib/python3.11/site-packages/tomli_w/_writer.py in format_literal(obj, ctx, nest_level)
100 if isinstance(obj, dict):
101 return format_inline_table(obj, ctx)
--> 102 raise TypeError(f"Object of type {type(obj)} is not TOML serializable")
103
104
TypeError: Object of type <class 'NoneType'> is not TOML serializable
Describe the solution you'd like
It seems that TOML plainly does not represent None
values, preferring instead to omit such keys (see toml-lang/toml#30 ). That would be perfectly fine since None
in Python is used to indicate missing value. Thus I think the default behavior for TOML should be to just not serialize the None value instead of giving this TypeError
.
Describe alternatives you've considered
Currently it seems one needs to use another format e.g. yaml or json if the dataclass has any possibly None
values.
I'm stupid, there's an omit_none
option, ha.
Good spot!
At this moment you can use omit_none
code generation option, as you mentioned, but you will need to set omit_none=True
each time you call to_toml
with it.
I think it’s time to add a new omit_none
option to the Config
and Dialect
classes to change the default behavior. It could be set to True
in TOMLDialect
that is used to generate to_toml
method. With this new feature None
values could be skipped out of the box for TOML.
Let me reopen this issue to keep track of it for the next release.
I have made some changes, which I wrote about above. Now using DataClassTOMLMixin
will be more convenient:
@dataclass
class InnerDataClassWithOptionalField(DataClassTOMLMixin):
x: Optional[int] = None
@dataclass
class DataClass(DataClassTOMLMixin):
x: Optional[InnerDataClassWithOptionalField] = None
y: Optional[int] = None
obj = DataClass()
assert obj.to_dict() == {"x": None, "y": None}
assert obj.to_toml() == ""
obj = DataClass(InnerDataClassWithOptionalField())
assert obj.to_dict() == {"x": {"x": None}, "y": None}
assert obj.to_toml() == "[x]\n"
assert DataClass.from_toml("[x]\n") == obj