Skip to content

Commit b44fe0d

Browse files
author
Bernhard Kaindl
committed
Add to_settings_dict() complementing from_settings_dict()
to_settings_dict() complements the new from_settings_dict(): The key names provided are exactly as documented in these tables: https://networkmanager.dev/docs/api/latest/nm-settings-dbus.html Extend tests/async/test_get_settings.py to test to_settings_dict(). Contrary to dataclasses.asdict(), it provides the orignal dbus keys, e.g. with numerical prefixes like "802-11-", dashes, and "id"/"type".
1 parent 80ac885 commit b44fe0d

File tree

5 files changed

+85
-3
lines changed

5 files changed

+85
-3
lines changed

sdbus_async/networkmanager/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@
250250
NetworkManagerSetting,
251251
NetworkManagerSettingsDomain,
252252
NetworkManagerConnectionProperties,
253+
SettingsDict,
253254
)
254255

255256
DEVICE_TYPE_TO_CLASS = {
@@ -479,4 +480,5 @@
479480
'NetworkManagerSetting',
480481
'NetworkManagerSettingsDomain',
481482
'NetworkManagerConnectionProperties',
483+
'SettingsDict',
482484
)

sdbus_async/networkmanager/settings/base.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@
1010

1111
class NetworkManagerSettingsMixin:
1212
def to_dbus(self) -> NetworkManagerSettingsDomain:
13-
"""TODO: Add proper docstring"""
13+
"""Return a dbus dictionary for NetworkManager to add/update profiles
14+
15+
The key names provided are exactly as documented in these tables:
16+
https://networkmanager.dev/docs/api/latest/nm-settings-dbus.html
17+
"""
1418
new_dict: NetworkManagerSettingsDomain = {}
1519

1620
for x in fields(self):
@@ -27,6 +31,36 @@ def to_dbus(self) -> NetworkManagerSettingsDomain:
2731

2832
return new_dict
2933

34+
def to_settings_dict(self, defaults: bool = False) -> Dict[str, Any]:
35+
"""Return a simple dictionary using the same key names like the dbus
36+
dict from to_dbus(), but without the dbus signatures returned by it.
37+
38+
The key names provided are exactly as documented in these tables:
39+
https://networkmanager.dev/docs/api/latest/nm-settings-dbus.html
40+
41+
Contrary to dataclasses.asdict(), it provides the orignal dbus keys,
42+
e.g. with numerical prefixes like "802-11-", dashes, and "id"/"type".
43+
44+
In addition, it can be selected if defaults shall be omitted in output,
45+
like NetworkConnectionSettings.get_settings() omits default values:
46+
47+
Because of this, all NetworkManager clients which read profiles have
48+
to have hard-coded knowledge of these defaults. By this omission, they
49+
are part of the stable API: They can be relied upon to never change.
50+
Omitting the defaults makes the typical output really small for review.
51+
"""
52+
new_dict = {}
53+
for x in fields(self):
54+
value = getattr(self, x.name)
55+
if value in [None, {}, []]:
56+
continue
57+
if not defaults and value == x.default:
58+
continue
59+
if x.metadata['dbus_type'] == 'aa{sv}':
60+
value = [x.to_settings_dict(defaults) for x in value]
61+
new_dict[x.metadata['dbus_name']] = value
62+
return new_dict
63+
3064
@classmethod
3165
def _unpack_variant(cls, key: str, signature: str, value: Any) -> Any:
3266
if signature == 'aa{sv}':

sdbus_async/networkmanager/settings/profile.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
from .wireless import WirelessSettings
5252
from .wireless_security import WirelessSecuritySettings
5353
from .wpan import WpanSettings
54-
from ..types import NetworkManagerConnectionProperties
54+
from ..types import NetworkManagerConnectionProperties, SettingsDict
5555

5656

5757
@dataclass
@@ -344,6 +344,27 @@ def to_dbus(self) -> NetworkManagerConnectionProperties:
344344

345345
return new_dict
346346

347+
def to_settings_dict(self, defaults: bool = False) -> SettingsDict:
348+
"""Return a simple dictionary using the same key names like the dbus
349+
dict from to_dbus(), but without the dbus signatures returned by it.
350+
351+
Contrary to dataclasses.asdict(), it provides the orignal dbus keys,
352+
e.g. with numerical prefixes like "802-11-", dashes, and "id"/"type".
353+
354+
The key names provided are exactly as documented in these tables:
355+
https://networkmanager.dev/docs/api/latest/nm-settings-dbus.html
356+
357+
param defaults: Whether properies with default values are returned.
358+
"""
359+
new_dict = {}
360+
for x in fields(self):
361+
settings_class = getattr(self, x.name)
362+
if settings_class:
363+
settingsdomain_dict = settings_class.to_settings_dict(defaults)
364+
if settingsdomain_dict != {}:
365+
new_dict[x.metadata['dbus_name']] = settingsdomain_dict
366+
return new_dict
367+
347368
@property
348369
def dbus_name_to_settings_class(self) -> Dict[str, str]:
349370
return {f.metadata['dbus_name']: f.name
@@ -368,7 +389,7 @@ def from_dbus(cls, dbus_dict: NetworkManagerConnectionProperties
368389

369390
@classmethod
370391
def from_settings_dict(
371-
cls, settings_dict: Dict[str, Dict[str, Any]]
392+
cls, settings_dict: SettingsDict
372393
) -> ConnectionProfile:
373394
"""Return a ConnectionProfile created from a simple settings dict
374395
A simple settings dict uses the same keys as from_dbus() and to_dbus()

sdbus_async/networkmanager/types.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,6 @@
2727

2828
# All settings and properites of a connection, e.g. returned by get_settings()
2929
NetworkManagerConnectionProperties = Dict[str, NetworkManagerSettingsDomain]
30+
31+
# All settings and properites of a connection, but without dbus signatures
32+
SettingsDict = Dict[str, Dict[str, Any]]

tests/async/test_get_settings.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,12 +83,34 @@ async def test_autoconnect_true_not_returned_by_networkmanager() -> None:
8383
assert "autoconnect" not in profile_from_manager["connection"]
8484

8585

86+
def test_autoconnect_true_returned_by_settings_dict_when_requested() -> None:
87+
"""to_settings_dict(defaults=True) returns autoconnect=True (is default)"""
88+
profile_from_dbus_dict = profile_with_autoconnect_set_to(True)
89+
settings_dict = profile_from_dbus_dict.to_settings_dict(defaults=True)
90+
assert settings_dict["connection"]["autoconnect"] is True
91+
92+
93+
def test_autoconnect_true_not_returned_by_to_settings_dict() -> None:
94+
"""to_settings_dict(defaults=False) does not return autoconnect=True"""
95+
96+
# Like checked by test_autoconnect_true_not_returned_by_networkmanager()
97+
# above (which checks that get_settings) does not return autoconnect=True
98+
# (because it is the default), test the same for .to_settings_dict():
99+
profile_from_dbus_dict = profile_with_autoconnect_set_to(True)
100+
settings_dict = profile_from_dbus_dict.to_settings_dict(defaults=False)
101+
102+
# Assert that networkmanager did not return autoconnect (default is True)
103+
assert "autoconnect" not in settings_dict["connection"]
104+
105+
86106
async def check_to_settings_dict_profile_eqal_to_from_settings_dict() -> None:
87107
"""Async main function to run all tests when not run by pytest"""
88108
"""The tests can be run by pytest (and from IDEs by running this module)"""
89109
set_sdbus_default_bus()
90110
await test_autoconnect_false_returned_by_networkmanager()
91111
await test_autoconnect_true_not_returned_by_networkmanager()
112+
test_autoconnect_true_returned_by_settings_dict_when_requested()
113+
test_autoconnect_true_not_returned_by_to_settings_dict()
92114

93115

94116
if __name__ == "__main__":

0 commit comments

Comments
 (0)