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
pymobiledevice3/usbmux.py CHANGED
@@ -5,75 +5,115 @@ import time
5
5
  from dataclasses import dataclass
6
6
  from typing import Optional
7
7
 
8
- from construct import Const, CString, Enum, FixedSized, GreedyBytes, Int16ul, Int32ul, Padding, Prefixed, StreamError, \
9
- Struct, Switch, this
8
+ from construct import (
9
+ Const,
10
+ CString,
11
+ Enum,
12
+ FixedSized,
13
+ GreedyBytes,
14
+ Int16ul,
15
+ Int32ul,
16
+ Padding,
17
+ Prefixed,
18
+ StreamError,
19
+ Struct,
20
+ Switch,
21
+ this,
22
+ )
10
23
 
11
- from pymobiledevice3.exceptions import BadCommandError, BadDevError, ConnectionFailedError, \
12
- ConnectionFailedToUsbmuxdError, MuxException, MuxVersionError, NotPairedError
24
+ from pymobiledevice3.exceptions import (
25
+ BadCommandError,
26
+ BadDevError,
27
+ ConnectionFailedError,
28
+ ConnectionFailedToUsbmuxdError,
29
+ MuxException,
30
+ MuxVersionError,
31
+ NotPairedError,
32
+ )
13
33
  from pymobiledevice3.osu.os_utils import get_os_utils
14
34
 
15
- usbmuxd_version = Enum(Int32ul,
16
- BINARY=0,
17
- PLIST=1,
18
- )
19
-
20
- usbmuxd_result = Enum(Int32ul,
21
- OK=0,
22
- BADCOMMAND=1,
23
- BADDEV=2,
24
- CONNREFUSED=3,
25
- BADVERSION=6,
26
- )
27
-
28
- usbmuxd_msgtype = Enum(Int32ul,
29
- RESULT=1,
30
- CONNECT=2,
31
- LISTEN=3,
32
- ADD=4,
33
- REMOVE=5,
34
- PAIRED=6,
35
- PLIST=8,
36
- )
35
+ usbmuxd_version = Enum(
36
+ Int32ul,
37
+ BINARY=0,
38
+ PLIST=1,
39
+ )
40
+
41
+ usbmuxd_result = Enum(
42
+ Int32ul,
43
+ OK=0,
44
+ BADCOMMAND=1,
45
+ BADDEV=2,
46
+ CONNREFUSED=3,
47
+ NOSUCHSERVICE=4,
48
+ BADVERSION=6,
49
+ )
50
+
51
+ usbmuxd_msgtype = Enum(
52
+ Int32ul,
53
+ RESULT=1,
54
+ CONNECT=2,
55
+ LISTEN=3,
56
+ ADD=4,
57
+ REMOVE=5,
58
+ PAIRED=6,
59
+ PLIST=8,
60
+ )
37
61
 
38
62
  usbmuxd_header = Struct(
39
- 'version' / usbmuxd_version, # protocol version
40
- 'message' / usbmuxd_msgtype, # message type
41
- 'tag' / Int32ul, # responses to this query will echo back this tag
63
+ "version" / usbmuxd_version, # protocol version
64
+ "message" / usbmuxd_msgtype, # message type
65
+ "tag" / Int32ul, # responses to this query will echo back this tag
42
66
  )
43
67
 
44
- usbmuxd_request = Prefixed(Int32ul, Struct(
45
- 'header' / usbmuxd_header,
46
- 'data' / Switch(this.header.message, {
47
- usbmuxd_msgtype.CONNECT: Struct(
48
- 'device_id' / Int32ul,
49
- 'port' / Int16ul, # TCP port number
50
- 'reserved' / Const(0, Int16ul),
68
+ usbmuxd_request = Prefixed(
69
+ Int32ul,
70
+ Struct(
71
+ "header" / usbmuxd_header,
72
+ "data"
73
+ / Switch(
74
+ this.header.message,
75
+ {
76
+ usbmuxd_msgtype.CONNECT: Struct(
77
+ "device_id" / Int32ul,
78
+ "port" / Int16ul, # TCP port number
79
+ "reserved" / Const(0, Int16ul),
80
+ ),
81
+ usbmuxd_msgtype.PLIST: GreedyBytes,
82
+ },
51
83
  ),
52
- usbmuxd_msgtype.PLIST: GreedyBytes,
53
- }),
54
- ), includelength=True)
84
+ ),
85
+ includelength=True,
86
+ )
55
87
 
56
88
  usbmuxd_device_record = Struct(
57
- 'device_id' / Int32ul,
58
- 'product_id' / Int16ul,
59
- 'serial_number' / FixedSized(256, CString('ascii')),
89
+ "device_id" / Int32ul,
90
+ "product_id" / Int16ul,
91
+ "serial_number" / FixedSized(256, CString("ascii")),
60
92
  Padding(2),
61
- 'location' / Int32ul
93
+ "location" / Int32ul,
62
94
  )
63
95
 
64
- usbmuxd_response = Prefixed(Int32ul, Struct(
65
- 'header' / usbmuxd_header,
66
- 'data' / Switch(this.header.message, {
67
- usbmuxd_msgtype.RESULT: Struct(
68
- 'result' / usbmuxd_result,
96
+ usbmuxd_response = Prefixed(
97
+ Int32ul,
98
+ Struct(
99
+ "header" / usbmuxd_header,
100
+ "data"
101
+ / Switch(
102
+ this.header.message,
103
+ {
104
+ usbmuxd_msgtype.RESULT: Struct(
105
+ "result" / usbmuxd_result,
106
+ ),
107
+ usbmuxd_msgtype.ADD: usbmuxd_device_record,
108
+ usbmuxd_msgtype.REMOVE: Struct(
109
+ "device_id" / Int32ul,
110
+ ),
111
+ usbmuxd_msgtype.PLIST: GreedyBytes,
112
+ },
69
113
  ),
70
- usbmuxd_msgtype.ADD: usbmuxd_device_record,
71
- usbmuxd_msgtype.REMOVE: Struct(
72
- 'device_id' / Int32ul,
73
- ),
74
- usbmuxd_msgtype.PLIST: GreedyBytes,
75
- }),
76
- ), includelength=True)
114
+ ),
115
+ includelength=True,
116
+ )
77
117
 
78
118
 
79
119
  @dataclass
@@ -86,24 +126,24 @@ class MuxDevice:
86
126
  mux = create_mux(usbmux_address=usbmux_address)
87
127
  try:
88
128
  return mux.connect(self, port)
89
- except: # noqa: E722
129
+ except:
90
130
  mux.close()
91
131
  raise
92
132
 
93
133
  @property
94
134
  def is_usb(self) -> bool:
95
- return self.connection_type == 'USB'
135
+ return self.connection_type == "USB"
96
136
 
97
137
  @property
98
138
  def is_network(self) -> bool:
99
- return self.connection_type == 'Network'
139
+ return self.connection_type == "Network"
100
140
 
101
141
  def matches_udid(self, udid: str) -> bool:
102
- return self.serial.replace('-', '') == udid.replace('-', '')
142
+ return self.serial.replace("-", "") == udid.replace("-", "")
103
143
 
104
144
 
105
145
  class SafeStreamSocket:
106
- """ wrapper to native python socket object to be used with construct as a stream """
146
+ """wrapper to native python socket object to be used with construct as a stream"""
107
147
 
108
148
  def __init__(self, address, family):
109
149
  self._offset = 0
@@ -116,12 +156,12 @@ class SafeStreamSocket:
116
156
  return len(msg)
117
157
 
118
158
  def recv(self, size: int) -> bytes:
119
- msg = b''
159
+ msg = b""
120
160
  while len(msg) < size:
121
161
  chunk = self.sock.recv(size - len(msg))
122
162
  self._offset += len(chunk)
123
163
  if not chunk:
124
- raise MuxException('socket connection broken')
164
+ raise MuxException("socket connection broken")
125
165
  msg += chunk
126
166
  return msg
127
167
 
@@ -143,18 +183,18 @@ class SafeStreamSocket:
143
183
 
144
184
  class MuxConnection:
145
185
  # used on Windows
146
- ITUNES_HOST = ('127.0.0.1', 27015)
186
+ ITUNES_HOST = ("127.0.0.1", 27015)
147
187
 
148
188
  # used for macOS and Linux
149
- USBMUXD_PIPE = '/var/run/usbmuxd'
189
+ USBMUXD_PIPE = "/var/run/usbmuxd"
150
190
 
151
191
  @staticmethod
152
192
  def create_usbmux_socket(usbmux_address: Optional[str] = None) -> SafeStreamSocket:
153
193
  try:
154
194
  if usbmux_address is not None:
155
- if ':' in usbmux_address:
195
+ if ":" in usbmux_address:
156
196
  # assume tcp address
157
- hostname, port = usbmux_address.split(':')
197
+ hostname, port = usbmux_address.split(":")
158
198
  port = int(port)
159
199
  address = (hostname, port)
160
200
  family = socket.AF_INET
@@ -165,23 +205,25 @@ class MuxConnection:
165
205
  else:
166
206
  address, family = get_os_utils().usbmux_address
167
207
  return SafeStreamSocket(address, family)
168
- except ConnectionRefusedError:
169
- raise ConnectionFailedToUsbmuxdError()
208
+ except ConnectionRefusedError as e:
209
+ raise ConnectionFailedToUsbmuxdError() from e
170
210
 
171
211
  @staticmethod
172
212
  def create(usbmux_address: Optional[str] = None):
173
213
  # first attempt to connect with possibly the wrong version header (plist protocol)
174
214
  sock = MuxConnection.create_usbmux_socket(usbmux_address=usbmux_address)
175
215
 
176
- message = usbmuxd_request.build({
177
- 'header': {'version': usbmuxd_version.PLIST, 'message': usbmuxd_msgtype.PLIST, 'tag': 1},
178
- 'data': plistlib.dumps({'MessageType': 'ReadBUID'})
179
- })
180
- sock.send(message)
181
- response = usbmuxd_response.parse_stream(sock)
182
-
183
- # if we sent a bad request, we should re-create the socket in the correct version this time
184
- sock.close()
216
+ try:
217
+ message = usbmuxd_request.build({
218
+ "header": {"version": usbmuxd_version.PLIST, "message": usbmuxd_msgtype.PLIST, "tag": 1},
219
+ "data": plistlib.dumps({"MessageType": "ReadBUID"}),
220
+ })
221
+ sock.send(message)
222
+ response = usbmuxd_response.parse_stream(sock)
223
+
224
+ finally:
225
+ # If we sent a bad request, we should re-create the socket in the correct version this time
226
+ sock.close()
185
227
  sock = MuxConnection.create_usbmux_socket(usbmux_address=usbmux_address)
186
228
 
187
229
  if response.header.version == usbmuxd_version.BINARY:
@@ -189,7 +231,7 @@ class MuxConnection:
189
231
  elif response.header.version == usbmuxd_version.PLIST:
190
232
  return PlistMuxConnection(sock)
191
233
 
192
- raise MuxVersionError(f'usbmuxd returned unsupported version: {response.version}')
234
+ raise MuxVersionError(f"usbmuxd returned unsupported version: {response.version}")
193
235
 
194
236
  def __init__(self, sock: SafeStreamSocket):
195
237
  self._sock = sock
@@ -206,36 +248,37 @@ class MuxConnection:
206
248
 
207
249
  @abc.abstractmethod
208
250
  def _connect(self, device_id: int, port: int):
209
- """ initiate a "Connect" request to target port """
251
+ """initiate a "Connect" request to target port"""
210
252
  pass
211
253
 
212
254
  @abc.abstractmethod
213
- def get_device_list(self, timeout: float = None):
255
+ def get_device_list(self, timeout: Optional[float] = None):
214
256
  """
215
257
  request an update to current device list
216
258
  """
217
259
  pass
218
260
 
219
261
  def connect(self, device: MuxDevice, port: int) -> socket.socket:
220
- """ connect to a relay port on target machine and get a raw python socket object for the connection """
262
+ """connect to a relay port on target machine and get a raw python socket object for the connection"""
221
263
  self._connect(device.devid, socket.htons(port))
222
264
  self._connected = True
223
265
  return self._sock.sock
224
266
 
225
267
  def close(self):
226
- """ close current socket """
268
+ """close current socket"""
227
269
  self._sock.close()
228
270
 
229
271
  def _assert_not_connected(self):
230
- """ verify active state is in state for control messages """
272
+ """verify active state is in state for control messages"""
231
273
  if self._connected:
232
- raise MuxException('Mux is connected, cannot issue control packets')
274
+ raise MuxException("Mux is connected, cannot issue control packets")
233
275
 
234
276
  def _raise_mux_exception(self, result: int, message: Optional[str] = None) -> None:
235
277
  exceptions = {
236
278
  int(usbmuxd_result.BADCOMMAND): BadCommandError,
237
279
  int(usbmuxd_result.BADDEV): BadDevError,
238
280
  int(usbmuxd_result.CONNREFUSED): ConnectionFailedError,
281
+ int(usbmuxd_result.NOSUCHSERVICE): ConnectionFailedError,
239
282
  int(usbmuxd_result.BADVERSION): MuxVersionError,
240
283
  }
241
284
  exception = exceptions.get(result, MuxException)
@@ -249,14 +292,14 @@ class MuxConnection:
249
292
 
250
293
 
251
294
  class BinaryMuxConnection(MuxConnection):
252
- """ old binary protocol """
295
+ """old binary protocol"""
253
296
 
254
297
  def __init__(self, sock: SafeStreamSocket):
255
298
  super().__init__(sock)
256
299
  self._version = usbmuxd_version.BINARY
257
300
 
258
- def get_device_list(self, timeout: float = None):
259
- """ use timeout to wait for the device list to be fully populated """
301
+ def get_device_list(self, timeout: Optional[float] = None):
302
+ """use timeout to wait for the device list to be fully populated"""
260
303
  self._assert_not_connected()
261
304
  end = time.time() + timeout
262
305
  self.listen()
@@ -266,55 +309,54 @@ class BinaryMuxConnection(MuxConnection):
266
309
  self._receive_device_state_update()
267
310
  except (BlockingIOError, StreamError):
268
311
  continue
269
- except IOError:
312
+ except OSError as e:
270
313
  try:
271
314
  self._sock.setblocking(True)
272
315
  self.close()
273
316
  except OSError:
274
317
  pass
275
- raise MuxException('Exception in listener socket')
318
+ raise MuxException("Exception in listener socket") from e
276
319
 
277
320
  def listen(self):
278
- """ start listening for events of attached and detached devices """
321
+ """start listening for events of attached and detached devices"""
279
322
  self._send_receive(usbmuxd_msgtype.LISTEN)
280
323
 
281
324
  def _connect(self, device_id: int, port: int):
282
- self._send({'header': {'version': self._version,
283
- 'message': usbmuxd_msgtype.CONNECT,
284
- 'tag': self._tag},
285
- 'data': {'device_id': device_id, 'port': port},
286
- })
325
+ self._send({
326
+ "header": {"version": self._version, "message": usbmuxd_msgtype.CONNECT, "tag": self._tag},
327
+ "data": {"device_id": device_id, "port": port},
328
+ })
287
329
  response = self._receive()
288
330
  if response.header.message != usbmuxd_msgtype.RESULT:
289
- raise MuxException(f'unexpected message type received: {response}')
331
+ raise MuxException(f"unexpected message type received: {response}")
290
332
 
291
333
  if response.data.result != usbmuxd_result.OK:
292
- raise self._raise_mux_exception(int(response.data.result),
293
- f'failed to connect to device: {device_id} at port: {port}. reason: '
294
- f'{response.data.result}')
334
+ raise self._raise_mux_exception(
335
+ int(response.data.result),
336
+ f"failed to connect to device: {device_id} at port: {port}. reason: {response.data.result}",
337
+ )
295
338
 
296
339
  def _send(self, data: dict) -> None:
297
340
  self._assert_not_connected()
298
341
  self._sock.send(usbmuxd_request.build(data))
299
342
  self._tag += 1
300
343
 
301
- def _receive(self, expected_tag: int = None):
344
+ def _receive(self, expected_tag: Optional[int] = None):
302
345
  self._assert_not_connected()
303
346
  response = usbmuxd_response.parse_stream(self._sock)
304
347
  if expected_tag and response.header.tag != expected_tag:
305
- raise MuxException(f'Reply tag mismatch: expected {expected_tag}, got {response.header.tag}')
348
+ raise MuxException(f"Reply tag mismatch: expected {expected_tag}, got {response.header.tag}")
306
349
  return response
307
350
 
308
351
  def _send_receive(self, message_type: int):
309
- self._send({'header': {'version': self._version, 'message': message_type, 'tag': self._tag},
310
- 'data': b''})
352
+ self._send({"header": {"version": self._version, "message": message_type, "tag": self._tag}, "data": b""})
311
353
  response = self._receive(self._tag - 1)
312
354
  if response.header.message != usbmuxd_msgtype.RESULT:
313
- raise MuxException(f'unexpected message type received: {response}')
355
+ raise MuxException(f"unexpected message type received: {response}")
314
356
 
315
357
  result = response.data.result
316
358
  if result != usbmuxd_result.OK:
317
- raise self._raise_mux_exception(int(result), f'{message_type} failed: error {result}')
359
+ raise self._raise_mux_exception(int(result), f"{message_type} failed: error {result}")
318
360
 
319
361
  def _add_device(self, device: MuxDevice):
320
362
  self.devices.append(device)
@@ -326,11 +368,11 @@ class BinaryMuxConnection(MuxConnection):
326
368
  response = self._receive()
327
369
  if response.header.message == usbmuxd_msgtype.ADD:
328
370
  # old protocol only supported USB devices
329
- self._add_device(MuxDevice(response.data.device_id, response.data.serial_number, 'USB'))
371
+ self._add_device(MuxDevice(response.data.device_id, response.data.serial_number, "USB"))
330
372
  elif response.header.message == usbmuxd_msgtype.REMOVE:
331
373
  self._remove_device(response.data.device_id)
332
374
  else:
333
- raise MuxException(f'Invalid packet type received: {response}')
375
+ raise MuxException(f"Invalid packet type received: {response}")
334
376
 
335
377
 
336
378
  class PlistMuxConnection(BinaryMuxConnection):
@@ -339,71 +381,77 @@ class PlistMuxConnection(BinaryMuxConnection):
339
381
  self._version = usbmuxd_version.PLIST
340
382
 
341
383
  def listen(self) -> None:
342
- self._send_receive({'MessageType': 'Listen'})
384
+ self._send_receive({"MessageType": "Listen"})
343
385
 
344
386
  def get_pair_record(self, serial: str) -> dict:
345
387
  # serials are saved inside usbmuxd without '-'
346
- self._send({'MessageType': 'ReadPairRecord', 'PairRecordID': serial})
388
+ self._send({"MessageType": "ReadPairRecord", "PairRecordID": serial})
347
389
  response = self._receive(self._tag - 1)
348
- pair_record = response.get('PairRecordData')
390
+ pair_record = response.get("PairRecordData")
349
391
  if pair_record is None:
350
- raise NotPairedError('device should be paired first')
392
+ raise NotPairedError("device should be paired first")
351
393
  return plistlib.loads(pair_record)
352
394
 
353
- def get_device_list(self, timeout: float = None) -> None:
354
- """ get device list synchronously without waiting the timeout """
395
+ def get_device_list(self, timeout: Optional[float] = None) -> None:
396
+ """get device list synchronously without waiting the timeout"""
355
397
  self.devices = []
356
- self._send({'MessageType': 'ListDevices'})
398
+ self._send({"MessageType": "ListDevices"})
357
399
  response = self._receive(self._tag - 1)
358
- device_list = response.get('DeviceList')
400
+ device_list = response.get("DeviceList")
359
401
  if device_list is None:
360
- raise MuxException(f'Got an invalid response from usbmux: {response}')
402
+ raise MuxException(f"Got an invalid response from usbmux: {response}")
361
403
  for response in device_list:
362
- if response['MessageType'] == 'Attached':
363
- super()._add_device(MuxDevice(response['DeviceID'], response['Properties']['SerialNumber'],
364
- response['Properties']['ConnectionType']))
365
- elif response['MessageType'] == 'Detached':
366
- super()._remove_device(response['DeviceID'])
404
+ if response["MessageType"] == "Attached":
405
+ super()._add_device(
406
+ MuxDevice(
407
+ response["DeviceID"],
408
+ response["Properties"]["SerialNumber"],
409
+ response["Properties"]["ConnectionType"],
410
+ )
411
+ )
412
+ elif response["MessageType"] == "Detached":
413
+ super()._remove_device(response["DeviceID"])
367
414
  else:
368
- raise MuxException(f'Invalid packet type received: {response}')
415
+ raise MuxException(f"Invalid packet type received: {response}")
369
416
 
370
417
  def get_buid(self) -> str:
371
- """ get SystemBUID """
372
- self._send({'MessageType': 'ReadBUID'})
373
- return self._receive(self._tag - 1)['BUID']
418
+ """get SystemBUID"""
419
+ self._send({"MessageType": "ReadBUID"})
420
+ return self._receive(self._tag - 1)["BUID"]
374
421
 
375
422
  def save_pair_record(self, serial: str, device_id: int, record_data: bytes):
376
423
  # serials are saved inside usbmuxd without '-'
377
- self._send_receive({'MessageType': 'SavePairRecord',
378
- 'PairRecordID': serial,
379
- 'PairRecordData': record_data,
380
- 'DeviceID': device_id})
424
+ self._send_receive({
425
+ "MessageType": "SavePairRecord",
426
+ "PairRecordID": serial,
427
+ "PairRecordData": record_data,
428
+ "DeviceID": device_id,
429
+ })
381
430
 
382
431
  def _connect(self, device_id: int, port: int):
383
- self._send_receive({'MessageType': 'Connect', 'DeviceID': device_id, 'PortNumber': port})
432
+ self._send_receive({"MessageType": "Connect", "DeviceID": device_id, "PortNumber": port})
384
433
 
385
434
  def _send(self, data: dict):
386
- request = {'ClientVersionString': 'qt4i-usbmuxd', 'ProgName': 'pymobiledevice3', 'kLibUSBMuxVersion': 3}
435
+ request = {"ClientVersionString": "qt4i-usbmuxd", "ProgName": "pymobiledevice3", "kLibUSBMuxVersion": 3}
387
436
  request.update(data)
388
- super()._send({'header': {'version': self._version,
389
- 'message': usbmuxd_msgtype.PLIST,
390
- 'tag': self._tag},
391
- 'data': plistlib.dumps(request),
392
- })
437
+ super()._send({
438
+ "header": {"version": self._version, "message": usbmuxd_msgtype.PLIST, "tag": self._tag},
439
+ "data": plistlib.dumps(request),
440
+ })
393
441
 
394
- def _receive(self, expected_tag: int = None) -> dict:
442
+ def _receive(self, expected_tag: Optional[int] = None) -> dict:
395
443
  response = super()._receive(expected_tag=expected_tag)
396
444
  if response.header.message != usbmuxd_msgtype.PLIST:
397
- raise MuxException(f'Received non-plist type {response}')
445
+ raise MuxException(f"Received non-plist type {response}")
398
446
  return plistlib.loads(response.data)
399
447
 
400
448
  def _send_receive(self, data: dict):
401
449
  self._send(data)
402
450
  response = self._receive(self._tag - 1)
403
- if response['MessageType'] != 'Result':
404
- raise MuxException(f'got an invalid message: {response}')
405
- if response['Number'] != 0:
406
- raise self._raise_mux_exception(response['Number'], f'got an error message: {response}')
451
+ if response["MessageType"] != "Result":
452
+ raise MuxException(f"got an invalid message: {response}")
453
+ if response["Number"] != 0:
454
+ raise self._raise_mux_exception(response["Number"], f"got an error message: {response}")
407
455
 
408
456
 
409
457
  def create_mux(usbmux_address: Optional[str] = None) -> MuxConnection:
@@ -412,14 +460,17 @@ def create_mux(usbmux_address: Optional[str] = None) -> MuxConnection:
412
460
 
413
461
  def list_devices(usbmux_address: Optional[str] = None) -> list[MuxDevice]:
414
462
  mux = create_mux(usbmux_address=usbmux_address)
415
- mux.get_device_list(0.1)
416
- devices = mux.devices
417
- mux.close()
463
+ try:
464
+ mux.get_device_list(0.1)
465
+ devices = mux.devices
466
+ finally:
467
+ mux.close()
418
468
  return devices
419
469
 
420
470
 
421
- def select_device(udid: str = None, connection_type: str = None, usbmux_address: Optional[str] = None) \
422
- -> Optional[MuxDevice]:
471
+ def select_device(
472
+ udid: Optional[str] = None, connection_type: Optional[str] = None, usbmux_address: Optional[str] = None
473
+ ) -> Optional[MuxDevice]:
423
474
  """
424
475
  select a UsbMux device according to given arguments.
425
476
  if more than one device could be selected, always prefer the usb one.
pymobiledevice3/utils.py CHANGED
@@ -15,16 +15,16 @@ def plist_access_path(d, path: tuple, type_=None, required=False):
15
15
  if d is None:
16
16
  break
17
17
 
18
- if type_ == bool and isinstance(d, str):
19
- if d.lower() not in ('true', 'false'):
18
+ if type_ is bool and isinstance(d, str):
19
+ if d.lower() not in ("true", "false"):
20
20
  raise ValueError()
21
- d = 'true' == d.lower()
21
+ d = d.lower() == "true"
22
22
  elif type_ is not None and not isinstance(d, type_):
23
23
  # wrong type
24
24
  d = None
25
25
 
26
26
  if d is None and required:
27
- raise KeyError(f'path: {path} doesn\'t exist in given plist object')
27
+ raise KeyError(f"path: {path} doesn't exist in given plist object")
28
28
 
29
29
  return d
30
30
 
@@ -35,7 +35,7 @@ def bytes_to_uint(b: bytes):
35
35
 
36
36
  def try_decode(s: bytes):
37
37
  try:
38
- return s.decode('utf8')
38
+ return s.decode("utf8")
39
39
  except UnicodeDecodeError:
40
40
  return s
41
41
 
@@ -45,7 +45,7 @@ def asyncio_print_traceback(f: Callable):
45
45
  async def wrapper(*args, **kwargs):
46
46
  try:
47
47
  return await f(*args, **kwargs)
48
- except Exception as e: # noqa: E72
48
+ except (Exception, RuntimeError) as e:
49
49
  if not isinstance(e, asyncio.CancelledError):
50
50
  traceback.print_exc()
51
51
  raise
@@ -57,7 +57,7 @@ def get_asyncio_loop() -> asyncio.AbstractEventLoop:
57
57
  try:
58
58
  loop = asyncio.get_running_loop()
59
59
  if loop.is_closed():
60
- raise RuntimeError('The existing loop is closed.')
60
+ raise RuntimeError("The existing loop is closed.")
61
61
  except RuntimeError:
62
62
  # This happens when there is no current event loop
63
63
  loop = asyncio.new_event_loop()
@@ -67,14 +67,17 @@ def get_asyncio_loop() -> asyncio.AbstractEventLoop:
67
67
 
68
68
  def file_download(url: str, outfile: Path, chunk_size=1024) -> None:
69
69
  resp = requests.get(url, stream=True)
70
- total = int(resp.headers.get('content-length', 0))
71
- with outfile.open('wb') as file, tqdm(
70
+ total = int(resp.headers.get("content-length", 0))
71
+ with (
72
+ outfile.open("wb") as file,
73
+ tqdm(
72
74
  desc=outfile.name,
73
75
  total=total,
74
- unit='iB',
76
+ unit="iB",
75
77
  unit_scale=True,
76
78
  unit_divisor=1024,
77
- ) as bar:
79
+ ) as bar,
80
+ ):
78
81
  for data in resp.iter_content(chunk_size=chunk_size):
79
82
  size = file.write(data)
80
83
  bar.update(size)