Skip to content

Releases: doronz88/pymobiledevice3

v10.10.3

Choose a tag to compare

@doronz88 doronz88 released this 21 Aug 12:18
62e2957

Highlights

🐛 DTX/XCTest payloads decode correctly under Python 3.15 lazy imports

On Python 3.15+ the CLI and test suite run with PEP 810 lazy imports. The NSKeyedArchive proxy classes used to decode DTX and XCTest payloads were registered with bpylist2 only as an import-time side effect, and a lazily-imported module body does not run until one of its names is actually used. Decode paths that reach archiver.unarchive() without touching such a name therefore saw an unpopulated class map and silently fell back to plistlib, logging a warning per message and returning the raw archive dict instead of the decoded object.

Two independent cases were affected and are now fixed by registering the classes through an explicit, idempotent call instead of relying on the import side effect:

  • DTX tap messages (DTSysmonTapMessage, DTTapHeartbeatMessage, ...) — surfaced as decode warnings on developer dvt sysmon and other tap-based commands.
  • XCTest result types (XCTIssue, XCActivityRecord, XCTestConfiguration, ...) — XCUITest results decoded to raw plist dicts instead of typed objects.
# Previously noisy on Python 3.15; now clean:
pymobiledevice3 developer dvt sysmon process single -k execName

This only affected Python 3.15; Python ≤ 3.14 (eager imports) was never impacted.

What's Changed

Full Changelog: v10.10.2...v10.10.3

v10.10.2

Choose a tag to compare

@doronz88 doronz88 released this 21 Aug 07:43
a302e0c

Highlights

🐛 usbmux connect() no longer hands back a blocking socket

MuxConnection.connect() used to flip the socket back to blocking mode right before returning it — a leftover from the old synchronous API that survived the asyncio migration. Every consumer drives that socket through asyncio, where a blocking socket can stall the entire event loop (and raises ValueError: the socket must be non-blocking in asyncio debug mode). The socket is now returned non-blocking, matching the async contract of MuxDevice.connect(). If you consume this API directly and read from the raw socket synchronously, call sock.setblocking(True) yourself.

🐛 WDA reachability probe no longer leaks its socket

developer wda probes the WDA port through usbmuxd while waiting for the runner to come up; the probe socket (and its usbmuxd connection) was discarded without being closed. It is now closed as soon as the probe succeeds.

What's Changed

Full Changelog: v10.10.1...v10.10.2

v10.10.1

Choose a tag to compare

@doronz88 doronz88 released this 20 Aug 20:52
593dbd4

Highlights

🐛 TSS requests now send Expect: 100-continue

TSS requests (SHSH blob fetching and restore/update signing) used to send an empty Expect header, which some HTTP proxies — corporate proxies in particular — choke on. The header now carries its only valid value, 100-continue, so restore update/restore erase/restore tss work behind such proxies. Verified against Apple's production TSS server with a full on-device update.

What's Changed

New Contributors

Full Changelog: v10.10.0...v10.10.1

v10.10.0

Choose a tag to compare

@doronz88 doronz88 released this 20 Aug 08:53
6e42f2c

Highlights

✨ Filter syslog live by os_log subsystem/category

New first-class -s/--subsystem and -c/--category options for syslog live match directly on the parsed os_log label, replacing the previous brittle combination of --label plus --match on the rendered [subsystem][category] substring:

# Only WebKit subsystems
pymobiledevice3 syslog live -s 'com.apple.WebKit*'

# Networking subsystem, connection category only
pymobiledevice3 syslog live -s com.apple.network -c connection

Patterns are case-insensitive globs; each option is repeatable (OR within an option, AND across the two), entries without a subsystem/category label are dropped when either filter is set, and the filters apply to both text and JSON output.

📝 Claude Code plugin renamed to pymobiledevice3-device-operator

The Claude Code plugin shipped from this repo's marketplace is now named pymobiledevice3-device-operator (matching the skill it vendors), and the README now advertises how to install it:

/plugin marketplace add doronz88/pymobiledevice3
/plugin install pymobiledevice3-device-operator@pymobiledevice3

What's Changed

Full Changelog: v10.9.0...v10.10.0

v10.9.0

Choose a tag to compare

@doronz88 doronz88 released this 18 Aug 19:58
5ab6e80

Highlights

Mount AFC filesystems in Finder over WebDAV

A new webdav subcommand under each AFC-backed group serves the device's AFC filesystem over a local WebDAV server, so macOS Finder (or any WebDAV client) can browse and edit it read-write as a mounted volume:

  • pymobiledevice3 afc webdav [PATH] [--mount]
  • pymobiledevice3 apps webdav BUNDLE_ID [PATH] [--documents] [--mount]
  • pymobiledevice3 crash webdav [PATH] [--mount]

--mount mounts and reveals the volume using the host's native mechanism (mount_webdav on macOS, net use on Windows, gio on Linux), naming the mount point pmd-<udid>-<service>-<rand> so it's easy to spot; otherwise the URL is printed for manual mounting. Requires Python ≥ 3.10.

See the new guide: Mounting AFC in Finder (WebDAV).

Backup: avoid BackupAgent2 OOM on large files

device_link now chunks download_files transfers at 32 KiB, preventing an out-of-memory condition in BackupAgent2 when backing up large files (#1858).

v10.8.1

Choose a tag to compare

@doronz88 doronz88 released this 17 Aug 07:20
e83c65b

Highlights

⚡ Faster library imports: IPython is no longer imported at module scope

pymobiledevice3.utils imported IPython at module scope, but only uses it in one place — start_ipython_shell(). Since pymobiledevice3.lockdown imports utils, every library consumer opening a lockdown connection paid to import a REPL it never starts. The import is now deferred to start_ipython_shell() itself, cutting from pymobiledevice3.lockdown import create_using_usbmux from ~180 ms to ~125 ms (Python 3.13). Unlike the PEP 810 lazy-imports mode (which is CLI-only and requires Python 3.15+), this speedup applies to library consumers on every Python version.

What's Changed

New Contributors

Full Changelog: v10.8.0...v10.8.1

v10.8.0

Choose a tag to compare

@doronz88 doronz88 released this 16 Aug 18:03
74faf40

Highlights

✨ ~2.4x faster CLI startup on Python 3.15 (PEP 810 lazy imports)

On Python 3.15+, the CLI now runs with PEP 810 lazy imports, scoped via sys.set_lazy_imports_filter() so that only pymobiledevice3's own imports are deferred — heavy dependency chains (pykdebugparser, fastapi, IPython, qh3, cryptography) load on first use instead of at startup, while third-party packages' internals stay eager (unscoped process-wide laziness is known to break import hooks such as pytest's assertion rewriter). Measured on 3.15.0b3 with a device connected: pymobiledevice3 --help drops from 0.55s to 0.23s and pymobiledevice3 usbmux list from 0.53s to 0.39s (the remainder is device I/O). On Python <= 3.14 the shim is a no-op — the 3.9 floor is unchanged, and nothing needs configuring either way:

uvx -p 3.15 pymobiledevice3 --help   # lazy imports engage automatically on 3.15+

As a library, pymobiledevice3 never changes a host process's import semantics: importing pymobiledevice3.* from your own code stays eager. App authors who own their process can opt in with the same three-line scoped snippet, now documented in the Python API guide. The full test suite was validated against a physical device with lazy mode active — results identical to eager.

🧪 Python 3.15 in the CI matrix + trove classifier

The 3.15 prerelease joined the test matrix on all three platforms — every PR now exercises the scoped lazy-imports mode end to end (the test suite enables it via tests/conftest.py, mirroring what the CLI ships) — and the package now declares the Programming Language :: Python :: 3.15 classifier.

What's Changed

Full Changelog: v10.7.4...v10.8.0

v10.7.4

Choose a tag to compare

@doronz88 doronz88 released this 13 Aug 06:18
f24499a

Highlights

🐛 Userspace tunnel: abandoned service connections no longer leak

A systematic stability audit of the userspace tunnel (measured live on a physical device) found that service connections abandoned by their client — while the device neither sent a byte nor closed its side — parked their relay handler at recv() forever: 24 of 30 abandoned connections leaked their handler task, network socket, and TCP session until the tunnel was torn down, accumulating without bound in long-lived sessions. The relay now fully closes the tunnel-side socket on client EOF, so such connections are reaped by the stack's orphan timer and the census is 0 leaked tasks. Throughput and latency are unchanged.

🐛 Userspace tunnel: pmd-pytcp floor raised to 0.3.7 (complete stability-audit series)

The same audit produced four same-day pmd-pytcp releases (0.3.4–0.3.7) fixing every confirmed way the tunnel's TCP/IP stack could strand a session, port, waiter, or timer — including a neighbor-discovery black hole where a device that slept for ~3 seconds could kill the tunnel until process restart, datapath loops that died silently on their first exception, per-connection 1 kHz timer spins, and unbounded reassembly/out-of-order/receive queues. The dependency floor now guarantees every install carries the full series.

🐛 AccessibilityAudit works over RSD

pymobiledevice3 accessibility commands hung (or died with ConnectionTerminatedError) when connected over RSD: the axAuditDaemon.remoteserver.shim.remote shim resets the connection unless the RSDCheckin handshake is performed first. RSD connections to .shim.remote DTX services are now routed through the lockdown check-in, so the audit services connect reliably on iOS 17+. Thanks @navin772!

pymobiledevice3 accessibility settings show

What's Changed

Full Changelog: v10.7.3...v10.7.4

v10.7.3

Choose a tag to compare

@doronz88 doronz88 released this 11 Aug 07:01
408a6f6

Highlights

🐛 Fixed TSS Yonkers component selection (iOS 27 restores)

iOS 27 beta manifests add a Yonkers,SepObject entry that carries neither EPRO nor FabRevision, so it slipped past the selection filters and got signed instead of the correct Yonkers,SysTopPatch* component — failing the restore. The selector is now restricted to SysTopPatch entries (mirroring libimobiledevice/libtatsu#8).

While verifying that against real BuildManifests, a second bug turned up: EPRO=False entries skipped the production-mode filter entirely, so production devices could select the dev-fused SEP patch component. Both are fixed and covered by unit tests modeled on the real iPhone17,1 iOS 27 beta manifest.

What's Changed

Full Changelog: v10.7.2...v10.7.3

v10.7.2

Choose a tag to compare

@doronz88 doronz88 released this 10 Aug 05:11
e371828

Highlights

🐛 Fix Recovery Mode device detection on Windows

The USB scan for Recovery/DFU devices read the manufacturer string descriptor of every enumerated USB device, which requires opening it. On Windows, unrelated devices without a libusb-compatible driver raise NotImplementedError on open, aborting the whole scan before ever reaching a connected Apple device — making pymobiledevice3 restore commands fail to find an iPhone in Recovery Mode.

Devices are now matched by their VID/PID (cached at enumeration, no open required), so unrelated devices are never touched at all, and Apple devices that can't be opened (missing driver/permissions) are skipped instead of crashing the scan.

What's Changed

Full Changelog: v10.7.1...v10.7.2