pymobiledevice3 5.0.0__py3-none-any.whl → 5.0.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.

Potentially problematic release.


This version of pymobiledevice3 might be problematic. Click here for more details.

Files changed (143) hide show
  1. misc/plist_sniffer.py +15 -15
  2. misc/remotexpc_sniffer.py +29 -28
  3. pymobiledevice3/__main__.py +128 -102
  4. pymobiledevice3/_version.py +2 -2
  5. pymobiledevice3/bonjour.py +26 -49
  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 +25 -18
  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 +602 -520
  18. pymobiledevice3/cli/diagnostics.py +38 -33
  19. pymobiledevice3/cli/lockdown.py +79 -74
  20. pymobiledevice3/cli/mounter.py +85 -68
  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 +186 -110
  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 +157 -79
  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 +394 -241
  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 +62 -41
  52. pymobiledevice3/remote/tunnel_service.py +371 -293
  53. pymobiledevice3/remote/utils.py +12 -11
  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 +35 -54
  65. pymobiledevice3/restore/recovery.py +125 -135
  66. pymobiledevice3/restore/restore.py +524 -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 +136 -126
  72. pymobiledevice3/services/afc.py +350 -291
  73. pymobiledevice3/services/amfi.py +21 -18
  74. pymobiledevice3/services/companion.py +23 -19
  75. pymobiledevice3/services/crash_reports.py +60 -46
  76. pymobiledevice3/services/debugserver_applist.py +3 -3
  77. pymobiledevice3/services/device_arbitration.py +8 -8
  78. pymobiledevice3/services/device_link.py +55 -47
  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 +40 -50
  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 +330 -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 +69 -51
  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 +180 -176
  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 +2 -2
  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 +127 -116
  132. pymobiledevice3/tcp_forwarder.py +35 -22
  133. pymobiledevice3/tunneld/api.py +20 -15
  134. pymobiledevice3/tunneld/server.py +212 -133
  135. pymobiledevice3/usbmux.py +183 -138
  136. pymobiledevice3/utils.py +14 -11
  137. {pymobiledevice3-5.0.0.dist-info → pymobiledevice3-5.0.2.dist-info}/METADATA +1 -1
  138. pymobiledevice3-5.0.2.dist-info/RECORD +173 -0
  139. pymobiledevice3-5.0.0.dist-info/RECORD +0 -173
  140. {pymobiledevice3-5.0.0.dist-info → pymobiledevice3-5.0.2.dist-info}/WHEEL +0 -0
  141. {pymobiledevice3-5.0.0.dist-info → pymobiledevice3-5.0.2.dist-info}/entry_points.txt +0 -0
  142. {pymobiledevice3-5.0.0.dist-info → pymobiledevice3-5.0.2.dist-info}/licenses/LICENSE +0 -0
  143. {pymobiledevice3-5.0.0.dist-info → pymobiledevice3-5.0.2.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env python3
2
2
  import contextlib
3
+ from typing import Optional
3
4
 
4
5
  from pymobiledevice3.lockdown import LockdownClient
5
6
  from pymobiledevice3.lockdown_service_provider import LockdownServiceProvider
@@ -7,8 +8,8 @@ from pymobiledevice3.services.lockdown_service import LockdownService
7
8
 
8
9
 
9
10
  class PowerAssertionService(LockdownService):
10
- RSD_SERVICE_NAME = 'com.apple.mobile.assertion_agent.shim.remote'
11
- SERVICE_NAME = 'com.apple.mobile.assertion_agent'
11
+ RSD_SERVICE_NAME = "com.apple.mobile.assertion_agent.shim.remote"
12
+ SERVICE_NAME = "com.apple.mobile.assertion_agent"
12
13
 
13
14
  def __init__(self, lockdown: LockdownServiceProvider):
14
15
  if isinstance(lockdown, LockdownClient):
@@ -17,17 +18,17 @@ class PowerAssertionService(LockdownService):
17
18
  super().__init__(lockdown, self.RSD_SERVICE_NAME)
18
19
 
19
20
  @contextlib.contextmanager
20
- def create_power_assertion(self, type_: str, name: str, timeout: float, details: str = None):
21
- """ Trigger IOPMAssertionCreateWithName """
21
+ def create_power_assertion(self, type_: str, name: str, timeout: float, details: Optional[str] = None):
22
+ """Trigger IOPMAssertionCreateWithName"""
22
23
  msg = {
23
- 'CommandKey': 'CommandCreateAssertion',
24
- 'AssertionTypeKey': type_,
25
- 'AssertionNameKey': name,
26
- 'AssertionTimeoutKey': timeout,
24
+ "CommandKey": "CommandCreateAssertion",
25
+ "AssertionTypeKey": type_,
26
+ "AssertionNameKey": name,
27
+ "AssertionTimeoutKey": timeout,
27
28
  }
28
29
 
29
30
  if details is not None:
30
- msg['AssertionDetailKey'] = details
31
+ msg["AssertionDetailKey"] = details
31
32
 
32
33
  self.service.send_recv_plist(msg)
33
34
  yield
@@ -6,8 +6,8 @@ from pymobiledevice3.services.lockdown_service import LockdownService
6
6
 
7
7
 
8
8
  class PreboardService(LockdownService):
9
- RSD_SERVICE_NAME = 'com.apple.preboardservice_v2.shim.remote'
10
- SERVICE_NAME = 'com.apple.preboardservice_v2'
9
+ RSD_SERVICE_NAME = "com.apple.preboardservice_v2.shim.remote"
10
+ SERVICE_NAME = "com.apple.preboardservice_v2"
11
11
 
12
12
  def __init__(self, lockdown: LockdownServiceProvider):
13
13
  if isinstance(lockdown, LockdownClient):
@@ -16,7 +16,7 @@ class PreboardService(LockdownService):
16
16
  super().__init__(lockdown, self.RSD_SERVICE_NAME)
17
17
 
18
18
  def create_stashbag(self, manifest):
19
- return self.service.send_recv_plist({'Command': 'CreateStashbag', 'Manifest': manifest})
19
+ return self.service.send_recv_plist({"Command": "CreateStashbag", "Manifest": manifest})
20
20
 
21
21
  def commit(self, manifest):
22
- return self.service.send_recv_plist({'Command': 'CommitStashbag', 'Manifest': manifest})
22
+ return self.service.send_recv_plist({"Command": "CommitStashbag", "Manifest": manifest})
@@ -15,32 +15,34 @@ class DSCFile:
15
15
 
16
16
 
17
17
  class RemoteFetchSymbolsService(RemoteService):
18
- SERVICE_NAME = 'com.apple.dt.remoteFetchSymbols'
18
+ SERVICE_NAME = "com.apple.dt.remoteFetchSymbols"
19
19
 
20
20
  def __init__(self, rsd: RemoteServiceDiscoveryService):
21
21
  super().__init__(rsd, self.SERVICE_NAME)
22
22
 
23
23
  async def get_dsc_file_list(self) -> list[DSCFile]:
24
24
  files: list[DSCFile] = []
25
- response = await self.service.send_receive_request({'XPCDictionary_sideChannel': uuid.uuid4(), 'DSCFilePaths': []})
26
- file_count = response['DSCFilePaths']
27
- for i in range(file_count):
25
+ response = await self.service.send_receive_request({
26
+ "XPCDictionary_sideChannel": uuid.uuid4(),
27
+ "DSCFilePaths": [],
28
+ })
29
+ file_count = response["DSCFilePaths"]
30
+ for _i in range(file_count):
28
31
  response = await self.service.receive_response()
29
- response = response['DSCFilePaths']
30
- file_transfer = response['fileTransfer']
31
- expected_length = file_transfer['expectedLength']
32
- file_path = response['filePath']
32
+ response = response["DSCFilePaths"]
33
+ file_transfer = response["fileTransfer"]
34
+ expected_length = file_transfer["expectedLength"]
35
+ file_path = response["filePath"]
33
36
  files.append(DSCFile(file_path=file_path, file_size=expected_length))
34
37
  return files
35
38
 
36
39
  async def download(self, out: Path) -> None:
37
40
  files = await self.get_dsc_file_list()
38
41
  for i, file in enumerate(files):
39
- self.logger.info(f'Downloading {file}')
42
+ self.logger.info(f"Downloading {file}")
40
43
  out_file = out / file.file_path[1:] # trim the "/" prefix
41
44
  out_file.parent.mkdir(parents=True, exist_ok=True)
42
- with open(out_file, 'wb') as f:
43
- with tqdm(total=files[i].file_size, dynamic_ncols=True) as pb:
44
- async for chunk in self.service.iter_file_chunks(files[i].file_size, file_idx=i):
45
- f.write(chunk)
46
- pb.update(len(chunk))
45
+ with open(out_file, "wb") as f, tqdm(total=files[i].file_size, dynamic_ncols=True) as pb:
46
+ async for chunk in self.service.iter_file_chunks(files[i].file_size, file_idx=i):
47
+ f.write(chunk)
48
+ pb.update(len(chunk))
@@ -1,3 +1,4 @@
1
+ import contextlib
1
2
  import copy
2
3
  import io
3
4
  import os
@@ -6,18 +7,33 @@ import uuid
6
7
  from functools import partial
7
8
  from pprint import pprint
8
9
  from queue import Empty, Queue
10
+ from typing import ClassVar, Optional
9
11
 
10
12
  import IPython
11
13
  from bpylist2 import archiver
12
- from construct import Adapter, Const, Default, GreedyBytes, GreedyRange, Int16ul, Int32sl, Int32ul, Int64ul, Prefixed, \
13
- Select, Struct, Switch, this
14
+ from construct import (
15
+ Adapter,
16
+ Const,
17
+ Default,
18
+ GreedyBytes,
19
+ GreedyRange,
20
+ Int16ul,
21
+ Int32sl,
22
+ Int32ul,
23
+ Int64ul,
24
+ Prefixed,
25
+ Select,
26
+ Struct,
27
+ Switch,
28
+ this,
29
+ )
14
30
  from pygments import formatters, highlight, lexers
15
31
 
16
32
  from pymobiledevice3.exceptions import DvtException, UnrecognizedSelectorError
17
33
  from pymobiledevice3.lockdown_service_provider import LockdownServiceProvider
18
34
  from pymobiledevice3.services.lockdown_service import LockdownService
19
35
 
20
- SHELL_USAGE = '''
36
+ SHELL_USAGE = """
21
37
  # This shell allows you to send messages to the DVTSecureSocketProxy and receive answers easily.
22
38
  # Generally speaking, each channel represents a group of actions.
23
39
  # Calling actions is done using a selector and auxiliary (parameters).
@@ -45,7 +61,7 @@ channel.killPid_(args, expects_reply=False) # Killing a process doesn't require
45
61
  # In some rare cases, you might want to receive the auxiliary and the selector return value.
46
62
  # For that cases you can use the recv_plist method.
47
63
  return_value, auxiliary = developer.recv_plist()
48
- '''
64
+ """
49
65
 
50
66
 
51
67
  class BplitAdapter(Adapter):
@@ -57,29 +73,39 @@ class BplitAdapter(Adapter):
57
73
 
58
74
 
59
75
  message_aux_t_struct = Struct(
60
- 'magic' / Default(Int64ul, 0x1f0),
61
- 'aux' / Prefixed(Int64ul, GreedyRange(Struct(
62
- '_empty_dictionary' / Select(Const(0xa, Int32ul), Int32ul),
63
- 'type' / Int32ul,
64
- 'value' / Switch(this.type, {2: BplitAdapter(Prefixed(Int32ul, GreedyBytes)), 3: Int32ul, 6: Int64ul},
65
- default=GreedyBytes),
66
- )))
76
+ "magic" / Default(Int64ul, 0x1F0),
77
+ "aux"
78
+ / Prefixed(
79
+ Int64ul,
80
+ GreedyRange(
81
+ Struct(
82
+ "_empty_dictionary" / Select(Const(0xA, Int32ul), Int32ul),
83
+ "type" / Int32ul,
84
+ "value"
85
+ / Switch(
86
+ this.type,
87
+ {2: BplitAdapter(Prefixed(Int32ul, GreedyBytes)), 3: Int32ul, 6: Int64ul},
88
+ default=GreedyBytes,
89
+ ),
90
+ )
91
+ ),
92
+ ),
67
93
  )
68
94
  dtx_message_header_struct = Struct(
69
- 'magic' / Const(0x1F3D5B79, Int32ul),
70
- 'cb' / Int32ul,
71
- 'fragmentId' / Int16ul,
72
- 'fragmentCount' / Int16ul,
73
- 'length' / Int32ul,
74
- 'identifier' / Int32ul,
75
- 'conversationIndex' / Int32ul,
76
- 'channelCode' / Int32sl,
77
- 'expectsReply' / Int32ul,
95
+ "magic" / Const(0x1F3D5B79, Int32ul),
96
+ "cb" / Int32ul,
97
+ "fragmentId" / Int16ul,
98
+ "fragmentCount" / Int16ul,
99
+ "length" / Int32ul,
100
+ "identifier" / Int32ul,
101
+ "conversationIndex" / Int32ul,
102
+ "channelCode" / Int32sl,
103
+ "expectsReply" / Int32ul,
78
104
  )
79
105
  dtx_message_payload_header_struct = Struct(
80
- 'flags' / Int32ul,
81
- 'auxiliaryLength' / Int32ul,
82
- 'totalLength' / Int64ul,
106
+ "flags" / Int32ul,
107
+ "auxiliaryLength" / Int32ul,
108
+ "totalLength" / Int64ul,
83
109
  )
84
110
 
85
111
 
@@ -88,25 +114,25 @@ class MessageAux:
88
114
  self.values = []
89
115
 
90
116
  def append_int(self, value: int):
91
- self.values.append({'type': 3, 'value': value})
117
+ self.values.append({"type": 3, "value": value})
92
118
  return self
93
119
 
94
120
  def append_long(self, value: int):
95
- self.values.append({'type': 6, 'value': value})
121
+ self.values.append({"type": 6, "value": value})
96
122
  return self
97
123
 
98
124
  def append_obj(self, value):
99
- self.values.append({'type': 2, 'value': value})
125
+ self.values.append({"type": 2, "value": value})
100
126
  return self
101
127
 
102
128
  def __bytes__(self):
103
- return message_aux_t_struct.build(dict(aux=self.values))
129
+ return message_aux_t_struct.build({"aux": self.values})
104
130
 
105
131
 
106
132
  class DTTapMessage:
107
133
  @staticmethod
108
134
  def decode_archive(archive_obj):
109
- return archive_obj.decode('DTTapMessagePlist')
135
+ return archive_obj.decode("DTTapMessagePlist")
110
136
 
111
137
 
112
138
  class NSNull:
@@ -122,10 +148,10 @@ class NSError:
122
148
 
123
149
  @staticmethod
124
150
  def decode_archive(archive_obj):
125
- user_info = archive_obj.decode('NSUserInfo')
126
- if user_info.get('NSLocalizedDescription', '').endswith(' - it does not respond to the selector'):
151
+ user_info = archive_obj.decode("NSUserInfo")
152
+ if user_info.get("NSLocalizedDescription", "").endswith(" - it does not respond to the selector"):
127
153
  raise UnrecognizedSelectorError(user_info)
128
- raise DvtException(archive_obj.decode('NSUserInfo'))
154
+ raise DvtException(archive_obj.decode("NSUserInfo"))
129
155
 
130
156
 
131
157
  class NSUUID(uuid.UUID):
@@ -135,11 +161,11 @@ class NSUUID(uuid.UUID):
135
161
  return NSUUID(bytes=os.urandom(16))
136
162
 
137
163
  def encode_archive(self, archive_obj: archiver.ArchivingObject):
138
- archive_obj.encode('NS.uuidbytes', self.bytes)
164
+ archive_obj.encode("NS.uuidbytes", self.bytes)
139
165
 
140
166
  @staticmethod
141
167
  def decode_archive(archive_obj: archiver.ArchivedObject):
142
- return NSUUID(bytes=archive_obj.decode('NS.uuidbytes'))
168
+ return NSUUID(bytes=archive_obj.decode("NS.uuidbytes"))
143
169
 
144
170
 
145
171
  class NSURL:
@@ -148,74 +174,72 @@ class NSURL:
148
174
  self.relative = relative
149
175
 
150
176
  def encode_archive(self, archive_obj: archiver.ArchivingObject):
151
- archive_obj.encode('NS.base', self.base)
152
- archive_obj.encode('NS.relative', self.relative)
177
+ archive_obj.encode("NS.base", self.base)
178
+ archive_obj.encode("NS.relative", self.relative)
153
179
 
154
180
  @staticmethod
155
181
  def decode_archive(archive_obj: archiver.ArchivedObject):
156
- return NSURL(archive_obj.decode('NS.base'), archive_obj.decode('NS.relative'))
182
+ return NSURL(archive_obj.decode("NS.base"), archive_obj.decode("NS.relative"))
157
183
 
158
184
 
159
185
  class NSValue:
160
186
  @staticmethod
161
187
  def decode_archive(archive_obj: archiver.ArchivedObject):
162
- return archive_obj.decode('NS.rectval')
188
+ return archive_obj.decode("NS.rectval")
163
189
 
164
190
 
165
191
  class NSMutableData:
166
192
  @staticmethod
167
193
  def decode_archive(archive_obj: archiver.ArchivedObject):
168
- return archive_obj.decode('NS.data')
194
+ return archive_obj.decode("NS.data")
169
195
 
170
196
 
171
197
  class NSMutableString:
172
198
  @staticmethod
173
199
  def decode_archive(archive_obj: archiver.ArchivedObject):
174
- return archive_obj.decode('NS.string')
200
+ return archive_obj.decode("NS.string")
175
201
 
176
202
 
177
203
  class XCTestConfiguration:
178
- _default = {
204
+ _default: ClassVar = {
179
205
  # 'testBundleURL': UID(3),
180
206
  # 'sessionIdentifier': UID(8), # UUID
181
- 'aggregateStatisticsBeforeCrash': {
182
- 'XCSuiteRecordsKey': {}
183
- },
184
- 'automationFrameworkPath': '/Developer/Library/PrivateFrameworks/XCTAutomationSupport.framework',
185
- 'baselineFileRelativePath': None,
186
- 'baselineFileURL': None,
187
- 'defaultTestExecutionTimeAllowance': None,
188
- 'disablePerformanceMetrics': False,
189
- 'emitOSLogs': False,
190
- 'formatVersion': plistlib.UID(2), # store in UID
191
- 'gatherLocalizableStringsData': False,
192
- 'initializeForUITesting': True,
193
- 'maximumTestExecutionTimeAllowance': None,
194
- 'productModuleName': 'WebDriverAgentRunner', # set to other value is also OK
195
- 'randomExecutionOrderingSeed': None,
196
- 'reportActivities': True,
197
- 'reportResultsToIDE': True,
198
- 'systemAttachmentLifetime': 2,
199
- 'targetApplicationArguments': [], # maybe useless
200
- 'targetApplicationBundleID': None,
201
- 'targetApplicationEnvironment': None,
202
- 'targetApplicationPath': '/whatever-it-does-not-matter/but-should-not-be-empty',
203
- 'testApplicationDependencies': {},
204
- 'testApplicationUserOverrides': None,
205
- 'testBundleRelativePath': None,
206
- 'testExecutionOrdering': 0,
207
- 'testTimeoutsEnabled': False,
208
- 'testsDrivenByIDE': False,
209
- 'testsMustRunOnMainThread': True,
210
- 'testsToRun': None,
211
- 'testsToSkip': None,
212
- 'treatMissingBaselinesAsFailures': False,
213
- 'userAttachmentLifetime': 1
207
+ "aggregateStatisticsBeforeCrash": {"XCSuiteRecordsKey": {}},
208
+ "automationFrameworkPath": "/Developer/Library/PrivateFrameworks/XCTAutomationSupport.framework",
209
+ "baselineFileRelativePath": None,
210
+ "baselineFileURL": None,
211
+ "defaultTestExecutionTimeAllowance": None,
212
+ "disablePerformanceMetrics": False,
213
+ "emitOSLogs": False,
214
+ "formatVersion": plistlib.UID(2), # store in UID
215
+ "gatherLocalizableStringsData": False,
216
+ "initializeForUITesting": True,
217
+ "maximumTestExecutionTimeAllowance": None,
218
+ "productModuleName": "WebDriverAgentRunner", # set to other value is also OK
219
+ "randomExecutionOrderingSeed": None,
220
+ "reportActivities": True,
221
+ "reportResultsToIDE": True,
222
+ "systemAttachmentLifetime": 2,
223
+ "targetApplicationArguments": [], # maybe useless
224
+ "targetApplicationBundleID": None,
225
+ "targetApplicationEnvironment": None,
226
+ "targetApplicationPath": "/whatever-it-does-not-matter/but-should-not-be-empty",
227
+ "testApplicationDependencies": {},
228
+ "testApplicationUserOverrides": None,
229
+ "testBundleRelativePath": None,
230
+ "testExecutionOrdering": 0,
231
+ "testTimeoutsEnabled": False,
232
+ "testsDrivenByIDE": False,
233
+ "testsMustRunOnMainThread": True,
234
+ "testsToRun": None,
235
+ "testsToSkip": None,
236
+ "treatMissingBaselinesAsFailures": False,
237
+ "userAttachmentLifetime": 1,
214
238
  }
215
239
 
216
240
  def __init__(self, kv: dict):
217
- assert 'testBundleURL' in kv
218
- assert 'sessionIdentifier' in kv
241
+ assert "testBundleURL" in kv
242
+ assert "sessionIdentifier" in kv
219
243
  self._config = copy.deepcopy(self._default)
220
244
  self._config.update(kv)
221
245
 
@@ -228,27 +252,29 @@ class XCTestConfiguration:
228
252
  return archive_obj.object
229
253
 
230
254
 
231
- archiver.update_class_map({'DTSysmonTapMessage': DTTapMessage,
232
- 'DTTapHeartbeatMessage': DTTapMessage,
233
- 'DTTapStatusMessage': DTTapMessage,
234
- 'DTKTraceTapMessage': DTTapMessage,
235
- 'DTActivityTraceTapMessage': DTTapMessage,
236
- 'DTTapMessage': DTTapMessage,
237
- 'NSNull': NSNull,
238
- 'NSError': NSError,
239
- 'NSUUID': NSUUID,
240
- 'NSURL': NSURL,
241
- 'NSValue': NSValue,
242
- 'NSMutableData': NSMutableData,
243
- 'NSMutableString': NSMutableString,
244
- 'XCTestConfiguration': XCTestConfiguration})
255
+ archiver.update_class_map({
256
+ "DTSysmonTapMessage": DTTapMessage,
257
+ "DTTapHeartbeatMessage": DTTapMessage,
258
+ "DTTapStatusMessage": DTTapMessage,
259
+ "DTKTraceTapMessage": DTTapMessage,
260
+ "DTActivityTraceTapMessage": DTTapMessage,
261
+ "DTTapMessage": DTTapMessage,
262
+ "NSNull": NSNull,
263
+ "NSError": NSError,
264
+ "NSUUID": NSUUID,
265
+ "NSURL": NSURL,
266
+ "NSValue": NSValue,
267
+ "NSMutableData": NSMutableData,
268
+ "NSMutableString": NSMutableString,
269
+ "XCTestConfiguration": XCTestConfiguration,
270
+ })
245
271
 
246
- archiver.Archive.inline_types = list(set(archiver.Archive.inline_types + [bytes]))
272
+ archiver.Archive.inline_types = list({*archiver.Archive.inline_types, bytes})
247
273
 
248
274
 
249
275
  class Channel(int):
250
276
  @classmethod
251
- def create(cls, value: int, service: 'RemoteServer'):
277
+ def create(cls, value: int, service: "RemoteServer"):
252
278
  channel = cls(value)
253
279
  channel._service = service
254
280
  return channel
@@ -270,10 +296,7 @@ class Channel(int):
270
296
  """
271
297
  Sanitize python name to ObjectiveC name.
272
298
  """
273
- if name.startswith('_'):
274
- name = '_' + name[1:].replace('_', ':')
275
- else:
276
- name = name.replace('_', ':')
299
+ name = "_" + name[1:].replace("_", ":") if name.startswith("_") else name.replace("_", ":")
277
300
  return name
278
301
 
279
302
  def __getitem__(self, item):
@@ -286,8 +309,8 @@ class Channel(int):
286
309
  class ChannelFragmenter:
287
310
  def __init__(self):
288
311
  self._messages = Queue()
289
- self._packet_data = b''
290
- self._stream_packet_data = b''
312
+ self._packet_data = b""
313
+ self._stream_packet_data = b""
291
314
 
292
315
  def get(self):
293
316
  return self._messages.get_nowait()
@@ -298,13 +321,13 @@ class ChannelFragmenter:
298
321
  if mheader.fragmentId == mheader.fragmentCount - 1:
299
322
  # last message
300
323
  self._messages.put(self._packet_data)
301
- self._packet_data = b''
324
+ self._packet_data = b""
302
325
  else:
303
326
  self._stream_packet_data += chunk
304
327
  if mheader.fragmentId == mheader.fragmentCount - 1:
305
328
  # last message
306
329
  self._messages.put(self._stream_packet_data)
307
- self._stream_packet_data = b''
330
+ self._stream_packet_data = b""
308
331
 
309
332
 
310
333
  class RemoteServer(LockdownService):
@@ -351,16 +374,22 @@ class RemoteServer(LockdownService):
351
374
  }
352
375
  }
353
376
  ```
354
- """ # noqa: E501
377
+ """
378
+
355
379
  BROADCAST_CHANNEL = 0
356
380
  INSTRUMENTS_MESSAGE_TYPE = 2
357
381
  EXPECTS_REPLY_MASK = 0x1000
358
382
 
359
- def __init__(self, lockdown: LockdownServiceProvider, service_name, remove_ssl_context: bool = True,
360
- is_developer_service: bool = True):
383
+ def __init__(
384
+ self,
385
+ lockdown: LockdownServiceProvider,
386
+ service_name,
387
+ remove_ssl_context: bool = True,
388
+ is_developer_service: bool = True,
389
+ ):
361
390
  super().__init__(lockdown, service_name, is_developer_service=is_developer_service)
362
391
 
363
- if remove_ssl_context and hasattr(self.service.socket, '_sslobj'):
392
+ if remove_ssl_context and hasattr(self.service.socket, "_sslobj"):
364
393
  self.service.socket._sslobj = None
365
394
 
366
395
  self.supported_identifiers = {}
@@ -372,22 +401,23 @@ class RemoteServer(LockdownService):
372
401
 
373
402
  def shell(self):
374
403
  IPython.embed(
375
- header=highlight(SHELL_USAGE, lexers.PythonLexer(), formatters.Terminal256Formatter(style='native')),
404
+ header=highlight(SHELL_USAGE, lexers.PythonLexer(), formatters.Terminal256Formatter(style="native")),
376
405
  user_ns={
377
- 'developer': self,
378
- 'broadcast': self.broadcast,
379
- 'MessageAux': MessageAux,
380
- })
406
+ "developer": self,
407
+ "broadcast": self.broadcast,
408
+ "MessageAux": MessageAux,
409
+ },
410
+ )
381
411
 
382
412
  def perform_handshake(self):
383
413
  args = MessageAux()
384
- args.append_obj({'com.apple.private.DTXBlockCompression': 0, 'com.apple.private.DTXConnection': 1})
385
- self.send_message(0, '_notifyOfPublishedCapabilities:', args, expects_reply=False)
414
+ args.append_obj({"com.apple.private.DTXBlockCompression": 0, "com.apple.private.DTXConnection": 1})
415
+ self.send_message(0, "_notifyOfPublishedCapabilities:", args, expects_reply=False)
386
416
  ret, aux = self.recv_plist()
387
- if ret != '_notifyOfPublishedCapabilities:':
388
- raise ValueError('Invalid answer')
417
+ if ret != "_notifyOfPublishedCapabilities:":
418
+ raise ValueError("Invalid answer")
389
419
  if not len(aux[0]):
390
- raise ValueError('Invalid answer')
420
+ raise ValueError("Invalid answer")
391
421
  self.supported_identifiers = aux[0].value
392
422
 
393
423
  def make_channel(self, identifier) -> Channel:
@@ -399,34 +429,39 @@ class RemoteServer(LockdownService):
399
429
  self.last_channel_code += 1
400
430
  code = self.last_channel_code
401
431
  args = MessageAux().append_int(code).append_obj(identifier)
402
- self.send_message(0, '_requestChannelWithCode:identifier:', args)
403
- ret, aux = self.recv_plist()
432
+ self.send_message(0, "_requestChannelWithCode:identifier:", args)
433
+ ret, _aux = self.recv_plist()
404
434
  assert ret is None
405
435
  channel = Channel.create(code, self)
406
436
  self.channel_cache[identifier] = channel
407
437
  self.channel_messages[code] = ChannelFragmenter()
408
438
  return channel
409
439
 
410
- def send_message(self, channel: int, selector: str = None, args: MessageAux = None, expects_reply: bool = True):
440
+ def send_message(
441
+ self, channel: int, selector: Optional[str] = None, args: MessageAux = None, expects_reply: bool = True
442
+ ):
411
443
  self.cur_message += 1
412
444
 
413
- aux = bytes(args) if args is not None else b''
414
- sel = archiver.archive(selector) if selector is not None else b''
445
+ aux = bytes(args) if args is not None else b""
446
+ sel = archiver.archive(selector) if selector is not None else b""
415
447
  flags = self.INSTRUMENTS_MESSAGE_TYPE
416
448
  if expects_reply:
417
449
  flags |= self.EXPECTS_REPLY_MASK
418
- pheader = dtx_message_payload_header_struct.build(dict(flags=flags, auxiliaryLength=len(aux),
419
- totalLength=len(aux) + len(sel)))
420
- mheader = dtx_message_header_struct.build(dict(
421
- cb=dtx_message_header_struct.sizeof(),
422
- fragmentId=0,
423
- fragmentCount=1,
424
- length=dtx_message_payload_header_struct.sizeof() + len(aux) + len(sel),
425
- identifier=self.cur_message,
426
- conversationIndex=0,
427
- channelCode=channel,
428
- expectsReply=int(expects_reply)
429
- ))
450
+ pheader = dtx_message_payload_header_struct.build({
451
+ "flags": flags,
452
+ "auxiliaryLength": len(aux),
453
+ "totalLength": len(aux) + len(sel),
454
+ })
455
+ mheader = dtx_message_header_struct.build({
456
+ "cb": dtx_message_header_struct.sizeof(),
457
+ "fragmentId": 0,
458
+ "fragmentCount": 1,
459
+ "length": dtx_message_payload_header_struct.sizeof() + len(aux) + len(sel),
460
+ "identifier": self.cur_message,
461
+ "conversationIndex": 0,
462
+ "channelCode": channel,
463
+ "expectsReply": int(expects_reply),
464
+ })
430
465
  msg = mheader + pheader + aux + sel
431
466
  self.service.sendall(msg)
432
467
 
@@ -435,11 +470,11 @@ class RemoteServer(LockdownService):
435
470
  if data is not None:
436
471
  try:
437
472
  data = archiver.unarchive(data)
438
- except archiver.MissingClassMapping as e:
473
+ except archiver.MissingClassMapping:
439
474
  pprint(plistlib.loads(data))
440
- raise e
475
+ raise
441
476
  except plistlib.InvalidFileException:
442
- self.logger.warning(f'got an invalid plist: {data[:40]}')
477
+ self.logger.warning(f"got an invalid plist: {data[:40]}")
443
478
  return data, aux
444
479
 
445
480
  def recv_message(self, channel: int = BROADCAST_CHANNEL):
@@ -448,12 +483,9 @@ class RemoteServer(LockdownService):
448
483
 
449
484
  compression = (pheader.flags & 0xFF000) >> 12
450
485
  if compression:
451
- raise NotImplementedError('Compressed')
486
+ raise NotImplementedError("Compressed")
452
487
 
453
- if pheader.auxiliaryLength:
454
- aux = message_aux_t_struct.parse_stream(packet_stream).aux
455
- else:
456
- aux = None
488
+ aux = message_aux_t_struct.parse_stream(packet_stream).aux if pheader.auxiliaryLength else None
457
489
  obj_size = pheader.totalLength - pheader.auxiliaryLength
458
490
  data = packet_stream.read(obj_size) if obj_size else None
459
491
  return data, aux
@@ -477,9 +509,8 @@ class RemoteServer(LockdownService):
477
509
  if received_channel_code not in self.channel_messages:
478
510
  self.channel_messages[received_channel_code] = ChannelFragmenter()
479
511
 
480
- if not mheader.conversationIndex:
481
- if mheader.identifier > self.cur_message:
482
- self.cur_message = mheader.identifier
512
+ if not mheader.conversationIndex and mheader.identifier > self.cur_message:
513
+ self.cur_message = mheader.identifier
483
514
 
484
515
  if mheader.fragmentCount > 1 and mheader.fragmentId == 0:
485
516
  # when reading multiple message fragments, the first fragment contains only a message header
@@ -493,15 +524,14 @@ class RemoteServer(LockdownService):
493
524
 
494
525
  def close(self):
495
526
  aux = MessageAux()
496
- codes = [code for code in self.channel_messages.keys() if code > 0]
527
+ codes = [code for code in self.channel_messages if code > 0]
497
528
  if codes:
498
529
  for code in codes:
499
530
  aux.append_int(code)
500
- try:
501
- self.send_message(self.BROADCAST_CHANNEL, '_channelCanceled:', aux, expects_reply=False)
502
- except OSError:
531
+
532
+ with contextlib.suppress(OSError):
503
533
  # ignore: OSError: [Errno 9] Bad file descriptor
504
- pass
534
+ self.send_message(self.BROADCAST_CHANNEL, "_channelCanceled:", aux, expects_reply=False)
505
535
  super().close()
506
536
 
507
537