Skip to content

Commit

Permalink
update docs for TOMLWizard
Browse files Browse the repository at this point in the history
  • Loading branch information
rnag committed Nov 14, 2024
1 parent 7b25ef0 commit 8c91519
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 13 deletions.
2 changes: 2 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ In addition to the ``JSONWizard``, here are a few extra Mixin_ classes that migh

* `JSONListWizard`_ -- Extends ``JSONWizard`` to return `Container`_ -- instead of *list* -- objects where possible.
* `JSONFileWizard`_ -- Makes it easier to convert dataclass instances from/to JSON files on a local drive.
* `TOMLWizard`_ -- Provides support to convert dataclass instances to/from TOML.
* `YAMLWizard`_ -- Provides support to convert dataclass instances to/from YAML, using the default ``PyYAML`` parser.


Expand Down Expand Up @@ -961,6 +962,7 @@ This package was created with Cookiecutter_ and the `rnag/cookiecutter-pypackage
.. _`open an issue`: https://github.com/rnag/dataclass-wizard/issues
.. _`JSONListWizard`: https://dataclass-wizard.readthedocs.io/en/latest/common_use_cases/wizard_mixins.html#jsonlistwizard
.. _`JSONFileWizard`: https://dataclass-wizard.readthedocs.io/en/latest/common_use_cases/wizard_mixins.html#jsonfilewizard
.. _`TOMLWizard`: https://dataclass-wizard.readthedocs.io/en/latest/common_use_cases/wizard_mixins.html#tomlwizard
.. _`YAMLWizard`: https://dataclass-wizard.readthedocs.io/en/latest/common_use_cases/wizard_mixins.html#yamlwizard
.. _`Container`: https://dataclass-wizard.readthedocs.io/en/latest/dataclass_wizard.html#dataclass_wizard.Container
.. _`Supported Types`: https://dataclass-wizard.readthedocs.io/en/latest/overview.html#supported-types
Expand Down
28 changes: 17 additions & 11 deletions dataclass_wizard/wizard_mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,8 @@ def from_toml(cls: Type[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 ``header`` is provided and the corresponding value in the parsed
data is a ``list``, the return type is ``List[T]``.
"""
if decoder is None: # pragma: no cover
decoder = toml.loads
Expand All @@ -149,11 +149,11 @@ def from_toml_file(cls: Type[T], file: str, *,
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.
Reads the contents of a TOML file and converts them
into an instance (or list of instances) of the dataclass.
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]``.
Similar to :meth:`from_toml`, it can return a list if ``header``
is specified and points to a list in the TOML data.
"""
if decoder is None: # pragma: no cover
decoder = toml.load
Expand All @@ -171,7 +171,11 @@ def to_toml(self: T,
multiline_strings: bool = False,
indent: int = 4) -> AnyStr:
"""
Converts the dataclass instance to a TOML `string` representation.
Converts a dataclass instance to a TOML `string`.
Optional parameters include ``multiline_strings``
for enabling/disabling multiline formatting of strings,
and ``indent`` for setting the indentation level.
"""
if encoder is None: # pragma: no cover
encoder = toml_w.dumps
Expand All @@ -180,12 +184,14 @@ def to_toml(self: T,
multiline_strings=multiline_strings,
indent=indent)

def to_toml_file(self: T, file: str, mode: str = 'w',
def to_toml_file(self: T, file: str, mode: str = 'wb',
encoder: Optional[FileEncoder] = None,
multiline_strings: bool = False,
indent: int = 4) -> None:
"""
Serializes the instance and writes it to a TOML file.
Serializes a dataclass instance and writes it to a TOML file.
By default, opens the file in "write binary" mode.
"""
if encoder is None: # pragma: no cover
encoder = toml_w.dump
Expand All @@ -202,8 +208,8 @@ def list_to_toml(cls: Type[T],
encoder: Optional[Encoder] = None,
**encoder_kwargs) -> AnyStr:
"""
Converts a ``list`` of dataclass instances to a TOML `string`
representation.
Serializes a ``list`` of dataclass instances into a TOML `string`,
grouped under a specified header.
"""
if encoder is None:
encoder = toml_w.dumps
Expand Down
145 changes: 145 additions & 0 deletions docs/common_use_cases/wizard_mixins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -191,3 +191,148 @@ A (mostly) complete example of using the :class:`YAMLWizard` is as follows:
# ...
.. _PyYAML: https://pypi.org/project/PyYAML/

:class:`TOMLWizard`
~~~~~~~~~~~~~~~~~~~

The TOML Wizard provides an easy, convenient interface for converting ``dataclass`` instances to/from `TOML`_. This mixin enables simple loading, saving, and flexible serialization of TOML data, including support for custom key casing transforms.

.. note::
By default, *NO* key transform is used in the TOML dump process. This means that a `snake_case` field name in Python is saved as `snake_case` in TOML. However, this can be customized without subclassing from :class:`JSONWizard`, as below.

>>> @dataclass
>>> class MyClass(TOMLWizard, key_transform='CAMEL'):
>>> ...

Dependencies
------------
- For reading TOML, `TOMLWizard` uses `Tomli`_ for Python 3.9 and 3.10, and the built-in `tomllib`_ for Python 3.11+.
- For writing TOML, `Tomli-W`_ is used across all Python versions.

.. _TOML: https://toml.io/en/
.. _Tomli: https://pypi.org/project/tomli/
.. _Tomli-W: https://pypi.org/project/tomli-w/
.. _tomllib: https://docs.python.org/3/library/tomllib.html

Example
-------

A (mostly) complete example of using the :class:`TOMLWizard` is as follows:

.. code:: python3
from dataclasses import dataclass, field
from dataclass_wizard import TOMLWizard
@dataclass
class InnerData:
my_float: float
my_list: list[str] = field(default_factory=list)
@dataclass
class MyData(TOMLWizard):
my_str: str
my_dict: dict[str, int] = field(default_factory=dict)
inner_data: InnerData = field(default_factory=lambda: InnerData(3.14, ["hello", "world"]))
# TOML input string with nested tables and lists
toml_string = """
my_str = 'example'
[my_dict]
key1 = 1
key2 = '2'
[inner_data]
my_float = 2.718
my_list = ['apple', 'banana', 'cherry']
"""
# Load from TOML string
data = MyData.from_toml(toml_string)
# Sample output of `data` after loading from TOML:
#> my_str = 'example'
#> my_dict = {'key1': 1, 'key2': 2}
#> inner_data = InnerData(my_float=2.718, my_list=['apple', 'banana', 'cherry'])
# Save to TOML file
data.to_toml_file('data.toml')
# Now read it back from the TOML file
new_data = MyData.from_toml_file('data.toml')
# Assert we get back the same data
assert data == new_data, "Data read from TOML file does not match the original."
# Create a list of dataclass instances
data_list = [data, new_data, MyData("another_example", {"key3": 3}, InnerData(1.618, ["one", "two"]))]
# Serialize the list to a TOML string
toml_output = MyData.list_to_toml(data_list, header='testing')
print(toml_output)
# [[testing]]
# my_str = "example"
#
# [testing.my_dict]
# key1 = 1
# key2 = 2
#
# [testing.inner_data]
# my_float = 2.718
# my_list = [
# "apple",
# "banana",
# "cherry",
# ]
# ...
This approach provides a straightforward way to handle TOML data within Python dataclasses.

Methods
-------

.. method:: from_toml(cls, string_or_stream, *, decoder=None, header='items', parse_float=float)

Parses a TOML `string` or stream and converts it into an instance (or list of instances) of the dataclass. If `header` is provided and the corresponding value in the parsed data is a list, the return type is `List[T]`.

**Example usage:**

>>> data_str = '''my_str = "test"\n[inner]\nmy_float = 1.2'''
>>> obj = MyClass.from_toml(data_str)

.. method:: from_toml_file(cls, file, *, decoder=None, header='items', parse_float=float)

Reads the contents of a TOML file and converts them into an instance (or list of instances) of the dataclass. Similar to :meth:`from_toml`, it can return a list if `header` is specified and points to a list in the TOML data.

**Example usage:**

>>> obj = MyClass.from_toml_file('config.toml')

.. method:: to_toml(self, /, *encoder_args, encoder=None, multiline_strings=False, indent=4)

Converts a dataclass instance to a TOML string. Optional parameters include `multiline_strings` for enabling/disabling multiline formatting of strings and `indent` for setting the indentation level.

**Example usage:**

>>> toml_str = obj.to_toml()

.. method:: to_toml_file(self, file, mode='wb', encoder=None, multiline_strings=False, indent=4)

Serializes a dataclass instance and writes it to a TOML file. By default, opens the file in "write binary" mode.

**Example usage:**

>>> obj.to_toml_file('output.toml')

.. method:: list_to_toml(cls, instances, header='items', encoder=None, **encoder_kwargs)

Serializes a list of dataclass instances into a TOML string, grouped under a specified `header`.

**Example usage:**

>>> obj_list = [MyClass(), MyClass(my_str="example")]
>>> toml_str = MyClass.list_to_toml(obj_list)
7 changes: 5 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,11 @@
tests_require=test_requirements,
extras_require={
'timedelta': ['pytimeparse>=1.1.7'],
'toml': ['tomli>=2,<3; python_version == "3.9" or python_version == "3.10"',
'tomli-w>=1,<2'],
'toml': [
'tomli>=2,<3; python_version=="3.9"',
'tomli>=2,<3; python_version=="3.10"',
'tomli-w>=1,<2'
],
'yaml': ['PyYAML>=6,<7'],
'dev': dev_requires + doc_requires + test_requirements,
},
Expand Down

0 comments on commit 8c91519

Please sign in to comment.