Releases: doronz88/pymobiledevice3
Release list
v10.10.3
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 ondeveloper dvt sysmonand 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 execNameThis only affected Python 3.15; Python ≤ 3.14 (eager imports) was never impacted.
What's Changed
- 03a0e3c dtx: Register NSKeyedArchive classes explicitly for lazy imports (#1864) (@doronz88)
- 264429b dtx: Register XCTest NSKeyedArchive classes explicitly for lazy imports (#1864) (@doronz88)
Full Changelog: v10.10.2...v10.10.3
v10.10.2
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
- 48e2f1a usbmux: don't flip the returned socket back to blocking mode (#1863) (@doronz88)
- 7d1492c wda: close the reachability-probe socket on success (#1863) (@doronz88)
Full Changelog: v10.10.1...v10.10.2
v10.10.1
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
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 connectionPatterns 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@pymobiledevice3What's Changed
- 804561e feat(syslog): add --subsystem/--category filters to
syslog live(#1861) (@doronz88) - 09ca5a7 docs: advertise the Claude Code plugin in README (#1860) (@doronz88)
- 677f9d6 claude-plugin: rename plugin to pymobiledevice3-device-operator (#1860) (@doronz88)
Full Changelog: v10.9.0...v10.10.0
v10.9.0
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
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
- 2929f11 utils: import IPython inside start_ipython_shell (#1856) (@GreenglassT)
New Contributors
- @GreenglassT made their first contribution in #1856
Full Changelog: v10.8.0...v10.8.1
v10.8.0
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
- 79605bf build: declare Python 3.15 support in trove classifiers (#1855) (@doronz88)
- fab3263 docs: document the scoped lazy-imports mode for contributors and library users (#1855) (@doronz88)
- 161ea02 ci: add the Python 3.15 prerelease to the test matrix (#1855) (@doronz88)
- 043e30a cli: adopt PEP 810 lazy imports on Python 3.15+ (no-op on older versions) (#1855) (@doronz88)
Full Changelog: v10.7.4...v10.8.0
v10.7.4
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 showWhat's Changed
- aa921d5 build: raise the pmd-pytcp floor to 0.3.7 (complete stability-audit series) (#1853) (@doronz88)
- 453347f userspace-tunnel: fully close abandoned relay sockets; consume pmd-pytcp 0.3.4 (#1852) (@doronz88)
- 3cfdfe5 dtx: Perform RSDCheckin for shim.remote DTX services (#1850) (@navin772)
Full Changelog: v10.7.3...v10.7.4
v10.7.3
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
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