From 717fd741f374b4b04cee9bee07cfecfa77a59b69 Mon Sep 17 00:00:00 2001 From: doronz88 Date: Thu, 4 Jul 2024 08:45:52 +0300 Subject: [PATCH] cli: refactor command group usages to provide a TL;DR --- README.md | 52 ++++++++++----------- pymobiledevice3/__main__.py | 6 +++ pymobiledevice3/cli/activation.py | 7 ++- pymobiledevice3/cli/afc.py | 7 ++- pymobiledevice3/cli/amfi.py | 7 ++- pymobiledevice3/cli/apps.py | 7 ++- pymobiledevice3/cli/backup.py | 7 ++- pymobiledevice3/cli/bonjour.py | 7 ++- pymobiledevice3/cli/companion_proxy.py | 7 ++- pymobiledevice3/cli/crash.py | 7 ++- pymobiledevice3/cli/developer.py | 21 ++++----- pymobiledevice3/cli/diagnostics.py | 21 ++++----- pymobiledevice3/cli/lockdown.py | 7 ++- pymobiledevice3/cli/mounter.py | 7 ++- pymobiledevice3/cli/notification.py | 7 ++- pymobiledevice3/cli/pcap.py | 11 ++--- pymobiledevice3/cli/power_assertion.py | 9 ++-- pymobiledevice3/cli/processes.py | 7 ++- pymobiledevice3/cli/profile.py | 7 ++- pymobiledevice3/cli/provision.py | 7 ++- pymobiledevice3/cli/remote.py | 7 ++- pymobiledevice3/cli/restore.py | 7 ++- pymobiledevice3/cli/springboard.py | 3 +- pymobiledevice3/cli/syslog.py | 7 ++- pymobiledevice3/cli/usbmux.py | 7 ++- pymobiledevice3/cli/version.py | 7 ++- pymobiledevice3/cli/webinspector.py | 7 ++- pymobiledevice3/services/power_assertion.py | 1 + 28 files changed, 123 insertions(+), 141 deletions(-) diff --git a/README.md b/README.md index 9e6bf6c7e..929eeab68 100644 --- a/README.md +++ b/README.md @@ -139,37 +139,37 @@ subcommand. This also means that every feature which isn't there won't require i This is the main CLI usage: ``` -Usage: python -m pymobiledevice3 [OPTIONS] COMMAND [ARGS]... +Usage: pymobiledevice3 [OPTIONS] COMMAND [ARGS]... Options: -h, --help Show this message and exit. Commands: - activation activation options - afc FileSystem utils - amfi amfi options - apps application options - backup2 backup utils - bonjour bonjour options - companion companion options - crash crash report options - developer developer options. - diagnostics diagnostics options - lockdown lockdown options - mounter mounter options - notification notification options - pcap sniff device traffic - power-assertion Create a power assertion (wraps... - processes processes cli - profile profile options - provision provision options - remote remote options - restore restore options - springboard springboard options - syslog syslog options - usbmux usbmuxd options - webinspector webinspector options - version get installed package version + activation Activate/Deactivate the device or query its state + afc Manage device multimedia files + amfi Enable/Disable developer-mode or query its state + apps Manage installed applications + backup2 Backup/Restore options + bonjour Browse devices over bonjour + companion List paired "companion" devices + crash Manage crash reports + developer Perform developer operations + diagnostics Reboot/Shutdown device or diagnostics services + lockdown Pair/Unpair device or access other lockdown services + mounter Mount/Umount DeveloperDiskImage or query related info + notification Post/Observe notifications + pcap Sniff device traffic + power-assertion Create a power assertion + processes View process list using diagnosticsd API + profile Managed installed profiles or install SSL certificates + provision Manage installed provision profiles + remote Create RemoteXPC tunnels + restore Restore an IPSW or access device in recovery mode + springboard Access device UI + syslog Watch syslog messages + usbmux List devices or forward a TCP port + webinspector Access webinspector services + version Query pymobiledevice3 version ``` ### Working with developer tools (iOS >= 17.0) diff --git a/pymobiledevice3/__main__.py b/pymobiledevice3/__main__.py index 4ea9b5772..f39913b79 100644 --- a/pymobiledevice3/__main__.py +++ b/pymobiledevice3/__main__.py @@ -92,6 +92,12 @@ def get_command(self, ctx, name): @click.command(cls=Pmd3Cli, context_settings=CONTEXT_SETTINGS) def cli(): + """ + \b + Interact with a connected iDevice (iPhone, iPad, ...) + For more information please look at: + https://github.com/doronz88/pymobiledevice3 + """ pass diff --git a/pymobiledevice3/cli/activation.py b/pymobiledevice3/cli/activation.py index 4b148e60e..67978c8be 100644 --- a/pymobiledevice3/cli/activation.py +++ b/pymobiledevice3/cli/activation.py @@ -6,14 +6,13 @@ @click.group() -def cli(): - """ cli """ +def cli() -> None: pass @cli.group() -def activation(): - """ activation options """ +def activation() -> None: + """ Activate/Deactivate the device or query its state """ pass diff --git a/pymobiledevice3/cli/afc.py b/pymobiledevice3/cli/afc.py index 21789f076..f67f39b09 100644 --- a/pymobiledevice3/cli/afc.py +++ b/pymobiledevice3/cli/afc.py @@ -7,14 +7,13 @@ @click.group() -def cli(): - """ apps cli """ +def cli() -> None: pass @cli.group() -def afc(): - """ FileSystem utils """ +def afc() -> None: + """ Manage device multimedia files """ pass diff --git a/pymobiledevice3/cli/amfi.py b/pymobiledevice3/cli/amfi.py index 1c36904a4..cefc045aa 100644 --- a/pymobiledevice3/cli/amfi.py +++ b/pymobiledevice3/cli/amfi.py @@ -10,14 +10,13 @@ @click.group() -def cli(): - """ amfi cli """ +def cli() -> None: pass @cli.group() -def amfi(): - """ amfi options """ +def amfi() -> None: + """ Enable/Disable developer-mode or query its state """ pass diff --git a/pymobiledevice3/cli/apps.py b/pymobiledevice3/cli/apps.py index c597ca405..f8535c4d7 100644 --- a/pymobiledevice3/cli/apps.py +++ b/pymobiledevice3/cli/apps.py @@ -10,14 +10,13 @@ @click.group() -def cli(): - """ apps cli """ +def cli() -> None: pass @cli.group() -def apps(): - """ application options """ +def apps() -> None: + """ Manage installed applications """ pass diff --git a/pymobiledevice3/cli/backup.py b/pymobiledevice3/cli/backup.py index 35531e58f..62e48f893 100644 --- a/pymobiledevice3/cli/backup.py +++ b/pymobiledevice3/cli/backup.py @@ -17,14 +17,13 @@ @click.group() -def cli(): - """ backup cli """ +def cli() -> None: pass @cli.group() -def backup2(): - """ backup utils """ +def backup2() -> None: + """ Backup/Restore options """ pass diff --git a/pymobiledevice3/cli/bonjour.py b/pymobiledevice3/cli/bonjour.py index 4ac88ad40..1cd00661f 100644 --- a/pymobiledevice3/cli/bonjour.py +++ b/pymobiledevice3/cli/bonjour.py @@ -11,14 +11,13 @@ @click.group() -def cli(): - """ bonjour cli """ +def cli() -> None: pass @cli.group('bonjour') -def bonjour_cli(): - """ bonjour options """ +def bonjour_cli() -> None: + """ Browse devices over bonjour """ pass diff --git a/pymobiledevice3/cli/companion_proxy.py b/pymobiledevice3/cli/companion_proxy.py index 196793f20..3ed4c1516 100644 --- a/pymobiledevice3/cli/companion_proxy.py +++ b/pymobiledevice3/cli/companion_proxy.py @@ -6,14 +6,13 @@ @click.group() -def cli(): - """ companion cli """ +def cli() -> None: pass @cli.group() -def companion(): - """ companion options """ +def companion() -> None: + """ List paired "companion" devices """ pass diff --git a/pymobiledevice3/cli/crash.py b/pymobiledevice3/cli/crash.py index 0c4e312da..a7bdec692 100644 --- a/pymobiledevice3/cli/crash.py +++ b/pymobiledevice3/cli/crash.py @@ -6,14 +6,13 @@ @click.group() -def cli(): - """ crash cli """ +def cli() -> None: pass @cli.group() -def crash(): - """ crash report options """ +def crash() -> None: + """ Manage crash reports """ pass diff --git a/pymobiledevice3/cli/developer.py b/pymobiledevice3/cli/developer.py index 2257e982d..1e6c42e01 100644 --- a/pymobiledevice3/cli/developer.py +++ b/pymobiledevice3/cli/developer.py @@ -21,8 +21,8 @@ import pymobiledevice3 from pymobiledevice3.cli.cli_common import BASED_INT, Command, RSDCommand, default_json_encoder, print_json, \ user_requested_colored_output -from pymobiledevice3.exceptions import ArgumentError, DeviceAlreadyInUseError, DvtDirListError, \ - ExtractingStackshotError, RSDRequiredError, UnrecognizedSelectorError +from pymobiledevice3.exceptions import DeviceAlreadyInUseError, DvtDirListError, ExtractingStackshotError, \ + RSDRequiredError, UnrecognizedSelectorError from pymobiledevice3.lockdown import LockdownClient from pymobiledevice3.lockdown_service_provider import LockdownServiceProvider from pymobiledevice3.osu.os_utils import get_os_utils @@ -76,15 +76,14 @@ @click.group() -def cli(): - """ developer cli """ +def cli() -> None: pass @cli.group() -def developer(): +def developer() -> None: """ - developer options. + Perform developer operations These options require the DeveloperDiskImage.dmg to be mounted on the device prior to execution. You can achieve this using: @@ -102,20 +101,20 @@ def developer(): @click.argument('service') @click.option('-r', '--remove-ssl-context', is_flag=True) def developer_shell(service_provider: LockdownClient, service, remove_ssl_context): - """ Launch developer shell. """ + """ Launch developer IPython shell """ with RemoteServer(service_provider, service, remove_ssl_context) as service: service.shell() @developer.group() -def dvt(): - """ dvt operations """ +def dvt() -> None: + """ Access advanced instrumentation APIs """ pass @dvt.command('proclist', cls=Command) def proclist(service_provider: LockdownClient): - """ show process list """ + """ Show process list """ with DvtSecureSocketProxyService(lockdown=service_provider) as dvt: processes = DeviceInfo(dvt).proclist() for process in processes: @@ -127,7 +126,7 @@ def proclist(service_provider: LockdownClient): @dvt.command('applist', cls=Command) def applist(service_provider: LockdownServiceProvider) -> None: - """ show application list """ + """ Show application list """ with DvtSecureSocketProxyService(lockdown=service_provider) as dvt: apps = ApplicationListing(dvt).applist() print_json(apps) diff --git a/pymobiledevice3/cli/diagnostics.py b/pymobiledevice3/cli/diagnostics.py index 15ca42a30..30fac572e 100644 --- a/pymobiledevice3/cli/diagnostics.py +++ b/pymobiledevice3/cli/diagnostics.py @@ -11,38 +11,37 @@ @click.group() -def cli(): - """ apps cli """ +def cli() -> None: pass @cli.group() -def diagnostics(): - """ diagnostics options """ +def diagnostics() -> None: + """ Reboot/Shutdown device or diagnostics services """ pass @diagnostics.command('restart', cls=Command) def diagnostics_restart(service_provider: LockdownClient): - """ restart device """ + """ Restart device """ DiagnosticsService(lockdown=service_provider).restart() @diagnostics.command('shutdown', cls=Command) def diagnostics_shutdown(service_provider: LockdownClient): - """ shutdown device """ + """ Shutdown device """ DiagnosticsService(lockdown=service_provider).shutdown() @diagnostics.command('sleep', cls=Command) def diagnostics_sleep(service_provider: LockdownClient): - """ put device into sleep """ + """ Put device into sleep """ DiagnosticsService(lockdown=service_provider).sleep() @diagnostics.command('info', cls=Command) def diagnostics_info(service_provider: LockdownClient): - """ get diagnostics info """ + """ Get diagnostics info """ print_json(DiagnosticsService(lockdown=service_provider).info()) @@ -51,20 +50,20 @@ def diagnostics_info(service_provider: LockdownClient): @click.option('--name') @click.option('--ioclass') def diagnostics_ioregistry(service_provider: LockdownClient, plane, name, ioclass): - """ get ioregistry info """ + """ Get ioregistry info """ print_json(DiagnosticsService(lockdown=service_provider).ioregistry(plane=plane, name=name, ioclass=ioclass)) @diagnostics.command('mg', cls=Command) @click.argument('keys', nargs=-1, default=None) def diagnostics_mg(service_provider: LockdownClient, keys): - """ get MobileGestalt key values from given list. If empty, return all known. """ + """ Get MobileGestalt key values from given list. If empty, return all known. """ print_json(DiagnosticsService(lockdown=service_provider).mobilegestalt(keys=keys)) @diagnostics.group('battery') def diagnostics_battery(): - """ battery options """ + """ Battery options """ pass diff --git a/pymobiledevice3/cli/lockdown.py b/pymobiledevice3/cli/lockdown.py index 993847d1a..aa32b729a 100644 --- a/pymobiledevice3/cli/lockdown.py +++ b/pymobiledevice3/cli/lockdown.py @@ -16,14 +16,13 @@ @click.group() -def cli(): - """ apps cli """ +def cli() -> None: pass @cli.group('lockdown') -def lockdown_group(): - """ lockdown options """ +def lockdown_group() -> None: + """ Pair/Unpair device or access other lockdown services """ pass diff --git a/pymobiledevice3/cli/mounter.py b/pymobiledevice3/cli/mounter.py index eee536dba..e8fc719c2 100644 --- a/pymobiledevice3/cli/mounter.py +++ b/pymobiledevice3/cli/mounter.py @@ -30,14 +30,13 @@ def catch_function(*args, **kwargs): @click.group() -def cli(): - """ mounter cli """ +def cli() -> None: pass @cli.group() -def mounter(): - """ mounter options """ +def mounter() -> None: + """ Mount/Umount DeveloperDiskImage or query related info """ pass diff --git a/pymobiledevice3/cli/notification.py b/pymobiledevice3/cli/notification.py index 68a29127e..a938e7955 100644 --- a/pymobiledevice3/cli/notification.py +++ b/pymobiledevice3/cli/notification.py @@ -11,14 +11,13 @@ @click.group() -def cli(): - """ notification options """ +def cli() -> None: pass @cli.group() -def notification(): - """ notification options """ +def notification() -> None: + """ Post/Observe notifications """ pass diff --git a/pymobiledevice3/cli/pcap.py b/pymobiledevice3/cli/pcap.py index 235e79802..4777986f2 100644 --- a/pymobiledevice3/cli/pcap.py +++ b/pymobiledevice3/cli/pcap.py @@ -5,13 +5,12 @@ from pygments import formatters, highlight, lexers from pymobiledevice3.cli.cli_common import Command, print_hex, user_requested_colored_output -from pymobiledevice3.lockdown import LockdownClient +from pymobiledevice3.lockdown_service_provider import LockdownServiceProvider from pymobiledevice3.services.pcapd import PcapdService @click.group() -def cli(): - """ apps cli """ +def cli() -> None: pass @@ -43,9 +42,9 @@ def print_packet(packet, color: Optional[bool] = None): @click.option('-c', '--count', type=click.INT, default=-1, help='Number of packets to sniff. Omit to endless sniff.') @click.option('--process', default=None, help='Process to filter. Omit for all.') @click.option('-i', '--interface', default=None, help='Interface name to filter. Omit for all.') -def pcap(service_provider: LockdownClient, out: Optional[IO], count: int, process: Optional[str], - interface: Optional[str]): - """ sniff device traffic """ +def pcap(service_provider: LockdownServiceProvider, out: Optional[IO], count: int, process: Optional[str], + interface: Optional[str]) -> None: + """ Sniff device traffic """ service = PcapdService(lockdown=service_provider) packets_generator = service.watch(packets_count=count, process=process, interface_name=interface) diff --git a/pymobiledevice3/cli/power_assertion.py b/pymobiledevice3/cli/power_assertion.py index 48cdc7694..d0c618a8a 100644 --- a/pymobiledevice3/cli/power_assertion.py +++ b/pymobiledevice3/cli/power_assertion.py @@ -3,13 +3,12 @@ import click from pymobiledevice3.cli.cli_common import Command -from pymobiledevice3.lockdown import LockdownClient +from pymobiledevice3.lockdown_service_provider import LockdownServiceProvider from pymobiledevice3.services.power_assertion import PowerAssertionService @click.group() -def cli(): - """ apps cli """ +def cli() -> None: pass @@ -19,8 +18,8 @@ def cli(): @click.argument('name') @click.argument('timeout', type=click.INT) @click.argument('details', required=False) -def power_assertion(service_provider: LockdownClient, type, name, timeout, details): - """ Create a power assertion (wraps IOPMAssertionCreateWithName()) """ +def power_assertion(service_provider: LockdownServiceProvider, type, name, timeout, details) -> None: + """ Create a power assertion """ with PowerAssertionService(service_provider).create_power_assertion(type, name, timeout, details): print('> Hit Ctrl+C to exit') time.sleep(timeout) diff --git a/pymobiledevice3/cli/processes.py b/pymobiledevice3/cli/processes.py index 1dc8618e2..c50055acc 100644 --- a/pymobiledevice3/cli/processes.py +++ b/pymobiledevice3/cli/processes.py @@ -10,14 +10,13 @@ @click.group() -def cli(): - """ apps cli """ +def cli() -> None: pass @cli.group() -def processes(): - """ processes cli """ +def processes() -> None: + """ View process list using diagnosticsd API """ pass diff --git a/pymobiledevice3/cli/profile.py b/pymobiledevice3/cli/profile.py index ed3e3d9e2..97c6b1c44 100644 --- a/pymobiledevice3/cli/profile.py +++ b/pymobiledevice3/cli/profile.py @@ -10,14 +10,13 @@ @click.group() -def cli(): - """ apps cli """ +def cli() -> None: pass @cli.group('profile') -def profile_group(): - """ profile options """ +def profile_group() -> None: + """ Managed installed profiles or install SSL certificates """ pass diff --git a/pymobiledevice3/cli/provision.py b/pymobiledevice3/cli/provision.py index 5195c0ab1..5f560afc3 100644 --- a/pymobiledevice3/cli/provision.py +++ b/pymobiledevice3/cli/provision.py @@ -11,14 +11,13 @@ @click.group() -def cli(): - """ provision cli """ +def cli() -> None: pass @cli.group() -def provision(): - """ provision options """ +def provision() -> None: + """ Manage installed provision profiles """ pass diff --git a/pymobiledevice3/cli/remote.py b/pymobiledevice3/cli/remote.py index a8c0e45cd..a649990d6 100644 --- a/pymobiledevice3/cli/remote.py +++ b/pymobiledevice3/cli/remote.py @@ -53,14 +53,13 @@ async def cli_browse(timeout: float = DEFAULT_BONJOUR_TIMEOUT) -> None: @click.group() -def cli(): - """ remote cli """ +def cli() -> None: pass @cli.group('remote') -def remote_cli(): - """ remote options """ +def remote_cli() -> None: + """ Create RemoteXPC tunnels """ pass diff --git a/pymobiledevice3/cli/restore.py b/pymobiledevice3/cli/restore.py index 0d71e51ed..db251de89 100644 --- a/pymobiledevice3/cli/restore.py +++ b/pymobiledevice3/cli/restore.py @@ -139,14 +139,13 @@ async def restore_update_task(device: Device, ipsw: ZipFile, tss: Optional[IO], @click.group() -def cli(): - """ cli """ +def cli() -> None: pass @cli.group() -def restore(): - """ restore options """ +def restore() -> None: + """ Restore an IPSW or access device in recovery mode """ pass diff --git a/pymobiledevice3/cli/springboard.py b/pymobiledevice3/cli/springboard.py index 5a5f69b35..0ffe6e19a 100644 --- a/pymobiledevice3/cli/springboard.py +++ b/pymobiledevice3/cli/springboard.py @@ -14,13 +14,12 @@ @click.group() def cli(): - """ apps cli """ pass @cli.group() def springboard(): - """ springboard options """ + """ Access device UI """ pass diff --git a/pymobiledevice3/cli/syslog.py b/pymobiledevice3/cli/syslog.py index c34b375bb..6403eba96 100644 --- a/pymobiledevice3/cli/syslog.py +++ b/pymobiledevice3/cli/syslog.py @@ -16,14 +16,13 @@ @click.group() -def cli(): - """ syslog cli """ +def cli() -> None: pass @cli.group() -def syslog(): - """ syslog options """ +def syslog() -> None: + """ Watch syslog messages """ pass diff --git a/pymobiledevice3/cli/usbmux.py b/pymobiledevice3/cli/usbmux.py index 13338b310..468276a6d 100644 --- a/pymobiledevice3/cli/usbmux.py +++ b/pymobiledevice3/cli/usbmux.py @@ -12,14 +12,13 @@ @click.group() -def cli(): - """ usbmuxd cli """ +def cli() -> None: pass @cli.group('usbmux') -def usbmux_cli(): - """ usbmuxd options """ +def usbmux_cli() -> None: + """ List devices or forward a TCP port """ pass diff --git a/pymobiledevice3/cli/version.py b/pymobiledevice3/cli/version.py index d46a98539..1d89063fd 100644 --- a/pymobiledevice3/cli/version.py +++ b/pymobiledevice3/cli/version.py @@ -2,14 +2,13 @@ @click.group() -def cli(): - """ version cli """ +def cli() -> None: pass @cli.command() -def version(): - """ get installed package version """ +def version() -> None: + """ Query pymobiledevice3 version """ try: from pymobiledevice3._version import __version__ print(__version__) diff --git a/pymobiledevice3/cli/webinspector.py b/pymobiledevice3/cli/webinspector.py index 9624ae9ff..6641f2cb7 100644 --- a/pymobiledevice3/cli/webinspector.py +++ b/pymobiledevice3/cli/webinspector.py @@ -65,14 +65,13 @@ @click.group() -def cli(): - """ webinspector cli """ +def cli() -> None: pass @cli.group() -def webinspector(): - """ webinspector options """ +def webinspector() -> None: + """ Access webinspector services """ pass diff --git a/pymobiledevice3/services/power_assertion.py b/pymobiledevice3/services/power_assertion.py index e3b37ff97..45d7ee5e2 100755 --- a/pymobiledevice3/services/power_assertion.py +++ b/pymobiledevice3/services/power_assertion.py @@ -18,6 +18,7 @@ def __init__(self, lockdown: LockdownServiceProvider): @contextlib.contextmanager def create_power_assertion(self, type_: str, name: str, timeout: float, details: str = None): + """ Trigger IOPMAssertionCreateWithName """ msg = { 'CommandKey': 'CommandCreateAssertion', 'AssertionTypeKey': type_,