CLI testing and execution

Todo

Write example and tutorial.

click_extra.testing API

        classDiagram
  BytesIO <|-- BytesIOCopy
  CliRunner <|-- ExtraCliRunner
    

CLI testing and simulation of their execution.

click_extra.testing.INDENT = '  '

Constants for rendering of CLI execution.

click_extra.testing.TNestedArgs

Types for arbitrary nested CLI arguments.

Arguments can be str, pathlib.Path objects or None values.

alias of Iterable[str | Path | None | Iterable[TNestedArgs]]

click_extra.testing.args_cleanup(*args)[source]

Flatten recursive iterables, remove all None, and cast each element to strings.

Helps serialize pathlib.Path and other objects.

It also allows for nested iterables and None values as CLI arguments for convenience. We just need to flatten and filters them out.

Return type:

tuple[str, ...]

click_extra.testing.format_cli_prompt(cmd_args, extra_env=None)[source]

Simulate the console prompt used to invoke the CLI.

Return type:

str

click_extra.testing.render_cli_run(args, result, env=None)[source]

Generates the full simulation of CLI execution, including output.

Mostly used to print debug traces to user or in test results.

Return type:

str

click_extra.testing.print_cli_run(args, result, env=None)[source]

Prints the full simulation of CLI execution, including output.

Return type:

None

click_extra.testing.INVOKE_ARGS = {'args', 'catch_exceptions', 'cli', 'color', 'env', 'input', 'self'}

Parameter IDs of click.testing.CliRunner.invoke().

We need to collect them to help us identify which extra parameters passed to invoke() collides with its original signature.

Warning

This has been reported upstream to Click project but has been rejected and not considered an issue worth fixing.

class click_extra.testing.BytesIOCopy(copy_to)[source]

Bases: BytesIO

Patch io.BytesIO to let the written stream be copied to another.

Caution

This has been proposed upstream to Click project but has not been merged yet.

flush()[source]

Does nothing.

Return type:

None

write(b)[source]

Write bytes to file.

Return the number of bytes written.

Return type:

int

class click_extra.testing.ExtraCliRunner(charset='utf-8', env=None, echo_stdin=False, catch_exceptions=True)[source]

Bases: CliRunner

Augment click.testing.CliRunner with extra features and bug fixes.

env: cabc.Mapping[str, str | None]
force_color: bool = False

Global class attribute to override the color parameter in invoke.

Note

This was initially developed to force the initialization of the runner during the setup of Sphinx new directives. This was the only way we found, as to patch some code we had to operate at the class level.

invoke2(cli, args=None, input=None, env=None, catch_exceptions=True, color=False, **extra)[source]

Copy of click.testing.CliRunner.invoke() with extra <output> stream. :rtype: Result

Caution

This is a hard-copy of the modified invoke() method from click#2523 PR which has not been merged upstream yet.

Todo

Reduce the code duplication here by using clever monkeypatching?

invoke(cli, *args, input=None, env=None, catch_exceptions=True, color=None, **extra)[source]

Same as click.testing.CliRunner.invoke() with extra features.

  • The first positional parameter is the CLI to invoke. The remaining positional parameters of the function are the CLI arguments. All other parameters are required to be named.

  • The CLI arguments can be nested iterables of arbitrary depth. This is useful for argument composition of test cases with @pytest.mark.parametrize.

  • Allow forcing of the color property at the class-level via force_color attribute.

  • Adds a special case in the form of color="forced" parameter, which allows colored output to be kept, while forcing the initialization of Context.color = True. This is not allowed in current implementation of click.testing.CliRunner.invoke() because of colliding parameters.

  • Strips all ANSI codes from results if color was explicirely set to False.

  • Always prints a simulation of the CLI execution as the user would see it in its terminal. Including colors.

  • Pretty-prints a formatted exception traceback if the command fails.

Parameters:
  • cli (Command) – CLI to invoke.

  • *args

    can be nested iterables composed of str, pathlib.Path objects and None values. The nested structure will be flattened and None values will be filtered out. Then all elements will be casted to str. See args_cleanup() for details.

  • input (str | bytes | IO | None) – same as click.testing.CliRunner.invoke().

  • env (Optional[Mapping[str, str | None]]) – same as click.testing.CliRunner.invoke().

  • catch_exceptions (bool) – same as click.testing.CliRunner.invoke().

  • color (Union[bool, Literal['forced'], None]) – If a boolean, the parameter will be passed as-is to click.testing.CliRunner.isolation(). If "forced", the parameter will be passed as True to click.testing.CliRunner.isolation() and an extra color=True parameter will be passed to the invoked CLI.

  • **extra

    same as click.testing.CliRunner.invoke(), but colliding parameters are allowed and properly passed on to the invoked CLI.

Return type:

Result