bash
File "/opt/anaconda3/envs/airtest-multi-device/lib/python3.13/site-packages/poco/proxy.py", line 657, in wait
while not self.exists():
~~~~~~~~~~~^^
File "/opt/anaconda3/envs/airtest-multi-device/lib/python3.13/site-packages/poco/proxy.py", line 72, in wrapped
return func(proxy, *args, **kwargs)
File "/opt/anaconda3/envs/airtest-multi-device/lib/python3.13/site-packages/poco/proxy.py", line 774, in exists
return self.attr('visible')
~~~~~~~~~^^^^^^^^^^^
File "/opt/anaconda3/envs/airtest-multi-device/lib/python3.13/site-packages/poco/proxy.py", line 39, in wrapped
return func(self, *args, **kwargs)
File "/opt/anaconda3/envs/airtest-multi-device/lib/python3.13/site-packages/poco/proxy.py", line 734, in attr
nodes = self._do_query(multiple=False)
File "/opt/anaconda3/envs/airtest-multi-device/lib/python3.13/site-packages/poco/proxy.py", line 884, in _do_query
self._nodes = self.poco.agent.hierarchy.select(self.query, multiple)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
File "/opt/anaconda3/envs/airtest-multi-device/lib/python3.13/site-packages/poco/freezeui/hierarchy.py", line 90, in select
return self.selector.select(query, multiple)
~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
File "/opt/anaconda3/envs/airtest-multi-device/lib/python3.13/site-packages/poco/sdk/Selector.py", line 77, in select
return self.selectImpl(cond, multiple, self.getRoot(), 9999, True, True)
~~~~~~~~~~~~^^
File "/opt/anaconda3/envs/airtest-multi-device/lib/python3.13/site-packages/poco/sdk/Selector.py", line 71, in getRoot
return self.dumper.getRoot()
~~~~~~~~~~~~~~~~~~~^^
File "/opt/anaconda3/envs/airtest-multi-device/lib/python3.13/site-packages/poco/freezeui/hierarchy.py", line 35, in getRoot
root = Node(self.dumpHierarchy())
~~~~~~~~~~~~~~~~~~^^
File "/opt/anaconda3/envs/airtest-multi-device/lib/python3.13/site-packages/poco/drivers/ios/__init__.py", line 54, in dumpHierarchy
jsonObj = self.client.driver.source(format='json')
File "/opt/anaconda3/envs/airtest-multi-device/lib/python3.13/site-packages/wda/__init__.py", line 464, in source
return self.http.get('source?format=' + format).value
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/anaconda3/envs/airtest-multi-device/lib/python3.13/site-packages/wda/utils.py", line 43, in _inner
raise RuntimeError("call depth exceed %d" % n)
RuntimeError: call depth exceed 4
根因分析
错误的完整调用链如下:
gongjiao()调用poco("测试").wait(timeout=10).exists()wait()内部循环调用exists(),每次都会触发 UI 层级dumpexists()→dumpHierarchy()→wda.source()→http.get()→_fetch()- 关键点 :
wda库的_fetch()方法被@limit_call_depth(4)装饰器包裹。当 HTTP 请求失败时,错误回调返回RET_RETRY,导致_fetch()递归调用自身 - 递归4次后,
limit_call_depth装饰器抛出RuntimeError: call depth exceed 4
修复方案
捕获报错,可以继续运行
python
def safe_poco_wait_exists(poco_proxy, timeout=10, retries=3, interval=5):
"""安全的 poco wait+exists 封装,捕获 wda call depth exceed 异常并重试。
Args:
poco_proxy: poco 元素代理对象,如 poco("公交")
timeout: wait 超时时间(秒)
retries: 遇到 RuntimeError 时的最大重试次数
interval: 重试间隔(秒)
Returns:
bool: 元素是否存在(全部重试失败后返回 False)
"""
for attempt in range(retries):
try:
return poco_proxy.wait(timeout=timeout).exists()
except RuntimeError as e:
if "call depth exceed" in str(e):
log(f"[safe_poco_wait_exists] 第{attempt + 1}次遇到 wda call depth exceed 错误,{interval}秒后重试: {e}")
if attempt < retries - 1:
sleep(interval)
else:
log(f"[safe_poco_wait_exists] 已达最大重试次数({retries}),返回 False")
return False
else:
raise
return False