Skip to content

Commit 5b94da4

Browse files
committed
Add load_settings_toml
1 parent c23dc24 commit 5b94da4

File tree

4 files changed

+204
-0
lines changed

4 files changed

+204
-0
lines changed

pytest.ini

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# SPDX-FileCopyrightText: 2025 Justin Myers
2+
#
3+
# SPDX-License-Identifier: MIT
4+
[pytest]
5+
pythonpath = src

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
"pyftdi>=0.40.0",
100100
"adafruit-circuitpython-typing",
101101
"sysv_ipc>=1.1.0;sys_platform=='linux' and platform_machine!='mips'",
102+
"toml>=0.10.2;python_version<'3.11'",
102103
]
103104
+ board_reqs,
104105
license="MIT",

src/adafruit_blinka/__init__.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@
88
* Author(s): cefn
99
"""
1010

11+
import os
12+
13+
try:
14+
import tomllib
15+
except ImportError:
16+
import toml as tomllib
17+
1118

1219
class Enum:
1320
"""
@@ -74,6 +81,41 @@ def unlock(self):
7481
self._locked = False
7582

7683

84+
def load_settings_toml(*, return_toml=False):
85+
"""Load values from settings.toml into os.environ, so that os.getenv returns them."""
86+
if not os.path.isfile("settings.toml"):
87+
raise FileNotFoundError("settings.toml not cound in current directory.")
88+
89+
print("settings.toml found. Updating environment variables:")
90+
with open("settings.toml", "rb") as toml_file:
91+
try:
92+
settings = tomllib.load(toml_file)
93+
except tomllib.TOMLDecodeError as e:
94+
raise tomllib.TOMLDecodeError("Error with settings.toml file.") from e
95+
96+
invalid_types = set()
97+
for key, value in settings.items():
98+
if not isinstance(value, (bool, int, float, str)):
99+
invalid_types.add(type(value).__name__)
100+
if invalid_types:
101+
invalid_types_string = ", ".join(invalid_types)
102+
raise ValueError(
103+
f"The types: '{invalid_types_string}' are not supported in settings.toml."
104+
)
105+
106+
for key, value in settings.items():
107+
key = str(key)
108+
if key in os.environ:
109+
print(f" - {key} already exists in environment")
110+
continue
111+
os.environ[key] = str(value)
112+
print(f" - {key} added")
113+
114+
if return_toml:
115+
return settings
116+
return None
117+
118+
77119
def patch_system():
78120
"""Patch modules that may be different due to the platform."""
79121
# pylint: disable=import-outside-toplevel

tests/test_load_settings_toml.py

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
# SPDX-FileCopyrightText: 2025 Justin Myers
2+
#
3+
# SPDX-License-Identifier: MIT
4+
import os
5+
from unittest import mock
6+
import pytest
7+
from adafruit_blinka import load_settings_toml
8+
9+
try:
10+
import tomllib
11+
except ImportError:
12+
import toml as tomllib
13+
14+
# pylint: disable=no-self-use,unused-argument
15+
16+
CONVERTED_TOML = {
17+
"123": 123,
18+
"test": "test",
19+
"test-hyphen": "test-hyphen",
20+
"test_bool": True,
21+
"test_number": 123,
22+
"test_space": "test space",
23+
"test_underscore": "test_underscore",
24+
"true": False,
25+
}
26+
27+
28+
INVALID_TOML = b"""
29+
# strings
30+
test=test
31+
"""
32+
33+
34+
VALID_TOML = b"""
35+
# strings
36+
test="test"
37+
test_space="test space"
38+
test_underscore="test_underscore"
39+
test-hyphen="test-hyphen"
40+
# number
41+
test_number=123
42+
# bool
43+
test_bool=true
44+
# other
45+
123=123
46+
true=false
47+
"""
48+
49+
VALID_TOML_WITH_UNSUPPORTED_DATA_DICT = b"""
50+
# dict
51+
data = { key_1 = "value", key_2 = "value" }
52+
"""
53+
54+
VALID_TOML_WITH_UNSUPPORTED_DATA_LIST = b"""
55+
# list
56+
numbers = [ 1, 2, 3 ]
57+
"""
58+
59+
VALID_TOML_WITH_UNSUPPORTED_DATA_MANY = b"""
60+
# dict
61+
data = { key_1 = "value", key_2 = "value" }
62+
63+
# list
64+
numbers = [ 1, 2, 3 ]
65+
66+
[nested]
67+
test="test"
68+
"""
69+
70+
VALID_TOML_WITH_UNSUPPORTED_DATA_NESTED = b"""
71+
[nested]
72+
test="test"
73+
"""
74+
75+
76+
class TestLoadSettingsToml:
77+
@mock.patch("adafruit_blinka.os.path.isfile", mock.Mock(return_value=False))
78+
def test_raises_with_no_file(self):
79+
with pytest.raises(
80+
FileNotFoundError, match="settings.toml not cound in current directory."
81+
):
82+
load_settings_toml(return_toml=True)
83+
84+
@mock.patch("adafruit_blinka.os.path.isfile", mock.Mock(return_value=True))
85+
@mock.patch("builtins.open", mock.mock_open(read_data=INVALID_TOML))
86+
def test_raises_with_invalid_file(self):
87+
with pytest.raises(
88+
tomllib.TOMLDecodeError, match="Error with settings.toml file."
89+
):
90+
load_settings_toml(return_toml=True)
91+
92+
@mock.patch("adafruit_blinka.os.path.isfile", mock.Mock(return_value=True))
93+
@mock.patch(
94+
"builtins.open", mock.mock_open(read_data=VALID_TOML_WITH_UNSUPPORTED_DATA_DICT)
95+
)
96+
def test_raises_with_invalid_file_dict(self):
97+
with pytest.raises(
98+
ValueError, match="The types: 'dict' are not supported in settings.toml."
99+
):
100+
load_settings_toml(return_toml=True)
101+
102+
@mock.patch("adafruit_blinka.os.path.isfile", mock.Mock(return_value=True))
103+
@mock.patch(
104+
"builtins.open", mock.mock_open(read_data=VALID_TOML_WITH_UNSUPPORTED_DATA_LIST)
105+
)
106+
def test_raises_with_invalid_file_list(self):
107+
with pytest.raises(
108+
ValueError, match="The types: 'list' are not supported in settings.toml."
109+
):
110+
load_settings_toml(return_toml=True)
111+
112+
@mock.patch("adafruit_blinka.os.path.isfile", mock.Mock(return_value=True))
113+
@mock.patch(
114+
"builtins.open", mock.mock_open(read_data=VALID_TOML_WITH_UNSUPPORTED_DATA_MANY)
115+
)
116+
def test_raises_with_invalid_file_many(self):
117+
with pytest.raises(
118+
ValueError,
119+
match="The types: 'dict, list' are not supported in settings.toml.",
120+
):
121+
load_settings_toml(return_toml=True)
122+
123+
@mock.patch("adafruit_blinka.os.path.isfile", mock.Mock(return_value=True))
124+
@mock.patch(
125+
"builtins.open",
126+
mock.mock_open(read_data=VALID_TOML_WITH_UNSUPPORTED_DATA_NESTED),
127+
)
128+
def test_raises_with_invalid_file_nested(self):
129+
with pytest.raises(
130+
ValueError, match="The types: 'dict' are not supported in settings.toml."
131+
):
132+
load_settings_toml(return_toml=True)
133+
134+
@mock.patch("adafruit_blinka.os.path.isfile", mock.Mock(return_value=True))
135+
@mock.patch("builtins.open", mock.mock_open(read_data=VALID_TOML))
136+
@mock.patch.dict(os.environ, {}, clear=True)
137+
def test_returns_data(self):
138+
for key in CONVERTED_TOML:
139+
assert os.getenv(key) is None
140+
141+
assert load_settings_toml() is None
142+
143+
for key, value in CONVERTED_TOML.items():
144+
assert os.getenv(key) == str(value)
145+
146+
@mock.patch("adafruit_blinka.os.path.isfile", mock.Mock(return_value=True))
147+
@mock.patch("builtins.open", mock.mock_open(read_data=VALID_TOML))
148+
@mock.patch.dict(os.environ, {}, clear=True)
149+
def test_returns_data_when_asked(self):
150+
for key in CONVERTED_TOML:
151+
assert os.getenv(key) is None
152+
153+
assert load_settings_toml(return_toml=True) == CONVERTED_TOML
154+
155+
for key, value in CONVERTED_TOML.items():
156+
assert os.getenv(key) == str(value)

0 commit comments

Comments
 (0)