pymobiledevice3 4.27.4__py3-none-any.whl → 5.1.2__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 (143) hide show
  1. misc/plist_sniffer.py +15 -15
  2. misc/remotexpc_sniffer.py +29 -28
  3. pymobiledevice3/__main__.py +123 -98
  4. pymobiledevice3/_version.py +2 -2
  5. pymobiledevice3/bonjour.py +351 -117
  6. pymobiledevice3/ca.py +32 -24
  7. pymobiledevice3/cli/activation.py +7 -7
  8. pymobiledevice3/cli/afc.py +19 -19
  9. pymobiledevice3/cli/amfi.py +4 -4
  10. pymobiledevice3/cli/apps.py +51 -39
  11. pymobiledevice3/cli/backup.py +58 -32
  12. pymobiledevice3/cli/bonjour.py +27 -20
  13. pymobiledevice3/cli/cli_common.py +112 -81
  14. pymobiledevice3/cli/companion_proxy.py +4 -4
  15. pymobiledevice3/cli/completions.py +10 -10
  16. pymobiledevice3/cli/crash.py +37 -31
  17. pymobiledevice3/cli/developer.py +601 -519
  18. pymobiledevice3/cli/diagnostics.py +38 -33
  19. pymobiledevice3/cli/lockdown.py +82 -72
  20. pymobiledevice3/cli/mounter.py +84 -67
  21. pymobiledevice3/cli/notification.py +10 -10
  22. pymobiledevice3/cli/pcap.py +19 -14
  23. pymobiledevice3/cli/power_assertion.py +12 -10
  24. pymobiledevice3/cli/processes.py +10 -10
  25. pymobiledevice3/cli/profile.py +88 -77
  26. pymobiledevice3/cli/provision.py +17 -17
  27. pymobiledevice3/cli/remote.py +188 -111
  28. pymobiledevice3/cli/restore.py +43 -40
  29. pymobiledevice3/cli/springboard.py +30 -28
  30. pymobiledevice3/cli/syslog.py +85 -58
  31. pymobiledevice3/cli/usbmux.py +21 -20
  32. pymobiledevice3/cli/version.py +3 -2
  33. pymobiledevice3/cli/webinspector.py +156 -78
  34. pymobiledevice3/common.py +1 -1
  35. pymobiledevice3/exceptions.py +154 -60
  36. pymobiledevice3/irecv.py +49 -53
  37. pymobiledevice3/irecv_devices.py +1489 -492
  38. pymobiledevice3/lockdown.py +400 -251
  39. pymobiledevice3/lockdown_service_provider.py +5 -7
  40. pymobiledevice3/osu/os_utils.py +18 -9
  41. pymobiledevice3/osu/posix_util.py +28 -15
  42. pymobiledevice3/osu/win_util.py +14 -8
  43. pymobiledevice3/pair_records.py +19 -19
  44. pymobiledevice3/remote/common.py +4 -4
  45. pymobiledevice3/remote/core_device/app_service.py +94 -67
  46. pymobiledevice3/remote/core_device/core_device_service.py +17 -14
  47. pymobiledevice3/remote/core_device/device_info.py +5 -5
  48. pymobiledevice3/remote/core_device/diagnostics_service.py +10 -8
  49. pymobiledevice3/remote/core_device/file_service.py +47 -33
  50. pymobiledevice3/remote/remote_service_discovery.py +53 -35
  51. pymobiledevice3/remote/remotexpc.py +64 -42
  52. pymobiledevice3/remote/tunnel_service.py +383 -297
  53. pymobiledevice3/remote/utils.py +14 -13
  54. pymobiledevice3/remote/xpc_message.py +145 -125
  55. pymobiledevice3/resources/dsc_uuid_map.py +19 -19
  56. pymobiledevice3/resources/firmware_notifications.py +16 -16
  57. pymobiledevice3/restore/asr.py +27 -27
  58. pymobiledevice3/restore/base_restore.py +90 -47
  59. pymobiledevice3/restore/consts.py +87 -66
  60. pymobiledevice3/restore/device.py +11 -11
  61. pymobiledevice3/restore/fdr.py +46 -46
  62. pymobiledevice3/restore/ftab.py +19 -19
  63. pymobiledevice3/restore/img4.py +130 -133
  64. pymobiledevice3/restore/mbn.py +587 -0
  65. pymobiledevice3/restore/recovery.py +125 -135
  66. pymobiledevice3/restore/restore.py +535 -523
  67. pymobiledevice3/restore/restore_options.py +122 -115
  68. pymobiledevice3/restore/restored_client.py +25 -22
  69. pymobiledevice3/restore/tss.py +378 -270
  70. pymobiledevice3/service_connection.py +50 -46
  71. pymobiledevice3/services/accessibilityaudit.py +137 -127
  72. pymobiledevice3/services/afc.py +352 -292
  73. pymobiledevice3/services/amfi.py +21 -18
  74. pymobiledevice3/services/companion.py +23 -19
  75. pymobiledevice3/services/crash_reports.py +61 -47
  76. pymobiledevice3/services/debugserver_applist.py +3 -3
  77. pymobiledevice3/services/device_arbitration.py +8 -8
  78. pymobiledevice3/services/device_link.py +56 -48
  79. pymobiledevice3/services/diagnostics.py +971 -968
  80. pymobiledevice3/services/dtfetchsymbols.py +8 -8
  81. pymobiledevice3/services/dvt/dvt_secure_socket_proxy.py +4 -4
  82. pymobiledevice3/services/dvt/dvt_testmanaged_proxy.py +4 -4
  83. pymobiledevice3/services/dvt/instruments/activity_trace_tap.py +85 -74
  84. pymobiledevice3/services/dvt/instruments/application_listing.py +2 -3
  85. pymobiledevice3/services/dvt/instruments/condition_inducer.py +7 -6
  86. pymobiledevice3/services/dvt/instruments/core_profile_session_tap.py +466 -384
  87. pymobiledevice3/services/dvt/instruments/device_info.py +11 -11
  88. pymobiledevice3/services/dvt/instruments/energy_monitor.py +1 -1
  89. pymobiledevice3/services/dvt/instruments/graphics.py +1 -1
  90. pymobiledevice3/services/dvt/instruments/location_simulation.py +1 -1
  91. pymobiledevice3/services/dvt/instruments/location_simulation_base.py +10 -10
  92. pymobiledevice3/services/dvt/instruments/network_monitor.py +17 -17
  93. pymobiledevice3/services/dvt/instruments/notifications.py +1 -1
  94. pymobiledevice3/services/dvt/instruments/process_control.py +25 -10
  95. pymobiledevice3/services/dvt/instruments/screenshot.py +2 -2
  96. pymobiledevice3/services/dvt/instruments/sysmontap.py +15 -15
  97. pymobiledevice3/services/dvt/testmanaged/xcuitest.py +42 -52
  98. pymobiledevice3/services/file_relay.py +10 -10
  99. pymobiledevice3/services/heartbeat.py +8 -7
  100. pymobiledevice3/services/house_arrest.py +12 -15
  101. pymobiledevice3/services/installation_proxy.py +119 -100
  102. pymobiledevice3/services/lockdown_service.py +12 -5
  103. pymobiledevice3/services/misagent.py +22 -19
  104. pymobiledevice3/services/mobile_activation.py +84 -72
  105. pymobiledevice3/services/mobile_config.py +331 -301
  106. pymobiledevice3/services/mobile_image_mounter.py +137 -116
  107. pymobiledevice3/services/mobilebackup2.py +188 -150
  108. pymobiledevice3/services/notification_proxy.py +11 -11
  109. pymobiledevice3/services/os_trace.py +128 -74
  110. pymobiledevice3/services/pcapd.py +306 -306
  111. pymobiledevice3/services/power_assertion.py +10 -9
  112. pymobiledevice3/services/preboard.py +4 -4
  113. pymobiledevice3/services/remote_fetch_symbols.py +16 -14
  114. pymobiledevice3/services/remote_server.py +176 -146
  115. pymobiledevice3/services/restore_service.py +16 -16
  116. pymobiledevice3/services/screenshot.py +13 -10
  117. pymobiledevice3/services/simulate_location.py +7 -7
  118. pymobiledevice3/services/springboard.py +15 -15
  119. pymobiledevice3/services/syslog.py +5 -5
  120. pymobiledevice3/services/web_protocol/alert.py +3 -3
  121. pymobiledevice3/services/web_protocol/automation_session.py +183 -179
  122. pymobiledevice3/services/web_protocol/cdp_screencast.py +44 -36
  123. pymobiledevice3/services/web_protocol/cdp_server.py +19 -19
  124. pymobiledevice3/services/web_protocol/cdp_target.py +411 -373
  125. pymobiledevice3/services/web_protocol/driver.py +47 -45
  126. pymobiledevice3/services/web_protocol/element.py +74 -63
  127. pymobiledevice3/services/web_protocol/inspector_session.py +106 -102
  128. pymobiledevice3/services/web_protocol/selenium_api.py +3 -3
  129. pymobiledevice3/services/web_protocol/session_protocol.py +15 -10
  130. pymobiledevice3/services/web_protocol/switch_to.py +11 -12
  131. pymobiledevice3/services/webinspector.py +142 -116
  132. pymobiledevice3/tcp_forwarder.py +35 -22
  133. pymobiledevice3/tunneld/api.py +20 -15
  134. pymobiledevice3/tunneld/server.py +310 -193
  135. pymobiledevice3/usbmux.py +197 -148
  136. pymobiledevice3/utils.py +14 -11
  137. {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/METADATA +1 -2
  138. pymobiledevice3-5.1.2.dist-info/RECORD +173 -0
  139. pymobiledevice3-4.27.4.dist-info/RECORD +0 -172
  140. {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/WHEEL +0 -0
  141. {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/entry_points.txt +0 -0
  142. {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/licenses/LICENSE +0 -0
  143. {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/top_level.txt +0 -0
@@ -1,15 +1,20 @@
1
1
  import asyncio
2
+ import contextlib
2
3
  import json
3
4
  import logging
4
5
  import uuid
6
+ from collections.abc import Coroutine
5
7
  from dataclasses import dataclass, fields
6
8
  from enum import Enum
7
- from typing import Optional, Union
9
+ from typing import Any, Optional, Union
8
10
 
9
11
  import nest_asyncio
10
12
 
11
- from pymobiledevice3.exceptions import LaunchingApplicationError, RemoteAutomationNotEnabledError, \
12
- WebInspectorNotEnabledError
13
+ from pymobiledevice3.exceptions import (
14
+ LaunchingApplicationError,
15
+ RemoteAutomationNotEnabledError,
16
+ WebInspectorNotEnabledError,
17
+ )
13
18
  from pymobiledevice3.lockdown import LockdownClient
14
19
  from pymobiledevice3.lockdown_service_provider import LockdownServiceProvider
15
20
  from pymobiledevice3.service_connection import ServiceConnection
@@ -17,55 +22,55 @@ from pymobiledevice3.services.web_protocol.automation_session import AutomationS
17
22
  from pymobiledevice3.services.web_protocol.inspector_session import InspectorSession
18
23
  from pymobiledevice3.services.web_protocol.session_protocol import SessionProtocol
19
24
 
20
- SAFARI = 'com.apple.mobilesafari'
25
+ SAFARI = "com.apple.mobilesafari"
21
26
 
22
27
 
23
28
  def key_to_pid(key: str) -> int:
24
- return int(key.split(':')[1])
29
+ return int(key.split(":")[1])
25
30
 
26
31
 
27
32
  class WirTypes(Enum):
28
- AUTOMATION = 'WIRTypeAutomation'
29
- ITML = 'WIRTypeITML'
30
- JAVASCRIPT = 'WIRTypeJavaScript'
31
- PAGE = 'WIRTypePage'
32
- SERVICE_WORKER = 'WIRTypeServiceWorker'
33
- WEB = 'WIRTypeWeb'
34
- WEB_PAGE = 'WIRTypeWebPage'
35
- AUTOMATICALLY_PAUSE = 'WIRAutomaticallyPause'
33
+ AUTOMATION = "WIRTypeAutomation"
34
+ ITML = "WIRTypeITML"
35
+ JAVASCRIPT = "WIRTypeJavaScript"
36
+ PAGE = "WIRTypePage"
37
+ SERVICE_WORKER = "WIRTypeServiceWorker"
38
+ WEB = "WIRTypeWeb"
39
+ WEB_PAGE = "WIRTypeWebPage"
40
+ AUTOMATICALLY_PAUSE = "WIRAutomaticallyPause"
36
41
 
37
42
 
38
43
  class AutomationAvailability(Enum):
39
- NOT_AVAILABLE = 'WIRAutomationAvailabilityNotAvailable'
40
- AVAILABLE = 'WIRAutomationAvailabilityAvailable'
41
- UNKNOWN = 'WIRAutomationAvailabilityUnknown'
44
+ NOT_AVAILABLE = "WIRAutomationAvailabilityNotAvailable"
45
+ AVAILABLE = "WIRAutomationAvailabilityAvailable"
46
+ UNKNOWN = "WIRAutomationAvailabilityUnknown"
42
47
 
43
48
 
44
49
  @dataclass
45
50
  class Page:
46
51
  id_: int
47
52
  type_: WirTypes
48
- web_url: str = ''
49
- web_title: str = ''
53
+ web_url: str = ""
54
+ web_title: str = ""
50
55
  automation_is_paired_key: bool = False
51
- automation_name: str = ''
52
- automation_version: str = ''
53
- automation_session_id: str = ''
54
- automation_connection_id: str = ''
56
+ automation_name: str = ""
57
+ automation_version: str = ""
58
+ automation_session_id: str = ""
59
+ automation_connection_id: str = ""
55
60
 
56
61
  @classmethod
57
- def from_page_dictionary(cls, page_dict: dict) -> 'Page':
58
- p = cls(page_dict['WIRPageIdentifierKey'], WirTypes(page_dict['WIRTypeKey']))
62
+ def from_page_dictionary(cls, page_dict: dict) -> "Page":
63
+ p = cls(page_dict["WIRPageIdentifierKey"], WirTypes(page_dict["WIRTypeKey"]))
59
64
  if p.type_ in (WirTypes.WEB, WirTypes.WEB_PAGE):
60
- p.web_title = page_dict['WIRTitleKey']
61
- p.web_url = page_dict['WIRURLKey']
65
+ p.web_title = page_dict["WIRTitleKey"]
66
+ p.web_url = page_dict["WIRURLKey"]
62
67
  if p.type_ == WirTypes.AUTOMATION:
63
- p.automation_is_paired_key = page_dict['WIRAutomationTargetIsPairedKey']
64
- p.automation_name = page_dict['WIRAutomationTargetNameKey']
65
- p.automation_version = page_dict['WIRAutomationTargetVersionKey']
66
- p.automation_session_id = page_dict['WIRSessionIdentifierKey']
67
- if 'WIRConnectionIdentifierKey' in page_dict:
68
- p.automation_connection_id = page_dict['WIRConnectionIdentifierKey']
68
+ p.automation_is_paired_key = page_dict["WIRAutomationTargetIsPairedKey"]
69
+ p.automation_name = page_dict["WIRAutomationTargetNameKey"]
70
+ p.automation_version = page_dict["WIRAutomationTargetVersionKey"]
71
+ p.automation_session_id = page_dict["WIRSessionIdentifierKey"]
72
+ if "WIRConnectionIdentifierKey" in page_dict:
73
+ p.automation_connection_id = page_dict["WIRConnectionIdentifierKey"]
69
74
  return p
70
75
 
71
76
  def update(self, page_dict: dict):
@@ -74,7 +79,7 @@ class Page:
74
79
  setattr(self, field.name, getattr(new_p, field.name))
75
80
 
76
81
  def __str__(self):
77
- return f'id: {self.id_}, title: {self.web_title}, url: {self.web_url}'
82
+ return f"id: {self.id_}, title: {self.web_title}, url: {self.web_url}"
78
83
 
79
84
 
80
85
  @dataclass
@@ -87,20 +92,20 @@ class Application:
87
92
  active: int
88
93
  proxy: bool
89
94
  ready: bool
90
- host: str = ''
95
+ host: str = ""
91
96
 
92
97
  @classmethod
93
- def from_application_dictionary(cls, app_dict) -> 'Application':
98
+ def from_application_dictionary(cls, app_dict) -> "Application":
94
99
  return cls(
95
- app_dict['WIRApplicationIdentifierKey'],
96
- app_dict['WIRApplicationBundleIdentifierKey'],
97
- key_to_pid(app_dict['WIRApplicationIdentifierKey']),
98
- app_dict['WIRApplicationNameKey'],
99
- AutomationAvailability(app_dict['WIRAutomationAvailabilityKey']),
100
- app_dict['WIRIsApplicationActiveKey'],
101
- app_dict['WIRIsApplicationProxyKey'],
102
- app_dict['WIRIsApplicationReadyKey'],
103
- app_dict.get('WIRHostApplicationIdentifierKey', ''),
100
+ app_dict["WIRApplicationIdentifierKey"],
101
+ app_dict["WIRApplicationBundleIdentifierKey"],
102
+ key_to_pid(app_dict["WIRApplicationIdentifierKey"]),
103
+ app_dict["WIRApplicationNameKey"],
104
+ AutomationAvailability(app_dict["WIRAutomationAvailabilityKey"]),
105
+ app_dict["WIRIsApplicationActiveKey"],
106
+ app_dict["WIRIsApplicationProxyKey"],
107
+ app_dict["WIRIsApplicationReadyKey"],
108
+ app_dict.get("WIRHostApplicationIdentifierKey", ""),
104
109
  )
105
110
 
106
111
 
@@ -110,12 +115,12 @@ class ApplicationPage:
110
115
  page: Page
111
116
 
112
117
  def __str__(self) -> str:
113
- return f'<{self.application.name}({self.application.pid}) TYPE:{self.page.type_.value} URL:{self.page.web_url}>'
118
+ return f"<{self.application.name}({self.application.pid}) TYPE:{self.page.type_.value} URL:{self.page.web_url}>"
114
119
 
115
120
 
116
121
  class WebinspectorService:
117
- SERVICE_NAME = 'com.apple.webinspector'
118
- RSD_SERVICE_NAME = 'com.apple.webinspector.shim.remote'
122
+ SERVICE_NAME = "com.apple.webinspector"
123
+ RSD_SERVICE_NAME = "com.apple.webinspector.shim.remote"
119
124
 
120
125
  def __init__(self, lockdown: LockdownServiceProvider, loop=None):
121
126
  if loop is None:
@@ -142,32 +147,30 @@ class WebinspectorService:
142
147
  self.wir_message_results = {}
143
148
  self.wir_events = []
144
149
  self.receive_handlers = {
145
- '_rpc_reportCurrentState:': self._handle_report_current_state,
146
- '_rpc_reportConnectedApplicationList:': self._handle_report_connected_application_list,
147
- '_rpc_reportConnectedDriverList:': self._handle_report_connected_driver_list,
148
- '_rpc_applicationSentListing:': self._handle_application_sent_listing,
149
- '_rpc_applicationUpdated:': self._handle_application_updated,
150
- '_rpc_applicationConnected:': self._handle_application_connected,
151
- '_rpc_applicationSentData:': self._handle_application_sent_data,
152
- '_rpc_applicationDisconnected:': self._handle_application_disconnected,
150
+ "_rpc_reportCurrentState:": self._handle_report_current_state,
151
+ "_rpc_reportConnectedApplicationList:": self._handle_report_connected_application_list,
152
+ "_rpc_reportConnectedDriverList:": self._handle_report_connected_driver_list,
153
+ "_rpc_applicationSentListing:": self._handle_application_sent_listing,
154
+ "_rpc_applicationUpdated:": self._handle_application_updated,
155
+ "_rpc_applicationConnected:": self._handle_application_connected,
156
+ "_rpc_applicationSentData:": self._handle_application_sent_data,
157
+ "_rpc_applicationDisconnected:": self._handle_application_disconnected,
153
158
  }
154
159
  self._recv_task: Optional[asyncio.Task] = None
155
160
 
156
- def connect(self, timeout: Union[float, int] = None):
161
+ def connect(self, timeout: Optional[Union[float, int]] = None):
157
162
  self.service = self.await_(self.lockdown.aio_start_lockdown_service(self.service_name))
158
163
  self.await_(self._report_identifier())
159
164
  try:
160
- self._handle_recv(self.await_(asyncio.wait_for(self._recv_message(), timeout)))
165
+ self._handle_recv(self._await_with_timeout(self._recv_message(), timeout))
161
166
  except asyncio.TimeoutError as e:
162
167
  raise WebInspectorNotEnabledError from e
163
168
  self._recv_task = self.loop.create_task(self._receiving_task())
164
169
 
165
170
  def close(self):
166
171
  self._recv_task.cancel()
167
- try:
172
+ with contextlib.suppress(asyncio.CancelledError):
168
173
  self.await_(self._recv_task)
169
- except asyncio.CancelledError:
170
- pass
171
174
  self.await_(self.service.aio_close())
172
175
 
173
176
  async def _recv_message(self):
@@ -182,7 +185,7 @@ class WebinspectorService:
182
185
  self._handle_recv(await self._recv_message())
183
186
 
184
187
  def automation_session(self, app: Application) -> AutomationSession:
185
- if self.state == 'WIRAutomationAvailabilityNotAvailable':
188
+ if self.state == "WIRAutomationAvailabilityNotAvailable":
186
189
  raise RemoteAutomationNotEnabledError()
187
190
  session_id = str(uuid.uuid4()).upper()
188
191
  self.await_(self._forward_automation_session_request(session_id, app.id_))
@@ -196,8 +199,10 @@ class WebinspectorService:
196
199
 
197
200
  async def inspector_session(self, app: Application, page: Page) -> InspectorSession:
198
201
  session_id = str(uuid.uuid4()).upper()
199
- return await InspectorSession.create(SessionProtocol(self, session_id, app, page, method_prefix=''),
200
- wait_target=page.type_ != WirTypes.JAVASCRIPT)
202
+ return await InspectorSession.create(
203
+ SessionProtocol(self, session_id, app, page, method_prefix=""),
204
+ wait_target=page.type_ != WirTypes.JAVASCRIPT,
205
+ )
201
206
 
202
207
  def get_open_pages(self) -> dict:
203
208
  apps = {}
@@ -225,9 +230,9 @@ class WebinspectorService:
225
230
  self.await_(self._request_application_launch(bundle))
226
231
  self.get_open_pages()
227
232
  try:
228
- return self.await_(asyncio.wait_for(self._wait_for_application(bundle), timeout=timeout))
229
- except TimeoutError:
230
- raise LaunchingApplicationError()
233
+ return self._await_with_timeout(self._wait_for_application(bundle), timeout)
234
+ except TimeoutError as e:
235
+ raise LaunchingApplicationError() from e
231
236
 
232
237
  async def send_socket_data(self, session_id: str, app_id: str, page_id: int, data: dict):
233
238
  await self._forward_socket_data(session_id, app_id, page_id, data)
@@ -244,18 +249,30 @@ class WebinspectorService:
244
249
  def flush_input(self, duration: Union[float, int] = 0):
245
250
  return self.await_(asyncio.sleep(duration))
246
251
 
247
- def await_(self, awaitable):
248
- return self.loop.run_until_complete(asyncio.ensure_future(awaitable, loop=self.loop))
252
+ def await_(self, awaitable: Coroutine) -> Any:
253
+ return self.loop.run_until_complete(awaitable)
254
+
255
+ def _await_with_timeout(self, coro: Coroutine, timeout: Optional[float] = None) -> Any:
256
+ # Create a task explicitly so we're definitely inside a Task
257
+ task = self.loop.create_task(coro)
258
+ done, _pending = self.loop.run_until_complete(asyncio.wait({task}, timeout=timeout))
259
+ if not done:
260
+ task.cancel()
261
+ # Give the task a chance to cancel cleanly
262
+ with contextlib.suppress(asyncio.CancelledError):
263
+ self.loop.run_until_complete(task)
264
+ raise WebInspectorNotEnabledError()
265
+ return task.result()
249
266
 
250
267
  def _handle_recv(self, plist):
251
- self.receive_handlers[plist['__selector']](plist['__argument'])
268
+ self.receive_handlers[plist["__selector"]](plist["__argument"])
252
269
 
253
270
  def _handle_report_current_state(self, arg):
254
- self.state = arg['WIRAutomationAvailabilityKey']
271
+ self.state = arg["WIRAutomationAvailabilityKey"]
255
272
 
256
273
  def _handle_report_connected_application_list(self, arg):
257
274
  self.connected_application = {}
258
- for key, application in arg['WIRApplicationDictionaryKey'].items():
275
+ for key, application in arg["WIRApplicationDictionaryKey"].items():
259
276
  self.connected_application[key] = Application.from_application_dictionary(application)
260
277
 
261
278
  # Immediately also query the application pages
@@ -265,19 +282,19 @@ class WebinspectorService:
265
282
  pass
266
283
 
267
284
  def _handle_application_sent_listing(self, arg):
268
- if arg['WIRApplicationIdentifierKey'] in self.application_pages:
285
+ if arg["WIRApplicationIdentifierKey"] in self.application_pages:
269
286
  # Update existing application pages
270
- for id_, page in arg['WIRListingKey'].items():
271
- if id_ in self.application_pages[arg['WIRApplicationIdentifierKey']]:
272
- self.application_pages[arg['WIRApplicationIdentifierKey']][id_].update(page)
287
+ for id_, page in arg["WIRListingKey"].items():
288
+ if id_ in self.application_pages[arg["WIRApplicationIdentifierKey"]]:
289
+ self.application_pages[arg["WIRApplicationIdentifierKey"]][id_].update(page)
273
290
  else:
274
- self.application_pages[arg['WIRApplicationIdentifierKey']][id_] = Page.from_page_dictionary(page)
291
+ self.application_pages[arg["WIRApplicationIdentifierKey"]][id_] = Page.from_page_dictionary(page)
275
292
  else:
276
293
  # Add new application pages
277
294
  pages = {}
278
- for id_, page in arg['WIRListingKey'].items():
295
+ for id_, page in arg["WIRListingKey"].items():
279
296
  pages[id_] = Page.from_page_dictionary(page)
280
- self.application_pages[arg['WIRApplicationIdentifierKey']] = pages
297
+ self.application_pages[arg["WIRApplicationIdentifierKey"]] = pages
281
298
 
282
299
  def _handle_application_updated(self, arg):
283
300
  app = Application.from_application_dictionary(arg)
@@ -288,72 +305,81 @@ class WebinspectorService:
288
305
  self.connected_application[app.id_] = app
289
306
 
290
307
  def _handle_application_sent_data(self, arg):
291
- response = json.loads(arg['WIRMessageDataKey'])
308
+ response = json.loads(arg["WIRMessageDataKey"])
292
309
 
293
- if 'id' in response:
294
- self.wir_message_results[response['id']] = response
310
+ if "id" in response:
311
+ self.wir_message_results[response["id"]] = response
295
312
  else:
296
313
  self.wir_events.append(response)
297
314
 
298
315
  def _handle_application_disconnected(self, arg):
299
- self.connected_application.pop(arg['WIRApplicationIdentifierKey'], None)
300
- self.application_pages.pop(arg['WIRApplicationIdentifierKey'], None)
316
+ self.connected_application.pop(arg["WIRApplicationIdentifierKey"], None)
317
+ self.application_pages.pop(arg["WIRApplicationIdentifierKey"], None)
301
318
 
302
319
  async def _report_identifier(self):
303
- await self._send_message('_rpc_reportIdentifier:')
320
+ await self._send_message("_rpc_reportIdentifier:")
304
321
 
305
322
  async def _forward_get_listing(self, app_id):
306
- self.logger.debug(f'Listing app with id {app_id}')
307
- await self._send_message('_rpc_forwardGetListing:', {'WIRApplicationIdentifierKey': app_id})
323
+ self.logger.debug(f"Listing app with id {app_id}")
324
+ await self._send_message("_rpc_forwardGetListing:", {"WIRApplicationIdentifierKey": app_id})
308
325
 
309
326
  async def _request_application_launch(self, bundle: str):
310
- await self._send_message('_rpc_requestApplicationLaunch:', {'WIRApplicationBundleIdentifierKey': bundle})
327
+ await self._send_message("_rpc_requestApplicationLaunch:", {"WIRApplicationBundleIdentifierKey": bundle})
311
328
 
312
329
  async def _get_connected_applications(self) -> None:
313
- await self._send_message('_rpc_getConnectedApplications:', {})
330
+ await self._send_message("_rpc_getConnectedApplications:", {})
314
331
 
315
332
  async def _forward_automation_session_request(self, session_id: str, app_id: str):
316
- await self._send_message('_rpc_forwardAutomationSessionRequest:', {
317
- 'WIRApplicationIdentifierKey': app_id,
318
- 'WIRSessionCapabilitiesKey': {
319
- 'org.webkit.webdriver.webrtc.allow-insecure-media-capture': True,
320
- 'org.webkit.webdriver.webrtc.suppress-ice-candidate-filtering': False,
333
+ await self._send_message(
334
+ "_rpc_forwardAutomationSessionRequest:",
335
+ {
336
+ "WIRApplicationIdentifierKey": app_id,
337
+ "WIRSessionCapabilitiesKey": {
338
+ "org.webkit.webdriver.webrtc.allow-insecure-media-capture": True,
339
+ "org.webkit.webdriver.webrtc.suppress-ice-candidate-filtering": False,
340
+ },
341
+ "WIRSessionIdentifierKey": session_id,
321
342
  },
322
- 'WIRSessionIdentifierKey': session_id
323
- })
343
+ )
324
344
 
325
345
  async def _forward_socket_setup(self, session_id: str, app_id: str, page_id: int, pause: bool = True):
326
346
  message = {
327
- 'WIRApplicationIdentifierKey': app_id,
328
- 'WIRPageIdentifierKey': page_id,
329
- 'WIRSenderKey': session_id,
330
- 'WIRMessageDataTypeChunkSupportedKey': 0,
347
+ "WIRApplicationIdentifierKey": app_id,
348
+ "WIRPageIdentifierKey": page_id,
349
+ "WIRSenderKey": session_id,
350
+ "WIRMessageDataTypeChunkSupportedKey": 0,
331
351
  }
332
352
  if not pause:
333
- message['WIRAutomaticallyPause'] = False
334
- await self._send_message('_rpc_forwardSocketSetup:', message)
353
+ message["WIRAutomaticallyPause"] = False
354
+ await self._send_message("_rpc_forwardSocketSetup:", message)
335
355
 
336
356
  async def _forward_socket_data(self, session_id: str, app_id: str, page_id: int, data: dict):
337
- await self._send_message('_rpc_forwardSocketData:', {
338
- 'WIRApplicationIdentifierKey': app_id,
339
- 'WIRPageIdentifierKey': page_id,
340
- 'WIRSessionIdentifierKey': session_id,
341
- 'WIRSenderKey': session_id,
342
- 'WIRSocketDataKey': json.dumps(data).encode(),
343
- })
357
+ await self._send_message(
358
+ "_rpc_forwardSocketData:",
359
+ {
360
+ "WIRApplicationIdentifierKey": app_id,
361
+ "WIRPageIdentifierKey": page_id,
362
+ "WIRSessionIdentifierKey": session_id,
363
+ "WIRSenderKey": session_id,
364
+ "WIRSocketDataKey": json.dumps(data).encode(),
365
+ },
366
+ )
344
367
 
345
368
  async def _forward_indicate_web_view(self, app_id: str, page_id: int, enable: bool):
346
- await self._send_message('_rpc_forwardIndicateWebView'), {
347
- 'WIRApplicationIdentifierKey': app_id,
348
- 'WIRPageIdentifierKey': page_id,
349
- 'WIRIndicateEnabledKey': enable,
350
- }
369
+ (
370
+ await self._send_message("_rpc_forwardIndicateWebView"),
371
+ {
372
+ "WIRApplicationIdentifierKey": app_id,
373
+ "WIRPageIdentifierKey": page_id,
374
+ "WIRIndicateEnabledKey": enable,
375
+ },
376
+ )
351
377
 
352
378
  async def _send_message(self, selector: str, args=None):
353
379
  if args is None:
354
380
  args = {}
355
- args['WIRConnectionIdentifierKey'] = self.connection_id
356
- await self.service.aio_send_plist({'__selector': selector, '__argument': args})
381
+ args["WIRConnectionIdentifierKey"] = self.connection_id
382
+ await self.service.aio_send_plist({"__selector": selector, "__argument": args})
357
383
 
358
384
  def _page_by_automation_session(self, session_id: str) -> Page:
359
385
  for app_id in self.application_pages:
@@ -369,7 +395,7 @@ class WebinspectorService:
369
395
  return page
370
396
  await asyncio.sleep(0)
371
397
 
372
- async def _wait_for_application(self, bundle: str = '', app_id: str = '') -> Application:
398
+ async def _wait_for_application(self, bundle: str = "", app_id: str = "") -> Application:
373
399
  while True:
374
400
  for app in self.connected_application.values():
375
401
  if bundle and app.bundle == bundle:
@@ -19,7 +19,7 @@ class TcpForwarderBase:
19
19
  MAX_FORWARDED_CONNECTIONS = 200
20
20
  TIMEOUT = 1
21
21
 
22
- def __init__(self, src_port: int, listening_event: threading.Event = None):
22
+ def __init__(self, src_port: int, listening_event: Optional[threading.Event] = None):
23
23
  """
24
24
  Initialize a new tcp forwarder
25
25
 
@@ -38,8 +38,8 @@ class TcpForwarderBase:
38
38
  # socket to its remote socket and vice versa
39
39
  self.connections = {}
40
40
 
41
- def start(self, address='0.0.0.0'):
42
- """ forward each connection from given local machine port to remote device port """
41
+ def start(self, address="0.0.0.0"):
42
+ """forward each connection from given local machine port to remote device port"""
43
43
  # create local tcp server socket
44
44
  self.server_socket = socket.socket()
45
45
  self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
@@ -55,7 +55,7 @@ class TcpForwarderBase:
55
55
  while self.inputs:
56
56
  # will only perform the socket select on the inputs. the outputs will handled
57
57
  # as synchronous blocking
58
- readable, writable, exceptional = select.select(self.inputs, [], self.inputs, self.TIMEOUT)
58
+ readable, _writable, exceptional = select.select(self.inputs, [], self.inputs, self.TIMEOUT)
59
59
  if self.stopped.is_set():
60
60
  self.logger.debug("Closing since stopped is set")
61
61
  break
@@ -70,7 +70,7 @@ class TcpForwarderBase:
70
70
  try:
71
71
  self._handle_data(current_sock, closed_sockets)
72
72
  except ConnectionResetError:
73
- self.logger.exception("Error when handling data")
73
+ self.logger.error("Error when handling data")
74
74
  self._handle_close_or_error(current_sock)
75
75
  else:
76
76
  self.logger.debug("Is closed")
@@ -85,7 +85,7 @@ class TcpForwarderBase:
85
85
  current_sock.close()
86
86
 
87
87
  def _handle_close_or_error(self, from_sock):
88
- """ if an error occurred its time to close the two sockets """
88
+ """if an error occurred its time to close the two sockets"""
89
89
  other_sock = self.connections[from_sock]
90
90
 
91
91
  other_sock.close()
@@ -93,7 +93,7 @@ class TcpForwarderBase:
93
93
  self.inputs.remove(other_sock)
94
94
  self.inputs.remove(from_sock)
95
95
 
96
- self.logger.info(f'connection {other_sock} was closed')
96
+ self.logger.info(f"connection {other_sock} was closed")
97
97
 
98
98
  def _handle_data(self, from_sock, closed_sockets):
99
99
  self.logger.debug(f"Handling data from {from_sock}")
@@ -104,8 +104,8 @@ class TcpForwarderBase:
104
104
  except BlockingIOError:
105
105
  self.logger.warning(f"Non-blocking read failed on {from_sock}, retrying later.")
106
106
  return
107
- except OSError as e:
108
- self.logger.error(f"Error reading from socket {from_sock}: {e}")
107
+ except OSError:
108
+ self.logger.error(f"Error reading from socket {from_sock}")
109
109
  self._handle_close_or_error(from_sock)
110
110
  closed_sockets.add(from_sock)
111
111
  return
@@ -127,8 +127,8 @@ class TcpForwarderBase:
127
127
  except BrokenPipeError:
128
128
  self.logger.error(f"Broken pipe error on {other_sock}.")
129
129
  raise
130
- except OSError as e:
131
- self.logger.error(f"Unhandled error while forwarding data: {e}")
130
+ except OSError:
131
+ self.logger.error("Unhandled error while forwarding data")
132
132
  self._handle_close_or_error(from_sock)
133
133
  closed_sockets.add(from_sock)
134
134
  closed_sockets.add(other_sock)
@@ -138,14 +138,14 @@ class TcpForwarderBase:
138
138
  pass
139
139
 
140
140
  def _handle_server_connection(self):
141
- """ accept the connection from local machine and attempt to connect at remote """
142
- local_connection, client_address = self.server_socket.accept()
141
+ """accept the connection from local machine and attempt to connect at remote"""
142
+ local_connection, _client_address = self.server_socket.accept()
143
143
  local_connection.setblocking(False)
144
144
 
145
145
  try:
146
146
  remote_connection = self._establish_remote_connection()
147
147
  except ConnectionFailedError:
148
- self.logger.error(f'failed to connect to port: {self.dst_port}')
148
+ self.logger.error(f"failed to connect to port: {self.dst_port}")
149
149
  local_connection.close()
150
150
  return
151
151
 
@@ -159,10 +159,10 @@ class TcpForwarderBase:
159
159
  self.connections[remote_connection] = local_connection
160
160
  self.connections[local_connection] = remote_connection
161
161
 
162
- self.logger.info('connection established from local to remote')
162
+ self.logger.info("connection established from local to remote")
163
163
 
164
164
  def stop(self):
165
- """ stop forwarding """
165
+ """stop forwarding"""
166
166
  self.stopped.set()
167
167
 
168
168
 
@@ -171,8 +171,15 @@ class UsbmuxTcpForwarder(TcpForwarderBase):
171
171
  Allows forwarding local tcp connection into the device via a given lockdown connection
172
172
  """
173
173
 
174
- def __init__(self, serial: str, dst_port: int, src_port: int, listening_event: threading.Event = None,
175
- usbmux_connection_type: str = None, usbmux_address: Optional[str] = None):
174
+ def __init__(
175
+ self,
176
+ serial: str,
177
+ dst_port: int,
178
+ src_port: int,
179
+ listening_event: Optional[threading.Event] = None,
180
+ usbmux_connection_type: Optional[str] = None,
181
+ usbmux_address: Optional[str] = None,
182
+ ):
176
183
  """
177
184
  Initialize a new tcp forwarder
178
185
 
@@ -191,8 +198,9 @@ class UsbmuxTcpForwarder(TcpForwarderBase):
191
198
 
192
199
  def _establish_remote_connection(self) -> socket.socket:
193
200
  # connect directly using usbmuxd
194
- mux_device = usbmux.select_device(self.serial, connection_type=self.usbmux_connection_type,
195
- usbmux_address=self.usbmux_address)
201
+ mux_device = usbmux.select_device(
202
+ self.serial, connection_type=self.usbmux_connection_type, usbmux_address=self.usbmux_address
203
+ )
196
204
  self.logger.debug("Selected device: %r", mux_device)
197
205
  if mux_device is None:
198
206
  raise ConnectionFailedError()
@@ -204,8 +212,13 @@ class LockdownTcpForwarder(TcpForwarderBase):
204
212
  Allows forwarding local tcp connection into the device via a given lockdown connection
205
213
  """
206
214
 
207
- def __init__(self, service_provider: LockdownServiceProvider, src_port: int, service_name: str,
208
- listening_event: threading.Event = None):
215
+ def __init__(
216
+ self,
217
+ service_provider: LockdownServiceProvider,
218
+ src_port: int,
219
+ service_name: str,
220
+ listening_event: Optional[threading.Event] = None,
221
+ ):
209
222
  """
210
223
  Initialize a new tcp forwarder
211
224