diff --git a/scripts/monitor_dbus_signals.py b/scripts/monitor_dbus_signals.py index 701fd28..dea198c 100755 --- a/scripts/monitor_dbus_signals.py +++ b/scripts/monitor_dbus_signals.py @@ -18,6 +18,14 @@ with respect to their properties. """ +# isort: STDLIB +import os +import xml.etree.ElementTree as ET +from typing import List + +# isort: THIRDPARTY +import dbus + _INTERFACE_RE = None _MO = None _SERVICE = None @@ -38,18 +46,199 @@ _EMITS_CHANGED_PROP = "org.freedesktop.DBus.Property.EmitsChangedSignal" + +class Diff: # pylint: disable=too-few-public-methods + """ + Diff between two different managed object results. + """ + + +class AddedProperty(Diff): # pylint: disable=too-few-public-methods + """ + Property appears in new result but not in recorded result. + """ + + def __init__(self, object_path, interface_name, key, new_value): + self.object_path = object_path + self.interface_name = interface_name + self.key = key + self.new_value = new_value + + def __repr__(self): + return ( + f"AddedProperty({self.object_path!r}, {self.interface_name!r}, " + f"{self.key!r}, {self.new_value!r})" + ) + + +class RemovedProperty(Diff): # pylint: disable=too-few-public-methods + """ + Property appears in recorded result but not in new result. + """ + + def __init__(self, object_path, interface_name, key, old_value): + self.object_path = object_path + self.interface_name = interface_name + self.key = key + self.old_value = old_value + + def __repr__(self): + return ( + f"RemovedProperty({self.object_path!r}, {self.interface_name!r}, " + f"{self.key!r}, {self.old_value!r})" + ) + + +class DifferentProperty(Diff): # pylint: disable=too-few-public-methods + """ + Difference between two properties. + """ + + def __init__( + self, object_path, interface_name, key, old_value, new_value + ): # pylint: disable=too-many-positional-arguments,too-many-arguments + self.object_path = object_path + self.interface_name = interface_name + self.key = key + self.old_value = old_value + self.new_value = new_value + + def __repr__(self): + return ( + f"DifferentProperty({self.object_path!r}, {self.interface_name!r}, " + f"{self.key!r}, {self.old_value!r}, {self.new_value!r})" + ) + + +class NotInvalidatedProperty(Diff): # pylint: disable=too-few-public-methods + """ + Represents a case where the property should have been invalidated but + was updated instead. + """ + + def __init__( + self, object_path, interface_name, key, old_value, new_value + ): # pylint: disable=too-many-positional-arguments,too-many-arguments + self.object_path = object_path + self.interface_name = interface_name + self.key = key + self.old_value = old_value + self.new_value = new_value + + def __repr__(self): + return ( + f"NotInvalidatedProperty({self.object_path!r}, " + f"{self.interface_name!r}, {self.key!r}, {self.old_value!r}, " + f"{self.new_value!r})" + ) + + +class ChangedProperty(Diff): # pylint: disable=too-few-public-methods + """ + Represents a case where the property should have been constant but + seems to have changed. + """ + + def __init__( + self, object_path, interface_name, key, old_value, new_value + ): # pylint: disable=too-many-positional-arguments,too-many-arguments + self.object_path = object_path + self.interface_name = interface_name + self.key = key + self.old_value = old_value + self.new_value = new_value + + def __repr__(self): + return ( + f"ChangedProperty({self.object_path!r}, " + f"{self.interface_name!r}, {self.key!r}, {self.old_value!r}, " + f"{self.new_value!r})" + ) + + +class RemovedObjectPath(Diff): # pylint: disable=too-few-public-methods + """ + Object path appears in recorded result but not in new result. + """ + + def __init__(self, object_path, old_value): + self.object_path = object_path + self.old_value = old_value + + def __repr__(self): + return f"RemovedObjectPath({self.object_path!r}, {self.old_value!r})" + + +class AddedInterface(Diff): # pylint: disable=too-few-public-methods + """ + Interface appears in new result but not in recorded result. + """ + + def __init__(self, object_path, interface_name, new_value): + self.object_path = object_path + self.interface_name = interface_name + self.new_value = new_value + + def __repr__(self): + return ( + f"AddedInterface({self.object_path!r}, {self.interface_name!r}, " + f"{self.new_value!r})" + ) + + +class AddedObjectPath(Diff): # pylint: disable=too-few-public-methods + """ + Object path appears in new result but not in recorded result. + """ + + def __init__(self, object_path, new_value): + self.object_path = object_path + self.new_value = new_value + + def __repr__(self): + return f"AddedObjectPath({self.object_path!r}, {self.new_value!r})" + + +class RemovedInterface(Diff): # pylint: disable=too-few-public-methods + """ + Interface appears in recorded result but not in new result. + """ + + def __init__(self, object_path, interface_name, old_value): + self.object_path = object_path + self.interface_name = interface_name + self.old_value = old_value + + def __repr__(self): + return ( + f"RemovedInterface({self.object_path!r}, {self.interface_name!r}, " + f"{self.old_value!r})" + ) + + +class MissingInterface(Diff): # pylint: disable=too-few-public-methods + """ + Attempted to update a property on this interface, but the interface + itself was missing when that happened. + """ + + def __init__(self, object_path, interface_name): + self.object_path = object_path + self.interface_name = interface_name + + def __repr__(self): + return f"MissingInterface({self.object_path!r}, {self.interface_name!r}" + + try: # isort: STDLIB import argparse - import os import re import sys import time - import xml.etree.ElementTree as ET from enum import Enum # isort: THIRDPARTY - import dbus import dbus.mainloop.glib from gi.repository import GLib @@ -90,7 +279,7 @@ def __repr__(self): INVALIDATED = Invalidated() - class MissingInterface: # pylint: disable=too-few-public-methods + class InterfaceMissing: # pylint: disable=too-few-public-methods """ Used to record in the updated GetManagedObjects value that when a property changed signal was received, the interface for that property @@ -98,9 +287,9 @@ class MissingInterface: # pylint: disable=too-few-public-methods """ def __repr__(self): - return "MissingInterface()" + return "Interface(Missing)" - MISSING_INTERFACE = MissingInterface() + INTERFACE_MISSING = InterfaceMissing() # a minimal chunk of introspection data, enough for the methods needed. _SPECS = { @@ -299,9 +488,9 @@ def _properties_changed(*props_changed, object_path=None): ) or re.fullmatch(_INTERFACE_RE, interface_name) is None: return - data[interface_name] = MISSING_INTERFACE + data[interface_name] = INTERFACE_MISSING - if data[interface_name] is MISSING_INTERFACE: + if data[interface_name] is INTERFACE_MISSING: return for prop, value in properties_changed.items(): @@ -311,7 +500,12 @@ def _properties_changed(*props_changed, object_path=None): except Exception as exc: # pylint: disable=broad-except _CALLBACK_ERRORS.append(exc) - def _monitor(service, manager, manager_interfaces, interface_re): + def _monitor( + service: str, + manager: str, + manager_interfaces: List[str], + interface_re: re.Pattern, + ): """ Monitor the signals and properties of the manager object. @@ -358,7 +552,9 @@ def _monitor(service, manager, manager_interfaces, interface_re): bus.add_signal_receiver( _properties_changed, + dbus_interface="org.freedesktop.DBus.Properties", signal_name="PropertiesChanged", + bus_name=_TOP_OBJECT.bus_name, path_keyword="object_path", ) @@ -429,178 +625,6 @@ def main(): except KeyboardInterrupt: - class Diff: # pylint: disable=too-few-public-methods - """ - Diff between two different managed object results. - """ - - class AddedProperty(Diff): # pylint: disable=too-few-public-methods - """ - Property appears in new result but not in recorded result. - """ - - def __init__(self, object_path, interface_name, key, new_value): - self.object_path = object_path - self.interface_name = interface_name - self.key = key - self.new_value = new_value - - def __repr__(self): - return ( - f"AddedProperty({self.object_path!r}, {self.interface_name!r}, " - f"{self.key!r}, {self.new_value!r})" - ) - - class RemovedProperty(Diff): # pylint: disable=too-few-public-methods - """ - Property appears in recorded result but not in new result. - """ - - def __init__(self, object_path, interface_name, key, old_value): - self.object_path = object_path - self.interface_name = interface_name - self.key = key - self.old_value = old_value - - def __repr__(self): - return ( - f"RemovedProperty({self.object_path!r}, {self.interface_name!r}, " - f"{self.key!r}, {self.old_value!r})" - ) - - class DifferentProperty(Diff): # pylint: disable=too-few-public-methods - """ - Difference between two properties. - """ - - def __init__( - self, object_path, interface_name, key, old_value, new_value - ): # pylint: disable=too-many-positional-arguments,too-many-arguments - self.object_path = object_path - self.interface_name = interface_name - self.key = key - self.old_value = old_value - self.new_value = new_value - - def __repr__(self): - return ( - f"DifferentProperty({self.object_path!r}, {self.interface_name!r}, " - f"{self.key!r}, {self.old_value!r}, {self.new_value!r})" - ) - - class NotInvalidatedProperty(Diff): # pylint: disable=too-few-public-methods - """ - Represents a case where the property should have been invalidated but - was updated instead. - """ - - def __init__( - self, object_path, interface_name, key, old_value, new_value - ): # pylint: disable=too-many-positional-arguments,too-many-arguments - self.object_path = object_path - self.interface_name = interface_name - self.key = key - self.old_value = old_value - self.new_value = new_value - - def __repr__(self): - return ( - f"NotInvalidatedProperty({self.object_path!r}, " - f"{self.interface_name!r}, {self.key!r}, {self.old_value!r}, " - f"{self.new_value!r})" - ) - - class ChangedProperty(Diff): # pylint: disable=too-few-public-methods - """ - Represents a case where the property should have been constant but - seems to have changed. - """ - - def __init__( - self, object_path, interface_name, key, old_value, new_value - ): # pylint: disable=too-many-positional-arguments,too-many-arguments - self.object_path = object_path - self.interface_name = interface_name - self.key = key - self.old_value = old_value - self.new_value = new_value - - def __repr__(self): - return ( - f"ChangedProperty({self.object_path!r}, " - f"{self.interface_name!r}, {self.key!r}, {self.old_value!r}, " - f"{self.new_value!r})" - ) - - class RemovedObjectPath(Diff): # pylint: disable=too-few-public-methods - """ - Object path appears in recorded result but not in new result. - """ - - def __init__(self, object_path, old_value): - self.object_path = object_path - self.old_value = old_value - - def __repr__(self): - return f"RemovedObjectPath({self.object_path!r}, {self.old_value!r})" - - class AddedInterface(Diff): # pylint: disable=too-few-public-methods - """ - Interface appears in new result but not in recorded result. - """ - - def __init__(self, object_path, interface_name, new_value): - self.object_path = object_path - self.interface_name = interface_name - self.new_value = new_value - - def __repr__(self): - return ( - f"AddedInterface({self.object_path!r}, {self.interface_name!r}, " - f"{self.new_value!r})" - ) - - class AddedObjectPath(Diff): # pylint: disable=too-few-public-methods - """ - Object path appears in new result but not in recorded result. - """ - - def __init__(self, object_path, new_value): - self.object_path = object_path - self.new_value = new_value - - def __repr__(self): - return f"AddedObjectPath({self.object_path!r}, {self.new_value!r})" - - class RemovedInterface(Diff): # pylint: disable=too-few-public-methods - """ - Interface appears in recorded result but not in new result. - """ - - def __init__(self, object_path, interface_name, old_value): - self.object_path = object_path - self.interface_name = interface_name - self.old_value = old_value - - def __repr__(self): - return ( - f"RemovedInterface({self.object_path!r}, {self.interface_name!r}, " - f"{self.old_value!r})" - ) - - class MissingInterface(Diff): # pylint: disable=too-few-public-methods - """ - Attempted to update a property on this interface, but the interface - itself was missing when that happened. - """ - - def __init__(self, object_path, interface_name): - self.object_path = object_path - self.interface_name = interface_name - - def __repr__(self): - return f"MissingInterface({self.object_path!r}, {self.interface_name!r}" - def _check_props(object_path, ifn, old_props, new_props): """ Find differences between two sets of properties. @@ -608,7 +632,7 @@ def _check_props(object_path, ifn, old_props, new_props): :param str object_path: D-Bus object path :param str ifn: a single interface name :param old_props: map of keys to stored property values - :type old_props: dict or MISSING_INTERFACE + :type old_props: dict or INTERFACE_MISSING :param dict new_props: map of keys to current property values :rtype list: @@ -620,7 +644,7 @@ def _check_props(object_path, ifn, old_props, new_props): proxy = dbus.SystemBus().get_object(_SERVICE, object_path, introspect=False) xml_data = ET.fromstring(_INTROSPECTABLE.Methods.Introspect(proxy, {})) - if old_props is MISSING_INTERFACE: + if old_props is INTERFACE_MISSING: diffs.append(MissingInterface(object_path, ifn)) return diffs