pymobiledevice3 5.0.1__py3-none-any.whl → 5.0.3__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 +36 -59
  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 +396 -242
  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 +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 +137 -127
  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 +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 +442 -421
  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 +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 +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 +129 -117
  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.1.dist-info → pymobiledevice3-5.0.3.dist-info}/METADATA +1 -1
  138. pymobiledevice3-5.0.3.dist-info/RECORD +173 -0
  139. pymobiledevice3-5.0.1.dist-info/RECORD +0 -173
  140. {pymobiledevice3-5.0.1.dist-info → pymobiledevice3-5.0.3.dist-info}/WHEEL +0 -0
  141. {pymobiledevice3-5.0.1.dist-info → pymobiledevice3-5.0.3.dist-info}/entry_points.txt +0 -0
  142. {pymobiledevice3-5.0.1.dist-info → pymobiledevice3-5.0.3.dist-info}/licenses/LICENSE +0 -0
  143. {pymobiledevice3-5.0.1.dist-info → pymobiledevice3-5.0.3.dist-info}/top_level.txt +0 -0
@@ -1,30 +1,33 @@
1
1
  #!/usr/bin/env python3
2
2
  import logging
3
3
 
4
- from pymobiledevice3.exceptions import AmfiError, DeveloperModeError, DeviceHasPasscodeSetError, \
5
- PyMobileDevice3Exception
4
+ from pymobiledevice3.exceptions import (
5
+ AmfiError,
6
+ DeveloperModeError,
7
+ DeviceHasPasscodeSetError,
8
+ PyMobileDevice3Exception,
9
+ )
6
10
  from pymobiledevice3.lockdown import LockdownClient, retry_create_using_usbmux
7
11
  from pymobiledevice3.services.heartbeat import HeartbeatService
8
12
 
9
13
 
10
14
  class AmfiService:
11
-
12
15
  DEVELOPER_MODE_REVEAL = 0
13
16
  DEVELOPER_MODE_ENABLE = 1
14
17
  DEVELOPER_MODE_ACCEPT = 2
15
18
 
16
- SERVICE_NAME = 'com.apple.amfi.lockdown'
19
+ SERVICE_NAME = "com.apple.amfi.lockdown"
17
20
 
18
21
  def __init__(self, lockdown: LockdownClient):
19
22
  self._lockdown = lockdown
20
23
  self._logger = logging.getLogger(self.__module__)
21
24
 
22
25
  def reveal_developer_mode_option_in_ui(self):
23
- """ create an empty file at AMFIShowOverridePath """
26
+ """create an empty file at AMFIShowOverridePath"""
24
27
  service = self._lockdown.start_lockdown_service(self.SERVICE_NAME)
25
- resp = service.send_recv_plist({'action': self.DEVELOPER_MODE_REVEAL})
26
- if not resp.get('success'):
27
- raise PyMobileDevice3Exception(f'create_AMFIShowOverridePath() failed with: {resp}')
28
+ resp = service.send_recv_plist({"action": self.DEVELOPER_MODE_REVEAL})
29
+ if not resp.get("success"):
30
+ raise PyMobileDevice3Exception(f"create_AMFIShowOverridePath() failed with: {resp}")
28
31
 
29
32
  def enable_developer_mode(self, enable_post_restart=True):
30
33
  """
@@ -33,16 +36,16 @@ class AmfiService:
33
36
  with "yes"
34
37
  """
35
38
  service = self._lockdown.start_lockdown_service(self.SERVICE_NAME)
36
- resp = service.send_recv_plist({'action': self.DEVELOPER_MODE_ENABLE})
37
- error = resp.get('Error')
39
+ resp = service.send_recv_plist({"action": self.DEVELOPER_MODE_ENABLE})
40
+ error = resp.get("Error")
38
41
 
39
42
  if error is not None:
40
- if error == 'Device has a passcode set':
43
+ if error == "Device has a passcode set":
41
44
  raise DeviceHasPasscodeSetError()
42
45
  raise AmfiError(error)
43
46
 
44
- if not resp.get('success'):
45
- raise DeveloperModeError(f'enable_developer_mode(): {resp}')
47
+ if not resp.get("success"):
48
+ raise DeveloperModeError(f"enable_developer_mode(): {resp}")
46
49
 
47
50
  if not enable_post_restart:
48
51
  return
@@ -50,14 +53,14 @@ class AmfiService:
50
53
  try:
51
54
  HeartbeatService(self._lockdown).start()
52
55
  except (ConnectionAbortedError, BrokenPipeError):
53
- self._logger.debug('device disconnected, awaiting reconnect')
56
+ self._logger.debug("device disconnected, awaiting reconnect")
54
57
 
55
58
  self._lockdown = retry_create_using_usbmux(None, serial=self._lockdown.udid)
56
59
  self.enable_developer_mode_post_restart()
57
60
 
58
61
  def enable_developer_mode_post_restart(self):
59
- """ answer the prompt that appears after the restart with "yes" """
62
+ """answer the prompt that appears after the restart with "yes" """
60
63
  service = self._lockdown.start_lockdown_service(self.SERVICE_NAME)
61
- resp = service.send_recv_plist({'action': self.DEVELOPER_MODE_ACCEPT})
62
- if not resp.get('success'):
63
- raise DeveloperModeError(f'enable_developer_mode_post_restart() failed: {resp}')
64
+ resp = service.send_recv_plist({"action": self.DEVELOPER_MODE_ACCEPT})
65
+ if not resp.get("success"):
66
+ raise DeveloperModeError(f"enable_developer_mode_post_restart() failed: {resp}")
@@ -8,8 +8,8 @@ from pymobiledevice3.services.lockdown_service import LockdownService
8
8
 
9
9
 
10
10
  class CompanionProxyService(LockdownService):
11
- SERVICE_NAME = 'com.apple.companion_proxy'
12
- RSD_SERVICE_NAME = 'com.apple.companion_proxy.shim.remote'
11
+ SERVICE_NAME = "com.apple.companion_proxy"
12
+ RSD_SERVICE_NAME = "com.apple.companion_proxy.shim.remote"
13
13
 
14
14
  def __init__(self, lockdown: LockdownServiceProvider):
15
15
  if isinstance(lockdown, LockdownClient):
@@ -19,48 +19,52 @@ class CompanionProxyService(LockdownService):
19
19
 
20
20
  def list(self):
21
21
  service = self.lockdown.start_lockdown_service(self.service_name)
22
- return service.send_recv_plist({'Command': 'GetDeviceRegistry'}).get('PairedDevicesArray', [])
22
+ return service.send_recv_plist({"Command": "GetDeviceRegistry"}).get("PairedDevicesArray", [])
23
23
 
24
24
  def listen_for_devices(self):
25
25
  service = self.lockdown.start_lockdown_service(self.service_name)
26
- service.send_plist({'Command': 'StartListeningForDevices'})
26
+ service.send_plist({"Command": "StartListeningForDevices"})
27
27
  while True:
28
28
  yield service.recv_plist()
29
29
 
30
30
  def get_value(self, udid: str, key: str):
31
31
  service = self.lockdown.start_lockdown_service(self.service_name)
32
- response = service.send_recv_plist({'Command': 'GetValueFromRegistry',
33
- 'GetValueGizmoUDIDKey': udid,
34
- 'GetValueKeyKey': key})
32
+ response = service.send_recv_plist({
33
+ "Command": "GetValueFromRegistry",
34
+ "GetValueGizmoUDIDKey": udid,
35
+ "GetValueKeyKey": key,
36
+ })
35
37
 
36
- value = response.get('RetrievedValueDictionary')
38
+ value = response.get("RetrievedValueDictionary")
37
39
  if value is not None:
38
40
  return value
39
41
 
40
- error = response.get('Error')
42
+ error = response.get("Error")
41
43
  raise PyMobileDevice3Exception(error)
42
44
 
43
- def start_forwarding_service_port(self, remote_port: int, service_name: Optional[str] = None,
44
- options: Optional[dict] = None):
45
+ def start_forwarding_service_port(
46
+ self, remote_port: int, service_name: Optional[str] = None, options: Optional[dict] = None
47
+ ):
45
48
  service = self.lockdown.start_lockdown_service(self.service_name)
46
49
 
47
- request = {'Command': 'StartForwardingServicePort',
48
- 'GizmoRemotePortNumber': remote_port,
49
- 'IsServiceLowPriority': False,
50
- 'PreferWifi': False}
50
+ request = {
51
+ "Command": "StartForwardingServicePort",
52
+ "GizmoRemotePortNumber": remote_port,
53
+ "IsServiceLowPriority": False,
54
+ "PreferWifi": False,
55
+ }
51
56
 
52
57
  if service_name is not None:
53
- request['ForwardedServiceName'] = service_name
58
+ request["ForwardedServiceName"] = service_name
54
59
 
55
60
  if options is not None:
56
61
  request.update(options)
57
62
 
58
- return service.send_recv_plist(request).get('CompanionProxyServicePort')
63
+ return service.send_recv_plist(request).get("CompanionProxyServicePort")
59
64
 
60
65
  def stop_forwarding_service_port(self, remote_port: int):
61
66
  service = self.lockdown.start_lockdown_service(self.service_name)
62
67
 
63
- request = {'Command': 'StopForwardingServicePort',
64
- 'GizmoRemotePortNumber': remote_port}
68
+ request = {"Command": "StopForwardingServicePort", "GizmoRemotePortNumber": remote_port}
65
69
 
66
70
  return service.send_recv_plist(request)
@@ -4,22 +4,26 @@ import re
4
4
  import time
5
5
  from collections.abc import Generator
6
6
  from json import JSONDecodeError
7
- from typing import Callable, Optional
7
+ from typing import Callable, ClassVar, Optional
8
8
 
9
9
  from pycrashreport.crash_report import get_crash_report_from_buf
10
10
  from xonsh.built_ins import XSH
11
11
  from xonsh.cli_utils import Annotated, Arg
12
12
 
13
- from pymobiledevice3.exceptions import AfcException, AfcFileNotFoundError, NotificationTimeoutError, \
14
- SysdiagnoseTimeoutError
13
+ from pymobiledevice3.exceptions import (
14
+ AfcException,
15
+ AfcFileNotFoundError,
16
+ NotificationTimeoutError,
17
+ SysdiagnoseTimeoutError,
18
+ )
15
19
  from pymobiledevice3.lockdown import LockdownClient
16
20
  from pymobiledevice3.lockdown_service_provider import LockdownServiceProvider
17
21
  from pymobiledevice3.services.afc import AfcService, AfcShell, path_completer
18
22
  from pymobiledevice3.services.notification_proxy import NotificationProxyService
19
23
  from pymobiledevice3.services.os_trace import OsTraceService
20
24
 
21
- SYSDIAGNOSE_PROCESS_NAMES = ('sysdiagnose', 'sysdiagnosed')
22
- SYSDIAGNOSE_DIR = 'DiagnosticLogs/sysdiagnose'
25
+ SYSDIAGNOSE_PROCESS_NAMES = ("sysdiagnose", "sysdiagnosed")
26
+ SYSDIAGNOSE_DIR = "DiagnosticLogs/sysdiagnose"
23
27
  SYSDIAGNOSE_IN_PROGRESS_MAX_TTL_SECS = 600
24
28
 
25
29
  # on iOS17, we need to wait for a moment before trying to fetch the sysdiagnose archive
@@ -27,14 +31,14 @@ IOS17_SYSDIAGNOSE_DELAY = 3
27
31
 
28
32
 
29
33
  class CrashReportsManager:
30
- COPY_MOBILE_NAME = 'com.apple.crashreportcopymobile'
31
- RSD_COPY_MOBILE_NAME = 'com.apple.crashreportcopymobile.shim.remote'
34
+ COPY_MOBILE_NAME = "com.apple.crashreportcopymobile"
35
+ RSD_COPY_MOBILE_NAME = "com.apple.crashreportcopymobile.shim.remote"
32
36
 
33
- CRASH_MOVER_NAME = 'com.apple.crashreportmover'
34
- RSD_CRASH_MOVER_NAME = 'com.apple.crashreportmover.shim.remote'
37
+ CRASH_MOVER_NAME = "com.apple.crashreportmover"
38
+ RSD_CRASH_MOVER_NAME = "com.apple.crashreportmover.shim.remote"
35
39
 
36
- APPSTORED_PATH = '/com.apple.appstored'
37
- IN_PROGRESS_SYSDIAGNOSE_EXTENSIONS = ['.tmp', '.tar.gz']
40
+ APPSTORED_PATH = "/com.apple.appstored"
41
+ IN_PROGRESS_SYSDIAGNOSE_EXTENSIONS: ClassVar = [".tmp", ".tar.gz"]
38
42
 
39
43
  def __init__(self, lockdown: LockdownServiceProvider):
40
44
  self.logger = logging.getLogger(__name__)
@@ -63,16 +67,16 @@ class CrashReportsManager:
63
67
  Clear all crash reports.
64
68
  """
65
69
  undeleted_items = []
66
- for filename in self.ls('/'):
70
+ for filename in self.ls("/"):
67
71
  undeleted_items.extend(self.afc.rm(filename, force=True))
68
72
 
69
73
  for item in undeleted_items:
70
74
  # special case of file that sometimes created automatically right after delete,
71
75
  # and then we can't delete the folder because it's not empty
72
76
  if item != self.APPSTORED_PATH:
73
- raise AfcException(f'failed to clear crash reports directory, undeleted items: {undeleted_items}', None)
77
+ raise AfcException(f"failed to clear crash reports directory, undeleted items: {undeleted_items}", None)
74
78
 
75
- def ls(self, path: str = '/', depth: int = 1) -> list[str]:
79
+ def ls(self, path: str = "/", depth: int = 1) -> list[str]:
76
80
  """
77
81
  List file and folder in the crash report's directory.
78
82
  :param path: Path to list, relative to the crash report's directory.
@@ -81,8 +85,9 @@ class CrashReportsManager:
81
85
  """
82
86
  return list(self.afc.dirlist(path, depth))[1:] # skip the root path '/'
83
87
 
84
- def pull(self, out: str, entry: str = '/', erase: bool = False, match: Optional[str] = None,
85
- progress_bar: bool = True) -> None:
88
+ def pull(
89
+ self, out: str, entry: str = "/", erase: bool = False, match: Optional[str] = None, progress_bar: bool = True
90
+ ) -> None:
86
91
  """
87
92
  Pull crash reports from the device.
88
93
  :param out: Directory to pull crash reports to.
@@ -93,20 +98,19 @@ class CrashReportsManager:
93
98
  """
94
99
 
95
100
  def log(src: str, dst: str) -> None:
96
- self.logger.info(f'{src} --> {dst}')
97
- if erase:
98
- if not self.afc.isdir(src):
99
- self.afc.rm_single(src, force=True)
101
+ self.logger.info(f"{src} --> {dst}")
102
+ if erase and not self.afc.isdir(src):
103
+ self.afc.rm_single(src, force=True)
100
104
 
101
105
  match = None if match is None else re.compile(match)
102
106
  self.afc.pull(entry, out, match, callback=log, progress_bar=progress_bar, ignore_errors=True)
103
107
 
104
108
  def flush(self) -> None:
105
- """ Trigger com.apple.crashreportmover to flush all products into CrashReports directory """
106
- ack = b'ping\x00'
109
+ """Trigger com.apple.crashreportmover to flush all products into CrashReports directory"""
110
+ ack = b"ping\x00"
107
111
  assert ack == self.lockdown.start_lockdown_service(self.crash_mover_service_name).recvall(len(ack))
108
112
 
109
- def watch(self, name: str = None, raw: bool = False) -> Generator[str, None, None]:
113
+ def watch(self, name: Optional[str] = None, raw: bool = False) -> Generator[str, None, None]:
110
114
  """
111
115
  Monitor creation of new crash reports for a given process name.
112
116
 
@@ -114,16 +118,18 @@ class CrashReportsManager:
114
118
  representation for the crash.
115
119
  """
116
120
  for syslog_entry in OsTraceService(lockdown=self.lockdown).syslog():
117
- if (posixpath.basename(syslog_entry.filename) != 'osanalyticshelper') or \
118
- (posixpath.basename(syslog_entry.image_name) != 'OSAnalytics') or \
119
- not syslog_entry.message.startswith('Saved type '):
121
+ if (
122
+ (posixpath.basename(syslog_entry.filename) != "osanalyticshelper")
123
+ or (posixpath.basename(syslog_entry.image_name) != "OSAnalytics")
124
+ or not syslog_entry.message.startswith("Saved type ")
125
+ ):
120
126
  # skip non-ips creation syslog lines
121
127
  continue
122
128
 
123
129
  filename = posixpath.basename(syslog_entry.message.split()[-1])
124
- self.logger.debug(f'crash report: {filename}')
130
+ self.logger.debug(f"crash report: {filename}")
125
131
 
126
- if posixpath.splitext(filename)[-1] not in ('.ips', '.panic'):
132
+ if posixpath.splitext(filename)[-1] not in (".ips", ".panic"):
127
133
  continue
128
134
 
129
135
  while True:
@@ -141,8 +147,14 @@ class CrashReportsManager:
141
147
  else:
142
148
  yield crash_report
143
149
 
144
- def get_new_sysdiagnose(self, out: str, erase: bool = True, *, timeout: Optional[float] = None,
145
- callback: Optional[Callable[[float], None]] = None) -> None:
150
+ def get_new_sysdiagnose(
151
+ self,
152
+ out: str,
153
+ erase: bool = True,
154
+ *,
155
+ timeout: Optional[float] = None,
156
+ callback: Optional[Callable[[float], None]] = None,
157
+ ) -> None:
146
158
  """
147
159
  Monitor the creation of a newly created sysdiagnose archive and pull it
148
160
  :param out: filename
@@ -160,7 +172,7 @@ class CrashReportsManager:
160
172
  if callback is not None:
161
173
  callback(time.monotonic() - start_time)
162
174
 
163
- self.logger.info('sysdiagnose tarball creation has been started')
175
+ self.logger.info("sysdiagnose tarball creation has been started")
164
176
  self._wait_for_sysdiagnose_to_finish(timeout)
165
177
 
166
178
  if callback is not None:
@@ -173,17 +185,17 @@ class CrashReportsManager:
173
185
 
174
186
  def _wait_for_sysdiagnose_to_finish(self, end_time: Optional[float] = None) -> None:
175
187
  with NotificationProxyService(self.lockdown, timeout=end_time) as service:
176
- stop_notification = 'com.apple.sysdiagnose.sysdiagnoseStopped'
188
+ stop_notification = "com.apple.sysdiagnose.sysdiagnoseStopped"
177
189
  service.notify_register_dispatch(stop_notification)
178
190
  try:
179
191
  for event in service.receive_notification():
180
- if event['Name'] != stop_notification:
192
+ if event["Name"] != stop_notification:
181
193
  continue
182
- self.logger.debug(f'Received {event}')
194
+ self.logger.debug(f"Received {event}")
183
195
  time.sleep(IOS17_SYSDIAGNOSE_DELAY)
184
196
  break
185
197
  except NotificationTimeoutError as e:
186
- raise SysdiagnoseTimeoutError('Timeout waiting for sysdiagnose completion') from e
198
+ raise SysdiagnoseTimeoutError("Timeout waiting for sysdiagnose completion") from e
187
199
 
188
200
  def _get_new_sysdiagnose_filename(self, end_time: Optional[float] = None) -> str:
189
201
  sysdiagnose_filename = None
@@ -193,17 +205,19 @@ class CrashReportsManager:
193
205
  try:
194
206
  for filename in self.afc.listdir(SYSDIAGNOSE_DIR):
195
207
  # search for an IN_PROGRESS archive
196
- if filename not in excluded_temp_files and 'IN_PROGRESS_' in filename:
208
+ if filename not in excluded_temp_files and "IN_PROGRESS_" in filename:
197
209
  for ext in self.IN_PROGRESS_SYSDIAGNOSE_EXTENSIONS:
198
210
  if filename.endswith(ext):
199
- delta = self.lockdown.date - \
200
- self.afc.stat(posixpath.join(SYSDIAGNOSE_DIR, filename))['st_mtime']
211
+ delta = (
212
+ self.lockdown.date
213
+ - self.afc.stat(posixpath.join(SYSDIAGNOSE_DIR, filename))["st_mtime"]
214
+ )
201
215
  # Ignores IN_PROGRESS sysdiagnose files older than the defined time to live
202
216
  if delta.total_seconds() < SYSDIAGNOSE_IN_PROGRESS_MAX_TTL_SECS:
203
- self.logger.debug(f'Detected in progress sysdiagnose {filename}')
217
+ self.logger.debug(f"Detected in progress sysdiagnose {filename}")
204
218
  sysdiagnose_filename = filename.rsplit(ext)[0]
205
- sysdiagnose_filename = sysdiagnose_filename.replace('IN_PROGRESS_', '')
206
- sysdiagnose_filename = f'{sysdiagnose_filename}.tar.gz'
219
+ sysdiagnose_filename = sysdiagnose_filename.replace("IN_PROGRESS_", "")
220
+ sysdiagnose_filename = f"{sysdiagnose_filename}.tar.gz"
207
221
  return posixpath.join(SYSDIAGNOSE_DIR, sysdiagnose_filename)
208
222
  else:
209
223
  self.logger.warning(f"Old sysdiagnose temp file ignored {filename}")
@@ -212,7 +226,7 @@ class CrashReportsManager:
212
226
  pass
213
227
 
214
228
  if self._check_timeout(end_time):
215
- raise SysdiagnoseTimeoutError('Timeout finding in-progress sysdiagnose filename')
229
+ raise SysdiagnoseTimeoutError("Timeout finding in-progress sysdiagnose filename")
216
230
 
217
231
  def _check_timeout(self, end_time: Optional[float] = None) -> bool:
218
232
  return end_time is not None and time.monotonic() > end_time
@@ -222,16 +236,16 @@ class CrashReportsShell(AfcShell):
222
236
  @classmethod
223
237
  def create(cls, service_provider: LockdownServiceProvider, **kwargs):
224
238
  manager = CrashReportsManager(service_provider)
225
- XSH.ctx['_manager'] = manager
239
+ XSH.ctx["_manager"] = manager
226
240
  super(CrashReportsShell, CrashReportsShell).create(service_provider, service=manager.afc)
227
241
 
228
242
  def _setup_shell_commands(self):
229
243
  super()._setup_shell_commands()
230
- self._register_arg_parse_alias('parse', self._do_parse)
231
- self._register_arg_parse_alias('clear', self._do_clear)
244
+ self._register_arg_parse_alias("parse", self._do_parse)
245
+ self._register_arg_parse_alias("clear", self._do_clear)
232
246
 
233
247
  def _do_parse(self, filename: Annotated[str, Arg(completer=path_completer)]) -> None:
234
248
  print(get_crash_report_from_buf(self.afc.get_file_contents(filename).decode(), filename=filename))
235
249
 
236
250
  def _do_clear(self) -> None:
237
- XSH.ctx['_manager'].clear()
251
+ XSH.ctx["_manager"].clear()
@@ -7,14 +7,14 @@ CHUNK_SIZE = 200
7
7
 
8
8
 
9
9
  class DebugServerAppList(LockdownService):
10
- SERVICE_NAME = 'com.apple.debugserver.DVTSecureSocketProxy.applist'
10
+ SERVICE_NAME = "com.apple.debugserver.DVTSecureSocketProxy.applist"
11
11
 
12
12
  def __init__(self, lockdown: LockdownClient):
13
13
  super().__init__(lockdown, self.SERVICE_NAME)
14
14
 
15
15
  def get(self) -> dict:
16
- buf = b''
17
- while b'</plist>' not in buf:
16
+ buf = b""
17
+ while b"</plist>" not in buf:
18
18
  buf += self.service.recv(CHUNK_SIZE)
19
19
 
20
20
  return plistlib.loads(buf)
@@ -4,25 +4,25 @@ from pymobiledevice3.services.lockdown_service import LockdownService
4
4
 
5
5
 
6
6
  class DtDeviceArbitration(LockdownService):
7
- SERVICE_NAME = 'com.apple.dt.devicearbitration'
7
+ SERVICE_NAME = "com.apple.dt.devicearbitration"
8
8
 
9
9
  def __init__(self, lockdown: LockdownClient):
10
10
  super().__init__(lockdown, self.SERVICE_NAME, is_developer_service=True)
11
11
 
12
12
  @property
13
13
  def version(self) -> dict:
14
- return self.service.send_recv_plist({'command': 'version'})
14
+ return self.service.send_recv_plist({"command": "version"})
15
15
 
16
16
  def check_in(self, hostname: str, force: bool = False):
17
- request = {'command': 'check-in', 'hostname': hostname}
17
+ request = {"command": "check-in", "hostname": hostname}
18
18
  if force:
19
- request['command'] = 'force-check-in'
19
+ request["command"] = "force-check-in"
20
20
  response = self.service.send_recv_plist(request)
21
- if response.get('result') != 'success':
21
+ if response.get("result") != "success":
22
22
  raise DeviceAlreadyInUseError(response)
23
23
 
24
24
  def check_out(self):
25
- request = {'command': 'check-out'}
25
+ request = {"command": "check-out"}
26
26
  response = self.service.send_recv_plist(request)
27
- if response.get('result') != 'success':
28
- raise ArbitrationError(f'failed with: {response}')
27
+ if response.get("result") != "success":
28
+ raise ArbitrationError(f"failed with: {response}")