pymobiledevice3 4.14.6__py3-none-any.whl → 7.0.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. misc/plist_sniffer.py +15 -15
  2. misc/remotexpc_sniffer.py +29 -28
  3. misc/understanding_idevice_protocol_layers.md +15 -10
  4. pymobiledevice3/__main__.py +317 -127
  5. pymobiledevice3/_version.py +22 -4
  6. pymobiledevice3/bonjour.py +358 -113
  7. pymobiledevice3/ca.py +253 -16
  8. pymobiledevice3/cli/activation.py +31 -23
  9. pymobiledevice3/cli/afc.py +49 -40
  10. pymobiledevice3/cli/amfi.py +16 -21
  11. pymobiledevice3/cli/apps.py +87 -42
  12. pymobiledevice3/cli/backup.py +160 -90
  13. pymobiledevice3/cli/bonjour.py +44 -40
  14. pymobiledevice3/cli/cli_common.py +204 -198
  15. pymobiledevice3/cli/companion_proxy.py +14 -14
  16. pymobiledevice3/cli/crash.py +105 -56
  17. pymobiledevice3/cli/developer/__init__.py +62 -0
  18. pymobiledevice3/cli/developer/accessibility/__init__.py +65 -0
  19. pymobiledevice3/cli/developer/accessibility/settings.py +43 -0
  20. pymobiledevice3/cli/developer/arbitration.py +50 -0
  21. pymobiledevice3/cli/developer/condition.py +33 -0
  22. pymobiledevice3/cli/developer/core_device.py +294 -0
  23. pymobiledevice3/cli/developer/debugserver.py +244 -0
  24. pymobiledevice3/cli/developer/dvt/__init__.py +438 -0
  25. pymobiledevice3/cli/developer/dvt/core_profile_session.py +295 -0
  26. pymobiledevice3/cli/developer/dvt/simulate_location.py +56 -0
  27. pymobiledevice3/cli/developer/dvt/sysmon/__init__.py +69 -0
  28. pymobiledevice3/cli/developer/dvt/sysmon/process.py +188 -0
  29. pymobiledevice3/cli/developer/fetch_symbols.py +108 -0
  30. pymobiledevice3/cli/developer/simulate_location.py +51 -0
  31. pymobiledevice3/cli/diagnostics/__init__.py +75 -0
  32. pymobiledevice3/cli/diagnostics/battery.py +47 -0
  33. pymobiledevice3/cli/idam.py +42 -0
  34. pymobiledevice3/cli/lockdown.py +108 -103
  35. pymobiledevice3/cli/mounter.py +158 -99
  36. pymobiledevice3/cli/notification.py +38 -26
  37. pymobiledevice3/cli/pcap.py +45 -24
  38. pymobiledevice3/cli/power_assertion.py +18 -17
  39. pymobiledevice3/cli/processes.py +17 -23
  40. pymobiledevice3/cli/profile.py +165 -109
  41. pymobiledevice3/cli/provision.py +35 -34
  42. pymobiledevice3/cli/remote.py +217 -129
  43. pymobiledevice3/cli/restore.py +159 -143
  44. pymobiledevice3/cli/springboard.py +63 -53
  45. pymobiledevice3/cli/syslog.py +193 -86
  46. pymobiledevice3/cli/usbmux.py +73 -33
  47. pymobiledevice3/cli/version.py +5 -7
  48. pymobiledevice3/cli/webinspector.py +376 -214
  49. pymobiledevice3/common.py +3 -1
  50. pymobiledevice3/exceptions.py +182 -58
  51. pymobiledevice3/irecv.py +52 -53
  52. pymobiledevice3/irecv_devices.py +1489 -464
  53. pymobiledevice3/lockdown.py +473 -275
  54. pymobiledevice3/lockdown_service_provider.py +15 -8
  55. pymobiledevice3/osu/os_utils.py +27 -9
  56. pymobiledevice3/osu/posix_util.py +34 -15
  57. pymobiledevice3/osu/win_util.py +14 -8
  58. pymobiledevice3/pair_records.py +102 -21
  59. pymobiledevice3/remote/common.py +8 -4
  60. pymobiledevice3/remote/core_device/app_service.py +94 -67
  61. pymobiledevice3/remote/core_device/core_device_service.py +17 -14
  62. pymobiledevice3/remote/core_device/device_info.py +5 -5
  63. pymobiledevice3/remote/core_device/diagnostics_service.py +19 -4
  64. pymobiledevice3/remote/core_device/file_service.py +53 -23
  65. pymobiledevice3/remote/remote_service_discovery.py +79 -45
  66. pymobiledevice3/remote/remotexpc.py +73 -44
  67. pymobiledevice3/remote/tunnel_service.py +442 -317
  68. pymobiledevice3/remote/utils.py +14 -13
  69. pymobiledevice3/remote/xpc_message.py +145 -125
  70. pymobiledevice3/resources/dsc_uuid_map.py +19 -19
  71. pymobiledevice3/resources/firmware_notifications.py +20 -16
  72. pymobiledevice3/resources/notifications.txt +144 -0
  73. pymobiledevice3/restore/asr.py +27 -27
  74. pymobiledevice3/restore/base_restore.py +110 -21
  75. pymobiledevice3/restore/consts.py +87 -66
  76. pymobiledevice3/restore/device.py +59 -12
  77. pymobiledevice3/restore/fdr.py +46 -48
  78. pymobiledevice3/restore/ftab.py +19 -19
  79. pymobiledevice3/restore/img4.py +163 -0
  80. pymobiledevice3/restore/mbn.py +587 -0
  81. pymobiledevice3/restore/recovery.py +151 -151
  82. pymobiledevice3/restore/restore.py +562 -544
  83. pymobiledevice3/restore/restore_options.py +131 -110
  84. pymobiledevice3/restore/restored_client.py +51 -31
  85. pymobiledevice3/restore/tss.py +385 -267
  86. pymobiledevice3/service_connection.py +252 -59
  87. pymobiledevice3/services/accessibilityaudit.py +202 -120
  88. pymobiledevice3/services/afc.py +962 -365
  89. pymobiledevice3/services/amfi.py +24 -30
  90. pymobiledevice3/services/companion.py +23 -19
  91. pymobiledevice3/services/crash_reports.py +71 -47
  92. pymobiledevice3/services/debugserver_applist.py +3 -3
  93. pymobiledevice3/services/device_arbitration.py +8 -8
  94. pymobiledevice3/services/device_link.py +101 -79
  95. pymobiledevice3/services/diagnostics.py +973 -967
  96. pymobiledevice3/services/dtfetchsymbols.py +8 -8
  97. pymobiledevice3/services/dvt/dvt_secure_socket_proxy.py +4 -4
  98. pymobiledevice3/services/dvt/dvt_testmanaged_proxy.py +4 -4
  99. pymobiledevice3/services/dvt/instruments/activity_trace_tap.py +85 -74
  100. pymobiledevice3/services/dvt/instruments/application_listing.py +2 -3
  101. pymobiledevice3/services/dvt/instruments/condition_inducer.py +7 -6
  102. pymobiledevice3/services/dvt/instruments/core_profile_session_tap.py +466 -384
  103. pymobiledevice3/services/dvt/instruments/device_info.py +20 -11
  104. pymobiledevice3/services/dvt/instruments/energy_monitor.py +1 -1
  105. pymobiledevice3/services/dvt/instruments/graphics.py +1 -1
  106. pymobiledevice3/services/dvt/instruments/location_simulation.py +1 -1
  107. pymobiledevice3/services/dvt/instruments/location_simulation_base.py +10 -10
  108. pymobiledevice3/services/dvt/instruments/network_monitor.py +17 -17
  109. pymobiledevice3/services/dvt/instruments/notifications.py +1 -1
  110. pymobiledevice3/services/dvt/instruments/process_control.py +35 -10
  111. pymobiledevice3/services/dvt/instruments/screenshot.py +2 -2
  112. pymobiledevice3/services/dvt/instruments/sysmontap.py +15 -15
  113. pymobiledevice3/services/dvt/testmanaged/xcuitest.py +42 -52
  114. pymobiledevice3/services/file_relay.py +10 -10
  115. pymobiledevice3/services/heartbeat.py +9 -8
  116. pymobiledevice3/services/house_arrest.py +16 -15
  117. pymobiledevice3/services/idam.py +20 -0
  118. pymobiledevice3/services/installation_proxy.py +173 -81
  119. pymobiledevice3/services/lockdown_service.py +20 -10
  120. pymobiledevice3/services/misagent.py +22 -19
  121. pymobiledevice3/services/mobile_activation.py +147 -64
  122. pymobiledevice3/services/mobile_config.py +331 -294
  123. pymobiledevice3/services/mobile_image_mounter.py +141 -113
  124. pymobiledevice3/services/mobilebackup2.py +203 -145
  125. pymobiledevice3/services/notification_proxy.py +11 -11
  126. pymobiledevice3/services/os_trace.py +134 -74
  127. pymobiledevice3/services/pcapd.py +314 -302
  128. pymobiledevice3/services/power_assertion.py +10 -9
  129. pymobiledevice3/services/preboard.py +4 -4
  130. pymobiledevice3/services/remote_fetch_symbols.py +21 -14
  131. pymobiledevice3/services/remote_server.py +176 -146
  132. pymobiledevice3/services/restore_service.py +16 -16
  133. pymobiledevice3/services/screenshot.py +15 -12
  134. pymobiledevice3/services/simulate_location.py +7 -7
  135. pymobiledevice3/services/springboard.py +15 -15
  136. pymobiledevice3/services/syslog.py +5 -5
  137. pymobiledevice3/services/web_protocol/alert.py +11 -11
  138. pymobiledevice3/services/web_protocol/automation_session.py +251 -239
  139. pymobiledevice3/services/web_protocol/cdp_screencast.py +46 -37
  140. pymobiledevice3/services/web_protocol/cdp_server.py +19 -19
  141. pymobiledevice3/services/web_protocol/cdp_target.py +411 -373
  142. pymobiledevice3/services/web_protocol/driver.py +114 -111
  143. pymobiledevice3/services/web_protocol/element.py +124 -111
  144. pymobiledevice3/services/web_protocol/inspector_session.py +106 -102
  145. pymobiledevice3/services/web_protocol/selenium_api.py +49 -49
  146. pymobiledevice3/services/web_protocol/session_protocol.py +18 -12
  147. pymobiledevice3/services/web_protocol/switch_to.py +30 -27
  148. pymobiledevice3/services/webinspector.py +189 -155
  149. pymobiledevice3/tcp_forwarder.py +87 -69
  150. pymobiledevice3/tunneld/__init__.py +0 -0
  151. pymobiledevice3/tunneld/api.py +63 -0
  152. pymobiledevice3/tunneld/server.py +603 -0
  153. pymobiledevice3/usbmux.py +198 -147
  154. pymobiledevice3/utils.py +14 -11
  155. {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/METADATA +55 -28
  156. pymobiledevice3-7.0.6.dist-info/RECORD +188 -0
  157. {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/WHEEL +1 -1
  158. pymobiledevice3/cli/developer.py +0 -1215
  159. pymobiledevice3/cli/diagnostics.py +0 -99
  160. pymobiledevice3/tunneld.py +0 -524
  161. pymobiledevice3-4.14.6.dist-info/RECORD +0 -168
  162. {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/entry_points.txt +0 -0
  163. {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info/licenses}/LICENSE +0 -0
  164. {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/top_level.txt +0 -0
@@ -8,15 +8,15 @@ from pymobiledevice3.exceptions import InspectorEvaluateError
8
8
  from pymobiledevice3.services.web_protocol.session_protocol import SessionProtocol
9
9
 
10
10
  logger = logging.getLogger(__name__)
11
- console_logger = logging.getLogger('webinspector.console')
12
- heap_logger = logging.getLogger('webinspector.heap')
11
+ console_logger = logging.getLogger("webinspector.console")
12
+ heap_logger = logging.getLogger("webinspector.heap")
13
13
 
14
14
  webinspector_logger_handlers = {
15
- 'log': console_logger.info,
16
- 'info': console_logger.info,
17
- 'error': console_logger.error,
18
- 'debug': console_logger.debug,
19
- 'warning': console_logger.warning,
15
+ "log": console_logger.info,
16
+ "info": console_logger.info,
17
+ "error": console_logger.error,
18
+ "debug": console_logger.debug,
19
+ "warning": console_logger.warning,
20
20
  }
21
21
 
22
22
 
@@ -24,8 +24,8 @@ class JSObjectPreview(UserDict):
24
24
  def __init__(self, properties: list[dict]):
25
25
  super().__init__()
26
26
  for p in properties:
27
- name = p['name']
28
- value = p['value']
27
+ name = p["name"]
28
+ value = p["value"]
29
29
  self.data[name] = value
30
30
 
31
31
 
@@ -33,26 +33,25 @@ class JSObjectProperties(UserDict):
33
33
  def __init__(self, properties: list[dict]):
34
34
  super().__init__()
35
35
  for p in properties:
36
- name = p['name']
37
- if name == '__proto__':
38
- self.class_name = p['value']['className']
36
+ name = p["name"]
37
+ if name == "__proto__":
38
+ self.class_name = p["value"]["className"]
39
39
  continue
40
40
  # test if a getter/setter first
41
- value = p.get('get', p.get('set', p.get('value')))
41
+ value = p.get("get", p.get("set", p.get("value")))
42
42
  if value is None:
43
43
  continue
44
- preview = value.get('preview')
44
+ preview = value.get("preview")
45
45
  if preview is not None:
46
- value = JSObjectPreview(preview['properties'])
47
- elif value.get('className') == 'Function':
48
- value = value['description']
46
+ value = JSObjectPreview(preview["properties"])
47
+ elif value.get("className") == "Function":
48
+ value = value["description"]
49
49
  else:
50
- value = value.get('value')
50
+ value = value.get("value")
51
51
  self.data[name] = value
52
52
 
53
53
 
54
54
  class InspectorSession:
55
-
56
55
  def __init__(self, protocol: SessionProtocol, target_id: Optional[str] = None):
57
56
  """
58
57
  :param pymobiledevice3.services.web_protocol.session_protocol.SessionProtocol protocol: Session protocol.
@@ -64,14 +63,14 @@ class InspectorSession:
64
63
  self._dispatch_message_responses = {}
65
64
 
66
65
  self.response_methods = {
67
- 'Target.targetCreated': self._target_created,
68
- 'Target.targetDestroyed': self._target_destroyed,
69
- 'Target.dispatchMessageFromTarget': self._target_dispatch_message_from_target,
70
- 'Target.didCommitProvisionalTarget': self._target_did_commit_provisional_target,
71
- 'Console.messageAdded': self._console_message_added,
72
- 'Console.messagesCleared': lambda _: _,
73
- 'Console.messageRepeatCountUpdated': self._console_message_repeated_count_updated,
74
- 'Heap.garbageCollected': self._heap_garbage_collected,
66
+ "Target.targetCreated": self._target_created,
67
+ "Target.targetDestroyed": self._target_destroyed,
68
+ "Target.dispatchMessageFromTarget": self._target_dispatch_message_from_target,
69
+ "Target.didCommitProvisionalTarget": self._target_did_commit_provisional_target,
70
+ "Console.messageAdded": self._console_message_added,
71
+ "Console.messagesCleared": lambda _: _,
72
+ "Console.messageRepeatCountUpdated": self._console_message_repeated_count_updated,
73
+ "Heap.garbageCollected": self._heap_garbage_collected,
75
74
  }
76
75
 
77
76
  self._receive_task = asyncio.create_task(self._receive_loop())
@@ -88,64 +87,65 @@ class InspectorSession:
88
87
  while not protocol.inspector.wir_events:
89
88
  await asyncio.sleep(0)
90
89
  created = protocol.inspector.wir_events.pop(0)
91
- while 'targetInfo' not in created['params']:
90
+ while "targetInfo" not in created["params"]:
92
91
  created = protocol.inspector.wir_events.pop(0)
93
- target_id = created['params']['targetInfo']['targetId']
94
- logger.info(f'Created: {target_id}')
92
+ target_id = created["params"]["targetInfo"]["targetId"]
93
+ logger.info(f"Created: {target_id}")
95
94
  target = cls(protocol, target_id)
96
95
  return target
97
96
 
98
97
  def set_target_id(self, target_id):
99
98
  self.target_id = target_id
100
- logger.info(f'Changed to: {target_id}')
99
+ logger.info(f"Changed to: {target_id}")
101
100
 
102
101
  async def heap_gc(self):
103
- return await self.send_command('Heap.gc')
102
+ return await self.send_command("Heap.gc")
104
103
 
105
104
  async def heap_snapshot(self):
106
- snapshot = await self.send_command('Heap.snapshot')
105
+ snapshot = await self.send_command("Heap.snapshot")
107
106
  if self.target_id is not None:
108
- snapshot = json.loads(snapshot['params']['message'])
109
- snapshot = json.loads(snapshot)['result']['snapshotData']
107
+ snapshot = json.loads(snapshot["params"]["message"])
108
+ snapshot = json.loads(snapshot)["result"]["snapshotData"]
110
109
  return snapshot
111
110
 
112
111
  async def heap_enable(self):
113
- return await self.send_command('Heap.enable')
112
+ return await self.send_command("Heap.enable")
114
113
 
115
114
  async def console_enable(self):
116
- return await self.send_command('Console.enable')
115
+ return await self.send_command("Console.enable")
117
116
 
118
117
  async def runtime_enable(self):
119
- return await self.send_command('Runtime.enable')
118
+ return await self.send_command("Runtime.enable")
120
119
 
121
120
  async def send_command(self, method: str, **kwargs):
122
121
  if self.target_id is None:
123
122
  return await self.protocol.send_receive(method, **kwargs)
124
123
  else:
125
- return await self.send_and_receive({'method': method, 'params': kwargs})
124
+ return await self.send_and_receive({"method": method, "params": kwargs})
126
125
 
127
126
  async def runtime_evaluate(self, exp: str, return_by_value: bool = False):
128
127
  # if the expression is dict, it's needed to be in ()
129
128
  exp = exp.strip()
130
- if exp:
131
- if exp[0] == '{' and exp[-1] == '}':
132
- exp = f'({exp})'
133
-
134
- response = await self.send_and_receive({'method': 'Runtime.evaluate',
135
- 'params': {
136
- 'expression': exp,
137
- 'objectGroup': 'console',
138
- 'includeCommandLineAPI': True,
139
- 'doNotPauseOnExceptionsAndMuteConsole': False,
140
- 'silent': False,
141
- 'returnByValue': return_by_value,
142
- 'generatePreview': True,
143
- 'userGesture': True,
144
- 'awaitPromise': False,
145
- 'replMode': True,
146
- 'allowUnsafeEvalBlockedByCSP': False,
147
- 'uniqueContextId': '0.1'}
148
- })
129
+ if exp and exp[0] == "{" and exp[-1] == "}":
130
+ exp = f"({exp})"
131
+
132
+ response = await self.send_and_receive({
133
+ "method": "Runtime.evaluate",
134
+ "params": {
135
+ "expression": exp,
136
+ "objectGroup": "console",
137
+ "includeCommandLineAPI": True,
138
+ "doNotPauseOnExceptionsAndMuteConsole": False,
139
+ "silent": False,
140
+ "returnByValue": return_by_value,
141
+ "generatePreview": True,
142
+ "userGesture": True,
143
+ "awaitPromise": False,
144
+ "replMode": True,
145
+ "allowUnsafeEvalBlockedByCSP": False,
146
+ "uniqueContextId": "0.1",
147
+ },
148
+ })
149
149
 
150
150
  return await self._parse_runtime_evaluate(response)
151
151
 
@@ -154,18 +154,19 @@ class InspectorSession:
154
154
 
155
155
  async def send_and_receive(self, message: dict) -> dict:
156
156
  if self.target_id is None:
157
- message_id = await self.protocol.send_command(message['method'], **message.get('params', {}))
157
+ message_id = await self.protocol.send_command(message["method"], **message.get("params", {}))
158
158
  return await self.protocol.wait_for_message(message_id)
159
159
  else:
160
160
  message_id = await self.send_message_to_target(message)
161
161
  return await self.receive_response_by_id(message_id)
162
162
 
163
163
  async def send_message_to_target(self, message: dict) -> int:
164
- message['id'] = self.message_id
164
+ message["id"] = self.message_id
165
165
  self.message_id += 1
166
- await self.protocol.send_command('Target.sendMessageToTarget', targetId=self.target_id,
167
- message=json.dumps(message))
168
- return message['id']
166
+ await self.protocol.send_command(
167
+ "Target.sendMessageToTarget", targetId=self.target_id, message=json.dumps(message)
168
+ )
169
+ return message["id"]
169
170
 
170
171
  async def _receive_loop(self):
171
172
  while True:
@@ -173,11 +174,11 @@ class InspectorSession:
173
174
  await asyncio.sleep(0)
174
175
 
175
176
  response = self.protocol.inspector.wir_events.pop(0)
176
- response_method = response['method']
177
+ response_method = response["method"]
177
178
  if response_method in self.response_methods:
178
179
  self.response_methods[response_method](response)
179
180
  else:
180
- logger.error(f'Unknown response: {response}')
181
+ logger.error(f"Unknown response: {response}")
181
182
 
182
183
  async def receive_response_by_id(self, message_id: int) -> dict:
183
184
  while True:
@@ -187,64 +188,67 @@ class InspectorSession:
187
188
 
188
189
  async def get_properties(self, object_id: str) -> JSObjectProperties:
189
190
  message = await self.send_command(
190
- 'Runtime.getProperties', objectId=object_id, ownProperties=True, generatePreview=True)
191
+ "Runtime.getProperties", objectId=object_id, ownProperties=True, generatePreview=True
192
+ )
191
193
  if self.target_id is not None:
192
- message = json.loads(message['params']['message'])['result']
193
- return JSObjectProperties(message['properties'])
194
+ message = json.loads(message["params"]["message"])["result"]
195
+ return JSObjectProperties(message["properties"])
194
196
 
195
197
  async def _parse_runtime_evaluate(self, response: dict):
196
- if self.target_id is None:
197
- message = response
198
- else:
199
- message = json.loads(response['params']['message'])
200
- result = message['result']['result']
201
- if result.get('subtype', '') == 'error':
202
- properties = await self.get_properties(result['objectId'])
203
- raise InspectorEvaluateError(properties.class_name, properties['message'], properties.get('line'),
204
- properties.get('column'), properties.get('stack', '').split('\n'))
205
- elif result['type'] == 'bigint':
206
- return result['description']
207
- elif result['type'] == 'undefined':
198
+ message = response if self.target_id is None else json.loads(response["params"]["message"])
199
+ result = message["result"]["result"]
200
+ if result.get("subtype", "") == "error":
201
+ properties = await self.get_properties(result["objectId"])
202
+ raise InspectorEvaluateError(
203
+ properties.class_name,
204
+ properties["message"],
205
+ properties.get("line"),
206
+ properties.get("column"),
207
+ properties.get("stack", "").split("\n"),
208
+ )
209
+ elif result["type"] == "bigint":
210
+ return result["description"]
211
+ elif result["type"] == "undefined":
208
212
  pass
209
- elif result['type'] == 'object':
210
- value = result.get('value')
213
+ elif result["type"] == "object":
214
+ value = result.get("value")
211
215
  if value is not None:
212
216
  return value
213
217
 
214
218
  # TODO: JSObjectProperties()
215
- preview = result['preview']
216
- preview_buf = '{\n'
217
- for p in result['preview']['properties']:
218
- value = p.get('value', 'NOT_SUPPORTED_FOR_PREVIEW')
219
- preview_buf += f'\t{p["name"]}: {value}, // {p["type"]}\n'
220
- if preview.get('overflow'):
221
- preview_buf += '\t// ...\n'
222
- preview_buf += '}'
223
- return f'[object {result["className"]}]\n{preview_buf}'
224
- elif result['type'] == 'function':
225
- return result['description']
219
+ preview = result["preview"]
220
+ preview_buf = "{\n"
221
+ for p in result["preview"]["properties"]:
222
+ value = p.get("value", "NOT_SUPPORTED_FOR_PREVIEW")
223
+ preview_buf += f"\t{p['name']}: {value}, // {p['type']}\n"
224
+ if preview.get("overflow"):
225
+ preview_buf += "\t// ...\n"
226
+ preview_buf += "}"
227
+ return f"[object {result['className']}]\n{preview_buf}"
228
+ elif result["type"] == "function":
229
+ return result["description"]
226
230
  else:
227
- return result['value']
231
+ return result["value"]
228
232
 
229
233
  # response methods
230
234
  def _target_dispatch_message_from_target(self, response: dict):
231
- target_message = json.loads(response['params']['message'])
232
- receive_message_id = target_message.get('id')
235
+ target_message = json.loads(response["params"]["message"])
236
+ receive_message_id = target_message.get("id")
233
237
  if receive_message_id is None:
234
238
  self._missing_id_in_message(target_message)
235
239
  return
236
240
  self._dispatch_message_responses[receive_message_id] = response
237
241
 
238
242
  def _missing_id_in_message(self, message: dict):
239
- handler = self.response_methods.get(message['method'])
243
+ handler = self.response_methods.get(message["method"])
240
244
  if handler is not None:
241
245
  handler(message)
242
246
  else:
243
- logger.critical(f'unhandled message: {message}')
247
+ logger.critical(f"unhandled message: {message}")
244
248
 
245
249
  def _console_message_added(self, message: dict):
246
- log_level = message['params']['message']['level']
247
- text = message['params']['message']['text']
250
+ log_level = message["params"]["message"]["level"]
251
+ text = message["params"]["message"]["text"]
248
252
  self._last_console_message = message
249
253
  webinspector_logger_handlers[log_level](text)
250
254
 
@@ -252,7 +256,7 @@ class InspectorSession:
252
256
  self._console_message_added(self._last_console_message)
253
257
 
254
258
  def _heap_garbage_collected(self, message: dict):
255
- heap_logger.debug(message['params'])
259
+ heap_logger.debug(message["params"])
256
260
 
257
261
  def _target_created(self, response: dict):
258
262
  pass
@@ -261,4 +265,4 @@ class InspectorSession:
261
265
  pass
262
266
 
263
267
  def _target_did_commit_provisional_target(self, response: dict):
264
- self.set_target_id(response['params']['newTargetId'])
268
+ self.set_target_id(response["params"]["newTargetId"])
@@ -6,86 +6,86 @@ from pymobiledevice3.services.web_protocol.automation_session import By
6
6
 
7
7
  class SeleniumApi(ABC):
8
8
  @abstractmethod
9
- def find_element(self, by=By.ID, value=None):
9
+ async def find_element(self, by=By.ID, value=None):
10
10
  pass
11
11
 
12
12
  @abstractmethod
13
- def find_elements(self, by=By.ID, value=None):
13
+ async def find_elements(self, by=By.ID, value=None):
14
14
  pass
15
15
 
16
16
  @property
17
17
  @abstractmethod
18
- def screenshot_as_base64(self):
18
+ async def screenshot_as_base64(self) -> str:
19
19
  pass
20
20
 
21
- def find_element_by_class_name(self, name):
22
- return self.find_element(By.CLASS_NAME, name)
21
+ async def find_element_by_class_name(self, name):
22
+ return await self.find_element(By.CLASS_NAME, name)
23
23
 
24
- def find_element_by_css_selector(self, css_selector):
25
- return self.find_element(By.CSS_SELECTOR, css_selector)
24
+ async def find_element_by_css_selector(self, css_selector):
25
+ return await self.find_element(By.CSS_SELECTOR, css_selector)
26
26
 
27
- def find_element_by_id(self, id_):
28
- return self.find_element(value=id_)
27
+ async def find_element_by_id(self, id_):
28
+ return await self.find_element(value=id_)
29
29
 
30
- def find_element_by_link_text(self, link_text):
31
- return self.find_element(By.LINK_TEXT, link_text)
30
+ async def find_element_by_link_text(self, link_text):
31
+ return await self.find_element(By.LINK_TEXT, link_text)
32
32
 
33
- def find_element_by_name(self, name):
34
- return self.find_element(By.NAME, name)
33
+ async def find_element_by_name(self, name):
34
+ return await self.find_element(By.NAME, name)
35
35
 
36
- def find_element_by_partial_link_text(self, link_text):
37
- return self.find_element(By.PARTIAL_LINK_TEXT, link_text)
36
+ async def find_element_by_partial_link_text(self, link_text):
37
+ return await self.find_element(By.PARTIAL_LINK_TEXT, link_text)
38
38
 
39
- def find_element_by_tag_name(self, name):
40
- return self.find_element(By.TAG_NAME, name)
39
+ async def find_element_by_tag_name(self, name):
40
+ return await self.find_element(By.TAG_NAME, name)
41
41
 
42
- def find_element_by_xpath(self, xpath):
43
- return self.find_element(By.XPATH, xpath)
42
+ async def find_element_by_xpath(self, xpath):
43
+ return await self.find_element(By.XPATH, xpath)
44
44
 
45
- def find_elements_by_class_name(self, name):
46
- return self.find_elements(By.CLASS_NAME, name)
45
+ async def find_elements_by_class_name(self, name):
46
+ return await self.find_elements(By.CLASS_NAME, name)
47
47
 
48
- def find_elements_by_css_selector(self, css_selector):
49
- return self.find_elements(By.CSS_SELECTOR, css_selector)
48
+ async def find_elements_by_css_selector(self, css_selector):
49
+ return await self.find_elements(By.CSS_SELECTOR, css_selector)
50
50
 
51
- def find_elements_by_id(self, id_):
52
- return self.find_elements(value=id_)
51
+ async def find_elements_by_id(self, id_):
52
+ return await self.find_elements(value=id_)
53
53
 
54
- def find_elements_by_link_text(self, link_text):
55
- return self.find_elements(By.LINK_TEXT, link_text)
54
+ async def find_elements_by_link_text(self, link_text):
55
+ return await self.find_elements(By.LINK_TEXT, link_text)
56
56
 
57
- def find_elements_by_name(self, name):
58
- return self.find_elements(By.NAME, name)
57
+ async def find_elements_by_name(self, name):
58
+ return await self.find_elements(By.NAME, name)
59
59
 
60
- def find_elements_by_partial_link_text(self, link_text):
61
- return self.find_elements(By.PARTIAL_LINK_TEXT, link_text)
60
+ async def find_elements_by_partial_link_text(self, link_text):
61
+ return await self.find_elements(By.PARTIAL_LINK_TEXT, link_text)
62
62
 
63
- def find_elements_by_tag_name(self, name):
64
- return self.find_elements(By.TAG_NAME, name)
63
+ async def find_elements_by_tag_name(self, name):
64
+ return await self.find_elements(By.TAG_NAME, name)
65
65
 
66
- def find_elements_by_xpath(self, xpath):
67
- return self.find_elements(By.XPATH, xpath)
66
+ async def find_elements_by_xpath(self, xpath):
67
+ return await self.find_elements(By.XPATH, xpath)
68
68
 
69
- def screenshot(self, filename):
70
- png = self.screenshot_as_png()
69
+ async def screenshot(self, filename):
70
+ png = await self.screenshot_as_png()
71
71
  try:
72
- with open(filename, 'wb') as f:
72
+ with open(filename, "wb") as f:
73
73
  f.write(png)
74
- except IOError:
74
+ except OSError:
75
75
  return False
76
76
  return True
77
77
 
78
- def screenshot_as_png(self):
79
- return b64decode(self.screenshot_as_base64.encode('ascii'))
78
+ async def screenshot_as_png(self) -> bytes:
79
+ return b64decode((await self.screenshot_as_base64).encode("ascii"))
80
80
 
81
- def get_screenshot_as_base64(self):
82
- return self.screenshot_as_base64
81
+ async def get_screenshot_as_base64(self) -> str:
82
+ return await self.screenshot_as_base64
83
83
 
84
- def get_screenshot_as_file(self, filename):
85
- return self.screenshot(filename)
84
+ async def get_screenshot_as_file(self, filename):
85
+ return await self.screenshot(filename)
86
86
 
87
- def get_screenshot_as_png(self):
88
- return self.screenshot_as_png()
87
+ async def get_screenshot_as_png(self):
88
+ return await self.screenshot_as_png()
89
89
 
90
- def save_screenshot(self, filename) -> bool:
91
- return self.screenshot(filename)
90
+ async def save_screenshot(self, filename) -> bool:
91
+ return await self.screenshot(filename)
@@ -5,7 +5,7 @@ from pymobiledevice3.exceptions import WirError
5
5
 
6
6
 
7
7
  class SessionProtocol:
8
- def __init__(self, inspector, id_, app, page, method_prefix='Automation'):
8
+ def __init__(self, inspector, id_, app, page, method_prefix="Automation"):
9
9
  """
10
10
  :param pymobiledevice3.services.webinspector.WebinspectorService inspector:
11
11
  """
@@ -19,19 +19,25 @@ class SessionProtocol:
19
19
  async def send_command(self, method, **kwargs):
20
20
  wir_id = self._wir_messages_id
21
21
  self._wir_messages_id += 1
22
- await self.inspector.send_socket_data(self.id_, self.app.id_, self.page.id_, {
23
- 'method': f'{self.method_prefix}.{method}' if self.method_prefix else method,
24
- 'params': kwargs,
25
- 'id': wir_id,
26
- })
22
+ await self.inspector.send_socket_data(
23
+ self.id_,
24
+ self.app.id_,
25
+ self.page.id_,
26
+ {
27
+ "method": f"{self.method_prefix}.{method}" if self.method_prefix else method,
28
+ "params": kwargs,
29
+ "id": wir_id,
30
+ },
31
+ )
27
32
  return wir_id
28
33
 
29
34
  async def get_response(self, wir_id):
30
35
  response = await self.wait_for_message(wir_id)
31
- if 'result' in response:
32
- return response['result']
33
- elif 'error' in response:
34
- raise WirError(response['error']['message'])
36
+ if "result" in response:
37
+ return response["result"]
38
+ elif "error" in response:
39
+ raise WirError(response["error"]["message"])
40
+ raise WirError(f"Unknown response: {response}")
35
41
 
36
42
  async def send_receive(self, method, wait_for_response=True, **kwargs):
37
43
  wir_id = await self.send_command(method, **kwargs)
@@ -45,8 +51,8 @@ class SessionProtocol:
45
51
  await asyncio.sleep(0)
46
52
  return self.inspector.wir_message_results.pop(id_)
47
53
 
48
- def sync_send_receive(self, method, wait_for_response=True, **kwargs):
49
- return self.inspector.await_(self.send_receive(method, wait_for_response, **kwargs))
54
+ async def sync_send_receive(self, method, wait_for_response=True, **kwargs):
55
+ return await self.send_receive(method, wait_for_response, **kwargs)
50
56
 
51
57
  def __getattr__(self, item):
52
58
  return partial(self.sync_send_receive, method=item)
@@ -11,57 +11,60 @@ class SwitchTo:
11
11
  self.session = session
12
12
 
13
13
  @property
14
- def active_element(self) -> WebElement:
15
- """ Returns the element with focus, or BODY if nothing has focus. """
16
- self.session.wait_for_navigation_to_complete()
17
- elem = self.session.evaluate_js_function('function() { return document.activeElement; }', include_frame=False)
14
+ async def active_element(self) -> WebElement:
15
+ """Returns the element with focus, or BODY if nothing has focus."""
16
+ await self.session.wait_for_navigation_to_complete()
17
+ elem = await self.session.evaluate_js_function(
18
+ "function() { return document.activeElement; }", include_frame=False
19
+ )
18
20
  return WebElement(self.session, elem)
19
21
 
20
22
  @property
21
23
  def alert(self) -> Alert:
22
- """ Switches focus to an alert on the page. """
24
+ """Switches focus to an alert on the page."""
23
25
  return Alert(self.session)
24
26
 
25
- def default_content(self):
26
- """ Switch focus to the default frame. """
27
- self.session.switch_to_browsing_context('')
27
+ async def default_content(self):
28
+ """Switch focus to the default frame."""
29
+ await self.session.switch_to_browsing_context("")
28
30
 
29
- def frame(self, frame_reference):
31
+ async def frame(self, frame_reference):
30
32
  """
31
33
  Switches focus to the specified frame, by index, name, or web element.
32
34
  :param frame_reference: The name of the window to switch to, an integer representing the index,
33
35
  or a web element that is an (i)frame to switch to.
34
36
  """
35
- if isinstance(frame_reference, int) or isinstance(frame_reference, WebElement):
37
+ if isinstance(frame_reference, (int, WebElement)):
36
38
  frame = frame_reference
37
39
  elif isinstance(frame_reference, str):
38
- elem = self.session.find_elements(By.ID, frame_reference)
40
+ elem = await self.session.find_elements(By.ID, frame_reference)
39
41
  if elem is None:
40
- elem = self.session.find_elements(By.NAME, frame_reference)
42
+ elem = await self.session.find_elements(By.NAME, frame_reference)
41
43
  frame = WebElement(self.session, elem)
42
44
  else:
43
- raise ValueError()
45
+ raise TypeError()
44
46
 
45
- self.session.wait_for_navigation_to_complete()
47
+ await self.session.wait_for_navigation_to_complete()
46
48
  if isinstance(frame, int):
47
- self.session.switch_to_frame(frame_ordinal=frame)
49
+ await self.session.switch_to_frame(frame_ordinal=frame)
48
50
  else:
49
- self.session.switch_to_frame(frame_handle=frame)
51
+ await self.session.switch_to_frame(frame_handle=frame)
50
52
 
51
- def new_window(self, type_=''):
52
- """ Switches to a new top-level browsing context. """
53
- self.session.switch_to_window(self.session.create_window(type_))
53
+ async def new_window(self, type_=""):
54
+ """Switches to a new top-level browsing context."""
55
+ await self.session.switch_to_window(self.session.create_window(type_))
54
56
 
55
- def parent_frame(self):
57
+ async def parent_frame(self):
56
58
  """
57
59
  Switches focus to the parent context. If the current context is the top
58
60
  level browsing context, the context remains unchanged.
59
61
  """
60
- self.session.wait_for_navigation_to_complete()
61
- self.session.switch_to_browsing_context_frame(self.session.top_level_handle,
62
- self.session.current_parent_handle)
63
- self.session.switch_to_browsing_context(self.session.current_parent_handle)
62
+ await self.session.wait_for_navigation_to_complete()
63
+ await self.session.switch_to_browsing_context_frame(
64
+ self.session.top_level_handle, self.session.current_parent_handle
65
+ )
66
+ await self.session.switch_to_browsing_context(self.session.current_parent_handle)
64
67
 
65
- def window(self, window_name):
66
- """ Switches focus to the specified window. """
67
- self.session.switch_to_window(window_name)
68
+ async def window(self, window_name):
69
+ """Switches focus to the specified window."""
70
+ await self.session.switch_to_window(window_name)