]> Repositories - Adafruit_Blinka-hackapet.git/commitdiff
Add load_settings_toml
authorJustin Myers <justmobilize@users.noreply.github.com>
Tue, 4 Mar 2025 14:52:39 +0000 (06:52 -0800)
committerJustin Myers <justmobilize@users.noreply.github.com>
Tue, 4 Mar 2025 14:52:39 +0000 (06:52 -0800)
pytest.ini [new file with mode: 0644]
setup.py
src/adafruit_blinka/__init__.py
tests/test_load_settings_toml.py [new file with mode: 0644]

diff --git a/pytest.ini b/pytest.ini
new file mode 100644 (file)
index 0000000..891ae04
--- /dev/null
@@ -0,0 +1,5 @@
+# SPDX-FileCopyrightText: 2025 Justin Myers
+#
+# SPDX-License-Identifier: MIT
+[pytest]
+pythonpath = src
index efeab2e414b1a8bfea0a5d8890a1b22713e3b1fa..92ce579bf3cb4ad397a37718ea86cf322393ffa9 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -99,6 +99,7 @@ setup(
         "pyftdi>=0.40.0",
         "adafruit-circuitpython-typing",
         "sysv_ipc>=1.1.0;sys_platform=='linux' and platform_machine!='mips'",
         "pyftdi>=0.40.0",
         "adafruit-circuitpython-typing",
         "sysv_ipc>=1.1.0;sys_platform=='linux' and platform_machine!='mips'",
+        "toml>=0.10.2;python_version<'3.11'",
     ]
     + board_reqs,
     license="MIT",
     ]
     + board_reqs,
     license="MIT",
index a4ef21542d610b3c649ecda0541033e753bf58d1..e3cc50c69a8570bae983c043283c58aa048a7edb 100755 (executable)
@@ -8,6 +8,13 @@
 * Author(s): cefn
 """
 
 * Author(s): cefn
 """
 
+import os
+
+try:
+    import tomllib
+except ImportError:
+    import toml as tomllib
+
 
 class Enum:
     """
 
 class Enum:
     """
@@ -74,6 +81,41 @@ class Lockable(ContextManaged):
             self._locked = False
 
 
             self._locked = False
 
 
+def load_settings_toml(*, return_toml=False):
+    """Load values from settings.toml into os.environ, so that os.getenv returns them."""
+    if not os.path.isfile("settings.toml"):
+        raise FileNotFoundError("settings.toml not cound in current directory.")
+
+    print("settings.toml found. Updating environment variables:")
+    with open("settings.toml", "rb") as toml_file:
+        try:
+            settings = tomllib.load(toml_file)
+        except tomllib.TOMLDecodeError as e:
+            raise tomllib.TOMLDecodeError("Error with settings.toml file.") from e
+
+    invalid_types = set()
+    for key, value in settings.items():
+        if not isinstance(value, (bool, int, float, str)):
+            invalid_types.add(type(value).__name__)
+    if invalid_types:
+        invalid_types_string = ", ".join(invalid_types)
+        raise ValueError(
+            f"The types: '{invalid_types_string}' are not supported in settings.toml."
+        )
+
+    for key, value in settings.items():
+        key = str(key)
+        if key in os.environ:
+            print(f" - {key} already exists in environment")
+            continue
+        os.environ[key] = str(value)
+        print(f" - {key} added")
+
+    if return_toml:
+        return settings
+    return None
+
+
 def patch_system():
     """Patch modules that may be different due to the platform."""
     # pylint: disable=import-outside-toplevel
 def patch_system():
     """Patch modules that may be different due to the platform."""
     # pylint: disable=import-outside-toplevel
diff --git a/tests/test_load_settings_toml.py b/tests/test_load_settings_toml.py
new file mode 100644 (file)
index 0000000..da01555
--- /dev/null
@@ -0,0 +1,156 @@
+# SPDX-FileCopyrightText: 2025 Justin Myers
+#
+# SPDX-License-Identifier: MIT
+import os
+from unittest import mock
+import pytest
+from adafruit_blinka import load_settings_toml
+
+try:
+    import tomllib
+except ImportError:
+    import toml as tomllib
+
+# pylint: disable=no-self-use,unused-argument
+
+CONVERTED_TOML = {
+    "123": 123,
+    "test": "test",
+    "test-hyphen": "test-hyphen",
+    "test_bool": True,
+    "test_number": 123,
+    "test_space": "test space",
+    "test_underscore": "test_underscore",
+    "true": False,
+}
+
+
+INVALID_TOML = b"""
+# strings
+test=test
+"""
+
+
+VALID_TOML = b"""
+# strings
+test="test"
+test_space="test space"
+test_underscore="test_underscore"
+test-hyphen="test-hyphen"
+# number
+test_number=123
+# bool
+test_bool=true
+# other
+123=123
+true=false
+"""
+
+VALID_TOML_WITH_UNSUPPORTED_DATA_DICT = b"""
+# dict
+data = { key_1 = "value", key_2 = "value" }
+"""
+
+VALID_TOML_WITH_UNSUPPORTED_DATA_LIST = b"""
+# list
+numbers = [ 1, 2, 3 ]
+"""
+
+VALID_TOML_WITH_UNSUPPORTED_DATA_MANY = b"""
+# dict
+data = { key_1 = "value", key_2 = "value" }
+
+# list
+numbers = [ 1, 2, 3 ]
+
+[nested]
+test="test"
+"""
+
+VALID_TOML_WITH_UNSUPPORTED_DATA_NESTED = b"""
+[nested]
+test="test"
+"""
+
+
+class TestLoadSettingsToml:
+    @mock.patch("adafruit_blinka.os.path.isfile", mock.Mock(return_value=False))
+    def test_raises_with_no_file(self):
+        with pytest.raises(
+            FileNotFoundError, match="settings.toml not cound in current directory."
+        ):
+            load_settings_toml(return_toml=True)
+
+    @mock.patch("adafruit_blinka.os.path.isfile", mock.Mock(return_value=True))
+    @mock.patch("builtins.open", mock.mock_open(read_data=INVALID_TOML))
+    def test_raises_with_invalid_file(self):
+        with pytest.raises(
+            tomllib.TOMLDecodeError, match="Error with settings.toml file."
+        ):
+            load_settings_toml(return_toml=True)
+
+    @mock.patch("adafruit_blinka.os.path.isfile", mock.Mock(return_value=True))
+    @mock.patch(
+        "builtins.open", mock.mock_open(read_data=VALID_TOML_WITH_UNSUPPORTED_DATA_DICT)
+    )
+    def test_raises_with_invalid_file_dict(self):
+        with pytest.raises(
+            ValueError, match="The types: 'dict' are not supported in settings.toml."
+        ):
+            load_settings_toml(return_toml=True)
+
+    @mock.patch("adafruit_blinka.os.path.isfile", mock.Mock(return_value=True))
+    @mock.patch(
+        "builtins.open", mock.mock_open(read_data=VALID_TOML_WITH_UNSUPPORTED_DATA_LIST)
+    )
+    def test_raises_with_invalid_file_list(self):
+        with pytest.raises(
+            ValueError, match="The types: 'list' are not supported in settings.toml."
+        ):
+            load_settings_toml(return_toml=True)
+
+    @mock.patch("adafruit_blinka.os.path.isfile", mock.Mock(return_value=True))
+    @mock.patch(
+        "builtins.open", mock.mock_open(read_data=VALID_TOML_WITH_UNSUPPORTED_DATA_MANY)
+    )
+    def test_raises_with_invalid_file_many(self):
+        with pytest.raises(
+            ValueError,
+            match="The types: 'dict, list' are not supported in settings.toml.",
+        ):
+            load_settings_toml(return_toml=True)
+
+    @mock.patch("adafruit_blinka.os.path.isfile", mock.Mock(return_value=True))
+    @mock.patch(
+        "builtins.open",
+        mock.mock_open(read_data=VALID_TOML_WITH_UNSUPPORTED_DATA_NESTED),
+    )
+    def test_raises_with_invalid_file_nested(self):
+        with pytest.raises(
+            ValueError, match="The types: 'dict' are not supported in settings.toml."
+        ):
+            load_settings_toml(return_toml=True)
+
+    @mock.patch("adafruit_blinka.os.path.isfile", mock.Mock(return_value=True))
+    @mock.patch("builtins.open", mock.mock_open(read_data=VALID_TOML))
+    @mock.patch.dict(os.environ, {}, clear=True)
+    def test_returns_data(self):
+        for key in CONVERTED_TOML:
+            assert os.getenv(key) is None
+
+        assert load_settings_toml() is None
+
+        for key, value in CONVERTED_TOML.items():
+            assert os.getenv(key) == str(value)
+
+    @mock.patch("adafruit_blinka.os.path.isfile", mock.Mock(return_value=True))
+    @mock.patch("builtins.open", mock.mock_open(read_data=VALID_TOML))
+    @mock.patch.dict(os.environ, {}, clear=True)
+    def test_returns_data_when_asked(self):
+        for key in CONVERTED_TOML:
+            assert os.getenv(key) is None
+
+        assert load_settings_toml(return_toml=True) == CONVERTED_TOML
+
+        for key, value in CONVERTED_TOML.items():
+            assert os.getenv(key) == str(value)