Search code examples
pythonpython-click

Unable to read file with click.Path option inside runner.isolated_filesystem()


I am using click for the very first time to create a cli program using Python 3.12.2.

I created a simple click command that takes an option (the '-f' / '--file' option) as a filepath with type click.Path():

@click.command()
@click.option(
    "-f",
    "--file",
    "input_file_path",
    type=click.Path(path_type=Path, exists=True, resolve_path=True),
    required=True,
)
@click.option("-e", "--executor", "executor", default="pbiservice")
def validate(input_file_path: Path, executor):
    executor = EXECUTORS[executor]
    processed_validations_spec = (
        process_validations_spec_from_local_file_and_return_resulting_validations_spec(
            input_file_path, executor
        )
    )
    click.echo(processed_validations_spec)

When I am now trying to test this command using the CliRunner of click, I want to have an isolated temporary directory that get's destroyed after the test. This way I can create a test file, invoke the command pointing to that file path and then have it all cleaned up afterwards. So I created a test for the command using the runner.isolated_filesystem context manager. However, no matter what I try I am unable to access a file that I have previously written to the temporary directory using my created cli command inside runner.invoke(). Here is my test:

def test_validate(validations_spec):
    runner = CliRunner()
    with runner.isolated_filesystem() as td:
        file_name = "validations_spec.json"
        write_validations_spec_to_local_file(validations_spec, Path(file_name))
        result = runner.invoke(
            validate,
            [f"-f {file_name}"],
        )
        processed_validations_spec = validations_spec.process(MirrorExecutor())
        assert result.output == processed_validations_spec

This always fails with Error: Invalid value for '-f' / '--file': Path ' validations_spec.json' does not exist.

I have tried reading the file with the python builtin open function and that works, so the file does indeed exist. It's just that the click command cannot find it and I don't know why.


What I expect and tried:

The click command invoked inside an isolated_filesystem should be able to access the filepath to a file created inside this isolated fileystem. However, it claims that the file does not exist.

  • I checked that the file does indeed exist by reading it with the builtin open function
  • I played with the resolve_path=True/False parameter of the click.Path type but it did not change anything

Solution

  • Your problem is this bit of code here:

            result = runner.invoke(
                validate,
                [f"-f {file_name}"],
            )
    

    That's the equivalent of running a command line like:

    ./myprogram "-f validation_spec.json"
    

    In both cases, the space between -f and validation_spec.json is considered part of the filename. That's why your error message look like:

    Error: Invalid value for '-f' / '--file': Path ' validations_spec.json' does not exist.
    

    See that space in ' validation_spec.json'?

    The solution is to pass a list of arguments:

            result = runner.invoke(
                validate,
                ["-f", file_name],
            )