折腾:
【未解决】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