Skip to content

Commit 2c4d0bf

Browse files
committed
Added sdbus.utils.parse_properties_changed function
Parses the properties changed data in to a simple dictionary there keys are translated to python names and invalidated properties will have value of None.
1 parent 412f663 commit 2c4d0bf

File tree

7 files changed

+152
-18
lines changed

7 files changed

+152
-18
lines changed

docs/asyncio_api.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ Classes
7575

7676
Signal when one of the objects properties changes.
7777

78+
:py:func:`sdbus.utils.parse_properties_changed` can be used to transform
79+
this signal data in to an easier to work with dictionary.
80+
7881
Signal data is:
7982

8083
Interface name : str

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ If you are unfamiliar with D-Bus you might want to read following pages:
3838
asyncio_quick
3939
asyncio_api
4040
exceptions
41+
utils
4142
examples
4243
proxies
4344
code_generator

docs/utils.rst

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Utilities
2+
=========
3+
4+
Parsing utilities
5+
+++++++++++++++++
6+
7+
.. py:currentmodule:: sdbus.utils
8+
9+
.. py:function:: parse_properties_changed(interface, properties_changed_data, on_unknown_member='error')
10+
11+
Parse data from :py:meth:`properties_changed <sdbus.DbusInterfaceCommonAsync.properties_changed>` signal.
12+
13+
Member names will be translated to python defined names.
14+
Invalidated properties will have a value of None.
15+
16+
:param DbusInterfaceBaseAsync interface: Takes either D-Bus interface or interface class.
17+
:param Tuple properties_changed_data: Tuple caught from signal.
18+
:param str on_unknown_member: If an unknown D-Bus property was encountered
19+
either raise an ``"error"`` (default), ``"ignore"`` the property
20+
or ``"reuse"`` the D-Bus name for the member.
21+
:returns: Dictionary of changed properties with keys translated to python
22+
names. Invalidated properties will have value of None.

src/sdbus/dbus_common_funcs.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
from asyncio import Future, get_running_loop
2424
from contextvars import ContextVar
25-
from typing import Generator, Iterator
25+
from typing import Any, Dict, Generator, Iterator, Literal, Tuple
2626
from warnings import warn
2727

2828
from .sd_bus_internals import (
@@ -159,3 +159,29 @@ def _check_sync_in_async_env() -> bool:
159159
return False
160160
except RuntimeError:
161161
return True
162+
163+
164+
def _parse_properties_vardict(
165+
properties_name_map: Dict[str, str],
166+
properties_vardict: Dict[str, Tuple[str, Any]],
167+
on_unknown_member: Literal['error', 'ignore', 'reuse'],
168+
) -> Dict[str, Any]:
169+
170+
properties_translated: Dict[str, Any] = {}
171+
172+
for member_name, variant in properties_vardict.items():
173+
try:
174+
python_name = properties_name_map[member_name]
175+
except KeyError:
176+
if on_unknown_member == 'error':
177+
raise
178+
elif on_unknown_member == 'ignore':
179+
continue
180+
elif on_unknown_member == 'reuse':
181+
python_name = member_name
182+
else:
183+
raise ValueError
184+
185+
properties_translated[python_name] = variant[1]
186+
187+
return properties_translated

src/sdbus/dbus_proxy_async_interfaces.py

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
from inspect import getmembers
2323
from typing import Any, Dict, List, Literal, Optional, Tuple
2424

25-
from .dbus_common_funcs import get_default_bus
25+
from .dbus_common_funcs import _parse_properties_vardict, get_default_bus
2626
from .dbus_proxy_async_interface_base import DbusInterfaceBaseAsync
2727
from .dbus_proxy_async_method import dbus_method_async
2828
from .dbus_proxy_async_property import DbusPropertyAsyncBinded
@@ -94,20 +94,13 @@ async def properties_get_all_dict(
9494
dbus_properties_data = await self._properties_get_all(
9595
interface_name)
9696

97-
for member_name, variant in dbus_properties_data.items():
98-
try:
99-
python_name = self._dbus_to_python_name_map[member_name]
100-
except KeyError:
101-
if on_unknown_member == 'error':
102-
raise
103-
elif on_unknown_member == 'ignore':
104-
continue
105-
elif on_unknown_member == 'reuse':
106-
python_name = member_name
107-
else:
108-
raise ValueError
109-
110-
properties[python_name] = variant[1]
97+
properties.update(
98+
_parse_properties_vardict(
99+
self._dbus_to_python_name_map,
100+
dbus_properties_data,
101+
on_unknown_member,
102+
)
103+
)
111104

112105
return properties
113106

src/sdbus/utils.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# SPDX-License-Identifier: LGPL-2.1-or-later
2+
3+
# Copyright (C) 2023 igo95862
4+
5+
# This file is part of python-sdbus
6+
7+
# This library is free software; you can redistribute it and/or
8+
# modify it under the terms of the GNU Lesser General Public
9+
# License as published by the Free Software Foundation; either
10+
# version 2.1 of the License, or (at your option) any later version.
11+
12+
# This library is distributed in the hope that it will be useful,
13+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
# Lesser General Public License for more details.
16+
17+
# You should have received a copy of the GNU Lesser General Public
18+
# License along with this library; if not, write to the Free Software
19+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20+
from __future__ import annotations
21+
22+
from typing import Any, Dict, Literal, Type, Union
23+
24+
from .dbus_common_funcs import _parse_properties_vardict
25+
from .dbus_proxy_async_interface_base import DbusInterfaceBaseAsync
26+
from .dbus_proxy_async_interfaces import DBUS_PROPERTIES_CHANGED_TYPING
27+
28+
29+
def parse_properties_changed(
30+
interface: Union[DbusInterfaceBaseAsync, Type[DbusInterfaceBaseAsync]],
31+
properties_changed_data: DBUS_PROPERTIES_CHANGED_TYPING,
32+
on_unknown_member: Literal['error', 'ignore', 'reuse'] = 'error',
33+
) -> Dict[str, Any]:
34+
changed_properties_data = properties_changed_data[1]
35+
36+
for invalidated_property in properties_changed_data[2]:
37+
changed_properties_data[invalidated_property] = ('0', None)
38+
39+
return _parse_properties_vardict(
40+
interface._dbus_to_python_name_map,
41+
properties_changed_data[1],
42+
on_unknown_member,
43+
)
44+
45+
46+
__all__ = (
47+
'parse_properties_changed',
48+
)

test/test_sd_bus_async.py

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# SPDX-License-Identifier: LGPL-2.1-or-later
22

3-
# Copyright (C) 2020, 2021 igo95862
3+
# Copyright (C) 2020-2023 igo95862
44

55
# This file is part of python-sdbus
66

@@ -22,10 +22,11 @@
2222

2323
from asyncio import Event, get_running_loop, sleep, wait_for
2424
from asyncio.subprocess import create_subprocess_exec
25-
from typing import Tuple
25+
from typing import Tuple, cast
2626
from unittest import SkipTest
2727

2828
from sdbus.dbus_common_funcs import PROPERTY_FLAGS_MASK, count_bits
29+
from sdbus.dbus_proxy_async_interfaces import DBUS_PROPERTIES_CHANGED_TYPING
2930
from sdbus.exceptions import (
3031
DbusFailedError,
3132
DbusFileExistsError,
@@ -41,6 +42,7 @@
4142
is_interface_name_valid,
4243
)
4344
from sdbus.unittest import IsolatedDbusTestCase
45+
from sdbus.utils import parse_properties_changed
4446

4547
from sdbus import (
4648
DbusInterfaceCommonAsync,
@@ -823,3 +825,42 @@ async def test_empty_signal(self) -> None:
823825
self.assertIsNone(await wait_for(aw_dbus, timeout=1))
824826

825827
self.assertIsNone(await wait_for(q.get(), timeout=1))
828+
829+
async def test_properties_changed(self) -> None:
830+
test_object, test_object_connection = initialize_object()
831+
832+
test_str = 'should_be_emited'
833+
834+
q = await test_object_connection.properties_changed._get_dbus_queue()
835+
836+
async def set_property() -> None:
837+
await test_object_connection.test_property.set_async(test_str)
838+
839+
await set_property()
840+
841+
properties_changed_data = cast(
842+
DBUS_PROPERTIES_CHANGED_TYPING,
843+
(await q.get()).get_contents(),
844+
)
845+
846+
parsed_dict_from_class = parse_properties_changed(
847+
TestInterface, properties_changed_data)
848+
self.assertEqual(
849+
test_str,
850+
parsed_dict_from_class['test_property'],
851+
)
852+
853+
parsed_dict_from_object = parse_properties_changed(
854+
test_object_connection, properties_changed_data)
855+
self.assertEqual(
856+
test_str,
857+
parsed_dict_from_object['test_property'],
858+
)
859+
860+
properties_changed_data[2].append('invalidated_property')
861+
parsed_dict_with_invalidation = parse_properties_changed(
862+
test_object, properties_changed_data,
863+
on_unknown_member='reuse',
864+
)
865+
self.assertIsNone(
866+
parsed_dict_with_invalidation['invalidated_property'])

0 commit comments

Comments
 (0)