pymobiledevice3 7.0.7__py3-none-any.whl → 7.1.0__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.
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '7.0.7'
32
- __version_tuple__ = version_tuple = (7, 0, 7)
31
+ __version__ = version = '7.1.0'
32
+ __version_tuple__ = version_tuple = (7, 1, 0)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -6,7 +6,9 @@ import struct
6
6
  import subprocess
7
7
  import sys
8
8
  from pathlib import Path
9
+ from tempfile import TemporaryDirectory
9
10
  from typing import Annotated, Optional
11
+ from zipfile import ZipFile
10
12
 
11
13
  import typer
12
14
  from packaging.version import Version
@@ -80,9 +82,9 @@ def debugserver_start_server(service_provider: ServiceProviderDep, local_port: O
80
82
  @cli.command("lldb")
81
83
  def debugserver_lldb(
82
84
  service_provider: RSDServiceProviderDep,
83
- xcodeproj_path: Annotated[
85
+ project_or_ipa_path: Annotated[
84
86
  Path,
85
- typer.Argument(exists=True, file_okay=False, dir_okay=True),
87
+ typer.Argument(exists=True, file_okay=True, dir_okay=True),
86
88
  ],
87
89
  configuration: Annotated[
88
90
  str,
@@ -106,11 +108,12 @@ def debugserver_lldb(
106
108
  ] = None,
107
109
  ) -> None:
108
110
  """
109
- Automate lldb launch for a given xcodeproj.
111
+ Automate lldb launch for a given xcodeproj or IPA.
110
112
 
111
113
  \b
112
114
  This will:
113
- - Build the given xcodeproj
115
+ - Build the given xcodeproj (if provided)
116
+ - Extract the given IPA (if provided)
114
117
  - Install it
115
118
  - Start a debugserver attached to it
116
119
  - Place breakpoints if given any
@@ -123,122 +126,154 @@ def debugserver_lldb(
123
126
  return
124
127
 
125
128
  commands = []
126
- with local.cwd(xcodeproj_path.parent):
127
- logger.info(f"Building {xcodeproj_path} for {configuration} configuration")
128
- local["xcodebuild"]["-configuration", configuration, "build"]()
129
- local_app = next(iter(Path(f"build/{configuration}-iphoneos").glob("*.app")))
130
- logger.info(f"Using app: {local_app}")
131
-
132
- info_plist_path = local_app / "Info.plist"
133
- info_plist = plistlib.loads(info_plist_path.read_bytes())
134
- bundle_identifier = info_plist["CFBundleIdentifier"]
135
- logger.info(f"Bundle identifier: {bundle_identifier}")
136
-
137
- commands.append("platform select remote-ios")
138
- commands.append(f'target create "{local_app.absolute()}"')
139
-
140
- with InstallationProxyService(create_using_usbmux()) as installation_proxy:
141
- logger.info("Installing app")
142
- installation_proxy.install_from_local(local_app)
143
- remote_path = installation_proxy.get_apps(bundle_identifiers=[bundle_identifier])[bundle_identifier]["Path"]
144
- logger.info(f"Remote path: {remote_path}")
129
+ temp_dir = None
130
+ local_app = None
131
+ install_source = None
132
+
133
+ if project_or_ipa_path.suffix == ".xcodeproj":
134
+ with local.cwd(project_or_ipa_path.parent):
135
+ logger.info(f"Building {project_or_ipa_path} for {configuration} configuration")
136
+ local["xcodebuild"]["-project", str(project_or_ipa_path), "-configuration", configuration, "build"]()
137
+ app_candidates = [app for app in Path("build").rglob("*.app") if (app / "Info.plist").exists()]
138
+ if not app_candidates:
139
+ logger.error("No built .app with Info.plist found under build/.")
140
+ return
141
+ app_candidates.sort(key=lambda p: p.stat().st_mtime, reverse=True)
142
+ local_app = app_candidates[0].absolute()
143
+ install_source = local_app
144
+ elif project_or_ipa_path.suffix == ".ipa":
145
+ temp_dir = TemporaryDirectory()
146
+ with ZipFile(project_or_ipa_path, "r") as ipa_zip:
147
+ ipa_zip.extractall(temp_dir.name)
148
+ payload_dir = Path(temp_dir.name) / "Payload"
149
+ apps = list(payload_dir.glob("*.app"))
150
+ if not apps:
151
+ logger.error("No .app bundle found in IPA Payload.")
152
+ temp_dir.cleanup()
153
+ return
154
+ if len(apps) > 1:
155
+ logger.error("Multiple .app bundles found in IPA Payload; please provide a single-app IPA.")
156
+ temp_dir.cleanup()
157
+ return
158
+ local_app = apps[0].absolute()
159
+ install_source = project_or_ipa_path
160
+ else:
161
+ logger.error("Expected an .xcodeproj directory or an .ipa file.")
162
+ return
163
+
164
+ logger.info(f"Using app: {local_app}")
165
+
166
+ info_plist_path = local_app / "Info.plist"
167
+ info_plist = plistlib.loads(info_plist_path.read_bytes())
168
+ bundle_identifier = info_plist["CFBundleIdentifier"]
169
+ logger.info(f"Bundle identifier: {bundle_identifier}")
145
170
 
171
+ commands.append("platform select remote-ios")
172
+ commands.append(f'target create "{local_app.absolute()}"')
173
+
174
+ with InstallationProxyService(create_using_usbmux()) as installation_proxy:
175
+ logger.info("Installing app")
176
+ installation_proxy.install_from_local(install_source)
177
+ remote_path = installation_proxy.get_apps(bundle_identifiers=[bundle_identifier])[bundle_identifier]["Path"]
178
+ logger.info(f"Remote path: {remote_path}")
146
179
  commands.append(f'script lldb.target.module[0].SetPlatformFileSpec(lldb.SBFileSpec("{remote_path}"))')
147
180
 
148
- debugserver_port = service_provider.get_service_port("com.apple.internal.dt.remote.debugproxy")
181
+ debugserver_port = service_provider.get_service_port("com.apple.internal.dt.remote.debugproxy")
182
+
183
+ # Add connection and launch commands
184
+ commands.append(f"process connect connect://[{service_provider.service.address[0]}]:{debugserver_port}")
149
185
 
150
- # Add connection and launch commands
151
- commands.append(f"process connect connect://[{service_provider.service.address[0]}]:{debugserver_port}")
186
+ if breakpoints:
187
+ for bp in breakpoints:
188
+ commands.append(f'breakpoint set -n "{bp}"')
152
189
 
153
- if breakpoints:
154
- for bp in breakpoints:
155
- commands.append(f'breakpoint set -n "{bp}"')
190
+ if launch:
191
+ commands.append("process launch")
156
192
 
157
- if launch:
158
- commands.append("process launch")
193
+ if user_commands:
194
+ # Add user commands
195
+ commands += user_commands
159
196
 
160
- if user_commands:
161
- # Add user commands
162
- commands += user_commands
197
+ logger.info("Starting lldb with automated setup and connection")
163
198
 
164
- logger.info("Starting lldb with automated setup and connection")
199
+ # Works only on unix-based systems, so keep these imports here
200
+ import fcntl
201
+ import pty
202
+ import select as select_module
203
+ import termios
204
+ import tty
165
205
 
166
- # Works only on unix-based systems, so keep these imports here
167
- import fcntl
168
- import pty
169
- import select as select_module
170
- import termios
171
- import tty
206
+ master, slave = pty.openpty()
172
207
 
173
- master, slave = pty.openpty()
208
+ process = None # Initialize process variable for signal handler
174
209
 
175
- process = None # Initialize process variable for signal handler
210
+ # Copy terminal size from the current terminal to PTY
211
+ def resize_pty() -> None:
212
+ """Update PTY size to match current terminal size"""
213
+ size = struct.unpack(
214
+ "HHHH", fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, struct.pack("HHHH", 0, 0, 0, 0))
215
+ )
216
+ fcntl.ioctl(master, termios.TIOCSWINSZ, struct.pack("HHHH", *size))
217
+ # Send SIGWINCH to the child process to notify it of the resize
218
+ if process is not None and process.poll() is None:
219
+ process.send_signal(signal.SIGWINCH)
176
220
 
177
- # Copy terminal size from the current terminal to PTY
178
- def resize_pty() -> None:
179
- """Update PTY size to match current terminal size"""
180
- size = struct.unpack(
181
- "HHHH", fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, struct.pack("HHHH", 0, 0, 0, 0))
182
- )
183
- fcntl.ioctl(master, termios.TIOCSWINSZ, struct.pack("HHHH", *size))
184
- # Send SIGWINCH to the child process to notify it of the resize
185
- if process is not None and process.poll() is None:
186
- process.send_signal(signal.SIGWINCH)
221
+ # Initial resize
222
+ resize_pty()
187
223
 
188
- # Initial resize
224
+ # Set up signal handler for window resize
225
+ def handle_sigwinch(signum, frame):
189
226
  resize_pty()
190
227
 
191
- # Set up signal handler for window resize
192
- def handle_sigwinch(signum, frame):
193
- resize_pty()
228
+ old_sigwinch_handler = signal.signal(signal.SIGWINCH, handle_sigwinch)
194
229
 
195
- old_sigwinch_handler = signal.signal(signal.SIGWINCH, handle_sigwinch)
230
+ # Save original terminal settings
231
+ old_tty = termios.tcgetattr(sys.stdin)
196
232
 
197
- # Save original terminal settings
198
- old_tty = termios.tcgetattr(sys.stdin)
233
+ try:
234
+ # Set TERM environment variable to enable colors
235
+ env = os.environ.copy()
236
+ env["TERM"] = os.environ.get("TERM", "xterm-256color")
199
237
 
200
- try:
201
- # Set TERM environment variable to enable colors
202
- env = os.environ.copy()
203
- env["TERM"] = os.environ.get("TERM", "xterm-256color")
238
+ process = subprocess.Popen([lldb_command], stdin=slave, stdout=slave, stderr=slave, env=env)
239
+ os.close(slave)
204
240
 
205
- process = subprocess.Popen([lldb_command], stdin=slave, stdout=slave, stderr=slave, env=env)
206
- os.close(slave)
241
+ # Put terminal in raw mode for proper interaction
242
+ tty.setraw(sys.stdin.fileno())
243
+ # Send all commands through stdin
244
+ for command in commands:
245
+ os.write(master, (command + "\n").encode())
207
246
 
208
- # Put terminal in raw mode for proper interaction
209
- tty.setraw(sys.stdin.fileno())
210
- # Send all commands through stdin
211
- for command in commands:
212
- os.write(master, (command + "\n").encode())
247
+ # Now redirect stdin from the terminal to lldb so user can interact
248
+ while True:
249
+ rlist, _, _ = select_module.select([sys.stdin, master], [], [])
213
250
 
214
- # Now redirect stdin from the terminal to lldb so user can interact
215
- while True:
216
- rlist, _, _ = select_module.select([sys.stdin, master], [], [])
251
+ if sys.stdin in rlist:
252
+ # User typed something
253
+ data = os.read(sys.stdin.fileno(), 1024)
254
+ if not data:
255
+ break
256
+ os.write(master, data)
217
257
 
218
- if sys.stdin in rlist:
219
- # User typed something
220
- data = os.read(sys.stdin.fileno(), 1024)
258
+ if master in rlist:
259
+ # lldb has output
260
+ try:
261
+ data = os.read(master, 1024)
221
262
  if not data:
222
263
  break
223
- os.write(master, data)
224
-
225
- if master in rlist:
226
- # lldb has output
227
- try:
228
- data = os.read(master, 1024)
229
- if not data:
230
- break
231
- os.write(sys.stdout.fileno(), data)
232
- except OSError:
233
- break
234
- except (KeyboardInterrupt, OSError):
235
- pass
236
- finally:
237
- # Restore terminal settings
238
- termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty)
239
- # Restore original SIGWINCH handler
240
- signal.signal(signal.SIGWINCH, old_sigwinch_handler)
241
- os.close(master)
242
- if process is not None:
243
- process.terminate()
244
- process.wait()
264
+ os.write(sys.stdout.fileno(), data)
265
+ except OSError:
266
+ break
267
+ except (KeyboardInterrupt, OSError):
268
+ pass
269
+ finally:
270
+ # Restore terminal settings
271
+ termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty)
272
+ # Restore original SIGWINCH handler
273
+ signal.signal(signal.SIGWINCH, old_sigwinch_handler)
274
+ os.close(master)
275
+ if process is not None:
276
+ process.terminate()
277
+ process.wait()
278
+ if temp_dir is not None:
279
+ temp_dir.cleanup()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pymobiledevice3
3
- Version: 7.0.7
3
+ Version: 7.1.0
4
4
  Summary: Pure python3 implementation for working with iDevices (iPhone, etc...)
5
5
  Author-email: doronz88 <doron88@gmail.com>, matan <matan1008@gmail.com>
6
6
  Maintainer-email: doronz88 <doron88@gmail.com>, matan <matan1008@gmail.com>
@@ -8,7 +8,7 @@ misc/understanding_idevice_protocol_layers.md,sha256=FMJQ-ik2j9kFLPS15JzDZg62uk1
8
8
  misc/usbmux_sniff.sh,sha256=iWtbucOEQ9_UEFXk9x-2VNt48Jg5zrPsnUbZ_LfZxwA,212
9
9
  pymobiledevice3/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  pymobiledevice3/__main__.py,sha256=Ogsyro4MUVVSciEtg8kZufL81AHcjuphItnaL0BT10Q,16386
11
- pymobiledevice3/_version.py,sha256=0c2yrCNjkahQT4hAahQeRLH51KQrBSwCjA0jYBtvoiI,704
11
+ pymobiledevice3/_version.py,sha256=hoQMYVAGxOg65i7V5PoVLpY46qxSMJVk6GEsfI-0lRI,704
12
12
  pymobiledevice3/bonjour.py,sha256=lmx3uCaiolF59AieftmTKqktNod5ZDyJ06oRZdYKHSE,13681
13
13
  pymobiledevice3/ca.py,sha256=5_Y4F-zDFX_KeDL-M_TRCKKyrRRb9h1lBE8MGTWv91o,10606
14
14
  pymobiledevice3/common.py,sha256=FZzF0BQYV5fCEUPbLo6jbt2Ig9s5YwR8AvX_iR124Ew,329
@@ -52,7 +52,7 @@ pymobiledevice3/cli/developer/__init__.py,sha256=idQOgVsitt3nfolyhZuA6KHN2j44JNV
52
52
  pymobiledevice3/cli/developer/arbitration.py,sha256=7Eod6-dlSjAMBljbsIGHL_QG9LFbOPIXqpS9Y_4-Q3Y,1576
53
53
  pymobiledevice3/cli/developer/condition.py,sha256=lb5BS8lVcgsb2yBgBsN2nK-j2u1_iVm_tBd1pSRHZB0,1294
54
54
  pymobiledevice3/cli/developer/core_device.py,sha256=QzMVfghq3fKLI2NQ2Kg7r_zyFIWGCBt7ZS-GTLWDZs4,10895
55
- pymobiledevice3/cli/developer/debugserver.py,sha256=4_BiZi69i31t6j7gpGEvt9m8Zo0yJsBQH7zQySyr68M,9315
55
+ pymobiledevice3/cli/developer/debugserver.py,sha256=0hP6-x3xChcYCacxI7CuEWEsM8-u4i4GNYa1HeSMPXg,10464
56
56
  pymobiledevice3/cli/developer/fetch_symbols.py,sha256=57HzVyUJPXL_I8EiqWArJ2Fczb2yX9y6nDMgjZcADo0,4353
57
57
  pymobiledevice3/cli/developer/simulate_location.py,sha256=IVJQPYKUrQcsdpCxf2D2sgy6Nb3mUOFvK-Og_y5lfDM,1555
58
58
  pymobiledevice3/cli/developer/accessibility/__init__.py,sha256=VpJSdnkEZhUuJXWy08wqgqObXv1PgZ2Ht4qy-560QRk,2271
@@ -180,9 +180,9 @@ pymobiledevice3/services/web_protocol/switch_to.py,sha256=TCdVrMfsvd18o-vZ0owVrE
180
180
  pymobiledevice3/tunneld/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
181
181
  pymobiledevice3/tunneld/api.py,sha256=Lwl1OdhPTgX6Zqezy8T4dEcXRfaEPwyGNClioTx3fUc,2338
182
182
  pymobiledevice3/tunneld/server.py,sha256=dMEZAv_X-76l0vSalpq4x0IVkbE-MNGR77T-u1TiHuE,25752
183
- pymobiledevice3-7.0.7.dist-info/licenses/LICENSE,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
184
- pymobiledevice3-7.0.7.dist-info/METADATA,sha256=vRzzMNV_Ahuu1AO2zkZFm2oZRI2MpNBaxUirarK3dd8,17500
185
- pymobiledevice3-7.0.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
186
- pymobiledevice3-7.0.7.dist-info/entry_points.txt,sha256=jJMlOanHlVwUxcY__JwvKeWPrvBJr_wJyEq4oHIZNKE,66
187
- pymobiledevice3-7.0.7.dist-info/top_level.txt,sha256=MjZoRqcWPOh5banG-BbDOnKEfsS3kCxqV9cv-nzyg2Q,21
188
- pymobiledevice3-7.0.7.dist-info/RECORD,,
183
+ pymobiledevice3-7.1.0.dist-info/licenses/LICENSE,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
184
+ pymobiledevice3-7.1.0.dist-info/METADATA,sha256=p_l-eHzSpqafrRTx3bUX1_OZEK_Af0-Mtfm34H6RFcw,17500
185
+ pymobiledevice3-7.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
186
+ pymobiledevice3-7.1.0.dist-info/entry_points.txt,sha256=jJMlOanHlVwUxcY__JwvKeWPrvBJr_wJyEq4oHIZNKE,66
187
+ pymobiledevice3-7.1.0.dist-info/top_level.txt,sha256=MjZoRqcWPOh5banG-BbDOnKEfsS3kCxqV9cv-nzyg2Q,21
188
+ pymobiledevice3-7.1.0.dist-info/RECORD,,