Skip to content

Commit

Permalink
Merge pull request #49 from seapagan/fix-none-values
Browse files Browse the repository at this point in the history
Don't save `None` values to the TOML file.
  • Loading branch information
seapagan authored Nov 2, 2023
2 parents bbd4484 + 44c518c commit 34ac406
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 3 deletions.
4 changes: 3 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@
found then look in the users home directory.
- Add an Option to not include the `schema_version` key. By default this key
**will** be included.
- Migrate to Ruff for linting and formatting
- Allow a global config file to be used, which will be overridden by a local
config file if it exists.
- option to not save config options that have the same value as the default.
13 changes: 13 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,19 @@ settings = MySettings("my_app_name", settings_file_name="my_settings.toml")

## Using the settings

!!! danger "`None` values"

The library does not support saving `None` values. If you need to save a
`None` value, you should use a different value (such as an empty string or
`0`) and convert it to `None` in your app.

This is because TOML does not support `None` values and the library will
convert `None` values to `null` when saving the settings.

We may add support for this in the future, but for now you should avoid
using `None` values unless they are a default and will never need to be
saved - **any `None` value will not be saved to the config file**.

Once you have created your settings class, you can use it like any other class:

```python
Expand Down
9 changes: 7 additions & 2 deletions simple_toml_settings/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,19 @@ def __post_create_hook__(self) -> None:
never be called manually.
"""

def get_attrs(self) -> dict[str, str]:
"""Return a dictionary of our setting values."""
def get_attrs(self, *, include_none: bool = False) -> dict[str, str]:
"""Return a dictionary of our setting values.
Values that are None are EXCLUDED by default, but can be included by
setting 'include_none' to True.
"""
return {
a: getattr(self, a)
for a in dir(self)
if not a.startswith("_")
and a not in self._ignored_attrs
and not callable(getattr(self, a))
and (include_none or getattr(self, a) is not None)
}

def save(self) -> None:
Expand Down
33 changes: 33 additions & 0 deletions tests/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,40 @@ def test_get_attrs(settings) -> None:
assert attrs["test_int_var"] == SettingsExample.test_int_var


def test_get_attrs_ignores_none_by_default(settings) -> None:
"""Test that None attributes are not returned."""
settings.set("new_key", None)
attrs = settings.get_attrs()
assert "new_key" not in attrs


def test_save_ignores_none_values(settings) -> None:
"""Test that None attributes are not saved."""
settings.set("new_key", None)
settings.save()
attrs = settings.get_attrs()
assert "new_key" not in attrs


def test_get_attrs_returns_none_if_include_none_true(settings) -> None:
"""Test that None attributes are returned if required is True."""
settings.set("new_key", None)
attrs = settings.get_attrs(include_none=True)
assert attrs["new_key"] is None


def test_get(settings) -> None:
"""Test we can get settings."""
assert settings.get("app_name") == TEST_APP_NAME
assert settings.get("test_string_var") == SettingsExample.test_string_var
assert settings.get("test_int_var") == SettingsExample.test_int_var


def test_get_missing_setting(settings) -> None:
"""Test that 'None' is returned when a setting is missing."""
assert settings.get("missing_setting") is None


def test_set(settings) -> None:
"""Test that a setting can be set."""
settings.set("app_name", "new_test_app")
Expand All @@ -76,6 +103,12 @@ def test_add_and_list_setting(settings) -> None:
assert settings_dict["new_key"] == "new_value"


def test_add_none_value(settings) -> None:
"""Test that a setting can be set to None."""
settings.set("new_key", None)
assert settings.get("new_key") is None


def test_load_settings(settings) -> None:
"""Test that settings are loaded from the settings file."""
settings.load()
Expand Down

0 comments on commit 34ac406

Please sign in to comment.