折腾:
【未解决】iOS自动化操作设置出错:启动设置后找不到无线局域网
期间,代码:
launchResult = self.wdaClient.app_launch(iOS_AppId_Settings)
logging.debug("launchResult=%s" % launchResult)调试又遇到之前的错误:
logging.debug("launchResult=%s" % launchResult)
TypeError: not all arguments converted during string formatting
重新调试,可以发现是:

和之前另外的代码:
def iOSGetAppState(self, appBundleId):
"""get iOS app state"""
curAppState = self.wdaClient.app_state(appBundleId)
logging.debug("curAppState=%s", curAppState)
# -> 发生异常: TypeError, not all arguments converted during string formatting
# curAppStateValue = curAppState["value"]
# -> 发生异常: TypeError, tuple indices must be integers or slices, not str
# curAppStateValue = curAppState["value"]
"""
{
"value" : 4,
"sessionId" : "5BBD460B-F420-461D-A5E3-244A74CDF5CE"
}
"""
# <GenericDict, len() = 3>
# curAppStateValue = curAppState[0]
curAppStatus = curAppState.status
curAppSessionId = curAppState.sessionId
logging.debug("curAppStatus=%s, curAppSessionId=%s", curAppStatus, curAppSessionId)
curAppStateValue = curAppState.value
logging.debug("curAppStateValue=%s", curAppStateValue)
curStateEnum = ApplicationState(curAppStateValue)
logging.debug("curStateEnum=%s", curStateEnum)
return curStateEnum遇到的情况是一模一样:
即,直接对于返回的值去打印,就会报错
而如果是:
curAppState[0]
获取第一个值,就可以。
或者是,注意到其有属性value,所以写成:
curAppState.value
也是可以获取到值的。
类似的还有:
curAppState.sessionId curAppState.status
应该是对应的:
curAppState[1] curAppState[2]
以及看到变量类型是:
<GenericDict, len() = 3>
即:
GenericDict
感觉是:
此处的facebook-wda的一些函数
/Users/xxx/.pyenv/versions/3.8.0/Python.framework/Versions/3.8/lib/python3.8/site-packages/wda/__init__.py
def app_launch(self,
bundle_id,
arguments=[],
environment={},
wait_for_quiescence=False):
。。。
return self._session_http.post(
"/wda/apps/launch", {
"bundleId": bundle_id,
"arguments": arguments,
"environment": environment,
"shouldWaitForQuiescence": wait_for_quiescence,
})
def app_activate(self, bundle_id):
return self._session_http.post("/wda/apps/launch", {
"bundleId": bundle_id,
})
def app_terminate(self, bundle_id):
return self._session_http.post("/wda/apps/terminate", {
"bundleId": bundle_id,
})
def app_state(self, bundle_id):
"""
Returns example:
{
"value": 4,
"sessionId": "0363BDC5-4335-47ED-A54E-F7CCB65C6A65"
}
value: enum ApplicationState
"""
return self._session_http.post("/wda/apps/state", {
"bundleId": bundle_id,
})中的
_session_http.post
所返回的,变量类型是GenericDict
且都无法直接打印,否则会报错
所以:要去搞清楚:
什么是GenericDict
为何不能打印值
python GenericDict
此处返回的json
Shell: curl -X POST -d '{"desiredCapabilities": {"bundleId": "com.apple.mobilesafari", "arguments": ["-u", "www.baidu.com"], "shouldWaitForQuiescence": true, "defaultAlertAction": "accept"}}' 'http://localhost:8100/session'
Return (20ms): {
"value" : {
"error" : "session not created",
"message" : "'capabilities' is mandatory to create a new session"
},
"sessionId" : "7A89D03F-EED8-473B-B0CB-2962CF7A19C3"
}对应着:
value和sessionId
但是status从哪里来的?
去看看代码
搜:
.status
找到
class WDARequestError(WDAError): def __init__(self, status, value): self.status = status self.value = value def __str__(self): return 'WDARequestError(status=%d, value=%s)' % (self.status, self.value)
-》对于WDARequestError是有额外的status的
但是此处不是error的,而是普通的response啊
def httpdo(url, method="GET", data=None):
"""
thread safe http request
"""
p = urlparse(url)
with namedlock(p.scheme + "://" + p.netloc):
return _unsafe_httpdo(url, method, data)
def _unsafe_httpdo(url, method='GET', data=None):
"""
Do HTTP Request
"""
start = time.time()
if DEBUG:
body = json.dumps(data) if data else ''
print("Shell: curl -X {method} -d '{body}' '{url}'".format(
method=method.upper(), body=body or '', url=url))
try:
response = requests.request(method,
url,
json=data,
timeout=HTTP_TIMEOUT)
except (requests.exceptions.ConnectionError,
requests.exceptions.ReadTimeout) as e:
raise
if DEBUG:
ms = (time.time() - start) * 1000
print('Return ({:.0f}ms): {}'.format(ms, response.text))
try:
retjson = response.json()
retjson['status'] = retjson.get('status', 0)
r = convert(retjson)
if r.status != 0:
raise WDARequestError(r.status, r.value)
if isinstance(r.value, dict) and r.value.get("error"):
raise WDARequestError(100, r.value['error']) # status:100 for new WebDriverAgent error
return r
except JSONDecodeError:
if response.text == "":
raise WDAEmptyResponseError(method, url, data)
raise WDAError(method, url, response.text)->看到了,是用:
retjson['status'] = retjson.get('status', 0)
r = convert(retjson)加了额外的status字段的。
然后看到了:
def convert(dictionary):
"""
Convert dict to namedtuple
"""
return namedtuple('GenericDict', list(dictionary.keys()))(**dictionary)此处是:
nametuple,名字是:GenericDict
即:
带名字的tuple元祖,名字叫GenericDict
-》真变态,一个元祖的名字,起了个dict。。。
然后注意到,想起来了,之前的wda的代码中,http的response处理中,也都是:
直接引用response即r的status和value(偶尔用到sessionId)的
所以:
后续正确用法是:
r.status
r.value
r.sessionId
但是如何确保:
logging等直接打印这个GenericDict 不报错呢?
/Users/xxx/.pyenv/versions/3.8.0/Python.framework/Versions/3.8/lib/python3.8/site-packages/wda/__init__.py
from collections import defaultdict, namedtuple
看到了:
from collections import namedtuple
def convert(dictionary):
return namedtuple('GenericDict', dictionary.keys())(**dictionary)
"""
>>> d = dictionary(a=1, b='b', c=[3])
>>> named = convert(d)
>>> named.a == d.a
True
>>> named.b == d.b
True
>>> named.c == d.c
True
"""->终于明白了:
此处是目标是把之前的dict即json,转换后,方便直接引用字段值
对于:
{
“status”: 0
}的dict,之前要写
someDict[“status”]
而变成namedtuple后,直接写:
someDict.status
即可-》更方便了。
如此而已。
接着继续去看看,如何能打印这个变量的值
难道要写成:
logging.info("launchResult: value=%s, status=%s, sessionId=%s", launchResult.value, launchResult.status, launchResult.sessionId)比较麻烦,虽然可用。
先调试看看
[200611 14:49:50][DevicesMethods.py 2602] launchResult: value=None, status=0, sessionId=79A39B72-F5F9-4A01-8E58-DD380452350A
另外试试
print(dict(launchResult)) print(str(launchResult))

经过测试,可以用:
str(launchResult)
转换成 字符串,即可打印。
logging.info("launchResult=%s", str(launchResult))调试
logging.info("launchResult=%s", str(launchResult))
# launchResult=GenericDict(value=None, sessionId='79A39B72-F5F9-4A01-8E58-DD380452350A', status=0)即可。
【总结】
此处facebook-wda的代码
/Users/xxx/.pyenv/versions/3.8.0/Python.framework/Versions/3.8/lib/python3.8/site-packages/wda/__init__.py
中有很多函数,比如:
def app_launch(self,
。。。
return self._session_http.post(
"/wda/apps/launch", {
。。。
})所返回的值,去logging打印:
launchResult = self.wdaClient.app_launch(iOS_AppId_Settings)
logging.debug("launchResult=%s" % launchResult)会报错:
TypeError: not all arguments converted during string formatting
原因:
此处是一个特殊的变量:
一个有名字的元祖,名字叫做GenericDict
具体实现是:
from collections import defaultdict, namedtuple
def convert(dictionary):
"""
Convert dict to namedtuple
"""
return namedtuple('GenericDict', list(dictionary.keys()))(**dictionary)对此,想要获取(此处的的返回的)response的值,则可以用:
response.status response.value response.sessionId
即可
想要打印其值,可以:
logging.info("launchResult=%s", str(launchResult))输出:
launchResult=GenericDict(value=None, sessionId='79A39B72-F5F9-4A01-8E58-DD380452350A', status=0)
或:
logging.info("launchResult: value=%s, status=%s, sessionId=%s", launchResult.value, launchResult.status, launchResult.sessionId)输出:
launchResult: value=None, status=0, sessionId=79A39B72-F5F9-4A01-8E58-DD380452350A
即可。
【后记20200611】
def iOSGetAppState(self, appBundleId):
"""get iOS app state"""
curAppState = self.wdaClient.app_state(appBundleId)
logging.info("curAppState=%s", curAppState)才注意到,之前此处已经在打印值了,一直没报错?
去调试,真的没报错:
[200611 14:58:12][DevicesMethods.py 1727] curAppState=GenericDict(value=4, sessionId='79A39B72-F5F9-4A01-8E58-DD380452350A', status=0)
-》说明直接logging打印是没问题的。
但是为何后续打印,偶尔又报错了呢?
很是奇怪,再回去测试:
launchResult = self.wdaClient.app_launch(iOS_AppId_Settings)
logging.debug("launchResult=%s" % launchResult)结果:
前面的info打印,是没问题的
[200611 15:00:52][DevicesMethods.py 1727] curAppState=GenericDict(value=2, sessionId='79A39B72-F5F9-4A01-8E58-DD380452350A', status=0)
怀疑:难道是debug输出到file,有问题?
去看看

也没问题
[200611 15:00:52][DevicesMethods.py 1727] curAppState=GenericDict(value=2, sessionId='79A39B72-F5F9-4A01-8E58-DD380452350A', status=0)
然后才注意到是:
logging.debug("launchResult=%s" % launchResult)是 % ,即字符串的format,格式化,不支持此处的GenericDict
而logging本身是支持的
logging.debug("launchResult=%s”, launchResult)即:
此处对于打印GenericDict的值,直接
logging.debug("launchResult=%s”, launchResult)即可。
无需额外的str(xxx) ,或 获取每个字段 再去打印值了。
另外:
之前代码
wdaTest/wdaTest.py
curAppState = curSession.app_state(gCurAppId)
# logging.debug("curAppState=%s", curAppState)
# -> 发生异常: TypeError, not all arguments converted during string formatting感觉像是:
之前就是正常的写法
logging.debug("curAppState=%s", curAppState)但是也报错?
不确定之前是什么情况。有空再去深究。
转载请注明:在路上 » 【已解决】Python代码logging打印报错:TypeError not all arguments converted during string formatting