示例#1
0
def shlex_process_stdin(process_command, helptext):
    """
    Use shlex to process stdin line-by-line.
    Also prints help text.

    Requires that @process_command be a Click command object, used for
    processing single lines of input. helptext is prepended to the standard
    message printed to interactive sessions.
    """
    # if input is interactive, print help to stderr
    if sys.stdin.isatty():
        safeprint(
            (
                "{}\n".format(helptext) + "Lines are split with shlex in POSIX mode: "
                "https://docs.python.org/library/shlex.html#parsing-rules\n"
                "Terminate input with Ctrl+D or <EOF>\n"
            ),
            write_to_stderr=True,
        )

    # use readlines() rather than implicit file read line looping to force
    # python to properly capture EOF (otherwise, EOF acts as a flush and
    # things get weird)
    for line in sys.stdin.readlines():
        # get the argument vector:
        # do a shlex split to handle quoted paths with spaces in them
        # also lets us have comments with #
        argv = shlex.split(line, comments=True)
        if argv:
            try:
                process_command.main(args=argv)
            except SystemExit as e:
                if e.code != 0:
                    raise
示例#2
0
    def check_completed():
        completed = client.task_wait(
            task_id, timeout=polling_interval, polling_interval=polling_interval
        )
        if completed:
            if heartbeat:
                safeprint("", write_to_stderr=True)
            # meowing tasks wake up!
            if meow:
                safeprint(
                    r"""
                  _..
  /}_{\           /.-'
 ( a a )-.___...-'/
 ==._.==         ;
      \ i _..._ /,
      {_;/   {_//""",
                    write_to_stderr=True,
                )

            # TODO: possibly update TransferClient.task_wait so that we don't
            # need to do an extra fetch to get the task status after completion
            res = client.get_task(task_id)
            formatted_print(res, text_format=FORMAT_SILENT)

            status = res["status"]
            if status == "SUCCEEDED":
                click.get_current_context().exit(0)
            else:
                click.get_current_context().exit(1)

        return completed
示例#3
0
def resolve_id_or_name(client, bookmark_id_or_name):
    # leading/trailing whitespace doesn't make sense for UUIDs and the Transfer
    # service outright forbids it for bookmark names, so we can strip it off
    bookmark_id_or_name = bookmark_id_or_name.strip()

    res = None
    try:
        UUID(bookmark_id_or_name)  # raises ValueError if argument not a UUID
    except ValueError:
        pass
    else:
        try:
            res = client.get_bookmark(bookmark_id_or_name.lower())
        except TransferAPIError as exception:
            if exception.code != 'BookmarkNotFound':
                raise

    if not res:  # non-UUID input or UUID not found; fallback to match by name
        try:
            # n.b. case matters to the Transfer service for bookmark names, so
            # two bookmarks can exist whose names vary only by their case
            res = next(bookmark_row for bookmark_row in client.bookmark_list()
                       if bookmark_row['name'] == bookmark_id_or_name)

        except StopIteration:
            safeprint(
                u'No bookmark found for "{}"'.format(bookmark_id_or_name),
                write_to_stderr=True)
            click.get_current_context().exit(1)

    return res
示例#4
0
def do_link_login_flow():
    """
    Prompts the user with a link to authorize the CLI to act on their behalf.
    """
    # get the NativeApp client object
    native_client = internal_auth_client()

    # start the Native App Grant flow, prefilling the
    # named grant label on the consent page if we can get a
    # hostname for the local system
    label = platform.node() or None
    native_client.oauth2_start_flow(refresh_tokens=True,
                                    prefill_named_grant=label,
                                    requested_scopes=SCOPES)

    # prompt
    linkprompt = 'Please log into Globus here'
    safeprint('{0}:\n{1}\n{2}\n{1}\n'.format(
        linkprompt, '-' * len(linkprompt),
        native_client.oauth2_get_authorize_url()))

    # come back with auth code
    auth_code = click.prompt(
        'Enter the resulting Authorization Code here').strip()

    # finish login flow
    exchange_code_and_store_config(native_client, auth_code)
示例#5
0
    def check_completed():
        completed = client.task_wait(task_id,
                                     timeout=polling_interval,
                                     polling_interval=polling_interval)
        if completed:
            if heartbeat:
                safeprint('', write_to_stderr=True)
            # meowing tasks wake up!
            if meow:
                safeprint(r"""
                  _..
  /}_{\           /.-'
 ( a a )-.___...-'/
 ==._.==         ;
      \ i _..._ /,
      {_;/   {_//""",
                          write_to_stderr=True)

            # TODO: possibly update TransferClient.task_wait so that we don't
            # need to do an extra fetch to get the task status after completion
            res = client.get_task(task_id)
            formatted_print(res, text_format=FORMAT_SILENT)

            status = res['status']
            if status == 'SUCCEEDED':
                click.get_current_context().exit(0)
            else:
                click.get_current_context().exit(1)

        return completed
示例#6
0
def whoami_command(linked_identities):
    """
    Executor for `globus whoami`
    """
    client = get_auth_client()

    # get userinfo from auth.
    # if we get back an error the user likely needs to log in again
    try:
        res = client.oauth2_userinfo()
    except AuthAPIError:
        safeprint(
            "Unable to get user information. Please try " "logging in again.",
            write_to_stderr=True,
        )
        click.get_current_context().exit(1)

    print_command_hint(
        "For information on which identities are in session see\n"
        "  globus session show\n"
    )

    # --linked-identities either displays all usernames or a table if verbose
    if linked_identities:
        try:
            formatted_print(
                res["identity_set"],
                fields=[
                    ("Username", "username"),
                    ("Name", "name"),
                    ("ID", "sub"),
                    ("Email", "email"),
                ],
                simple_text=(
                    None
                    if is_verbose()
                    else "\n".join([x["username"] for x in res["identity_set"]])
                ),
            )
        except KeyError:
            safeprint(
                "Your current login does not have the consents required "
                "to view your full identity set. Please log in again "
                "to agree to the required consents.",
                write_to_stderr=True,
            )

    # Default output is the top level data
    else:
        formatted_print(
            res,
            text_format=FORMAT_TEXT_RECORD,
            fields=[
                ("Username", "preferred_username"),
                ("Name", "name"),
                ("ID", "sub"),
                ("Email", "email"),
            ],
            simple_text=(None if is_verbose() else res["preferred_username"]),
        )
示例#7
0
def shlex_process_stdin(process_command, helptext):
    """
    Use shlex to process stdin line-by-line.
    Also prints help text.

    Requires that @process_command be a Click command object, used for
    processing single lines of input. helptext is prepended to the standard
    message printed to interactive sessions.
    """
    # if input is interactive, print help to stderr
    if sys.stdin.isatty():
        safeprint(('{}\n'.format(helptext) +
                   'Lines are split with shlex in POSIX mode: '
                   'https://docs.python.org/library/shlex.html#parsing-rules\n'
                   'Terminate input with Ctrl+D or <EOF>\n'),
                  write_to_stderr=True)

    # use readlines() rather than implicit file read line looping to force
    # python to properly capture EOF (otherwise, EOF acts as a flush and
    # things get weird)
    for line in sys.stdin.readlines():
        # get the argument vector:
        # do a shlex split to handle quoted paths with spaces in them
        # also lets us have comments with #
        argv = shlex.split(line, comments=True)
        if argv:
            try:
                process_command.main(args=argv)
            except SystemExit as e:
                if e.code != 0:
                    raise
示例#8
0
def do_link_auth_flow(session_params={}, force_new_client=False):
    """
    Prompts the user with a link to authenticate with globus auth
    and authorize the CLI to act on their behalf.
    """
    # get the ConfidentialApp client object
    auth_client = internal_auth_client(
        requires_instance=True, force_new_client=force_new_client)

    # start the Confidential App Grant flow
    auth_client.oauth2_start_flow(
        redirect_uri=auth_client.base_url + 'v2/web/auth-code',
        refresh_tokens=True, requested_scopes=SCOPES)

    # prompt
    additional_params = {"prompt": "login"}
    additional_params.update(session_params)
    linkprompt = 'Please authenticate with Globus here'
    safeprint('{0}:\n{1}\n{2}\n{1}\n'
              .format(linkprompt, '-' * len(linkprompt),
                      auth_client.oauth2_get_authorize_url(
                        additional_params=additional_params)))

    # come back with auth code
    auth_code = click.prompt(
        'Enter the resulting Authorization Code here').strip()

    # finish auth flow
    exchange_code_and_store_config(auth_client, auth_code)
    return True
示例#9
0
def autoactivate(client, endpoint_id, if_expires_in=None):
    """
    Attempts to auto-activate the given endpoint with the given client
    If auto-activation fails, parses the returned activation requirements
    to determine which methods of activation are supported, then tells
    the user to use 'globus endpoint activate' with the correct options(s)
    """
    kwargs = {}
    if if_expires_in is not None:
        kwargs["if_expires_in"] = if_expires_in

    res = client.endpoint_autoactivate(endpoint_id, **kwargs)
    if res["code"] == "AutoActivationFailed":

        message = (
            "The endpoint could not be auto-activated and must be "
            "activated before it can be used.\n\n"
            + activation_requirements_help_text(res, endpoint_id)
        )

        safeprint(message, write_to_stderr=True)
        click.get_current_context().exit(1)

    else:
        return res
示例#10
0
def resolve_id_or_name(client, bookmark_id_or_name):
    # leading/trailing whitespace doesn't make sense for UUIDs and the Transfer
    # service outright forbids it for bookmark names, so we can strip it off
    bookmark_id_or_name = bookmark_id_or_name.strip()

    res = None
    try:
        UUID(bookmark_id_or_name)  # raises ValueError if argument not a UUID
    except ValueError:
        pass
    else:
        try:
            res = client.get_bookmark(bookmark_id_or_name.lower())
        except TransferAPIError as exception:
            if exception.code != "BookmarkNotFound":
                raise

    if not res:  # non-UUID input or UUID not found; fallback to match by name
        try:
            # n.b. case matters to the Transfer service for bookmark names, so
            # two bookmarks can exist whose names vary only by their case
            res = next(
                bookmark_row
                for bookmark_row in client.bookmark_list()
                if bookmark_row["name"] == bookmark_id_or_name
            )

        except StopIteration:
            safeprint(
                u'No bookmark found for "{}"'.format(bookmark_id_or_name),
                write_to_stderr=True,
            )
            click.get_current_context().exit(1)

    return res
示例#11
0
文件: rm.py 项目: tkuennen/globus-cli
def rm_command(ignore_missing, star_silent, recursive, enable_globs,
               endpoint_plus_path, label, submission_id, dry_run, deadline,
               skip_activation_check, notify, meow, heartbeat,
               polling_interval, timeout):
    """
    Executor for `globus rm`
    """
    endpoint_id, path = endpoint_plus_path

    client = get_client()

    # attempt to activate unless --skip-activation-check is given
    if not skip_activation_check:
        autoactivate(client, endpoint_id, if_expires_in=60)

    delete_data = DeleteData(client,
                             endpoint_id,
                             label=label,
                             recursive=recursive,
                             ignore_missing=ignore_missing,
                             submission_id=submission_id,
                             deadline=deadline,
                             skip_activation_check=skip_activation_check,
                             interpret_globs=enable_globs,
                             **notify)

    if not star_silent and enable_globs and path.endswith('*'):
        # not intuitive, but `click.confirm(abort=True)` prints to stdout
        # unnecessarily, which we don't really want...
        # only do this check if stderr is a pty
        if (err_is_terminal() and term_is_interactive() and not click.confirm(
                'Are you sure you want to delete all files matching "{}"?'.
                format(path),
                err=True)):
            safeprint('Aborted.', write_to_stderr=True)
            click.get_current_context().exit(1)
    delete_data.add_item(path)

    if dry_run:
        formatted_print(delete_data,
                        response_key='DATA',
                        fields=[('Path', 'path')])
        # exit safely
        return

    # Print task submission to stderr so that `-Fjson` is still correctly
    # respected, as it will be by `task wait`
    res = client.submit_delete(delete_data)
    task_id = res['task_id']
    safeprint('Delete task submitted under ID "{}"'.format(task_id),
              write_to_stderr=True)

    # do a `task wait` equivalent, including printing and correct exit status
    task_wait_with_io(meow,
                      heartbeat,
                      polling_interval,
                      timeout,
                      task_id,
                      client=client)
示例#12
0
def list_commands():
    def _print_cmd(command):
        # print commands with short_help
        indent = 4
        min_space = 2

        # if the output would be pinched too close together, or if the command
        # name would overflow, use two separate lines
        if len(command.name) > _command_length - min_space:
            safeprint(" " * indent + command.name)
            safeprint(" " * (indent + _command_length) + command.short_help)
        # otherwise, it's all cool to cram into one line, just ljust command
        # names so that they form a nice column
        else:
            safeprint(
                " " * indent
                + "{}{}".format(command.name.ljust(_command_length), command.short_help)
            )

    def _print_cmd_group(command, parent_names):
        parents = " ".join(parent_names)
        if parents:
            parents = parents + " "
        safeprint("\n=== {}{} ===\n".format(parents, command.name))

    def _recursive_list_commands(command, parent_names=None):
        if parent_names is None:
            parent_names = []

        # names of parent commands, including this one, for passthrough to
        # recursive calls
        new_parent_names = copy.copy(parent_names) + [command.name]

        # group-style commands are printed as headers
        if isinstance(command, click.MultiCommand):
            _print_cmd_group(command, parent_names)

            # get the set of subcommands and recursively print all of them
            group_cmds = [
                v
                for v in command.commands.values()
                if isinstance(v, click.MultiCommand)
            ]
            func_cmds = [v for v in command.commands.values() if v not in group_cmds]
            # we want to print them all, but func commands first
            for cmd in func_cmds + group_cmds:
                _recursive_list_commands(cmd, parent_names=new_parent_names)

        # individual commands are printed solo
        else:
            _print_cmd(command)

    # get the root context (the click context for the entire CLI tree)
    root_ctx = click.get_current_context().find_root()

    _recursive_list_commands(root_ctx.command)
    # get an extra newline at the end
    safeprint("")
示例#13
0
 def wait_for_code(self):
     # workaround for handling control-c interrupt.
     # relevant Python issue discussing this behavior:
     # https://bugs.python.org/issue1360
     try:
         return self._auth_code_queue.get(block=True, timeout=3600)
     except Queue.Empty:
         safeprint("Login timed out. Please try again.", write_to_stderr=True)
         sys.exit(1)
示例#14
0
 def callback(ctx, param, value):
     if not value or ctx.resilient_parsing:
         return
     if value == "BASH":
         safeprint(bash_shell_completer)
     elif value == "ZSH":
         safeprint(zsh_shell_completer)
     else:
         raise ValueError('Unsupported shell completion')
     click.get_current_context().exit(0)
示例#15
0
 def callback(ctx, param, value):
     if not value or ctx.resilient_parsing:
         return
     if value == "BASH":
         safeprint(bash_shell_completer)
     elif value == "ZSH":
         safeprint(zsh_shell_completer)
     else:
         raise ValueError("Unsupported shell completion")
     click.get_current_context().exit(0)
示例#16
0
 def wait_for_code(self):
     # workaround for handling control-c interrupt.
     # relevant Python issue discussing this behavior:
     # https://bugs.python.org/issue1360
     try:
         return self._auth_code_queue.get(block=True, timeout=3600)
     except Queue.Empty:
         safeprint('Login timed out. Please try again.',
                   write_to_stderr=True)
         sys.exit(1)
示例#17
0
def list_commands():
    def _print_cmd(command):
        # print commands with short_help
        indent = 4
        min_space = 2

        # if the output would be pinched too close together, or if the command
        # name would overflow, use two separate lines
        if len(command.name) > _command_length - min_space:
            safeprint(' '*indent + command.name)
            safeprint(' '*(indent + _command_length) + command.short_help)
        # otherwise, it's all cool to cram into one line, just ljust command
        # names so that they form a nice column
        else:
            safeprint(' '*indent + '{}{}'.format(
                command.name.ljust(_command_length), command.short_help))

    def _print_cmd_group(command, parent_names):
        parents = ' '.join(parent_names)
        if parents:
            parents = parents + ' '
        safeprint('\n=== {}{} ===\n'.format(parents, command.name))

    def _recursive_list_commands(command, parent_names=None):
        if parent_names is None:
            parent_names = []

        # names of parent commands, including this one, for passthrough to
        # recursive calls
        new_parent_names = copy.copy(parent_names) + [command.name]

        # group-style commands are printed as headers
        if isinstance(command, click.MultiCommand):
            _print_cmd_group(command, parent_names)

            # get the set of subcommands and recursively print all of them
            group_cmds = [v for v in command.commands.values()
                          if isinstance(v, click.MultiCommand)]
            func_cmds = [v for v in command.commands.values()
                         if v not in group_cmds]
            # we want to print them all, but func commands first
            for cmd in (func_cmds + group_cmds):
                _recursive_list_commands(cmd, parent_names=new_parent_names)

        # individual commands are printed solo
        else:
            _print_cmd(command)

    # get the root context (the click context for the entire CLI tree)
    root_ctx = click.get_current_context().find_root()

    _recursive_list_commands(root_ctx.command)
    # get an extra newline at the end
    safeprint('')
示例#18
0
def filename_command():
    """
    Executor for `globus config filename`
    """
    try:
        config = get_config_obj(file_error=True)
    except IOError as e:
        safeprint(e, write_to_stderr=True)
        click.get_current_context().exit(1)
    else:
        safeprint(config.filename)
示例#19
0
def filename_command():
    """
    Executor for `globus config filename`
    """
    try:
        config = get_config_obj(file_error=True)
    except IOError as e:
        safeprint(e, write_to_stderr=True)
        click.get_current_context().exit(1)
    else:
        safeprint(config.filename)
示例#20
0
 def invoke(self, ctx):
     # if no subcommand was given (but, potentially, flags were passed),
     # ctx.protected_args will be empty
     # improves upon the built-in detection given on click.Group by
     # no_args_is_help , since that treats options (without a subcommand) as
     # being arguments and blows up with a "Missing command" failure
     # for reference to the original version (as of 2017-02-26):
     # https://github.com/pallets/click/blob/02ea9ee7e864581258b4902d6e6c1264b0226b9f/click/core.py#L1039-L1052
     if self.no_args_is_help and not ctx.protected_args:
         safeprint(ctx.get_help())
         ctx.exit()
     return super(GlobusCommandGroup, self).invoke(ctx)
示例#21
0
 def invoke(self, ctx):
     # if no subcommand was given (but, potentially, flags were passed),
     # ctx.protected_args will be empty
     # improves upon the built-in detection given on click.Group by
     # no_args_is_help , since that treats options (without a subcommand) as
     # being arguments and blows up with a "Missing command" failure
     # for reference to the original version (as of 2017-02-26):
     # https://github.com/pallets/click/blob/02ea9ee7e864581258b4902d6e6c1264b0226b9f/click/core.py#L1039-L1052
     if self.no_args_is_help and not ctx.protected_args:
         safeprint(ctx.get_help())
         ctx.exit()
     return super(GlobusCommandGroup, self).invoke(ctx)
示例#22
0
def login_command(force, no_local_server):
    # if not forcing, stop if user already logged in
    if not force and check_logged_in():
        safeprint(_LOGGED_IN_RESPONSE)
        return

    # use a link login if remote session or user requested
    if no_local_server or is_remote_session():
        do_link_login_flow()
    # otherwise default to a local server login flow
    else:
        do_local_server_login_flow()
示例#23
0
def show_command(parameter):
    """
    Executor for `globus config show`
    """
    section = "cli"
    if '.' in parameter:
        section, parameter = parameter.split('.', 1)

    value = lookup_option(parameter, section=section)

    if value is None:
        safeprint('{} not set'.format(parameter))
    else:
        safeprint('{} = {}'.format(parameter, value))
示例#24
0
def session_hook(exception):
    """
    Expects an exception with an authorization_paramaters field in its raw_json
    """
    safeprint(
        "The resource you are trying to access requires you to "
        "re-authenticate with specific identities."
    )

    params = exception.raw_json["authorization_parameters"]
    message = params.get("session_message")
    if message:
        safeprint("message: {}".format(message))

    identities = params.get("session_required_identities")
    if identities:
        id_str = " ".join(identities)
        safeprint(
            "Please run\n\n"
            "    globus session update {}\n\n"
            "to re-authenticate with the required identities".format(id_str)
        )
    else:
        safeprint(
            'Please use "globus session update" to re-authenticate '
            "with specific identities".format(id_str)
        )

    exit_with_mapped_status(exception.http_status)
示例#25
0
文件: show.py 项目: globus/globus-cli
def show_command(parameter):
    """
    Executor for `globus config show`
    """
    section = "cli"
    if "." in parameter:
        section, parameter = parameter.split(".", 1)

    value = lookup_option(parameter, section=section)

    if value is None:
        safeprint("{} not set".format(parameter))
    else:
        safeprint("{} = {}".format(parameter, value))
示例#26
0
    def _print_as_text():
        # if we're given simple text, print that and exit
        if simple_text is not None:
            safeprint(simple_text)
            return

        # if there's a preamble, print it beofre any other text
        if text_preamble is not None:
            safeprint(text_preamble)

        # if there's a response key, key into it
        data = (response_data
                if response_key is None else
                response_data[response_key])

        #  do the various kinds of printing
        if text_format == FORMAT_TEXT_TABLE:
            _assert_fields()
            print_table(data, fields)
        elif text_format == FORMAT_TEXT_RECORD:
            _assert_fields()
            colon_formatted_print(data, fields)
        elif text_format == FORMAT_TEXT_RAW:
            safeprint(data)
        elif text_format == FORMAT_TEXT_CUSTOM:
            _custom_text_formatter(data)

        # if there's an epilog, print it after any text
        if text_epilog is not None:
            safeprint(text_epilog)
示例#27
0
def print_unix_response(res):
    res = _jmespath_preprocess(res)
    try:
        unix_formatted_print(res)
    # Attr errors indicate that we got data which cannot be unix formatted
    # likely a scalar + non-scalar in an array, though there may be other cases
    # print good error and exit(2) (Count this as UsageError!)
    except AttributeError:
        safeprint(
            'UNIX formatting of output failed.'
            '\n  '
            'This usually means that data has a structure which cannot be '
            'handled by the UNIX formatter.'
            '\n  '
            'To avoid this error in the future, ensure that you query the '
            'exact properties you want from output data with "--jmespath"',
            write_to_stderr=True)
        click.get_current_context().exit(2)
示例#28
0
def whoami_command(linked_identities):
    """
    Executor for `globus whoami`
    """
    client = get_auth_client()

    # get userinfo from auth.
    # if we get back an error the user likely needs to log in again
    try:
        res = client.oauth2_userinfo()
    except AuthAPIError:
        safeprint(
            'Unable to get user information. Please try '
            'logging in again.',
            write_to_stderr=True)
        click.get_current_context().exit(1)

    print_command_hint(
        "For information on which identities are in session see\n"
        "  globus session show\n")

    # --linked-identities either displays all usernames or a table if verbose
    if linked_identities:
        try:
            formatted_print(res["identity_set"],
                            fields=[("Username", "username"), ("Name", "name"),
                                    ("ID", "sub"), ("Email", "email")],
                            simple_text=(None if is_verbose() else "\n".join(
                                [x["username"] for x in res["identity_set"]])))
        except KeyError:
            safeprint(
                "Your current login does not have the consents required "
                "to view your full identity set. Please log in again "
                "to agree to the required consents.",
                write_to_stderr=True)

    # Default output is the top level data
    else:
        formatted_print(
            res,
            text_format=FORMAT_TEXT_RECORD,
            fields=[("Username", "preferred_username"), ("Name", "name"),
                    ("ID", "sub"), ("Email", "email")],
            simple_text=(None if is_verbose() else res["preferred_username"]))
示例#29
0
文件: set.py 项目: globus/globus-cli
def set_command(value, parameter):
    """
    Executor for `globus config set`
    """
    conf = get_config_obj()

    section = "cli"
    if "." in parameter:
        section, parameter = parameter.split(".", 1)

    # ensure that the section exists
    if section not in conf:
        conf[section] = {}
    # set the value for the given parameter
    conf[section][parameter] = value

    # write to disk
    safeprint("Writing updated config to {}".format(conf.filename))
    conf.write()
示例#30
0
def remove_command(parameter):
    """
    Executor for `globus config remove`
    """
    conf = get_config_obj()

    section = "cli"
    if '.' in parameter:
        section, parameter = parameter.split('.', 1)

    # ensure that the section exists
    if section not in conf:
        conf[section] = {}
    # remove the value for the given parameter
    del conf[section][parameter]

    # write to disk
    safeprint('Writing updated config to {}'.format(conf.filename))
    conf.write()
示例#31
0
def do_bash_complete():
    comp_line = os.environ.get("COMP_LINE", "")
    comp_words, quoted = safe_split_line(comp_line)
    cur_index = int(os.environ.get("COMP_POINT", len(comp_line)))

    # now, figure out the current word in the line by "parsing"
    # the chunk of it up to cur_index
    partial_comp_words, _ = safe_split_line(comp_line[:cur_index])
    cur = partial_comp_words[-1]

    if comp_line[-1].isspace():
        cur = None
        completed_args = comp_words[1:]
    else:
        completed_args = comp_words[1:-1]

    choices = [name for (name, helpstr) in get_all_choices(completed_args, cur, quoted)]

    safeprint("\t".join(choices), newline=False)
    click.get_current_context().exit(0)
示例#32
0
def do_bash_complete():
    comp_line = os.environ.get('COMP_LINE', '')
    comp_words, quoted = safe_split_line(comp_line)
    cur_index = int(os.environ.get('COMP_POINT', len(comp_line)))

    # now, figure out the current word in the line by "parsing"
    # the chunk of it up to cur_index
    partial_comp_words, _ = safe_split_line(comp_line[:cur_index])
    cur = partial_comp_words[-1]

    if comp_line[-1].isspace():
        cur = None
        completed_args = comp_words[1:]
    else:
        completed_args = comp_words[1:-1]

    choices = [name for (name, helpstr) in
               get_all_choices(completed_args, cur, quoted)]

    safeprint('\t'.join(choices), newline=False)
    click.get_current_context().exit(0)
示例#33
0
    def _custom_text_format(identities):
        """
        Non-verbose text output is customized
        """
        def resolve_identity(value):
            """
            helper to deal with variable inputs and uncertain response order
            """
            for identity in identities:
                if identity["id"] == value:
                    return identity["username"]
                if identity["username"] == value:
                    return identity["id"]
            return "NO_SUCH_IDENTITY"

        # standard output is one resolved identity per line in the same order
        # as the inputs. A resolved identity is either a username if given a
        # UUID vice versa, or "NO_SUCH_IDENTITY" if the identity could not be
        # found
        for val in resolved_values:
            safeprint(resolve_identity(val))
示例#34
0
def init_command(default_output_format, default_myproxy_username):
    """
    Executor for `globus config init`
    """
    # now handle the output format, requires a little bit more care
    # first, prompt if it isn't given, but be clear that we have a sensible
    # default if they don't set it
    # then, make sure that if it is given, it's a valid format (discard
    # otherwise)
    # finally, set it only if given and valid
    if not default_output_format:
        safeprint(
            textwrap.fill(
                'This must be one of "json" or "text". Other values will be '
                'ignored. ENTER to skip.'))
        default_output_format = click.prompt(
            'Default CLI output format (cli.output_format)',
            default='text',
        ).strip().lower()
        if default_output_format not in ('json', 'text'):
            default_output_format = None

    if not default_myproxy_username:
        safeprint(textwrap.fill("ENTER to skip."))
        default_myproxy_username = click.prompt(
            "Default myproxy username (cli.default_myproxy_username)",
            default="",
            show_default=False).strip()

    # write to disk
    safeprint('\n\nWriting updated config to {0}'.format(
        os.path.expanduser('~/.globus.cfg')))
    write_option(OUTPUT_FORMAT_OPTNAME, default_output_format)
    write_option(MYPROXY_USERNAME_OPTNAME, default_myproxy_username)
示例#35
0
def do_local_server_auth_flow(session_params=None, force_new_client=False):
    """
    Starts a local http server, opens a browser to have the user authenticate,
    and gets the code redirected to the server (no copy and pasting required)
    """
    session_params = session_params or {}

    # start local server and create matching redirect_uri
    with start_local_server(listen=("127.0.0.1", 0)) as server:
        _, port = server.socket.getsockname()
        redirect_uri = "http://localhost:{}".format(port)

        # get the ConfidentialApp client object and start a flow
        auth_client = internal_auth_client(
            requires_instance=True, force_new_client=force_new_client
        )
        auth_client.oauth2_start_flow(
            refresh_tokens=True, redirect_uri=redirect_uri, requested_scopes=SCOPES
        )
        additional_params = {"prompt": "login"}
        additional_params.update(session_params)
        url = auth_client.oauth2_get_authorize_url(additional_params=additional_params)

        # open web-browser for user to log in, get auth code
        webbrowser.open(url, new=1)
        auth_code = server.wait_for_code()

    if isinstance(auth_code, LocalServerError):
        safeprint("Authorization failed: {}".format(auth_code), write_to_stderr=True)
        click.get_current_context().exit(1)
    elif isinstance(auth_code, Exception):
        safeprint(
            "Authorization failed with unexpected error:\n{}".format(auth_code),
            write_to_stderr=True,
        )
        click.get_current_context().exit(1)

    # finish auth flow and return true
    exchange_code_and_store_config(auth_client, auth_code)
    return True
示例#36
0
    def _custom_text_format(identities):
        """
        Non-verbose text output is customized
        """

        def resolve_identity(value):
            """
            helper to deal with variable inputs and uncertain response order
            """
            for identity in identities:
                if identity["id"] == value:
                    return identity["username"]
                if identity["username"] == value:
                    return identity["id"]
            return "NO_SUCH_IDENTITY"

        # standard output is one resolved identity per line in the same order
        # as the inputs. A resolved identity is either a username if given a
        # UUID vice versa, or "NO_SUCH_IDENTITY" if the identity could not be
        # found
        for val in resolved_values:
            safeprint(resolve_identity(val))
示例#37
0
def do_zsh_complete():
    commandline = os.environ["COMMANDLINE"]
    comp_words, quoted = safe_split_line(commandline)
    comp_words = comp_words[1:]

    if comp_words and not commandline.endswith(" "):
        cur = comp_words[-1]
        completed_args = comp_words[:-1]
    else:
        cur = None
        completed_args = comp_words

    def clean_help(helpstr):
        r"""
        Replace
            " with \"
            ' with '"'"'
            ` with \`
            $ with \$

        Because we'll put these single quote chars in '...'
        quotation, we need to do
        '   -- end single quotes
        "'" -- single quote string (will concatenate in ZSH)
        '   -- start single quotes again
        """
        return (
            helpstr.replace('"', '\\"')
            .replace("'", "'\"'\"'")
            .replace("`", "\\`")
            .replace("$", "\\$")
        )

    choices = get_all_choices(completed_args, cur, quoted)
    choices = [
        '{}\\:"{}"'.format(name, clean_help(helpstr)) for (name, helpstr) in choices
    ]

    safeprint("_arguments '*: :(({}))'".format("\n".join(choices)), newline=False)
示例#38
0
def exchange_code_and_store_config(native_client, auth_code):
    """
    Finishes login flow after code is gotten from command line or local server.
    Exchanges code for tokens and gets user info from auth.
    Stores tokens and user info in config.
    """
    # do a token exchange with the given code
    tkn = native_client.oauth2_exchange_code_for_tokens(auth_code)
    tkn = tkn.by_resource_server

    # extract access tokens from final response
    transfer_at = (tkn['transfer.api.globus.org']['access_token'])
    transfer_at_expires = (
        tkn['transfer.api.globus.org']['expires_at_seconds'])
    transfer_rt = (tkn['transfer.api.globus.org']['refresh_token'])
    auth_at = (tkn['auth.globus.org']['access_token'])
    auth_at_expires = (tkn['auth.globus.org']['expires_at_seconds'])
    auth_rt = (tkn['auth.globus.org']['refresh_token'])

    # get the identity that the tokens were issued to
    auth_client = AuthClient(authorizer=AccessTokenAuthorizer(auth_at))
    res = auth_client.oauth2_userinfo()

    # revoke any existing tokens
    for token_opt in (TRANSFER_RT_OPTNAME, TRANSFER_AT_OPTNAME,
                      AUTH_RT_OPTNAME, AUTH_AT_OPTNAME):
        token = lookup_option(token_opt)
        if token:
            native_client.oauth2_revoke_token(token)

    # write new tokens to config
    write_option(TRANSFER_RT_OPTNAME, transfer_rt)
    write_option(TRANSFER_AT_OPTNAME, transfer_at)
    write_option(TRANSFER_AT_EXPIRES_OPTNAME, transfer_at_expires)
    write_option(AUTH_RT_OPTNAME, auth_rt)
    write_option(AUTH_AT_OPTNAME, auth_at)
    write_option(AUTH_AT_EXPIRES_OPTNAME, auth_at_expires)

    safeprint(_LOGIN_EPILOG.format(res["preferred_username"]))
示例#39
0
def do_zsh_complete():
    commandline = os.environ['COMMANDLINE']
    comp_words, quoted = safe_split_line(commandline)
    comp_words = comp_words[1:]

    if comp_words and not commandline.endswith(' '):
        cur = comp_words[-1]
        completed_args = comp_words[:-1]
    else:
        cur = None
        completed_args = comp_words

    def clean_help(helpstr):
        r"""
        Replace
            " with \"
            ' with '"'"'
            ` with \`
            $ with \$

        Because we'll put these single quote chars in '...'
        quotation, we need to do
        '   -- end single quotes
        "'" -- single quote string (will concatenate in ZSH)
        '   -- start single quotes again
        """
        return (helpstr
                .replace('"', '\\"')
                .replace("'", "'\"'\"'")
                .replace("`", "\\`")
                .replace("$", "\\$"))

    choices = get_all_choices(completed_args, cur, quoted)
    choices = ['{}\\:"{}"'.format(name, clean_help(helpstr))
               for (name, helpstr) in choices]

    safeprint("_arguments '*: :(({}))'".format('\n'.join(choices)),
              newline=False)
示例#40
0
    def _custom_text_format(res):
        explicit_pauses = [
            field for field in EXPLICIT_PAUSE_MSG_FIELDS
            # n.b. some keys are absent for completed tasks
            if res.get(field[1])
        ]
        effective_pause_rules = res['pause_rules']

        if not explicit_pauses and not effective_pause_rules:
            safeprint('Task {} is not paused.'.format(task_id))
            click.get_current_context().exit(0)

        if explicit_pauses:
            formatted_print(
                res, fields=explicit_pauses, text_format=FORMAT_TEXT_RECORD,
                text_preamble='This task has been explicitly paused.\n',
                text_epilog='\n' if effective_pause_rules else None)

        if effective_pause_rules:
            formatted_print(
                effective_pause_rules, fields=PAUSE_RULE_DISPLAY_FIELDS,
                text_preamble=(
                    'The following pause rules are effective on this task:\n'))
示例#41
0
def autoactivate(client, endpoint_id, if_expires_in=None):
    """
    Attempts to auto-activate the given endpoint with the given client
    If auto-activation fails, parses the returned activation requirements
    to determine which methods of activation are supported, then tells
    the user to use 'globus endpoint activate' with the correct options(s)
    """
    kwargs = {}
    if if_expires_in is not None:
        kwargs['if_expires_in'] = if_expires_in

    res = client.endpoint_autoactivate(endpoint_id, **kwargs)
    if res["code"] == "AutoActivationFailed":

        message = ("The endpoint could not be auto-activated and must be "
                   "activated before it can be used.\n\n" +
                   activation_requirements_help_text(res, endpoint_id))

        safeprint(message, write_to_stderr=True)
        click.get_current_context().exit(1)

    else:
        return res
示例#42
0
def do_local_server_auth_flow(session_params={}, force_new_client=False):
    """
    Starts a local http server, opens a browser to have the user authenticate,
    and gets the code redirected to the server (no copy and pasting required)
    """
    # start local server and create matching redirect_uri
    with start_local_server(listen=('127.0.0.1', 0)) as server:
        _, port = server.socket.getsockname()
        redirect_uri = 'http://localhost:{}'.format(port)

        # get the ConfidentialApp client object and start a flow
        auth_client = internal_auth_client(
            requires_instance=True, force_new_client=force_new_client)
        auth_client.oauth2_start_flow(
            refresh_tokens=True, redirect_uri=redirect_uri,
            requested_scopes=SCOPES)
        additional_params = {"prompt": "login"}
        additional_params.update(session_params)
        url = auth_client.oauth2_get_authorize_url(
            additional_params=additional_params)

        # open web-browser for user to log in, get auth code
        webbrowser.open(url, new=1)
        auth_code = server.wait_for_code()

    if isinstance(auth_code, LocalServerError):
        safeprint('Authorization failed: {}'.format(auth_code),
                  write_to_stderr=True)
        click.get_current_context().exit(1)
    elif isinstance(auth_code, Exception):
        safeprint('Authorization failed with unexpected error:\n{}'.
                  format(auth_code), write_to_stderr=True)
        click.get_current_context().exit(1)

    # finish auth flow and return true
    exchange_code_and_store_config(auth_client, auth_code)
    return True
示例#43
0
def do_link_auth_flow(session_params=None, force_new_client=False):
    """
    Prompts the user with a link to authenticate with globus auth
    and authorize the CLI to act on their behalf.
    """
    session_params = session_params or {}

    # get the ConfidentialApp client object
    auth_client = internal_auth_client(
        requires_instance=True, force_new_client=force_new_client
    )

    # start the Confidential App Grant flow
    auth_client.oauth2_start_flow(
        redirect_uri=auth_client.base_url + "v2/web/auth-code",
        refresh_tokens=True,
        requested_scopes=SCOPES,
    )

    # prompt
    additional_params = {"prompt": "login"}
    additional_params.update(session_params)
    linkprompt = "Please authenticate with Globus here"
    safeprint(
        "{0}:\n{1}\n{2}\n{1}\n".format(
            linkprompt,
            "-" * len(linkprompt),
            auth_client.oauth2_get_authorize_url(additional_params=additional_params),
        )
    )

    # come back with auth code
    auth_code = click.prompt("Enter the resulting Authorization Code here").strip()

    # finish auth flow
    exchange_code_and_store_config(auth_client, auth_code)
    return True
示例#44
0
    def _print_cmd(command):
        # print commands with short_help
        indent = 4
        min_space = 2

        # if the output would be pinched too close together, or if the command
        # name would overflow, use two separate lines
        if len(command.name) > _command_length - min_space:
            safeprint(' '*indent + command.name)
            safeprint(' '*(indent + _command_length) + command.short_help)
        # otherwise, it's all cool to cram into one line, just ljust command
        # names so that they form a nice column
        else:
            safeprint(' '*indent + '{}{}'.format(
                command.name.ljust(_command_length), command.short_help))
示例#45
0
def local_id(personal):
    """
    Executor for `globus endpoint local-id`
    """
    if personal:
        try:
            ep_id = LocalGlobusConnectPersonal().endpoint_id
        except IOError as e:
            safeprint(e, write_to_stderr=True)
            click.get_current_context().exit(1)

        if ep_id is not None:
            safeprint(ep_id)
        else:
            safeprint("No Globus Connect Personal installation found.")
            click.get_current_context().exit(1)
示例#46
0
def local_id(personal):
    """
    Executor for `globus endpoint local-id`
    """
    if personal:
        try:
            ep_id = LocalGlobusConnectPersonal().endpoint_id
        except IOError as e:
            safeprint(e, write_to_stderr=True)
            click.get_current_context().exit(1)

        if ep_id is not None:
            safeprint(ep_id)
        else:
            safeprint('No Globus Connect Personal installation found.')
            click.get_current_context().exit(1)
示例#47
0
def print_table(iterable, headers_and_keys, print_headers=True):
    # the iterable may not be safe to walk multiple times, so we must walk it
    # only once -- however, to let us write things naturally, convert it to a
    # list and we can assume it is safe to walk repeatedly
    iterable = list(iterable)

    # extract headers and keys as separate lists
    headers = [h for (h, k) in headers_and_keys]
    keys = [k for (h, k) in headers_and_keys]

    # convert all keys to keyfuncs
    keyfuncs = [_key_to_keyfunc(key) for key in keys]

    # use the iterable to find the max width of an element for each column, in
    # the same order as the headers_and_keys array
    # use a special function to handle empty iterable
    def get_max_colwidth(kf):
        def _safelen(x):
            try:
                return len(x)
            except TypeError:
                return len(str(x))
        lengths = [_safelen(kf(i)) for i in iterable]
        if not lengths:
            return 0
        else:
            return max(lengths)

    widths = [get_max_colwidth(kf) for kf in keyfuncs]
    # handle the case in which the column header is the widest thing
    widths = [max(w, len(h)) for w, h in zip(widths, headers)]

    # create a format string based on column widths
    format_str = u' | '.join(u'{:' + str(w) + u'}' for w in widths)

    def none_to_null(val):
        if val is None:
            return 'NULL'
        return val

    # print headers
    if print_headers:
        safeprint(format_str.format(*[h for h in headers]))
        safeprint(format_str.format(*['-'*w for w in widths]))
    # print the rows of data
    for i in iterable:
        safeprint(format_str.format(*[none_to_null(kf(i)) for kf in keyfuncs]))
示例#48
0
def session_hook(exception):
    """
    Expects an exception with an authorization_paramaters field in its raw_json
    """
    safeprint("The resource you are trying to access requires you to "
              "re-authenticate with specific identities.")

    params = exception.raw_json["authorization_parameters"]
    message = params.get("session_message")
    if message:
        safeprint("message: {}".format(message))

    identities = params.get("session_required_identities")
    if identities:
        id_str = " ".join(identities)
        safeprint("Please run\n\n"
                  "    globus session boost {}\n\n"
                  "to re-authenticate with the required identities"
                  .format(id_str))
    else:
        safeprint('Please use "globus session boost" to re-authenticate '
                  'with specific identities'.format(id_str))

    exit_with_mapped_status(exception.http_status)
示例#49
0
    def _print_cmd(command):
        # print commands with short_help
        indent = 4
        min_space = 2

        # if the output would be pinched too close together, or if the command
        # name would overflow, use two separate lines
        if len(command.name) > _command_length - min_space:
            safeprint(" " * indent + command.name)
            safeprint(" " * (indent + _command_length) + command.short_help)
        # otherwise, it's all cool to cram into one line, just ljust command
        # names so that they form a nice column
        else:
            safeprint(
                " " * indent
                + "{}{}".format(command.name.ljust(_command_length), command.short_help)
            )
示例#50
0
文件: init.py 项目: globus/globus-cli
def init_command(default_output_format, default_myproxy_username):
    """
    Executor for `globus config init`
    """
    # now handle the output format, requires a little bit more care
    # first, prompt if it isn't given, but be clear that we have a sensible
    # default if they don't set it
    # then, make sure that if it is given, it's a valid format (discard
    # otherwise)
    # finally, set it only if given and valid
    if not default_output_format:
        safeprint(
            textwrap.fill(
                'This must be one of "json" or "text". Other values will be '
                "ignored. ENTER to skip."
            )
        )
        default_output_format = (
            click.prompt(
                "Default CLI output format (cli.output_format)", default="text"
            )
            .strip()
            .lower()
        )
        if default_output_format not in ("json", "text"):
            default_output_format = None

    if not default_myproxy_username:
        safeprint(textwrap.fill("ENTER to skip."))
        default_myproxy_username = click.prompt(
            "Default myproxy username (cli.default_myproxy_username)",
            default="",
            show_default=False,
        ).strip()

    # write to disk
    safeprint(
        "\n\nWriting updated config to {0}".format(os.path.expanduser("~/.globus.cfg"))
    )
    write_option(OUTPUT_FORMAT_OPTNAME, default_output_format)
    write_option(MYPROXY_USERNAME_OPTNAME, default_myproxy_username)
示例#51
0
def do_local_server_login_flow():
    """
    Starts a local http server, opens a browser to have the user login,
    and gets the code redirected to the server (no copy and pasting required)
    """
    safeprint(
        "You are running 'globus login', which should automatically open "
        "a browser window for you to login.\n"
        "If this fails or you experience difficulty, try "
        "'globus login --no-local-server'"
        "\n---")
    # start local server and create matching redirect_uri
    with start_local_server(listen=('127.0.0.1', 0)) as server:
        _, port = server.socket.getsockname()
        redirect_uri = 'http://localhost:{}'.format(port)

        # get the NativeApp client object and start a flow
        # if available, use the system-name to prefill the grant
        label = platform.node() or None
        native_client = internal_auth_client()
        native_client.oauth2_start_flow(refresh_tokens=True,
                                        prefill_named_grant=label,
                                        redirect_uri=redirect_uri,
                                        requested_scopes=SCOPES)
        url = native_client.oauth2_get_authorize_url()

        # open web-browser for user to log in, get auth code
        webbrowser.open(url, new=1)
        auth_code = server.wait_for_code()

    if isinstance(auth_code, LocalServerError):
        safeprint('Login failed: {}'.format(auth_code), write_to_stderr=True)
        click.get_current_context().exit(1)
    elif isinstance(auth_code, Exception):
        safeprint('Login failed with unexpected error:\n{}'.format(auth_code),
                  write_to_stderr=True)
        click.get_current_context().exit(1)

    # finish login flow
    exchange_code_and_store_config(native_client, auth_code)
示例#52
0
def login_command(no_local_server, force):
    # if not forcing, stop if user already logged in
    if not force and check_logged_in():
        safeprint(_LOGGED_IN_RESPONSE)
        return

    # use a link login if remote session or user requested
    if no_local_server or is_remote_session():
        do_link_auth_flow(force_new_client=True)

    # otherwise default to a local server login flow
    else:
        safeprint(
            "You are running 'globus login', which should automatically open "
            "a browser window for you to login.\n"
            "If this fails or you experience difficulty, try "
            "'globus login --no-local-server'"
            "\n---"
        )
        do_local_server_auth_flow(force_new_client=True)

    # print epilog
    safeprint(_LOGIN_EPILOG)
示例#53
0
 def _print_cmd_group(command, parent_names):
     parents = " ".join(parent_names)
     if parents:
         parents = parents + " "
     safeprint("\n=== {}{} ===\n".format(parents, command.name))
示例#54
0
def update_command(yes, development, development_version):
    """
    Executor for `globus update`
    """
    # enforce that pip MUST be installed
    # Why not just include it in the setup.py requirements? Mostly weak
    # reasons, but it shouldn't matter much.
    # - if someone has installed the CLI without pip, then they haven't
    #   followed our install instructions, so it's mostly a non-issue
    # - we don't want to have `pip install -U globus-cli` upgrade pip -- that's
    #   a little bit invasive and easy to do by accident on modern versions of
    #   pip where `--upgrade-strategy` defaults to `eager`
    # - we may want to do distributions in the future with dependencies baked
    #   into a package, but we'd never want to do that with pip. More changes
    #   would be needed to support that use-case, which we've discussed, but
    #   not depending directly on pip gives us a better escape hatch
    # - if we depend on pip, we need to start thinking about what versions we
    #   support. In point of fact, that becomes an issue as soon as we add this
    #   command, but not being explicit about it lets us punt for now (maybe
    #   indefinitely) on figuring out version requirements. All of that is to
    #   say: not including it is bad, and from that badness we reap the rewards
    #   of procrastination and non-explicit requirements
    # - Advanced usage, like `pip install -t` can produce an installed version
    #   of the CLI which can't import its installing `pip`. If we depend on
    #   pip, anyone doing this is forced to get two copies of pip, which seems
    #   kind of nasty (even if "they're asking for it")
    if not _check_pip_installed():
        safeprint("`globus update` requires pip. " "Please install pip and try again")
        click.get_current_context().exit(1)

    # --development-version implies --development
    development = development or (development_version is not None)

    # if we're running with `--development`, then the target version is a
    # tarball from GitHub, and we can skip out on the safety checks
    if development:
        # default to master
        development_version = development_version or "master"
        target_version = (
            "https://github.com/globus/globus-cli/archive/{}" ".tar.gz#egg=globus-cli"
        ).format(development_version)
    else:
        # lookup version from PyPi, abort if we can't get it
        latest, current = get_versions()
        if latest is None:
            safeprint("Failed to lookup latest version. Aborting.")
            click.get_current_context().exit(1)

        # in the case where we're already up to date, do nothing and exit
        if current == latest:
            safeprint("You are already running the latest version: {}".format(current))
            return

        # if we're up to date (or ahead, meaning a dev version was installed)
        # then prompt before continuing, respecting `--yes`
        else:
            safeprint(
                (
                    "You are already running version {0}\n"
                    "The latest version is           {1}"
                ).format(current, latest)
            )
            if not yes and (
                not click.confirm("Continue with the upgrade?", default=True)
            ):
                click.get_current_context().exit(1)

        # if we make it through to here, it means we didn't hit any safe (or
        # unsafe) abort conditions, so set the target version for upgrade to
        # the latest
        target_version = "globus-cli=={}".format(latest)

    # print verbose warning/help message, to guide less fortunate souls who hit
    # Ctrl+C at a foolish time, lose connectivity, or don't invoke with `sudo`
    # on a global install of the CLI
    safeprint(
        (
            "The Globus CLI will now update itself.\n"
            "In the event that an error occurs or the update is interrupted, we "
            "recommend uninstalling and reinstalling the CLI.\n"
            "Update Target: {}\n"
        ).format(target_version)
    )

    # register the upgrade activity as an atexit function
    # this ensures that most library teardown (other than whatever libs might
    # jam into atexit themselves...) has already run, and therefore protects us
    # against most potential bugs resulting from upgrading click while a click
    # command is running
    #
    # NOTE: there is a risk that we will see bugs on upgrade if the act of
    # doing a pip upgrade install changes state on disk and we (or a lib we
    # use) rely on that via pkg_resources, lazy/deferred imports, or good
    # old-fashioned direct inspection of `__file__` and the like DURING an
    # atexit method. Anything outside of atexit methods remains safe!
    @atexit.register
    def do_upgrade():
        install_args = ["install", "--upgrade", target_version]
        if IS_USER_INSTALL:
            install_args.insert(1, "--user")
        _call_pip(*install_args)
示例#55
0
def print_version():
    """
    Print out the current version, and at least try to fetch the latest from
    PyPi to print alongside it.

    It may seem odd that this isn't in globus_cli.version , but it's done this
    way to separate concerns over printing the version from looking it up.
    """
    latest, current = get_versions()
    if latest is None:
        safeprint(
            ("Installed Version: {0}\n" "Failed to lookup latest version.").format(
                current
            )
        )
    else:
        safeprint(
            ("Installed Version: {0}\n" "Latest Version:    {1}\n" "\n{2}").format(
                current,
                latest,
                "You are running the latest version of the Globus CLI"
                if current == latest
                else (
                    "You should update your version of the Globus CLI with\n"
                    "  globus update"
                )
                if current < latest
                else "You are running a preview version of the Globus CLI",
            )
        )

    # verbose shows more platform and python info
    # it also includes versions of some CLI dependencies
    if is_verbose():
        moddata = _get_package_data()

        safeprint("\nVerbose Data\n---")

        safeprint("platform:")
        safeprint("  platform: {}".format(platform.platform()))
        safeprint("  py_implementation: {}".format(platform.python_implementation()))
        safeprint("  py_version: {}".format(platform.python_version()))
        safeprint("  sys.executable: {}".format(sys.executable))
        safeprint("  site.USER_BASE: {}".format(site.USER_BASE))

        safeprint("modules:")
        for mod, modversion, modfile, modpath in moddata:
            safeprint("  {}:".format(mod))
            safeprint("    __version__: {}".format(modversion))
            safeprint("    __file__: {}".format(modfile))
            safeprint("    __path__: {}".format(modpath))
示例#56
0
def delete_command(
    batch,
    ignore_missing,
    star_silent,
    recursive,
    enable_globs,
    endpoint_plus_path,
    label,
    submission_id,
    dry_run,
    deadline,
    skip_activation_check,
    notify,
):
    """
    Executor for `globus delete`
    """
    endpoint_id, path = endpoint_plus_path
    if path is None and (not batch):
        raise click.UsageError("delete requires either a PATH OR --batch")

    client = get_client()

    # attempt to activate unless --skip-activation-check is given
    if not skip_activation_check:
        autoactivate(client, endpoint_id, if_expires_in=60)

    delete_data = DeleteData(
        client,
        endpoint_id,
        label=label,
        recursive=recursive,
        ignore_missing=ignore_missing,
        submission_id=submission_id,
        deadline=deadline,
        skip_activation_check=skip_activation_check,
        interpret_globs=enable_globs,
        **notify
    )

    if batch:
        # although this sophisticated structure (like that in transfer)
        # isn't strictly necessary, it gives us the ability to add options in
        # the future to these lines with trivial modifications
        @click.command()
        @click.argument("path", type=TaskPath(base_dir=path))
        def process_batch_line(path):
            """
            Parse a line of batch input and add it to the delete submission
            item.
            """
            delete_data.add_item(str(path))

        shlex_process_stdin(process_batch_line, "Enter paths to delete, line by line.")
    else:
        if not star_silent and enable_globs and path.endswith("*"):
            # not intuitive, but `click.confirm(abort=True)` prints to stdout
            # unnecessarily, which we don't really want...
            # only do this check if stderr is a pty
            if (
                err_is_terminal()
                and term_is_interactive()
                and not click.confirm(
                    'Are you sure you want to delete all files matching "{}"?'.format(
                        path
                    ),
                    err=True,
                )
            ):
                safeprint("Aborted.", write_to_stderr=True)
                click.get_current_context().exit(1)
        delete_data.add_item(path)

    if dry_run:
        formatted_print(delete_data, response_key="DATA", fields=[("Path", "path")])
        # exit safely
        return

    res = client.submit_delete(delete_data)
    formatted_print(
        res,
        text_format=FORMAT_TEXT_RECORD,
        fields=(("Message", "message"), ("Task ID", "task_id")),
    )
示例#57
0
def task_wait_with_io(
    meow, heartbeat, polling_interval, timeout, task_id, timeout_exit_code, client=None
):
    """
    Options are the core "task wait" options, including the `--meow` easter
    egg.

    This does the core "task wait" loop, including all of the IO.
    It *does exit* on behalf of the caller. (We can enhance with a
    `noabort=True` param or somesuch in the future if necessary.)
    """
    client = client or get_client()

    def timed_out(waited_time):
        if timeout is None:
            return False
        else:
            return waited_time >= timeout

    def check_completed():
        completed = client.task_wait(
            task_id, timeout=polling_interval, polling_interval=polling_interval
        )
        if completed:
            if heartbeat:
                safeprint("", write_to_stderr=True)
            # meowing tasks wake up!
            if meow:
                safeprint(
                    r"""
                  _..
  /}_{\           /.-'
 ( a a )-.___...-'/
 ==._.==         ;
      \ i _..._ /,
      {_;/   {_//""",
                    write_to_stderr=True,
                )

            # TODO: possibly update TransferClient.task_wait so that we don't
            # need to do an extra fetch to get the task status after completion
            res = client.get_task(task_id)
            formatted_print(res, text_format=FORMAT_SILENT)

            status = res["status"]
            if status == "SUCCEEDED":
                click.get_current_context().exit(0)
            else:
                click.get_current_context().exit(1)

        return completed

    # Tasks start out sleepy
    if meow:
        safeprint(
            r"""
   |\      _,,,---,,_
   /,`.-'`'    -.  ;-;;,_
  |,4-  ) )-,_..;\ (  `'-'
 '---''(_/--'  `-'\_)""",
            write_to_stderr=True,
        )

    waited_time = 0
    while not timed_out(waited_time) and not check_completed():
        if heartbeat:
            safeprint(".", write_to_stderr=True, newline=False)
            sys.stderr.flush()

        waited_time += polling_interval

    # add a trailing newline to heartbeats if we fail
    if heartbeat:
        safeprint("", write_to_stderr=True)

    exit_code = 1
    if timed_out(waited_time):
        safeprint(
            "Task has yet to complete after {} seconds".format(timeout),
            write_to_stderr=True,
        )
        exit_code = timeout_exit_code

    # output json if requested, but nothing for text mode
    res = client.get_task(task_id)
    formatted_print(res, text_format=FORMAT_SILENT)

    click.get_current_context().exit(exit_code)
示例#58
0
def custom_except_hook(exc_info):
    """
    A custom excepthook to present python errors produced by the CLI.
    We don't want to show end users big scary stacktraces if they aren't python
    programmers, so slim it down to some basic info. We keep a "DEBUGMODE" env
    variable kicking around to let us turn on stacktraces if we ever need them.

    Additionally, does global suppression of EPIPE errors, which often occur
    when a python command is piped to a consumer like `head` which closes its
    input stream before python has sent all of its output.
    DANGER: There is a (small) risk that this will bite us if there are EPIPE
    errors produced within the Globus SDK. We should keep an eye on this
    possibility, as it may demand more sophisticated handling of EPIPE.
    Possible TODO item to reduce this risk: inspect the exception and only hide
    EPIPE if it comes from within the globus_cli package.
    """
    exception_type, exception, traceback = exc_info

    # check if we're in debug mode, and run the real excepthook if we are
    ctx = click.get_current_context()
    state = ctx.ensure_object(CommandState)
    if state.debug:
        sys.excepthook(exception_type, exception, traceback)

    # we're not in debug mode, do custom handling
    else:

        # if it's a click exception, re-raise as original -- Click's main
        # execution context will handle pretty-printing
        if isinstance(exception, click.ClickException):
            reraise(exception_type, exception, traceback)

        # catch any session errors to give helpful instructions
        # on how to use globus session update
        elif (
            isinstance(exception, exc.GlobusAPIError)
            and exception.raw_json
            and "authorization_parameters" in exception.raw_json
        ):
            session_hook(exception)

        # handle the Globus-raised errors with our special hooks
        # these will present the output (on stderr) as JSON
        elif isinstance(exception, exc.TransferAPIError):
            if exception.code == "ClientError.AuthenticationFailed":
                authentication_hook(exception)
            else:
                transferapi_hook(exception)

        elif isinstance(exception, exc.AuthAPIError):
            if exception.code == "UNAUTHORIZED":
                authentication_hook(exception)
            # invalid_grant occurs when the users refresh tokens are not valid
            elif exception.message == "invalid_grant":
                invalidrefresh_hook(exception)
            else:
                authapi_hook(exception)

        elif isinstance(exception, exc.GlobusAPIError):
            globusapi_hook(exception)

        # specific checks fell through -- now check if it's any kind of
        # GlobusError
        elif isinstance(exception, exc.GlobusError):
            globus_generic_hook(exception)

        # not a GlobusError, not a ClickException -- something like ValueError
        # or NotImplementedError bubbled all the way up here: just print it
        # out, basically
        else:
            safeprint(u"{}: {}".format(exception_type.__name__, exception))
            sys.exit(1)
示例#59
0
文件: rm.py 项目: globus/globus-cli
def rm_command(
    ignore_missing,
    star_silent,
    recursive,
    enable_globs,
    endpoint_plus_path,
    label,
    submission_id,
    dry_run,
    deadline,
    skip_activation_check,
    notify,
    meow,
    heartbeat,
    polling_interval,
    timeout,
    timeout_exit_code,
):
    """
    Executor for `globus rm`
    """
    endpoint_id, path = endpoint_plus_path

    client = get_client()

    # attempt to activate unless --skip-activation-check is given
    if not skip_activation_check:
        autoactivate(client, endpoint_id, if_expires_in=60)

    delete_data = DeleteData(
        client,
        endpoint_id,
        label=label,
        recursive=recursive,
        ignore_missing=ignore_missing,
        submission_id=submission_id,
        deadline=deadline,
        skip_activation_check=skip_activation_check,
        interpret_globs=enable_globs,
        **notify
    )

    if not star_silent and enable_globs and path.endswith("*"):
        # not intuitive, but `click.confirm(abort=True)` prints to stdout
        # unnecessarily, which we don't really want...
        # only do this check if stderr is a pty
        if (
            err_is_terminal()
            and term_is_interactive()
            and not click.confirm(
                'Are you sure you want to delete all files matching "{}"?'.format(path),
                err=True,
            )
        ):
            safeprint("Aborted.", write_to_stderr=True)
            click.get_current_context().exit(1)
    delete_data.add_item(path)

    if dry_run:
        formatted_print(delete_data, response_key="DATA", fields=[("Path", "path")])
        # exit safely
        return

    # Print task submission to stderr so that `-Fjson` is still correctly
    # respected, as it will be by `task wait`
    res = client.submit_delete(delete_data)
    task_id = res["task_id"]
    safeprint(
        'Delete task submitted under ID "{}"'.format(task_id), write_to_stderr=True
    )

    # do a `task wait` equivalent, including printing and correct exit status
    task_wait_with_io(
        meow,
        heartbeat,
        polling_interval,
        timeout,
        task_id,
        timeout_exit_code,
        client=client,
    )