基于python-dbus-next的DBus属性断言功能故障排查
问题描述
我基于python-dbus-next库开发DBus对象测试基础设施,想要实现如下形式的属性断言调用:
iface = await IFaceClient(bus_name, path, interface) await iface.call_method('bla') await iface.wait_property('bla_property').equal(30)
核心需求是通过wait_property监听指定属性,直到其值等于预期值。但当前实现存在问题:equal方法始终获取初始值None,无法感知回调中更新的属性值,即使属性从10→20→30变化,断言也无法识别。我试过用asyncio.Event结合同步回调,也考虑过Future但它只能生效一次,无法覆盖属性过渡状态的场景。
附当前实现代码片段:
接口中wait_property实现
def wait_property(self, prop_name): event = asyncio.Event() value = None def property_changed(prop): nonlocal value nonlocal event value = prop.get(prop_name) self._iface.on_property_changed(property_changed) return Comparer(event, value)
Comparer类实现
class Comparer: timeout = 10 def __init__(self, event, value): self.event = event self.value = value def until(self, t): self.timeout = t return self async def _eq(self, expected): f = asyncio.get_event_loop().create_future() try: await self.event.wait() assert self.value == expected f.set_result(self.value) except AssertionError: self.event.clear() return await f async def equal(self, value): try: return await asyncio.wait_for(self._eq(value), timeout=self.timeout) except asyncio.TimeoutError: raise AssertionError(f'Values isn`t equal: {self.value} != {value}')
问题分析与修复方案
核心问题点
- Event从未被触发:
property_changed回调仅更新了局部变量value,但未调用event.set(),导致await self.event.wait()永久阻塞,永远不会检查新值。 - 值引用不共享:
Comparer初始化时接收的是value的初始值(None),回调更新的是wait_property函数内的局部变量,和Comparer实例的self.value完全无关,所以即使属性变化,Comparer拿到的还是初始值。 - 单次Event无法处理多次属性变更:即使触发了Event,后续属性变化不会重新触发检查,无法覆盖属性从10→20→30的过渡场景。
修复后的代码实现
修改wait_property方法
def wait_property(self, prop_name): comparer = Comparer(prop_name) def property_changed(props): # 每次属性变更时更新Comparer的当前值,并触发预期值检查 comparer.current_value = props.get(prop_name) comparer._check_condition() self._iface.on_property_changed(property_changed) # 主动获取初始属性值,避免属性已为预期值时仍等待信号 try: initial_props = self._iface.get_properties() comparer.current_value = initial_props.get(prop_name) except Exception: pass # 处理初始值获取失败的场景 return comparer
修改Comparer类
class Comparer: def __init__(self, prop_name): self.prop_name = prop_name self.current_value = None self.expected_value = None self.wait_future = None self.timeout = 10 def until(self, t): self.timeout = t return self def _check_condition(self): # 若存在等待的Future且当前值符合预期,立即完成Future if self.wait_future and not self.wait_future.done(): if self.current_value == self.expected_value: self.wait_future.set_result(self.current_value) async def equal(self, expected): self.expected_value = expected # 先检查当前值是否已满足预期,无需等待信号 if self.current_value == expected: return self.current_value # 创建Future等待属性值符合预期 self.wait_future = asyncio.get_event_loop().create_future() try: return await asyncio.wait_for(self.wait_future, timeout=self.timeout) except asyncio.TimeoutError: raise AssertionError(f"属性[{self.prop_name}]超时未达到预期值: 当前值={self.current_value}, 预期值={expected}") finally: # 清理Future,避免内存泄漏 self.wait_future = None
修复说明
- 移除冗余的Event:改用Future跟踪等待状态,每次属性变更时主动检查是否满足预期,满足则立即完成Future。
- 共享属性值引用:让Comparer直接持有当前属性值,回调直接更新实例的
current_value,确保值能实时同步。 - 初始值预检查:创建Comparer时主动获取一次属性当前值,若已符合预期直接返回,提升效率。
- 动态响应属性变更:每次属性变化都触发
_check_condition,能处理属性多次变更的过渡场景,直到值符合预期或超时。
内容的提问来源于stack exchange,提问作者obs
相关产品推荐
相关产品推荐

