def main(): """Command line interface for the ``qpass`` program.""" # Initialize logging to the terminal. coloredlogs.install() # Prepare for command line argument parsing. action = show_matching_entry program_opts = dict(exclude_list=[]) show_opts = dict(filters=[], use_clipboard=is_clipboard_supported()) verbosity = 0 # Parse the command line arguments. try: options, arguments = getopt.gnu_getopt( sys.argv[1:], "elnp:f:x:vqh", ["edit", "list", "no-clipboard", "password-store=", "filter=", "exclude=", "verbose", "quiet", "help"], ) for option, value in options: if option in ("-e", "--edit"): action = edit_matching_entry elif option in ("-l", "--list"): action = list_matching_entries elif option in ("-n", "--no-clipboard"): show_opts["use_clipboard"] = False elif option in ("-p", "--password-store"): stores = program_opts.setdefault("stores", []) stores.append(PasswordStore(directory=value)) elif option in ("-f", "--filter"): show_opts["filters"].append(value) elif option in ("-x", "--exclude"): program_opts["exclude_list"].append(value) elif option in ("-v", "--verbose"): coloredlogs.increase_verbosity() verbosity += 1 elif option in ("-q", "--quiet"): coloredlogs.decrease_verbosity() verbosity -= 1 elif option in ("-h", "--help"): usage(__doc__) return else: raise Exception("Unhandled option! (programming error)") if not (arguments or action == list_matching_entries): usage(__doc__) return except Exception as e: warning("Error: %s", e) sys.exit(1) # Execute the requested action. try: show_opts["quiet"] = verbosity < 0 kw = show_opts if action == show_matching_entry else {} action(QuickPass(**program_opts), arguments, **kw) except PasswordStoreError as e: # Known issues don't get a traceback. logger.error("%s", e) sys.exit(1) except KeyboardInterrupt: # If the user interrupted an interactive prompt they most likely did so # intentionally, so there's no point in generating more output here. sys.exit(1)
def cryptdisks_stop_cli(): """ Usage: cryptdisks-stop-fallback NAME Reads /etc/crypttab and locks the encrypted filesystem with the given NAME. This program emulates the functionality of Debian's cryptdisks_stop program, but it only supports LUKS encryption and a small subset of the available encryption options. """ # Enable logging to the terminal and system log. coloredlogs.install(syslog=True) # Get the name of the encrypted filesystem from the command line arguments # and show a simple usage message when no name is given as an argument. try: target = sys.argv[1] except IndexError: usage(dedent(cryptdisks_stop_cli.__doc__)) else: # Call our Python implementation of `cryptdisks_stop'. try: cryptdisks_stop(target) except ValueError as e: # cryptdisks_stop() raises ValueError when the given target isn't # listed in /etc/crypttab. This doesn't deserve a traceback on the # terminal. warning("Error: %s", e) sys.exit(1) except Exception as e: # Any other exceptions are logged to the terminal and system log. logger.exception("Aborting due to exception!") sys.exit(1)
def main(): """Command line interface for the ``coloredlogs`` program.""" actions = [] try: # Parse the command line arguments. options, arguments = getopt.getopt(sys.argv[1:], 'cdh', [ 'convert', 'to-html', 'demo', 'help', ]) # Map command line options to actions. for option, value in options: if option in ('-c', '--convert', '--to-html'): actions.append(functools.partial(convert_command_output, *arguments)) arguments = [] elif option in ('-d', '--demo'): actions.append(demonstrate_colored_logging) elif option in ('-h', '--help'): usage(__doc__) return else: assert False, "Programming error: Unhandled option!" if not actions: usage(__doc__) return except Exception as e: warning("Error: %s", e) sys.exit(1) for function in actions: function()
def parse_arguments(arguments): """ Parse the command line arguments. :param arguments: A list of strings with command line arguments. :returns: ``True`` if a dry run was requested, ``False`` otherwise. """ dry_run = False try: options, arguments = getopt.gnu_getopt( arguments, 'nvqh', ['dry-run', 'verbose', 'quiet', 'help']) for option, value in options: if option in ('-n', '--dry-run'): dry_run = True elif option in ('-v', '--verbose'): coloredlogs.increase_verbosity() elif option in ('-q', '--quiet'): coloredlogs.decrease_verbosity() elif option in ('-h', '--help'): usage(USAGE_TEXT) sys.exit(0) else: assert False, "Unhandled option!" return dry_run except Exception as e: warning("Error: Failed to parse command line arguments! (%s)", e) sys.exit(1)
def main(): """Command line interface for the ``auto-adjust-display-brightness`` program.""" # Initialize logging to the terminal. coloredlogs.install() # Parse the command line arguments. step_brightness = None try: options, arguments = getopt.getopt( sys.argv[1:], 'fvqh', ['force', 'verbose', 'quiet', 'help']) for option, value in options: if option in ('-f', '--force'): step_brightness = False elif option in ('-v', '--verbose'): coloredlogs.increase_verbosity() elif option in ('-q', '--quiet'): coloredlogs.decrease_verbosity() elif option in ('-h', '--help'): usage(__doc__) return else: assert False, "Unhandled option!" except Exception as e: warning("Failed to parse command line arguments! (%s)", e) sys.exit(1) # Load the configuration file(s). try: config = load_config() except ConfigurationError as e: warning("%s", e) sys.exit(1) # Determine whether to change the brightness at once or gradually. if step_brightness is None: if find_system_uptime() < 60 * 5: logger.info( "Changing brightness at once (system has just booted).") step_brightness = False else: logger.info( "Changing brightness gradually (system has been running for a while)." ) step_brightness = True else: logger.info("Changing brightness at once (-f or --force was given).") # Change the brightness of the configured display(s). dark_outside = is_it_dark_outside( latitude=float(config['location']['latitude']), longitude=float(config['location']['longitude']), elevation=float(config['location']['elevation'])) method = 'decrease_brightness' if dark_outside else 'increase_brightness' num_success, num_failed = 0, 0 for controller in config['controllers']: try: getattr(controller, method)(10 if step_brightness else 100) num_success += 1 except Exception as e: logger.warning("Failed to change brightness of %s! (%s)", controller, e) num_failed += 1 if num_failed > 0 and num_success == 0: sys.exit(1)
def main(): """Command line interface for the ``dwim`` program.""" from dwim import DEFAULT_PROFILE, dwim # Initialize logging to the terminal. coloredlogs.install() # Define the command line option defaults. profile_script = DEFAULT_PROFILE # Parse the command line arguments. try: options, _ = getopt.getopt(sys.argv[1:], 'c:vqh', [ 'config=', 'verbose', 'quiet', 'help', ]) for option, value in options: if option in ('-c', '--config'): profile_script = value elif option in ('-v', '--verbose'): coloredlogs.increase_verbosity() elif option in ('-q', '--quiet'): coloredlogs.decrease_verbosity() elif option in ('-h', '--help'): usage(__doc__) sys.exit(0) except Exception as e: warning("Error: Failed to parse command line arguments! (%s)", e) sys.exit(1) # Execute the requested action(s). try: dwim(profile_script) except Exception: logger.exception("Caught a fatal exception! Terminating ..") sys.exit(1)
def parse_arguments(arguments): """ Parse the command line arguments. :param arguments: A list of strings with command line options and/or arguments. :returns: A list of strings with the positional arguments. """ try: options, arguments = getopt.gnu_getopt(arguments, 'vqh', [ 'verbose', 'quiet', 'help' ]) for option, value in options: if option in ('-v', '--verbose'): coloredlogs.increase_verbosity() elif option in ('-q', '--quiet'): coloredlogs.decrease_verbosity() elif option in ('-h', '--help'): usage(USAGE_TEXT) sys.exit(0) else: assert False, "Unhandled option!" if not arguments: usage(USAGE_TEXT) sys.exit(0) return arguments except Exception as e: warning("Error: Failed to parse command line arguments! (%s)", e) sys.exit(1)
def ensure_root_privileges(): """ Make sure we have root privileges. """ if os.getuid() != 0: warning("Error: Please run this command as root!") sys.exit(1)
def main(): """Command line interface for the ``negotiator-guest`` program.""" # Initialize logging to the terminal and system log. coloredlogs.install(syslog=True) # Parse the command line arguments. list_commands = False execute_command = None start_daemon = False timeout = DEFAULT_TIMEOUT character_device = None try: options, arguments = getopt.getopt(sys.argv[1:], 'le:dt:c:vqh', [ 'list-commands', 'execute=', 'daemon', 'timeout=', 'character-device=', 'verbose', 'quiet', 'help' ]) for option, value in options: if option in ('-l', '--list-commands'): list_commands = True elif option in ('-e', '--execute'): execute_command = value elif option in ('-d', '--daemon'): start_daemon = True elif option in ('-t', '--timeout'): timeout = int(value) elif option in ('-c', '--character-device'): character_device = value elif option in ('-v', '--verbose'): coloredlogs.increase_verbosity() elif option in ('-q', '--quiet'): coloredlogs.decrease_verbosity() elif option in ('-h', '--help'): usage(__doc__) sys.exit(0) if not (list_commands or execute_command or start_daemon): usage(__doc__) sys.exit(0) except Exception: warning("Error: Failed to parse command line arguments!") sys.exit(1) # Start the guest daemon. try: if not character_device: channel_name = HOST_TO_GUEST_CHANNEL_NAME if start_daemon else GUEST_TO_HOST_CHANNEL_NAME character_device = find_character_device(channel_name) ga = GuestAgent(character_device) if start_daemon: ga.enter_main_loop() elif list_commands: with TimeOut(timeout): print('\n'.join(ga.call_remote_method('list_commands'))) elif execute_command: with TimeOut(timeout): timer = Timer() output = ga.call_remote_method('execute', *shlex.split(execute_command), capture=True) logger.debug("Took %s to execute remote command.", timer) print(output.rstrip()) except Exception: logger.exception("Caught a fatal exception! Terminating ..") sys.exit(1)
def main(): """Command line interface for ``debuntu-nodejs-installer``.""" # Initialize logging to the terminal and system log. coloredlogs.install(syslog=True) silence_urllib_logger() # Parse the command line arguments. action = None context_opts = dict() installer_opts = dict() try: options, arguments = getopt.getopt(sys.argv[1:], 'iV:s:r:vqh', [ 'install', 'version=', 'sources-file=', 'remote-host=', 'verbose', 'quiet', 'help', ]) for option, value in options: if option in ('-i', '--install'): action = 'install' elif option in ('-V', '--version'): installer_opts['nodejs_version'] = value elif option in ('-s', '--sources-file'): installer_opts['sources_file'] = value elif option in ('-r', '--remote-host'): context_opts['ssh_alias'] = value elif option in ('-v', '--verbose'): coloredlogs.increase_verbosity() elif option in ('-q', '--quiet'): coloredlogs.decrease_verbosity() elif option in ('-h', '--help'): usage(__doc__) sys.exit(0) else: raise Exception("Unhandled option!") if arguments: raise Exception( "This program doesn't accept any positional arguments!") if not action: usage(__doc__) sys.exit(0) except Exception as e: warning("Failed to parse command line arguments! (%s)", e) sys.exit(1) # Execute the requested action. context = create_context(**context_opts) try: installer = NodeInstaller(context=context, **installer_opts) getattr(installer, action)() except (UnsupportedSystemError, ExternalCommandFailed) as e: logger.error("%s", e) sys.exit(1) except Exception: logger.exception("Encountered unexpected exception on %s!", context) sys.exit(1)
def main(): """Command line interface for the ``humanfriendly`` program.""" enable_ansi_support() try: options, arguments = getopt.getopt(sys.argv[1:], 'cd:l:n:s:bt:h', [ 'run-command', 'format-table', 'delimiter=', 'format-length=', 'format-number=', 'format-size=', 'binary', 'format-timespan=', 'parse-length=', 'parse-size=', 'demo', 'help', ]) except Exception as e: warning("Error: %s", e) sys.exit(1) actions = [] delimiter = None should_format_table = False binary = any(o in ('-b', '--binary') for o, v in options) for option, value in options: if option in ('-d', '--delimiter'): delimiter = value elif option == '--parse-size': actions.append(functools.partial(print_parsed_size, value)) elif option == '--parse-length': actions.append(functools.partial(print_parsed_length, value)) elif option in ('-c', '--run-command'): actions.append(functools.partial(run_command, arguments)) elif option in ('-l', '--format-length'): actions.append(functools.partial(print_formatted_length, value)) elif option in ('-n', '--format-number'): actions.append(functools.partial(print_formatted_number, value)) elif option in ('-s', '--format-size'): actions.append( functools.partial(print_formatted_size, value, binary)) elif option == '--format-table': should_format_table = True elif option in ('-t', '--format-timespan'): actions.append(functools.partial(print_formatted_timespan, value)) elif option == '--demo': actions.append(demonstrate_ansi_formatting) elif option in ('-h', '--help'): usage(__doc__) return if should_format_table: actions.append(functools.partial(print_formatted_table, delimiter)) if not actions: usage(__doc__) return for partial in actions: partial()
def main(): """Command line interface for the ``qpass`` program.""" # Initialize logging to the terminal. coloredlogs.install() # Prepare for command line argument parsing. action = show_matching_entry program_opts = dict() show_opts = dict(use_clipboard=is_clipboard_supported()) # Parse the command line arguments. try: options, arguments = getopt.gnu_getopt(sys.argv[1:], 'elnp:vqh', [ 'edit', 'list', 'no-clipboard', 'password-store=', 'verbose', 'quiet', 'help', ]) for option, value in options: if option in ('-e', '--edit'): action = edit_matching_entry elif option in ('-l', '--list'): action = list_matching_entries elif option in ('-n', '--no-clipboard'): show_opts['use_clipboard'] = False elif option in ('-p', '--password-store'): stores = program_opts.setdefault('stores', []) stores.append(PasswordStore(directory=value)) elif option in ('-v', '--verbose'): coloredlogs.increase_verbosity() elif option in ('-q', '--quiet'): coloredlogs.decrease_verbosity() elif option in ('-h', '--help'): usage(__doc__) return else: raise Exception("Unhandled option! (programming error)") if not (arguments or action == list_matching_entries): usage(__doc__) return except Exception as e: warning("Error: %s", e) sys.exit(1) # Execute the requested action. try: action(QuickPass(**program_opts), arguments, **(show_opts if action == show_matching_entry else {})) except PasswordStoreError as e: # Known issues don't get a traceback. logger.error("%s", e) sys.exit(1) except KeyboardInterrupt: # If the user interrupted an interactive prompt they most likely did so # intentionally, so there's no point in generating more output here. sys.exit(1)
def main(): """Command line interface for the ``negotiator-host`` program.""" # Initialize logging to the terminal and system log. coloredlogs.install(syslog=True) # Parse the command line arguments. actions = [] context = Context() try: options, arguments = getopt.getopt(sys.argv[1:], 'gce:t:dvqh', [ 'list-guests', 'list-commands', 'execute=', 'timeout=', 'daemon', 'verbose', 'quiet', 'help' ]) for option, value in options: if option in ('-g', '--list-guests'): actions.append(context.print_guest_names) elif option in ('-c', '--list-commands'): assert len(arguments) == 1, \ "Please provide the name of a guest as the 1st and only positional argument!" actions.append( functools.partial(context.print_commands, arguments[0])) elif option in ('-e', '--execute'): assert len(arguments) == 1, \ "Please provide the name of a guest as the 1st and only positional argument!" actions.append( functools.partial(context.execute_command, arguments[0], value)) elif option in ('-t', '--timeout'): context.timeout = int(value) elif option in ('-d', '--daemon'): actions.append(HostDaemon) elif option in ('-v', '--verbose'): coloredlogs.increase_verbosity() elif option in ('-q', '--quiet'): coloredlogs.decrease_verbosity() elif option in ('-h', '--help'): usage(__doc__) sys.exit(0) if not actions: usage(__doc__) sys.exit(0) except Exception: warning("Failed to parse command line arguments!") sys.exit(1) # Execute the requested action(s). try: for action in actions: action() except GuestDiscoveryError as e: # Don't spam the logs with tracebacks when the libvirt daemon is down. logger.error("%s", e) sys.exit(1) except Exception: # Do log a traceback for `unexpected' exceptions. logger.exception("Caught a fatal exception! Terminating ..") sys.exit(1)
def main(): """Command line interface for ``debuntu-kernel-manager``.""" # Initialize logging to the terminal and system log. coloredlogs.install(syslog=True) # Parse the command line arguments. action = 'render_summary' context_opts = dict() manager_opts = dict() try: options, arguments = getopt.getopt(sys.argv[1:], 'cfp:r:vqh', [ 'clean', 'remove', 'force', 'preserve-count=', 'remote-host=', 'verbose', 'quiet', 'help', ]) for option, value in options: if option in ('-c', '--clean', '--remove'): action = 'cleanup_packages' elif option in ('-f', '--force'): manager_opts['force'] = True elif option in ('-p', '--preserve-count'): manager_opts['preserve_count'] = int(value) elif option in ('-r', '--remote-host'): context_opts['ssh_alias'] = value elif option in ('-v', '--verbose'): coloredlogs.increase_verbosity() elif option in ('-q', '--quiet'): coloredlogs.decrease_verbosity() elif option in ('-h', '--help'): usage(__doc__) return else: raise Exception("Unhandled option!") # Any positional arguments are passed to apt-get. manager_opts['apt_options'] = arguments except Exception as e: warning("Failed to parse command line arguments! (%s)", e) sys.exit(1) # Execute the requested action(s). context = create_context(**context_opts) try: manager = KernelPackageManager(context=context, **manager_opts) getattr(manager, action)() except (CleanupError, ExternalCommandFailed) as e: logger.error("%s", e) sys.exit(1) except Exception: logger.exception("Encountered unexpected exception on %s!", context) sys.exit(1)
def main(): """Command line interface for the ``update-dotdee`` program.""" # Initialize logging to the terminal and system log. coloredlogs.install(syslog=True) # Parse the command line arguments. context_opts = {} program_opts = {} try: options, arguments = getopt.getopt(sys.argv[1:], 'fur:vqh', [ 'force', 'use-sudo', 'remote-host=', 'verbose', 'quiet', 'help', ]) for option, value in options: if option in ('-f', '--force'): program_opts['force'] = True elif option in ('-u', '--use-sudo'): context_opts['sudo'] = True elif option in ('-r', '--remote-host'): context_opts['ssh_alias'] = value elif option in ('-v', '--verbose'): coloredlogs.increase_verbosity() elif option in ('-q', '--quiet'): coloredlogs.decrease_verbosity() elif option in ('-h', '--help'): usage(__doc__) sys.exit(0) else: # Programming error... assert False, "Unhandled option!" if not arguments: usage(__doc__) sys.exit(0) if len(arguments) != 1: raise Exception( "Expected a filename as the first and only argument!") program_opts['filename'] = arguments[0] except Exception as e: warning("Error: %s", e) sys.exit(1) # Run the program. try: # Initialize the execution context. program_opts['context'] = create_context(**context_opts) # Initialize the program and update the file. UpdateDotDee(**program_opts).update_file() except Exception as e: logger.exception("Encountered unexpected exception, aborting!") sys.exit(1)
def main(): """Command line interface for the ``negotiator-host`` program.""" # Initialize logging to the terminal and system log. coloredlogs.install(syslog=True) # Parse the command line arguments. actions = [] context = Context() try: options, arguments = getopt.getopt(sys.argv[1:], 'gce:t:dvqh', [ 'list-guests', 'list-commands', 'execute=', 'timeout=', 'daemon', 'verbose', 'quiet', 'help' ]) for option, value in options: if option in ('-g', '--list-guests'): actions.append(context.print_guest_names) elif option in ('-c', '--list-commands'): assert len(arguments) == 1, \ "Please provide the name of a guest as the 1st and only positional argument!" actions.append(functools.partial(context.print_commands, arguments[0])) elif option in ('-e', '--execute'): assert len(arguments) == 1, \ "Please provide the name of a guest as the 1st and only positional argument!" actions.append(functools.partial(context.execute_command, arguments[0], value)) elif option in ('-t', '--timeout'): context.timeout = int(value) elif option in ('-d', '--daemon'): actions.append(HostDaemon) elif option in ('-v', '--verbose'): coloredlogs.increase_verbosity() elif option in ('-q', '--quiet'): coloredlogs.decrease_verbosity() elif option in ('-h', '--help'): usage(__doc__) sys.exit(0) if not actions: usage(__doc__) sys.exit(0) except Exception: warning("Failed to parse command line arguments!") sys.exit(1) # Execute the requested action(s). try: for action in actions: action() except GuestDiscoveryError as e: # Don't spam the logs with tracebacks when the libvirt daemon is down. logger.error("%s", e) sys.exit(1) except Exception: # Do log a traceback for `unexpected' exceptions. logger.exception("Caught a fatal exception! Terminating ..") sys.exit(1)
def main(): """Command line interface for ``debuntu-kernel-manager``.""" # Initialize logging to the terminal and system log. coloredlogs.install(syslog=True) # Parse the command line arguments. action = "render_summary" context_opts = dict() manager_opts = dict() try: options, arguments = getopt.getopt( sys.argv[1:], "cfp:r:vqh", [ "clean", "remove", "force", "preserve-count=", "remote-host=", "verbose", "quiet", "help" ], ) for option, value in options: if option in ("-c", "--clean", "--remove"): action = "cleanup_packages" elif option in ("-f", "--force"): manager_opts["force"] = True elif option in ("-p", "--preserve-count"): manager_opts["preserve_count"] = int(value) elif option in ("-r", "--remote-host"): context_opts["ssh_alias"] = value elif option in ("-v", "--verbose"): coloredlogs.increase_verbosity() elif option in ("-q", "--quiet"): coloredlogs.decrease_verbosity() elif option in ("-h", "--help"): usage(__doc__) return else: raise Exception("Unhandled option!") # Any positional arguments are passed to apt-get. manager_opts["apt_options"] = arguments except Exception as e: warning("Failed to parse command line arguments! (%s)", e) sys.exit(1) # Execute the requested action(s). context = create_context(**context_opts) try: manager = KernelPackageManager(context=context, **manager_opts) getattr(manager, action)() except (CleanupError, ExternalCommandFailed) as e: logger.error("%s", e) sys.exit(1) except Exception: logger.exception("Encountered unexpected exception on %s!", context) sys.exit(1)
def main(): """Command line interface for the ``humanfriendly`` program.""" try: options, arguments = getopt.getopt(sys.argv[1:], 'cd:l:n:s:bt:h', [ 'run-command', 'format-table', 'delimiter=', 'format-length=', 'format-number=', 'format-size=', 'binary', 'format-timespan=', 'parse-length=', 'parse-size=', 'demo', 'help', ]) except Exception as e: warning("Error: %s", e) sys.exit(1) actions = [] delimiter = None should_format_table = False binary = any(o in ('-b', '--binary') for o, v in options) for option, value in options: if option in ('-d', '--delimiter'): delimiter = value elif option == '--parse-size': actions.append(functools.partial(print_parsed_size, value)) elif option == '--parse-length': actions.append(functools.partial(print_parsed_length, value)) elif option in ('-c', '--run-command'): actions.append(functools.partial(run_command, arguments)) elif option in ('-l', '--format-length'): actions.append(functools.partial(print_formatted_length, value)) elif option in ('-n', '--format-number'): actions.append(functools.partial(print_formatted_number, value)) elif option in ('-s', '--format-size'): actions.append(functools.partial(print_formatted_size, value, binary)) elif option == '--format-table': should_format_table = True elif option in ('-t', '--format-timespan'): actions.append(functools.partial(print_formatted_timespan, value)) elif option == '--demo': actions.append(demonstrate_ansi_formatting) elif option in ('-h', '--help'): usage(__doc__) return if should_format_table: actions.append(functools.partial(print_formatted_table, delimiter)) if not actions: usage(__doc__) return for partial in actions: partial()
def main(): """Command line interface for ``reboot-remote-system``.""" # Initialize logging to the terminal and system log. coloredlogs.install(syslog=True) # Parse the command line arguments. do_shell = False try: options, arguments = getopt.gnu_getopt(sys.argv[1:], 'svqh', [ 'shell', 'verbose', 'quiet', 'help', ]) for option, value in options: if option in ('-s', '--shell'): do_shell = True elif option in ('-v', '--verbose'): coloredlogs.increase_verbosity() elif option in ('-q', '--quiet'): coloredlogs.decrease_verbosity() elif option in ('-h', '--help'): usage(__doc__) sys.exit(0) else: raise Exception("Unhandled option!") if not arguments: usage(__doc__) sys.exit(0) elif len(arguments) > 1: raise Exception("only one positional argument allowed") except Exception as e: warning("Failed to parse command line arguments! (%s)", e) sys.exit(1) # Reboot the remote system. try: account = RemoteAccount(arguments[0]) context = get_post_context(arguments[0]) reboot_remote_system(context=context, name=account.ssh_alias) if do_shell: start_interactive_shell(context) except EncryptedSystemError as e: logger.error("Aborting due to error: %s", e) sys.exit(2) except Exception: logger.exception("Aborting due to unexpected exception!") sys.exit(3)
def prompt_for_confirmation(question, default=None, padding=True): """ Prompt the user for confirmation. :param question: The text that explains what the user is confirming (a string). :param default: The default value (a boolean) or :data:`None`. :param padding: Refer to the documentation of :func:`prompt_for_input()`. :returns: - If the user enters 'yes' or 'y' then :data:`True` is returned. - If the user enters 'no' or 'n' then :data:`False` is returned. - If the user doesn't enter any text or standard input is not connected to a terminal (which makes it impossible to prompt the user) the value of the keyword argument ``default`` is returned (if that value is not :data:`None`). :raises: - Any exceptions raised by :func:`retry_limit()`. - Any exceptions raised by :func:`prompt_for_input()`. When `default` is :data:`False` and the user doesn't enter any text an error message is printed and the prompt is repeated: >>> prompt_for_confirmation("Are you sure?") <BLANKLINE> Are you sure? [y/n] <BLANKLINE> Error: Please enter 'yes' or 'no' (there's no default choice). <BLANKLINE> Are you sure? [y/n] The same thing happens when the user enters text that isn't recognized: >>> prompt_for_confirmation("Are you sure?") <BLANKLINE> Are you sure? [y/n] about what? <BLANKLINE> Error: Please enter 'yes' or 'no' (the text 'about what?' is not recognized). <BLANKLINE> Are you sure? [y/n] """ # Generate the text for the prompt. prompt_text = prepare_prompt_text(question, bold=True) # Append the valid replies (and default reply) to the prompt text. hint = "[Y/n]" if default else "[y/N]" if default is not None else "[y/n]" prompt_text += " %s " % prepare_prompt_text(hint, color=HIGHLIGHT_COLOR) # Loop until a valid response is given. logger.debug("Requesting interactive confirmation from terminal: %r", ansi_strip(prompt_text).rstrip()) for attempt in retry_limit(): reply = prompt_for_input(prompt_text, '', padding=padding, strip=True) if reply.lower() in ('y', 'yes'): logger.debug("Confirmation granted by reply (%r).", reply) return True elif reply.lower() in ('n', 'no'): logger.debug("Confirmation denied by reply (%r).", reply) return False elif (not reply) and default is not None: logger.debug("Default choice selected by empty reply (%r).", "granted" if default else "denied") return default else: details = ("the text '%s' is not recognized" % reply if reply else "there's no default choice") logger.debug("Got %s reply (%s), retrying (%i/%i) ..", "invalid" if reply else "empty", details, attempt, MAX_ATTEMPTS) warning("{indent}Error: Please enter 'yes' or 'no' ({details}).", indent=' ' if padding else '', details=details)
def prompt_for_choice(choices, default=None, padding=True): """ Prompt the user to select a choice from a group of options. :param choices: A sequence of strings with available options. :param default: The default choice if the user simply presses Enter (expected to be a string, defaults to :data:`None`). :param padding: Refer to the documentation of :func:`prompt_for_input()`. :returns: The string corresponding to the user's choice. :raises: - :exc:`~exceptions.ValueError` if `choices` is an empty sequence. - Any exceptions raised by :func:`retry_limit()`. - Any exceptions raised by :func:`prompt_for_input()`. When no options are given an exception is raised: >>> prompt_for_choice([]) Traceback (most recent call last): File "humanfriendly/prompts.py", line 148, in prompt_for_choice raise ValueError("Can't prompt for choice without any options!") ValueError: Can't prompt for choice without any options! If a single option is given the user isn't prompted: >>> prompt_for_choice(['only one choice']) 'only one choice' Here's what the actual prompt looks like by default: >>> prompt_for_choice(['first option', 'second option']) <BLANKLINE> 1. first option 2. second option <BLANKLINE> Enter your choice as a number or unique substring (Control-C aborts): second <BLANKLINE> 'second option' If you don't like the whitespace (empty lines and indentation): >>> prompt_for_choice(['first option', 'second option'], padding=False) 1. first option 2. second option Enter your choice as a number or unique substring (Control-C aborts): first 'first option' """ indent = ' ' if padding else '' # Make sure we can use 'choices' more than once (i.e. not a generator). choices = list(choices) if len(choices) == 1: # If there's only one option there's no point in prompting the user. logger.debug("Skipping interactive prompt because there's only option (%r).", choices[0]) return choices[0] elif not choices: # We can't render a choice prompt without any options. raise ValueError("Can't prompt for choice without any options!") # Generate the prompt text. prompt_text = ('\n\n' if padding else '\n').join([ # Present the available choices in a user friendly way. "\n".join([ (u" %i. %s" % (i, choice)) + (" (default choice)" if choice == default else "") for i, choice in enumerate(choices, start=1) ]), # Instructions for the user. "Enter your choice as a number or unique substring (Control-C aborts): ", ]) prompt_text = prepare_prompt_text(prompt_text, bold=True) # Loop until a valid choice is made. logger.debug("Requesting interactive choice on terminal (options are %s) ..", concatenate(map(repr, choices))) for attempt in retry_limit(): reply = prompt_for_input(prompt_text, '', padding=padding, strip=True) if not reply and default is not None: logger.debug("Default choice selected by empty reply (%r).", default) return default elif reply.isdigit(): index = int(reply) - 1 if 0 <= index < len(choices): logger.debug("Option (%r) selected by numeric reply (%s).", choices[index], reply) return choices[index] # Check for substring matches. matches = [] for choice in choices: lower_reply = reply.lower() lower_choice = choice.lower() if lower_reply == lower_choice: # If we have an 'exact' match we return it immediately. logger.debug("Option (%r) selected by reply (exact match).", choice) return choice elif lower_reply in lower_choice and len(lower_reply) > 0: # Otherwise we gather substring matches. matches.append(choice) if len(matches) == 1: # If a single choice was matched we return it. logger.debug("Option (%r) selected by reply (substring match on %r).", matches[0], reply) return matches[0] else: # Give the user a hint about what went wrong. if matches: details = format("text '%s' matches more than one choice: %s", reply, concatenate(matches)) elif reply.isdigit(): details = format("number %i is not a valid choice", int(reply)) elif reply and not reply.isspace(): details = format("text '%s' doesn't match any choices", reply) else: details = "there's no default choice" logger.debug("Got %s reply (%s), retrying (%i/%i) ..", "invalid" if reply else "empty", details, attempt, MAX_ATTEMPTS) warning("%sError: Invalid input (%s).", indent, details)
def main(): """Command line interface for the ``py2deb`` program.""" # Configure terminal output. coloredlogs.install() try: # Initialize a package converter. converter = PackageConverter() # Parse and validate the command line options. options, arguments = getopt.getopt(sys.argv[1:], 'c:r:yvh', [ 'config=', 'repository=', 'use-system-package=', 'name-prefix=', 'no-name-prefix=', 'rename=', 'install-prefix=', 'install-alternative=', 'python-callback=', 'report-dependencies=', 'yes', 'verbose', 'help', ]) control_file_to_update = None for option, value in options: if option in ('-c', '--config'): converter.load_configuration_file(value) elif option in ('-r', '--repository'): converter.set_repository(value) elif option == '--use-system-package': python_package_name, _, debian_package_name = value.partition(',') converter.use_system_package(python_package_name, debian_package_name) elif option == '--name-prefix': converter.set_name_prefix(value) elif option == '--no-name-prefix': converter.rename_package(value, value) elif option == '--rename': python_package_name, _, debian_package_name = value.partition(',') converter.rename_package(python_package_name, debian_package_name) elif option == '--install-prefix': converter.set_install_prefix(value) elif option == '--install-alternative': link, _, path = value.partition(',') converter.install_alternative(link, path) elif option == '--python-callback': converter.set_python_callback(value) elif option == '--report-dependencies': control_file_to_update = value if not os.path.isfile(control_file_to_update): msg = "The given control file doesn't exist! (%s)" raise Exception(msg % control_file_to_update) elif option in ('-y', '--yes'): converter.set_auto_install(True) elif option in ('-v', '--verbose'): coloredlogs.increase_verbosity() elif option in ('-h', '--help'): usage(__doc__) return else: assert False, "Unhandled option!" except Exception as e: warning("Failed to parse command line arguments: %s", e) sys.exit(1) # Convert the requested package(s). try: if arguments: archives, relationships = converter.convert(arguments) if relationships and control_file_to_update: patch_control_file(control_file_to_update, dict(depends=relationships)) else: usage(__doc__) except Exception: logger.exception("Caught an unhandled exception!") sys.exit(1)
def main(): """Command line interface for the ``negotiator-guest`` program.""" # Initialize logging to the terminal and system log. coloredlogs.install(syslog=True) # Parse the command line arguments. list_commands = False execute_command = None start_daemon = False timeout = DEFAULT_TIMEOUT character_device = None try: options, arguments = getopt.getopt(sys.argv[1:], 'le:dt:c:vqh', [ 'list-commands', 'execute=', 'daemon', 'timeout=', 'character-device=', 'verbose', 'quiet', 'help' ]) for option, value in options: if option in ('-l', '--list-commands'): list_commands = True elif option in ('-e', '--execute'): execute_command = value elif option in ('-d', '--daemon'): start_daemon = True elif option in ('-t', '--timeout'): timeout = int(value) elif option in ('-c', '--character-device'): character_device = value elif option in ('-v', '--verbose'): coloredlogs.increase_verbosity() elif option in ('-q', '--quiet'): coloredlogs.decrease_verbosity() elif option in ('-h', '--help'): usage(__doc__) sys.exit(0) if not (list_commands or execute_command or start_daemon): usage(__doc__) sys.exit(0) except Exception: warning("Error: Failed to parse command line arguments!") sys.exit(1) # Start the guest daemon. try: if not character_device: channel_name = HOST_TO_GUEST_CHANNEL_NAME if start_daemon else GUEST_TO_HOST_CHANNEL_NAME character_device = find_character_device(channel_name) if start_daemon: agent = GuestAgent(character_device=character_device, retry=False) agent.enter_main_loop() elif list_commands: with TimeOut(timeout): agent = GuestAgent(character_device, retry=True) print('\n'.join(agent.call_remote_method('list_commands'))) elif execute_command: with TimeOut(timeout): timer = Timer() agent = GuestAgent(character_device, retry=True) output = agent.call_remote_method( 'execute', *shlex.split(execute_command), capture=True) logger.debug("Took %s to execute remote command.", timer) print(output.rstrip()) except Exception: logger.exception("Caught a fatal exception! Terminating ..") sys.exit(1)
def main(): """Command line interface for the ``apt-mirror-updater`` program.""" # Initialize logging to the terminal and system log. coloredlogs.install(syslog=True) # Command line option defaults. context = LocalContext() updater = AptMirrorUpdater(context=context) limit = MAX_MIRRORS actions = [] # Parse the command line arguments. try: options, arguments = getopt.getopt(sys.argv[1:], 'r:fblc:aux:m:vqh', [ 'remote-host=', 'find-current-mirror', 'find-best-mirror', 'list-mirrors', 'change-mirror', 'auto-change-mirror', 'update', 'update-package-lists', 'exclude=', 'max=', 'verbose', 'quiet', 'help', ]) for option, value in options: if option in ('-r', '--remote-host'): if actions: msg = "The %s option should be the first option given on the command line!" raise Exception(msg % option) context = RemoteContext(value) updater = AptMirrorUpdater(context=context) elif option in ('-f', '--find-current-mirror'): actions.append( functools.partial(report_current_mirror, updater)) elif option in ('-b', '--find-best-mirror'): actions.append(functools.partial(report_best_mirror, updater)) elif option in ('-l', '--list-mirrors'): actions.append( functools.partial(report_available_mirrors, updater)) elif option in ('-c', '--change-mirror'): actions.append(functools.partial(updater.change_mirror, value)) elif option in ('-a', '--auto-change-mirror'): actions.append(updater.change_mirror) elif option in ('-u', '--update', '--update-package-lists'): actions.append(updater.smart_update) elif option in ('-x', '--exclude'): actions.insert(0, functools.partial(updater.ignore_mirror, value)) elif option in ('-m', '--max'): limit = int(value) elif option in ('-v', '--verbose'): coloredlogs.increase_verbosity() elif option in ('-q', '--quiet'): coloredlogs.decrease_verbosity() elif option in ('-h', '--help'): usage(__doc__) return else: assert False, "Unhandled option!" if not actions: usage(__doc__) return # Propagate options to the Python API. updater.max_mirrors = limit except Exception as e: warning("Error: Failed to parse command line arguments! (%s)" % e) sys.exit(1) # Perform the requested action(s). try: for callback in actions: callback() except Exception: logger.exception("Encountered unexpected exception! Aborting ..") sys.exit(1)
def main(): """Command line interface for the ``crypto-drive-manager`` program.""" # Initialize logging to the terminal and system log. coloredlogs.install(syslog=True) # Define command line option defaults. image_file = '/root/encryption-keys.img' mapper_name = 'encryption-keys' mount_point = '/mnt/keys' install_workaround = False # Parse the command line arguments. try: options, arguments = getopt.getopt(sys.argv[1:], 'i:n:m:vqh', [ 'image-file=', 'mapper-name=', 'mount-point=', 'install-systemd-workaround', 'verbose', 'quiet', 'help', ]) for option, value in options: if option in ('-i', '--image-file'): image_file = value elif option in ('-n', '--mapper-name'): mapper_name = value elif option in ('-m', '--mount-point'): mount_point = value elif option == '--install-systemd-workaround': install_workaround = True elif option in ('-v', '--verbose'): coloredlogs.increase_verbosity() elif option in ('-q', '--quiet'): coloredlogs.decrease_verbosity() elif option in ('-h', '--help'): usage(__doc__) return else: assert False, "Unhandled option!" except Exception as e: warning("Error: Failed to parse command line arguments! (%s)", e) sys.exit(1) # Make sure we're running as root (after parsing the command # line so that root isn't required to list the usage message). if os.getuid() != 0: warning("Error: Please run this command as root!") sys.exit(1) # Decide if the systemd workaround is being executed based on sys.argv[0]. if systemd_workaround_requested(): update_systemd_services() # In this case none of the other code should run. return # Check if the operator wants to install the systemd workaround. if install_workaround: install_systemd_workaround() # Initialize the keys device and use it to unlock the managed drives? # Only if we didn't just install the systemd workaround OR the operator # requested to unlock specific drives. if (not install_workaround) or arguments: try: initialize_keys_device( image_file=image_file, mapper_name=mapper_name, mount_point=mount_point, volumes=arguments, ) except KeyboardInterrupt: logger.error("Interrupted by Control-C, terminating ..") sys.exit(1) except Exception: logger.exception("Terminating due to unexpected exception!") sys.exit(1)
def main(): """Command line interface for the ``apt-smart`` program.""" # Initialize logging to the terminal and system log. coloredlogs.install(syslog=True) # Command line option defaults. context = LocalContext() updater = AptMirrorUpdater(context=context) limit = MAX_MIRRORS url_char_len = URL_CHAR_LEN actions = [] # Parse the command line arguments. try: options, arguments = getopt.getopt(sys.argv[1:], 'r:fF:blL:c:aux:m:vVR:qh', [ 'remote-host=', 'find-current-mirror', 'find-best-mirror', 'file-to-read=', 'list-mirrors', 'url-char-len=', 'change-mirror', 'auto-change-mirror', 'update', 'update-package-lists', 'exclude=', 'max=', 'verbose', 'version', 'create-chroot=', 'quiet', 'help', ]) for option, value in options: if option in ('-r', '--remote-host'): if actions: msg = "The %s option should be the first option given on the command line!" raise Exception(msg % option) context = RemoteContext(value) updater = AptMirrorUpdater(context=context) elif option in ('-f', '--find-current-mirror'): actions.append( functools.partial(report_current_mirror, updater)) elif option in ('-F', '--file-to-read'): updater.custom_mirror_file_path = value elif option in ('-b', '--find-best-mirror'): actions.append(functools.partial(report_best_mirror, updater)) elif option in ('-l', '--list-mirrors'): actions.append( functools.partial(report_available_mirrors, updater)) elif option in ('-L', '--url-char-len'): url_char_len = int(value) elif option in ('-c', '--change-mirror'): if value.strip().startswith(('http://', 'https://', 'ftp://')): actions.append( functools.partial(updater.change_mirror, value)) else: raise Exception("\'%s\' is not a valid mirror URL" % value) elif option in ('-a', '--auto-change-mirror'): actions.append(updater.change_mirror) elif option in ('-u', '--update', '--update-package-lists'): actions.append(updater.smart_update) elif option in ('-x', '--exclude'): actions.insert(0, functools.partial(updater.ignore_mirror, value)) elif option in ('-m', '--max'): limit = int(value) elif option in ('-v', '--verbose'): coloredlogs.increase_verbosity() elif option in ('-V', '--version'): output("Version: %s on Python %i.%i", updater_version, sys.version_info[0], sys.version_info[1]) return elif option in ('-R', '--create-chroot'): actions.append(functools.partial(updater.create_chroot, value)) elif option in ('-q', '--quiet'): coloredlogs.decrease_verbosity() elif option in ('-h', '--help'): usage(__doc__) return else: assert False, "Unhandled option!" if not actions: usage(__doc__) return # Propagate options to the Python API. updater.max_mirrors = limit updater.url_char_len = url_char_len except Exception as e: warning("Error: Failed to parse command line arguments! (%s)" % e) sys.exit(1) # Perform the requested action(s). try: for callback in actions: callback() except Exception: logger.exception("Encountered unexpected exception! Aborting ..") sys.exit(1)
def prompt_for_choice(choices, default=None, padding=True): """ Prompt the user to select a choice from a group of options. :param choices: A sequence of strings with available options. :param default: The default choice if the user simply presses Enter (expected to be a string, defaults to :data:`None`). :param padding: Refer to the documentation of :func:`prompt_for_input()`. :returns: The string corresponding to the user's choice. :raises: - :exc:`~exceptions.ValueError` if `choices` is an empty sequence. - Any exceptions raised by :func:`retry_limit()`. - Any exceptions raised by :func:`prompt_for_input()`. When no options are given an exception is raised: >>> prompt_for_choice([]) Traceback (most recent call last): File "humanfriendly/prompts.py", line 148, in prompt_for_choice raise ValueError("Can't prompt for choice without any options!") ValueError: Can't prompt for choice without any options! If a single option is given the user isn't prompted: >>> prompt_for_choice(['only one choice']) 'only one choice' Here's what the actual prompt looks like by default: >>> prompt_for_choice(['first option', 'second option']) <BLANKLINE> 1. first option 2. second option <BLANKLINE> Enter your choice as a number or unique substring (Control-C aborts): second <BLANKLINE> 'second option' If you don't like the whitespace (empty lines and indentation): >>> prompt_for_choice(['first option', 'second option'], padding=False) 1. first option 2. second option Enter your choice as a number or unique substring (Control-C aborts): first 'first option' """ indent = ' ' if padding else '' # Make sure we can use 'choices' more than once (i.e. not a generator). choices = list(choices) if len(choices) == 1: # If there's only one option there's no point in prompting the user. logger.debug("Skipping interactive prompt because there's only option (%r).", choices[0]) return choices[0] elif not choices: # We can't render a choice prompt without any options. raise ValueError("Can't prompt for choice without any options!") # Generate the prompt text. prompt_text = ('\n\n' if padding else '\n').join([ # Present the available choices in a user friendly way. "\n".join([ (u" %i. %s" % (i, choice)) + (" (default choice)" if choice == default else "") for i, choice in enumerate(choices, start=1) ]), # Instructions for the user. "Enter your choice as a number or unique substring (Control-C aborts): ", ]) if terminal_supports_colors(): prompt_text = ansi_wrap(prompt_text, bold=True, readline_hints=True) # Loop until a valid choice is made. logger.debug("Requesting interactive choice on terminal (options are %s) ..", concatenate(map(repr, choices))) for attempt in retry_limit(): reply = prompt_for_input(prompt_text, '', padding=padding, strip=True) if not reply and default is not None: logger.debug("Default choice selected by empty reply (%r).", default) return default elif reply.isdigit(): index = int(reply) - 1 if 0 <= index < len(choices): logger.debug("Option (%r) selected by numeric reply (%s).", choices[index], reply) return choices[index] # Check for substring matches. matches = [] for choice in choices: lower_reply = reply.lower() lower_choice = choice.lower() if lower_reply == lower_choice: # If we have an 'exact' match we return it immediately. logger.debug("Option (%r) selected by reply (exact match).", choice, reply) return choice elif lower_reply in lower_choice and len(lower_reply) > 0: # Otherwise we gather substring matches. matches.append(choice) if len(matches) == 1: # If a single choice was matched we return it. logger.debug("Option (%r) selected by reply (substring match on %r).", matches[0], reply) return matches[0] else: # Give the user a hint about what went wrong. if matches: details = format("text '%s' matches more than one choice: %s", reply, concatenate(matches)) elif reply.isdigit(): details = format("number %i is not a valid choice", int(reply)) elif reply and not reply.isspace(): details = format("text '%s' doesn't match any choices", reply) else: details = "there's no default choice" logger.debug("Got %s reply (%s), retrying (%i/%i) ..", "invalid" if reply else "empty", details, attempt, MAX_ATTEMPTS) warning("%sError: Invalid input (%s).", indent, details)
def main(): """The command line interface.""" # Initialize logging to the terminal and system log. coloredlogs.install(syslog=True) # Parse the command line options. try: options, arguments = getopt.gnu_getopt( sys.argv[1:], "b:m:Wc:B:l:nvqh", [ "block-size=", "hash-method=", "whole-file", "concurrency=", "benchmark=", "listen=", "dry-run", "verbose", "quiet", "help", ], ) except Exception as e: warning("Error: %s", e) sys.exit(1) # Command line option defaults. client_opts = {} server_opts = {} # Map parsed options to variables. for option, value in options: if option in ("-b", "--block-size"): client_opts["block_size"] = parse_size(value) elif option in ("-m", "--hash-method"): client_opts["hash_method"] = value elif option in ("-W", "--whole-file"): client_opts["delta_transfer"] = False elif option in ("-c", "--concurrency"): client_opts["concurrency"] = int(value) server_opts["concurrency"] = int(value) elif option in ("-B", "--benchmark"): client_opts["benchmark"] = int(value) elif option in ("-l", "--listen"): server_opts["address"] = value elif option in ("-n", "--dry-run"): client_opts["dry_run"] = True elif option in ("-v", "--verbose"): coloredlogs.increase_verbosity() elif option in ("-q", "--quiet"): coloredlogs.decrease_verbosity() elif option in ("-h", "--help"): usage(__doc__) sys.exit(0) # Execute the requested action. try: if arguments: if len(arguments) != 2: warning("Error: Two positional arguments expected!") sys.exit(1) client_opts['source'] = arguments[0] client_opts['target'] = arguments[1] run_client(**client_opts) else: run_server(**server_opts) except Exception: logger.exception("Program terminating due to exception!") sys.exit(1)
def main(): """Command line interface for the ``rsync-system-backup`` program.""" # Initialize logging to the terminal and system log. coloredlogs.install(syslog=True) # Parse the command line arguments. context_opts = dict() program_opts = dict() dest_opts = dict() try: options, arguments = getopt.gnu_getopt(sys.argv[1:], 'bsrm:c:t:i:unx:fvqh', [ 'backup', 'snapshot', 'rotate', 'mount=', 'crypto=', 'tunnel=', 'ionice=', 'no-sudo', 'dry-run', 'multi-fs', 'exclude=', 'force', 'disable-notifications', 'verbose', 'quiet', 'help', ]) for option, value in options: if option in ('-b', '--backup'): enable_explicit_action(program_opts, 'backup_enabled') elif option in ('-s', '--snapshot'): enable_explicit_action(program_opts, 'snapshot_enabled') elif option in ('-r', '--rotate'): enable_explicit_action(program_opts, 'rotate_enabled') elif option in ('-m', '--mount'): program_opts['mount_point'] = value elif option in ('-c', '--crypto'): program_opts['crypto_device'] = value elif option in ('-t', '--tunnel'): ssh_user, _, value = value.rpartition('@') ssh_alias, _, port_number = value.partition(':') tunnel_opts = dict( ssh_alias=ssh_alias, ssh_user=ssh_user, # The port number of the rsync daemon. remote_port=RSYNCD_PORT, ) if port_number: # The port number of the SSH server. tunnel_opts['port'] = int(port_number) dest_opts['ssh_tunnel'] = SecureTunnel(**tunnel_opts) elif option in ('-i', '--ionice'): value = value.lower().strip() validate_ionice_class(value) program_opts['ionice'] = value elif option in ('-u', '--no-sudo'): program_opts['sudo_enabled'] = False elif option in ('-n', '--dry-run'): logger.info("Performing a dry run (because of %s option) ..", option) program_opts['dry_run'] = True elif option in ('-f', '--force'): program_opts['force'] = True elif option in ('-x', '--exclude'): program_opts.setdefault('exclude_list', []) program_opts['exclude_list'].append(value) elif option == '--multi-fs': program_opts['multi_fs'] = True elif option == '--disable-notifications': program_opts['notifications_enabled'] = False elif option in ('-v', '--verbose'): coloredlogs.increase_verbosity() elif option in ('-q', '--quiet'): coloredlogs.decrease_verbosity() elif option in ('-h', '--help'): usage(__doc__) return else: raise Exception("Unhandled option! (programming error)") if len(arguments) > 2: msg = "Expected one or two positional arguments! (got %i)" raise Exception(msg % len(arguments)) if len(arguments) == 2: # Get the source from the first of two arguments. program_opts['source'] = arguments.pop(0) if arguments: # Get the destination from the second (or only) argument. dest_opts['expression'] = arguments[0] program_opts['destination'] = Destination(**dest_opts) elif not os.environ.get('RSYNC_MODULE_PATH'): # Show a usage message when no destination is given. usage(__doc__) return except Exception as e: warning("Error: %s", e) sys.exit(1) try: # Inject the source context into the program options. program_opts['source_context'] = create_context(**context_opts) # Initialize the program with the command line # options and execute the requested action(s). RsyncSystemBackup(**program_opts).execute() except Exception as e: if isinstance(e, RsyncSystemBackupError): # Special handling when the backup disk isn't available. if isinstance(e, MissingBackupDiskError): # Check if we're connected to a terminal to decide whether the # error should be propagated or silenced, the idea being that # rsync-system-backup should keep quiet when it's being run # from cron and the backup disk isn't available. if not connected_to_terminal(): logger.info("Skipping backup: %s", e) sys.exit(0) # Known problems shouldn't produce # an intimidating traceback to users. logger.error("Aborting due to error: %s", e) else: # Unhandled exceptions do get a traceback, # because it may help fix programming errors. logger.exception("Aborting due to unhandled exception!") sys.exit(1)
def main(): """Command line interface for ``update-samples.py``.""" # Enable verbose logging to the terminal. coloredlogs.install(level='INFO') # Validate the command line arguments. arguments = sys.argv[1:] if len(arguments) != 1: warning( "Please provide the SSH alias of a test host (a single argument).") sys.exit(1) # Construct the remote context. context = RemoteContext(ssh_alias=arguments[0]) # Prepare Linux kernel packages on the test host. context.execute( 'apt-get', 'install', '--yes', 'linux-headers-3.13.0-63', 'linux-headers-3.13.0-63-generic', 'linux-headers-3.13.0-88', 'linux-headers-generic-lts-xenial', 'linux-image-3.13.0-63-generic', 'linux-image-3.13.0-73-generic', 'linux-image-4.4.0-21', 'linux-image-4.4.0-21-generic', 'linux-image-extra-3.13.0-63-generic', 'linux-image-generic-lts-wily', 'linux-image-generic-lts-xenial', environment=dict(DEBIAN_FRONTEND='noninteractive'), sudo=True, tty=False, ) samples_directory = os.path.join(os.path.dirname(__file__), '..', 'docs') os.environ['COLOREDLOGS_FIELD_STYLES'] = ';'.join([ 'asctime=green', 'levelname=black,bold', ]) os.environ['COLOREDLOGS_LEVEL_STYLES'] = ';'.join([ 'verbose=green', 'warning=yellow', 'error=red', ]) os.environ['COLOREDLOGS_LOG_FORMAT'] = ' '.join([ '%(asctime)s', '%(levelname)s', '%(message)s', ]) # Capture a run that is expected to fail. with CaptureOutput() as capturer: execute( 'debuntu-kernel-manager', '--remote-host=%s' % context.ssh_alias, '--remove', '--verbose', check=False, tty=False, ) capture_html( capturer, os.path.join(samples_directory, 'sanity-check-says-no.html')) # Capture a run that is expected to succeed. with CaptureOutput() as capturer: execute( 'debuntu-kernel-manager', '--remote-host=%s' % context.ssh_alias, '--remove', '--force', '--verbose', '--', '--dry-run', '--quiet', '--quiet', tty=False, ) capture_html(capturer, os.path.join(samples_directory, 'operator-says-yes.html'))
def main(): """Command line interface for the ``deb-pkg-tools`` program.""" # Configure logging output. coloredlogs.install() # Command line option defaults. prompt = True actions = [] control_file = None control_fields = {} directory = None # Initialize the package cache. cache = get_default_cache() # Parse the command line options. try: options, arguments = getopt.getopt( sys.argv[1:], 'i:c:C:p:s:b:u:a:d:w:yvh', [ 'inspect=', 'collect=', 'check=', 'patch=', 'set=', 'build=', 'update-repo=', 'activate-repo=', 'deactivate-repo=', 'with-repo=', 'gc', 'garbage-collect', 'yes', 'verbose', 'help' ]) for option, value in options: if option in ('-i', '--inspect'): actions.append( functools.partial(show_package_metadata, archive=value)) elif option in ('-c', '--collect'): directory = check_directory(value) elif option in ('-C', '--check'): actions.append( functools.partial(check_package, archive=value, cache=cache)) elif option in ('-p', '--patch'): control_file = os.path.abspath(value) assert os.path.isfile( control_file), "Control file does not exist!" elif option in ('-s', '--set'): name, _, value = value.partition(':') control_fields[name] = value.strip() elif option in ('-b', '--build'): actions.append( functools.partial( build_package, check_directory(value), repository=tempfile.gettempdir(), )) elif option in ('-u', '--update-repo'): actions.append( functools.partial(update_repository, directory=check_directory(value), cache=cache)) elif option in ('-a', '--activate-repo'): actions.append( functools.partial(activate_repository, check_directory(value))) elif option in ('-d', '--deactivate-repo'): actions.append( functools.partial(deactivate_repository, check_directory(value))) elif option in ('-w', '--with-repo'): actions.append( functools.partial(with_repository_wrapper, directory=check_directory(value), command=arguments, cache=cache)) elif option in ('--gc', '--garbage-collect'): actions.append( functools.partial(cache.collect_garbage, force=True)) elif option in ('-y', '--yes'): prompt = False elif option in ('-v', '--verbose'): coloredlogs.increase_verbosity() elif option in ('-h', '--help'): usage(__doc__) return # We delay the patch_control_file() and collect_packages() partials # until all command line options have been parsed, to ensure that the # order of the command line options doesn't matter. if control_file: if not control_fields: raise Exception( "Please specify one or more control file fields to patch!") actions.append( functools.partial(patch_control_file, control_file, control_fields)) if directory: actions.append( functools.partial(collect_packages, archives=arguments, directory=directory, prompt=prompt, cache=cache)) except Exception as e: warning("Error: %s", e) sys.exit(1) # Execute the selected action. try: if actions: for action in actions: action() cache.collect_garbage() else: usage(__doc__) except Exception: logger.exception("An error occurred! Aborting..") sys.exit(1)
def prompt_for_confirmation(question, default=None, padding=True): """ Prompt the user for confirmation. :param question: The text that explains what the user is confirming (a string). :param default: The default value (a boolean) or :data:`None`. :param padding: Refer to the documentation of :func:`prompt_for_input()`. :returns: - If the user enters 'yes' or 'y' then :data:`True` is returned. - If the user enters 'no' or 'n' then :data:`False` is returned. - If the user doesn't enter any text or standard input is not connected to a terminal (which makes it impossible to prompt the user) the value of the keyword argument ``default`` is returned (if that value is not :data:`None`). :raises: - Any exceptions raised by :func:`retry_limit()`. - Any exceptions raised by :func:`prompt_for_input()`. When `default` is :data:`False` and the user doesn't enter any text an error message is printed and the prompt is repeated: >>> prompt_for_confirmation("Are you sure?") <BLANKLINE> Are you sure? [y/n] <BLANKLINE> Error: Please enter 'yes' or 'no' (there's no default choice). <BLANKLINE> Are you sure? [y/n] The same thing happens when the user enters text that isn't recognized: >>> prompt_for_confirmation("Are you sure?") <BLANKLINE> Are you sure? [y/n] about what? <BLANKLINE> Error: Please enter 'yes' or 'no' (the text 'about what?' is not recognized). <BLANKLINE> Are you sure? [y/n] """ # Generate the text for the prompt. prompt_text = question if terminal_supports_colors(): prompt_text = ansi_wrap(prompt_text, bold=True, readline_hints=True) # Append the valid replies (and default reply) to the prompt text. hint = "[Y/n]" if default else "[y/N]" if default is not None else "[y/n]" if terminal_supports_colors(): hint = ansi_wrap(hint, color=HIGHLIGHT_COLOR, readline_hints=True) prompt_text += " %s " % hint # Loop until a valid response is given. logger.debug("Requesting interactive confirmation from terminal: %r", ansi_strip(prompt_text).rstrip()) for attempt in retry_limit(): reply = prompt_for_input(prompt_text, '', padding=padding, strip=True) if reply.lower() in ('y', 'yes'): logger.debug("Confirmation granted by reply (%r).", reply) return True elif reply.lower() in ('n', 'no'): logger.debug("Confirmation denied by reply (%r).", reply) return False elif (not reply) and default is not None: logger.debug("Default choice selected by empty reply (%r).", "granted" if default else "denied") return default else: details = ("the text '%s' is not recognized" % reply if reply else "there's no default choice") logger.debug("Got %s reply (%s), retrying (%i/%i) ..", "invalid" if reply else "empty", details, attempt, MAX_ATTEMPTS) warning("{indent}Error: Please enter 'yes' or 'no' ({details}).", indent=' ' if padding else '', details=details)
def main(): """Command line interface for the ``chat-archive`` program.""" # Enable logging to the terminal. coloredlogs.install() # Parse the command line options. program_opts = dict() command_name = None try: options, arguments = getopt.gnu_getopt( sys.argv[1:], "C:fl:c:p:vqh", [ "context=", "force", "log-file=", "color=", "colour=", "profile=", "verbose", "quiet", "help", ], ) for option, value in options: if option in ("-C", "--context"): program_opts["context"] = int(value) elif option in ("-f", "--force"): program_opts["force"] = True elif option in ("-l", "--log-file"): handler = logging.FileHandler(parse_path(value)) handler.setFormatter( logging.Formatter( fmt= "%(asctime)s %(name)s[%(process)d] %(levelname)s %(message)s", datefmt="%Y-%m-%d %H:%M:%S")) handler.setLevel(logging.DEBUG) logging.root.addHandler(handler) logging.root.setLevel(logging.NOTSET) elif option in ("-c", "--color", "--colour"): mapping = dict(always=True, never=False) program_opts["use_colors"] = mapping[ value] if value in mapping else coerce_boolean(value) elif option in ("-p", "--profile"): program_opts["profile_file"] = parse_path(value) elif option in ("-v", "--verbose"): coloredlogs.increase_verbosity() elif option in ("-q", "--quiet"): coloredlogs.decrease_verbosity() elif option in ("-h", "--help"): usage(__doc__) sys.exit(0) else: assert False, "Unhandled option!" # Make sure the operator provided a command. if not arguments: usage(__doc__) sys.exit(0) except Exception as e: warning("Failed to parse command line arguments: %s", e) sys.exit(1) try: # We extract any search keywords from the command line arguments before # initializing an instance of the UserInterface class, to enable # initialization of the KeywordHighlighter class. if arguments[0] == "search": program_opts["keywords"] = arguments[1:] # Initialize the chat archive. with UserInterface(**program_opts) as program: # Validate the requested command. command_name = arguments.pop(0) method_name = "%s_cmd" % command_name if not hasattr(program, method_name): warning("Error: Invalid command name '%s'!", command_name) sys.exit(1) # Execute the requested command. command_fn = getattr(program, method_name) command_fn(arguments) except KeyboardInterrupt: logger.notice("Interrupted by Control-C ..") sys.exit(1) except Exception: logger.exception("Aborting due to unexpected exception!") sys.exit(1)
def main(): """Command line interface for ``unlock-remote-system``.""" # Initialize logging to the terminal and system log. coloredlogs.install(syslog=True) # Parse the command line arguments. program_opts = {} identity_file = None do_shell = False do_watch = False watch_all = False try: options, arguments = getopt.gnu_getopt(sys.argv[1:], 'i:k:p:r:swavqh', [ 'identity-file=', 'known-hosts=', 'password='******'remote-host=', 'shell', 'watch', 'all', 'verbose', 'quiet', 'help', ]) for option, value in options: if option in ('-i', '--identity-file'): identity_file = parse_path(value) elif option in ('-k', '--known-hosts'): program_opts['known_hosts_file'] = parse_path(value) elif option in ('-p', '--password'): program_opts['password'] = get_password_from_store(value) elif option in ('-r', '--remote-host'): program_opts['ssh_proxy'] = value elif option in ('-s', '--shell'): do_shell = True elif option in ('-w', '--watch'): do_watch = True elif option in ('-a', '--all'): watch_all = True elif option in ('-v', '--verbose'): coloredlogs.increase_verbosity() elif option in ('-q', '--quiet'): coloredlogs.decrease_verbosity() elif option in ('-h', '--help'): usage(__doc__) sys.exit(0) else: raise Exception("Unhandled option!") if not arguments: usage(__doc__) sys.exit(0) elif len(arguments) > 2: raise Exception("only two positional arguments allowed") # Create a ConfigLoader object and prepare to pass it to the program to # avoid scanning for configuration files more than once (which isn't a # real problem but does generate somewhat confusing log output). loader = ConfigLoader(program_name='unlock-remote-system') program_opts['config_loader'] = loader # Check if a single positional argument was given that matches the name # of a user defined configuration section. if len(arguments) == 1 and arguments[0] in loader.section_names: logger.info("Loading configuration section '%s' ..", arguments[0]) program_opts['config_section'] = arguments[0] else: # The SSH connection profile of the pre-boot environment # is given as the first positional argument. program_opts['pre_boot'] = ConnectionProfile( expression=arguments[0], identity_file=identity_file) # The SSH connection profile of the post-boot environment # can be given as the second positional argument, otherwise # it will be inferred from the connection profile of # the pre-boot environment. if len(arguments) == 2: program_opts['post_boot'] = ConnectionProfile( expression=arguments[1]) else: # By default we don't use root to login to the post-boot environment. program_opts['post_boot'] = ConnectionProfile( expression=arguments[0]) program_opts['post_boot'].username = find_local_username() # Prompt the operator to enter the disk encryption password for the remote host? if not program_opts.get('password'): program_opts['password'] = prompt_for_password( program_opts['pre_boot'].hostname) except Exception as e: warning("Failed to parse command line arguments! (%s)", e) sys.exit(1) # Try to unlock the remote system. try: if do_watch and watch_all: watch_all_systems(loader) else: with EncryptedSystem(**program_opts) as program: if do_watch: program.watch_system() else: program.unlock_system() if do_shell: start_interactive_shell(program.post_context) except EncryptedSystemError as e: logger.error("Aborting due to error: %s", e) sys.exit(2) except Exception: logger.exception("Aborting due to unexpected exception!") sys.exit(3)
def main(): """Command line interface for the ``executor`` program.""" # Enable logging to the terminal and system log. coloredlogs.install(syslog=True) # Command line option defaults. command_timeout = 0 exclusive = False fudge_factor = 0 lock_name = None lock_timeout = 0 # Parse the command line options. try: options, arguments = getopt.getopt(sys.argv[1:], 'eT:l:t:f:vqh', [ 'exclusive', 'lock-timeout=', 'lock-file=', 'timeout=', 'fudge-factor=', 'verbose', 'quiet', 'help', ]) for option, value in options: if option in ('-e', '--exclusive'): exclusive = True elif option in ('-T', '--lock-timeout'): lock_timeout = parse_timespan(value) elif option in ('-l', '--lock-file'): lock_name = value elif option in ('-t', '--timeout'): command_timeout = parse_timespan(value) elif option in ('-f', '--fudge-factor'): fudge_factor = parse_timespan(value) elif option in ('-v', '--verbose'): coloredlogs.increase_verbosity() elif option in ('-q', '--quiet'): coloredlogs.decrease_verbosity() elif option in ('-h', '--help'): usage(__doc__) sys.exit(0) else: assert False, "Unhandled option!" # Make sure the operator provided a program to execute. if not arguments: usage(__doc__) sys.exit(0) # Make sure the program actually exists. program_name = arguments[0] if not os.path.isfile(program_name): # Only search the $PATH if the given program name # doesn't already include one or more path segments. if program_name == os.path.basename(program_name): matching_programs = which(program_name) if matching_programs: program_name = matching_programs[0] # The subprocess.Popen() call later on doesn't search the $PATH so we # make sure to give it the absolute pathname to the program. arguments[0] = program_name except Exception as e: warning("Failed to parse command line arguments: %s", e) sys.exit(1) # Apply the requested fudge factor. apply_fudge_factor(fudge_factor) # Run the requested command. try: if exclusive: # Select a default lock file name? if not lock_name: lock_name = os.path.basename(arguments[0]) logger.debug("Using base name of command as lock file name (%s).", lock_name) lock_file = get_lock_path(lock_name) lock = InterProcessLock(path=lock_file, logger=logger) logger.debug("Trying to acquire exclusive lock: %s", lock_file) if lock.acquire(blocking=(lock_timeout > 0), max_delay=lock_timeout): logger.info("Successfully acquired exclusive lock: %s", lock_file) run_command(arguments, timeout=command_timeout) else: logger.error("Failed to acquire exclusive lock: %s", lock_file) sys.exit(1) else: run_command(arguments, timeout=command_timeout) except ExternalCommandFailed as e: logger.error("%s", e.error_message) sys.exit(e.command.returncode)
def main(): """Command line interface for the ``deb-pkg-tools`` program.""" # Configure logging output. coloredlogs.install() # Command line option defaults. prompt = True actions = [] control_file = None control_fields = {} directory = None # Initialize the package cache. cache = get_default_cache() # Parse the command line options. try: options, arguments = getopt.getopt(sys.argv[1:], 'i:c:C:p:s:b:u:a:d:w:yvh', [ 'inspect=', 'collect=', 'check=', 'patch=', 'set=', 'build=', 'update-repo=', 'activate-repo=', 'deactivate-repo=', 'with-repo=', 'gc', 'garbage-collect', 'yes', 'verbose', 'help' ]) for option, value in options: if option in ('-i', '--inspect'): actions.append(functools.partial(show_package_metadata, archive=value)) elif option in ('-c', '--collect'): directory = check_directory(value) elif option in ('-C', '--check'): actions.append(functools.partial(check_package, archive=value, cache=cache)) elif option in ('-p', '--patch'): control_file = os.path.abspath(value) assert os.path.isfile(control_file), "Control file does not exist!" elif option in ('-s', '--set'): name, _, value = value.partition(':') control_fields[name] = value.strip() elif option in ('-b', '--build'): actions.append(functools.partial( build_package, check_directory(value), repository=tempfile.gettempdir(), )) elif option in ('-u', '--update-repo'): actions.append(functools.partial(update_repository, directory=check_directory(value), cache=cache)) elif option in ('-a', '--activate-repo'): actions.append(functools.partial(activate_repository, check_directory(value))) elif option in ('-d', '--deactivate-repo'): actions.append(functools.partial(deactivate_repository, check_directory(value))) elif option in ('-w', '--with-repo'): actions.append(functools.partial(with_repository_wrapper, directory=check_directory(value), command=arguments, cache=cache)) elif option in ('--gc', '--garbage-collect'): actions.append(functools.partial(cache.collect_garbage, force=True)) elif option in ('-y', '--yes'): prompt = False elif option in ('-v', '--verbose'): coloredlogs.increase_verbosity() elif option in ('-h', '--help'): usage(__doc__) return # We delay the patch_control_file() and collect_packages() partials # until all command line options have been parsed, to ensure that the # order of the command line options doesn't matter. if control_file: if not control_fields: raise Exception("Please specify one or more control file fields to patch!") actions.append(functools.partial(patch_control_file, control_file, control_fields)) if directory: actions.append(functools.partial(collect_packages, archives=arguments, directory=directory, prompt=prompt, cache=cache)) except Exception as e: warning("Error: %s", e) sys.exit(1) # Execute the selected action. try: if actions: for action in actions: action() cache.collect_garbage() else: usage(__doc__) except Exception as e: logger.exception("An error occurred!") sys.exit(1)
def main(): """ Command line interface for the ``py2deb`` program. """ # Configure terminal output. coloredlogs.install() try: # Initialize a package converter. converter = PackageConverter() # Parse and validate the command line options. options, arguments = getopt.getopt(sys.argv[1:], 'c:r:yvh', [ 'config=', 'repository=', 'name-prefix=', 'no-name-prefix=', 'rename=', 'install-prefix=', 'install-alternative=', 'python-callback=', 'report-dependencies=', 'yes', 'verbose', 'help', ]) control_file_to_update = None for option, value in options: if option in ('-c', '--config'): converter.load_configuration_file(value) elif option in ('-r', '--repository'): converter.set_repository(value) elif option == '--name-prefix': converter.set_name_prefix(value) elif option == '--no-name-prefix': converter.rename_package(value, value) elif option == '--rename': python_package_name, _, debian_package_name = value.partition(',') converter.rename_package(python_package_name, debian_package_name) elif option == '--install-prefix': converter.set_install_prefix(value) elif option == '--install-alternative': link, _, path = value.partition(',') converter.install_alternative(link, path) elif option == '--python-callback': converter.set_python_callback(value) elif option == '--report-dependencies': control_file_to_update = value if not os.path.isfile(control_file_to_update): msg = "The given control file doesn't exist! (%s)" raise Exception(msg % control_file_to_update) elif option in ('-y', '--yes'): converter.set_auto_install(True) elif option in ('-v', '--verbose'): coloredlogs.increase_verbosity() elif option in ('-h', '--help'): usage(__doc__) return else: assert False, "Unhandled option!" except Exception as e: warning("Failed to parse command line arguments: %s", e) sys.exit(1) # Convert the requested package(s). try: if arguments: archives, relationships = converter.convert(arguments) if relationships and control_file_to_update: patch_control_file(control_file_to_update, dict(depends=relationships)) else: usage(__doc__) except Exception: logger.exception("Caught an unhandled exception!") sys.exit(1)
def main(): """The command line interface of the ``vcs-tool`` program.""" # Initialize logging to the terminal. coloredlogs.install() # Command line option defaults. repository = None revision = None actions = [] # Parse the command line arguments. try: options, arguments = getopt.gnu_getopt(sys.argv[1:], 'r:dnisume:vqh', [ 'repository=', 'rev=', 'revision=', 'release=', 'find-directory', 'find-revision-number', 'find-revision-id', 'list-releases', 'select-release=', 'sum-revisions', 'vcs-control-field', 'update', 'merge-up', 'export=', 'verbose', 'quiet', 'help', ]) for option, value in options: if option in ('-r', '--repository'): value = value.strip() assert value, "Please specify the name of a repository! (using -r, --repository)" repository = coerce_repository(value) elif option in ('--rev', '--revision'): revision = value.strip() assert revision, "Please specify a nonempty revision string!" elif option == '--release': # TODO Right now --release and --merge-up cannot be combined # because the following statements result in a global # revision id which is immutable. If release objects had # something like an optional `mutable_revision_id' it # should be possible to support the combination of # --release and --merge-up. assert repository, "Please specify a repository first!" release_id = value.strip() assert release_id in repository.releases, "The given release identifier is invalid!" revision = repository.releases[release_id].revision.revision_id elif option in ('-d', '--find-directory'): assert repository, "Please specify a repository first!" actions.append(functools.partial(print_directory, repository)) elif option in ('-n', '--find-revision-number'): assert repository, "Please specify a repository first!" actions.append(functools.partial(print_revision_number, repository, revision)) elif option in ('-i', '--find-revision-id'): assert repository, "Please specify a repository first!" actions.append(functools.partial(print_revision_id, repository, revision)) elif option == '--list-releases': assert repository, "Please specify a repository first!" actions.append(functools.partial(print_releases, repository)) elif option == '--select-release': assert repository, "Please specify a repository first!" release_id = value.strip() assert release_id, "Please specify a nonempty release identifier!" actions.append(functools.partial(print_selected_release, repository, release_id)) elif option in ('-s', '--sum-revisions'): assert len(arguments) >= 2, "Please specify one or more repository/revision pairs!" actions.append(functools.partial(print_summed_revisions, arguments)) arguments = [] elif option == '--vcs-control-field': assert repository, "Please specify a repository first!" actions.append(functools.partial(print_vcs_control_field, repository, revision)) elif option in ('-u', '--update'): assert repository, "Please specify a repository first!" actions.append(functools.partial(repository.update)) elif option in ('-m', '--merge-up'): assert repository, "Please specify a repository first!" actions.append(functools.partial( repository.merge_up, target_branch=revision, feature_branch=arguments[0] if arguments else None, )) elif option in ('-e', '--export'): directory = value.strip() assert repository, "Please specify a repository first!" assert directory, "Please specify the directory where the revision should be exported!" actions.append(functools.partial(repository.export, directory, revision)) elif option in ('-v', '--verbose'): coloredlogs.increase_verbosity() elif option in ('-q', '--quiet'): coloredlogs.decrease_verbosity() elif option in ('-h', '--help'): usage(__doc__) return if not actions: usage(__doc__) return except Exception as e: warning("Error: %s", e) sys.exit(1) # Execute the requested action(s). try: for action in actions: action() except Exception: logger.exception("Failed to execute requested action(s)!") sys.exit(1)