Exemple #1
0
def test_missing_configuration_file(_default_filepath_mock, click_runner):
    cmd_args = ('ursula', 'run', '--network', TEMPORARY_DOMAIN)
    result = click_runner.invoke(nucypher_cli, cmd_args, catch_exceptions=False)
    assert result.exit_code != 0
    configuration_type = UrsulaConfiguration.NAME
    assert NO_CONFIGURATIONS_ON_DISK.format(name=configuration_type.capitalize(),
                                            command=configuration_type) in result.output
Exemple #2
0
def test_select_config_file_with_no_config_files(test_emitter, capsys,
                                                 alice_blockchain_test_config,
                                                 tmpdir):

    # Setup
    config_class = alice_blockchain_test_config

    # Prove there are no config files on the disk.
    assert not os.listdir(tmpdir)
    with pytest.raises(click.Abort):
        select_config_file(emitter=test_emitter,
                           config_class=config_class,
                           config_root=tmpdir)

    # Ensure we notified the user accurately.
    captured = capsys.readouterr()
    message = NO_CONFIGURATIONS_ON_DISK.format(
        name=config_class.NAME.capitalize(), command=config_class.NAME)
    assert message in captured.out
Exemple #3
0
def select_config_file(
    emitter: StdoutEmitter,
    config_class: Type[CharacterConfiguration],
    config_root: str = None,
    checksum_address: str = None,
) -> str:
    """
    Selects a nucypher character configuration file from the disk automatically or interactively.

    Behaviour
    ~~~~~~~~~

    - If checksum address is supplied by parameter or worker address env var - confirm there is a corresponding
      file on the disk or raise ValueError.

    - If there is only one configuration file for the character, automatically return its filepath.

    - If there are multiple character configurations on the disk in the same configuration root,
      use interactive selection.

    - Aborts if there are no configurations associated with the supplied character configuration class.

    """

    #
    # Scrape Disk Configurations
    #

    config_root = config_root or DEFAULT_CONFIG_ROOT
    default_config_file = glob.glob(
        config_class.default_filepath(config_root=config_root))
    glob_pattern = f'{config_root}/{config_class.NAME}-0x*.{config_class._CONFIG_FILE_EXTENSION}'
    secondary_config_files = glob.glob(glob_pattern)
    config_files = [*default_config_file, *secondary_config_files]
    if not config_files:
        emitter.message(NO_CONFIGURATIONS_ON_DISK.format(
            name=config_class.NAME.capitalize(), command=config_class.NAME),
                        color='red')
        raise click.Abort()

    checksum_address = checksum_address or os.environ.get(
        NUCYPHER_ENVVAR_WORKER_ADDRESS,
        None)  # TODO: Deprecate worker_address in favor of checksum_address
    if checksum_address:

        #
        # Manual
        #

        parsed_addresses = {
            config_class.checksum_address_from_filepath(fp): fp
            for fp in config_files
        }
        try:
            config_file = parsed_addresses[checksum_address]
        except KeyError:
            raise ValueError(
                f"'{checksum_address}' is not a known {config_class.NAME} configuration account."
            )

    elif len(config_files) > 1:

        #
        # Interactive
        #

        parsed_addresses = tuple(
            [config_class.checksum_address_from_filepath(fp)]
            for fp in config_files)

        # Display account info
        headers = ['Account']
        emitter.echo(
            tabulate(parsed_addresses, headers=headers, showindex='always'))

        # Prompt the user for selection, and return
        prompt = f"Select {config_class.NAME} configuration"
        account_range = click.IntRange(min=0, max=len(config_files) - 1)
        choice = click.prompt(prompt, type=account_range, default=0)
        config_file = config_files[choice]
        emitter.echo(f"Selected {choice}: {config_file}", color='blue')

    else:
        # Default: Only one config file, use it.
        config_file = config_files[0]

    return config_file
Exemple #4
0
def select_config_file(
    emitter: StdoutEmitter,
    config_class: Type[CharacterConfiguration],
    config_root: Optional[Path] = None,
    checksum_address: str = None,
) -> Path:
    """
    Selects a nucypher character configuration file from the disk automatically or interactively.

    Behaviour
    ~~~~~~~~~

    - If checksum address is supplied by parameter or operator address env var - confirm there is a corresponding
      file on the disk or raise ValueError.

    - If there is only one configuration file for the character, automatically return its filepath.

    - If there are multiple character configurations on the disk in the same configuration root,
      use interactive selection.

    - Aborts if there are no configurations associated with the supplied character configuration class.

    """

    config_root = config_root or DEFAULT_CONFIG_ROOT
    config_files = get_config_filepaths(config_class=config_class,
                                        config_root=config_root)
    if not config_files:
        emitter.message(NO_CONFIGURATIONS_ON_DISK.format(
            name=config_class.NAME.capitalize(), command=config_class.NAME),
                        color='red')
        raise click.Abort()

    checksum_address = checksum_address or os.environ.get(
        NUCYPHER_ENVVAR_OPERATOR_ADDRESS,
        None)  # TODO: Deprecate operator_address in favor of checksum_address

    parsed_config_files = list()
    parsed_addresses_and_filenames = list()
    # parse configuration files for checksum address values
    for fp in config_files:
        try:
            config_checksum_address = config_class.checksum_address_from_filepath(
                fp)
            if checksum_address and config_checksum_address == checksum_address:
                # matching configuration file found, no need to continue - return filepath
                return fp

            parsed_config_files.append(fp)
            parsed_addresses_and_filenames.append(
                [config_checksum_address,
                 Path(fp).name])  # store checksum & filename
        except config_class.OldVersion:
            # no use causing entire usage to crash if file can't be used anyway - inform the user; they can
            # decide for themself
            emitter.echo(IGNORE_OLD_CONFIGURATION.format(config_file=fp),
                         color='yellow')

    if checksum_address:
        # shouldn't get here if checksum address was specified and corresponding file found
        raise ValueError(
            f"'{checksum_address}' is not a known {config_class.NAME} configuration account."
        )

    if not parsed_config_files:
        # No available configuration files
        emitter.message(NO_CONFIGURATIONS_ON_DISK.format(
            name=config_class.NAME.capitalize(), command=config_class.NAME),
                        color='red')
        raise click.Abort()
    elif len(parsed_config_files) > 1:
        #
        # Interactive
        #
        emitter.echo(f"\nConfiguration Directory: {config_root}\n")

        parsed_addresses_and_filenames = tuple(
            parsed_addresses_and_filenames
        )  # must be tuple-of-iterables for tabulation

        # Display account info
        headers = ['Account', 'Configuration File']
        emitter.echo(
            tabulate(parsed_addresses_and_filenames,
                     headers=headers,
                     showindex='always'))

        # Prompt the user for selection, and return
        prompt = f"Select {config_class.NAME} configuration"
        account_range = click.IntRange(min=0, max=len(parsed_config_files) - 1)
        choice = click.prompt(prompt, type=account_range, default=0)
        config_file = parsed_config_files[choice]
        emitter.echo(f"Selected {choice}: {config_file}", color='blue')
    else:
        # Default: Only one config file, use it.
        config_file = parsed_config_files[0]
        emitter.echo(
            DEFAULT_TO_LONE_CONFIG_FILE.format(
                config_class=config_class.NAME.capitalize(),
                config_file=config_file))

    return config_file