Skip to content

Commit eafd46e

Browse files
authored
Merge pull request #22 from bernhardkaindl/settings-helper-from-simple-settings-dict
Add from_settings_dict() to read a dict without signatures
2 parents 6dd523d + 90ce2e8 commit eafd46e

File tree

4 files changed

+129
-0
lines changed

4 files changed

+129
-0
lines changed

pytest.ini

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[pytest]
2+
#
3+
# Newer version of pytest-asyncio issue a DeprecationWarning unless
4+
# asyncio_mode is specified. This is because it is planned to change
5+
# the legacy default of automatically decorating async fixtures from
6+
# automatc to strict (only all functions and fixtures have to be marked).
7+
#
8+
# But only very recent versions of pytest-asyncio versions support
9+
# the asyncio_mode configuration setting and then issue a config warning,
10+
# which can't be filtered.
11+
#
12+
# Thus, filter the Deprecationwarning of pytest-asyncio for some time
13+
# until all users can be assumed to use the latest pytest-asyncio.
14+
#
15+
filterwarnings =
16+
ignore:The 'asyncio_mode' default value will change to 'strict' in future, please explicitly use 'asyncio_mode=strict' or 'asyncio_mode=auto' in pytest configuration file.:DeprecationWarning
17+

sdbus_async/networkmanager/settings/base.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,21 @@ def from_dbus(
4848
}
4949
return cls(**unvarianted_options)
5050

51+
@classmethod
52+
def from_dict(cls,
53+
plain_dict: Dict[str, Any]
54+
) -> NetworkManagerSettingsMixin:
55+
options = {}
56+
for dataclass_field in fields(cls):
57+
dbus_name = dataclass_field.metadata["dbus_name"]
58+
if dbus_name in plain_dict:
59+
value = plain_dict[dbus_name]
60+
if dataclass_field.metadata["dbus_type"] == 'aa{sv}':
61+
inner_class = cls.setting_name_to_inner_class(dbus_name)
62+
value = [inner_class.from_dict(item) for item in value]
63+
options[dataclass_field.name] = value
64+
return cls(**options)
65+
5166
@classmethod
5267
@lru_cache(maxsize=None)
5368
def setting_name_reverse_mapping(cls) -> Dict[str, str]:

sdbus_async/networkmanager/settings/profile.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,22 @@ def from_dbus(cls, dbus_dict: NetworkManagerConnectionProperties
366366
raise e
367367
return cls(**unvarianted_options)
368368

369+
@classmethod
370+
def from_settings_dict(
371+
cls, settings_dict: Dict[str, Dict[str, Any]]
372+
) -> ConnectionProfile:
373+
"""Return a ConnectionProfile created from a simple settings dict
374+
A simple settings dict uses the same keys as from_dbus() and to_dbus()
375+
but without the dbus variable signatures used by NetworkManader.py
376+
377+
This means a simple settings dict does not use the underscore in keys
378+
like the attributes of this class have to use and use "id" and "type".
379+
"""
380+
unvarianted_options: Dict[str, Any] = {
381+
SETTING_DBUS_NAME_TO_NAME[k]: SETTING_TO_CLASS[k].from_dict(v)
382+
for k, v in settings_dict.items()}
383+
return cls(**unvarianted_options)
384+
369385

370386
SETTING_DBUS_NAME_TO_NAME: Dict[str, str] = {
371387
f.metadata['dbus_name']: f.name
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#!/usr/bin/env python
2+
# SPDX-License-Identifier: LGPL-2.1-or-later
3+
import asyncio
4+
import contextlib
5+
import sdbus
6+
import pytest
7+
from sdbus_async.networkmanager import (
8+
ConnectionProfile,
9+
NetworkConnectionSettings,
10+
NetworkManagerSettings as SettingsManager,
11+
NmSettingsInvalidConnectionError,
12+
)
13+
14+
# All test coroutines will be treated as marked.
15+
16+
17+
def test_wifi_wpa_psk_simple_from_dict() -> ConnectionProfile:
18+
"""Parse connection and ipv4 settings from dbus using ConnectionProfile"""
19+
profile = ConnectionProfile.from_settings_dict(
20+
{
21+
"connection": {
22+
"id": "WirelessWpaPskConnection",
23+
"type": "802-11-wireless",
24+
"uuid": "16ea7af1-0e35-4036-831e-ced975f48510",
25+
"autoconnect": False,
26+
},
27+
"ipv4": {"method": "auto"},
28+
"ipv6": {"method": "disabled"},
29+
"802-11-wireless": {
30+
"security": "802-11-wireless-security",
31+
"ssid": b"CafeSSID",
32+
},
33+
"802-11-wireless-security": {"key-mgmt": "wpa-psk"},
34+
}
35+
)
36+
assert profile.connection.connection_id == "WirelessWpaPskConnection"
37+
assert profile.connection.uuid == "16ea7af1-0e35-4036-831e-ced975f48510"
38+
assert profile.connection.connection_type == "802-11-wireless"
39+
assert profile.connection.autoconnect is False
40+
assert profile.ipv4
41+
assert profile.ipv4.method == "auto"
42+
assert profile.ipv4.address_data is None
43+
assert profile.ipv6
44+
assert profile.ipv6.method == "disabled"
45+
return profile
46+
47+
48+
async def delete_connection_by_uuid(nmset: SettingsManager, uuid: str) -> None:
49+
dpath = await nmset.get_connection_by_uuid(uuid)
50+
connection_settings = NetworkConnectionSettings(dpath)
51+
await connection_settings.delete()
52+
53+
54+
# @pytest_asyncio.fixture
55+
@pytest.mark.asyncio
56+
async def test_add_wifi_wpa_psk_simple_from_dict() -> None:
57+
"""Test adding a wireless WPA PSK connection profile(unsaved) from dict"""
58+
profile = test_wifi_wpa_psk_simple_from_dict()
59+
60+
# If we add many connections passing the same id, things get messy. Check:
61+
sdbus.set_default_bus(sdbus.sd_bus_open_system())
62+
settings = SettingsManager()
63+
assert profile.connection.uuid
64+
with contextlib.suppress(NmSettingsInvalidConnectionError):
65+
await settings.get_connection_by_uuid(profile.connection.uuid)
66+
print(f"Deleting existing connection with {profile.connection.uuid}")
67+
await delete_connection_by_uuid(settings, profile.connection.uuid)
68+
await settings.add_connection_unsaved(profile.to_dbus())
69+
await delete_connection_by_uuid(settings, profile.connection.uuid)
70+
71+
72+
if __name__ == "__main__":
73+
"""The tests can be run by pytest (and from IDEs by running this module)"""
74+
test_wifi_wpa_psk_simple_from_dict()
75+
76+
# Test using ConnectionProfile.from_dict() to create a ConnectionProfile
77+
# which can be used to add Wireless WPA PSK connection profile from dict
78+
# to a running NetworkManager. Requires access and permissions to access
79+
# a running NetworkManager. The added connection is not saved and deleted
80+
# immediately (and does not have autoconnect enabled):
81+
asyncio.run(test_add_wifi_wpa_psk_simple_from_dict())

0 commit comments

Comments
 (0)