diff --git a/dataclass_wizard/type_def.py b/dataclass_wizard/type_def.py index a75d5a82..db981f9d 100644 --- a/dataclass_wizard/type_def.py +++ b/dataclass_wizard/type_def.py @@ -16,6 +16,7 @@ 'JSONObject', 'ListOfJSONObject', 'JSONValue', + 'ParseFloat', 'Encoder', 'FileEncoder', 'Decoder', @@ -165,6 +166,9 @@ def __repr__(self): # Create the singleton instance ExplicitNull = ExplicitNullType() +# Type annotations +ParseFloat = Callable[[str], Any] + class Encoder(PyProtocol): """ diff --git a/dataclass_wizard/wizard_mixins.py b/dataclass_wizard/wizard_mixins.py index 9485b5e6..979de6aa 100644 --- a/dataclass_wizard/wizard_mixins.py +++ b/dataclass_wizard/wizard_mixins.py @@ -19,7 +19,7 @@ from .models import Container from .serial_json import JSONSerializable from .type_def import (T, ListOfJSONObject, - Encoder, Decoder, FileDecoder, FileEncoder) + Encoder, Decoder, FileDecoder, FileEncoder, ParseFloat) class JSONListWizard(JSONSerializable, str=False): @@ -125,33 +125,44 @@ def __init_subclass__(cls, key_transform=LetterCase.NONE): def from_toml(cls: Type[T], string_or_stream: Union[AnyStr, BinaryIO], *, decoder: Optional[Decoder] = None, - **decoder_kwargs) -> Union[T, List[T]]: + header: str = 'items', + parse_float: ParseFloat = float) -> Union[T, List[T]]: """ Converts a TOML `string` to an instance of the dataclass, or a list of the dataclass instances. + + If `header` is passed in, and the value of this key in the parsed + ``dict`` object is a ``list``, then the return type is a ``List[T]``. """ - if decoder is None: + if decoder is None: # pragma: no cover decoder = toml.loads - o = decoder(string_or_stream, **decoder_kwargs) + o = decoder(string_or_stream, parse_float=parse_float) - return fromdict(cls, o) if isinstance(o, dict) else fromlist(cls, o) + return (fromlist(cls, maybe_l) + if (maybe_l := o.get(header)) and isinstance(maybe_l, list) + else fromdict(cls, o)) @classmethod def from_toml_file(cls: Type[T], file: str, *, decoder: Optional[FileDecoder] = None, - **decoder_kwargs) -> Union[T, List[T]]: + header: str = 'items', + parse_float: ParseFloat = float) -> Union[T, List[T]]: """ Reads in the TOML file contents and converts to an instance of the dataclass, or a list of the dataclass instances. + + If `header` is passed in, and the value of this key in the parsed + ``dict`` object is a ``list``, then the return type is a ``List[T]``. """ - if decoder is None: + if decoder is None: # pragma: no cover decoder = toml.load with open(file, 'rb') as in_file: return cls.from_toml(in_file, decoder=decoder, - **decoder_kwargs) + header=header, + parse_float=parse_float) def to_toml(self: T, /, @@ -162,7 +173,7 @@ def to_toml(self: T, """ Converts the dataclass instance to a TOML `string` representation. """ - if encoder is None: + if encoder is None: # pragma: no cover encoder = toml_w.dumps return encoder(asdict(self), *encoder_args, @@ -176,7 +187,7 @@ def to_toml_file(self: T, file: str, mode: str = 'w', """ Serializes the instance and writes it to a TOML file. """ - if encoder is None: + if encoder is None: # pragma: no cover encoder = toml_w.dump with open(file, mode) as out_file: