tests package

Subpackages

Submodules

tests.conftest module

Fixtures, configuration and helpers for tests.

tests.test_accessibility module

tests.test_accessibility.report_cli()[source]

An extra command that echoes the resolved color flag and table format, then renders a table with a styled cell.

tests.test_accessibility.test_default_params_include_accessible()[source]
tests.test_accessibility.test_accessible_precedence(invoke, report_cli, extra_args, expected_color, expected_format)[source]
tests.test_accessibility.test_accessible_via_envvar(invoke, report_cli)[source]
tests.test_accessibility.test_explicit_flag_overrides_accessible_envvar(invoke, report_cli)[source]
tests.test_accessibility.test_default_renders_box_drawing_and_ansi(invoke, report_cli)[source]
tests.test_accessibility.test_accessible_strips_box_drawing_and_ansi(invoke, report_cli)[source]
tests.test_accessibility.test_standalone_decorator_on_plain_click_command(invoke)[source]

--accessible lowers the defaults of the sibling color and table options, so they must be composed alongside it on a plain click.command.

tests.test_accessibility.test_accessible_flag_published_to_context(invoke, args, expected)[source]

set_accessible publishes the resolved intent at ctx.meta[ACCESSIBLE].

tests.test_accessibility.test_clear_is_noop_under_accessible(invoke, monkeypatch)[source]

clear() defers to click.clear normally, but no-ops under –accessible.

tests.test_accessibility.test_echo_via_pager_streams_plainly_under_accessible(invoke, monkeypatch)[source]

echo_via_pager pages normally, but writes straight to stdout under –accessible.

tests.test_accessibility.test_pager_and_clear_defer_without_accessible_option(invoke, monkeypatch)[source]

With no –accessible option wired, the wrappers defer to Click unchanged.

tests.test_cli_wrapper module

Tests for the CLI wrapper feature.

tests.test_cli_wrapper.GREET_SCRIPT = 'import click\n\n@click.command()\n@click.option("--name", default="World", help="Name to greet.")\ndef hello(name):\n    """Greet someone."""\n    click.echo(f"Hello, {name}")\n\nif __name__ == "__main__":\n    hello()\n'

Plain @click.command() script: patched via decorator defaults.

tests.test_cli_wrapper.CUSTOM_CLS_SCRIPT = 'import click\n\nclass RecipeGroup(click.Group):\n    """Custom group like Flask\'s FlaskGroup."""\n\n@click.command(cls=RecipeGroup)\ndef kitchen():\n    """Manage recipes and ingredients."""\n\n@kitchen.command()\n@click.option("--servings", default=4, help="Number of servings.")\ndef bake(servings):\n    """Bake a cake."""\n    click.echo(f"Baking for {servings}")\n\nif __name__ == "__main__":\n    kitchen()\n'

Script with explicit cls=RecipeGroup: patched via method patching.

tests.test_cli_wrapper.MULTI_OPTION_SCRIPT = 'import click\n\n@click.command()\n@click.option("--city", default="Paris", help="City name.")\n@click.option("--unit", default="celsius", help="Temperature unit.")\n@click.option("--verbose", is_flag=True, help="Show details.")\ndef weather(city, unit, verbose):\n    """Check the weather."""\n    msg = f"{city}: 22 {unit}"\n    if verbose:\n        msg += " (detailed)"\n    click.echo(msg)\n\nif __name__ == "__main__":\n    weather()\n'

Script with multiple options for config passthrough tests.

tests.test_cli_wrapper.runner()[source]

CLI runner for wrapper tests.

tests.test_cli_wrapper.greet_script(tmp_path)[source]

A minimal Click CLI script for wrapping tests.

tests.test_cli_wrapper.custom_cls_script(tmp_path)[source]

A Click CLI with explicit cls=CustomGroup (like Flask’s FlaskGroup).

tests.test_cli_wrapper.weather_script(tmp_path)[source]

A Click CLI with multiple options for config tests.

tests.test_cli_wrapper.create_config(tmp_path)[source]

Produce a temporary configuration file.

tests.test_cli_wrapper.test_patched_class_inherits_click(cls, base)[source]
tests.test_cli_wrapper.test_patched_class_has_mixin(cls)[source]
tests.test_cli_wrapper.test_patched_class_context(cls)[source]
tests.test_cli_wrapper.test_patched_command_no_extra_params()[source]

Patched commands carry no default_params.

tests.test_cli_wrapper.test_resolve_target(script, expected_module, expected_func)[source]
tests.test_cli_wrapper.test_resolve_py_file(tmp_path)[source]
tests.test_cli_wrapper.test_resolve_py_file_missing(tmp_path)[source]

A .py path that doesn’t exist falls through to module resolution.

tests.test_cli_wrapper.test_resolve_not_found(script)[source]
tests.test_cli_wrapper.test_wrap_self(runner, args, expected)[source]
tests.test_cli_wrapper.test_run_invokes_target(runner, script_fixture, target_args, expected_text, request)[source]

The run subcommand forwards arguments to the target CLI.

tests.test_cli_wrapper.test_run_colorizes(runner, script_fixture, target_args, request)[source]

Help output contains ANSI escape codes.

tests.test_cli_wrapper.test_run_highlights_keywords_with_custom_cls(runner, custom_cls_script)[source]

Options and subcommands are individually styled, not just headings.

tests.test_cli_wrapper.test_run_unresolvable_target(runner)[source]
tests.test_cli_wrapper.test_resolve_target_command_returns_command_and_context(greet_script)[source]

The shared resolver returns the target’s command object and a context.

tests.test_cli_wrapper.test_resolve_target_command_drills_subcommand(custom_cls_script)[source]

Extra subcommands navigate into nested groups.

tests.test_cli_wrapper.test_wrap_man_renders_manpage(runner, greet_script)[source]

click-extra wrap --man SCRIPT prints the target’s roff page and exits.

tests.test_cli_wrapper.test_wrap_man_custom_class_group(runner, custom_cls_script)[source]

--man resolves a custom-class group target via the shared scanner.

tests.test_cli_wrapper.test_wrap_man_drills_into_subcommand(runner, custom_cls_script)[source]

Extra arguments after SCRIPT render the nested subcommand’s page.

tests.test_cli_wrapper.test_wrap_man_unresolvable_target(runner)[source]
tests.test_cli_wrapper.test_wrap_man_output_dir_writes_tree(runner, custom_cls_script, tmp_path)[source]

wrap --man --output-dir writes one .1 per (sub)command into the dir.

--output-dir must appear before SCRIPT, because wrap runs with allow_interspersed_args=False so that anything after SCRIPT is treated as a sub-command path rather than a click-extra flag.

tests.test_cli_wrapper.test_wrap_man_output_dir_creates_missing_directory(runner, greet_script, tmp_path)[source]

--output-dir creates the target dir when it does not exist yet.

tests.test_cli_wrapper.test_wrap_man_output_dir_rejects_subcommand(runner, custom_cls_script, tmp_path)[source]

--output-dir always emits the full tree; mixing in a SUBCOMMAND arg is rejected so the user cannot accidentally produce a tree of pages named after a partial path.

tests.test_cli_wrapper.test_group_dispatches_to_wrap(runner, greet_script, args, expected)[source]

All invocation forms reach the target CLI.

tests.test_cli_wrapper.test_group_options_work_with_wrap(runner, greet_script, group_opts)[source]

Default Group options are accepted alongside the wrap subcommand.

tests.test_cli_wrapper.test_group_known_subcommands_not_wrapped(runner, subcommand)[source]

Known demo subcommands are dispatched directly, not to wrap.

tests.test_cli_wrapper.test_config_verbosity(runner, greet_script, create_config)[source]

verbosity = "DEBUG" in pyproject.toml activates debug logging.

tests.test_cli_wrapper.test_config_group_theme(runner, greet_script, create_config)[source]

A [tool.click-extra] theme key sets the help-screen theme.

tests.test_cli_wrapper.test_config_target_string(runner, greet_script, create_config)[source]

A string config value is forwarded as --key value.

tests.test_cli_wrapper.test_config_target_bool_true(runner, weather_script, create_config)[source]

A true config value is forwarded as --flag.

tests.test_cli_wrapper.test_config_target_bool_false_is_noop(runner, weather_script, create_config)[source]

A false config value is skipped: the flag is simply not passed.

tests.test_cli_wrapper.test_config_target_multiple_keys(runner, weather_script, create_config)[source]

Multiple config keys are all forwarded.

tests.test_cli_wrapper.test_config_target_cli_overrides(runner, greet_script, create_config)[source]

Explicit CLI args override config target defaults.

tests.test_cli_wrapper.test_config_target_wrong_section_ignored(runner, greet_script, create_config)[source]

Config for a different script name has no effect.

tests.test_cli_wrapper.test_config_target_empty_section(runner, greet_script, create_config)[source]

An empty target section produces no extra args.

tests.test_cli_wrapper.test_config_target_no_config(runner, greet_script)[source]

No config file at all: target runs with its own defaults.

tests.test_cli_wrapper.test_config_target_invalid_option(runner, greet_script, create_config)[source]

An invalid config key is caught by the target CLI.

tests.test_cli_wrapper.test_config_args_for_target(section, script, expected)[source]
tests.test_cli_wrapper.test_config_args_no_config()[source]

No config loaded: returns empty tuple.

tests.test_cli_wrapper.test_config_args_no_wrap_section()[source]

Config exists but has no wrap section.

tests.test_color module

tests.test_color.test_standalone_color_option(invoke, option_decorator, param, expecting_colors, assert_output_regex)[source]

Check color option values, defaults and effects on all things colored, including verbosity option.

tests.test_color.test_no_color_env_convention(invoke, env, env_expect_colors, param, param_expect_colors)[source]
tests.test_color.test_resolve_color_env_term(monkeypatch, term, expected)[source]

A dumb/unknown TERM votes color-off, while any other value stays neutral.

tests.test_color.test_resolve_color_env_force_color_beats_dumb_term(monkeypatch, term)[source]

An explicit FORCE_COLOR stays authoritative over a dumb/unknown TERM.

tests.test_color.test_color_synonym_cli_resolution(invoke, when, ctx_color)[source]

Every canonical value and GNU synonym resolves –color to the right state.

tests.test_color.test_color_synonym_invalid_value(invoke, when)[source]

Unknown values, including the git-style true/false, still error. The message lists only the canonical choices, never the hidden synonyms.

tests.test_color.test_color_synonym_hidden_in_help(invoke)[source]

The GNU synonyms are accepted but never advertised: –help shows only the canonical metavar.

tests.test_color.test_color_synonym_cli_beats_env(invoke, when, env_var, ctx_color)[source]

A synonym on the command line keeps the same env precedence as its canonical twin: a command-line choice outranks the color environment variables.

tests.test_color.test_color_synonym_config_string(invoke, create_config, when, ctx_color)[source]

A configuration file accepts the GNU synonyms as strings, normalized exactly like on the command line.

tests.test_color.test_color_synonym_config_boolean(invoke, create_config, filename, raw, ctx_color)[source]

A configuration boolean maps true -> always and false -> never, including YAML’s coercion of yes/no/on/off, so a value means the same across formats.

tests.test_color.test_color_synonym_config_beats_env(invoke, create_config, rhs, env_var, ctx_color)[source]

A config synonym or boolean beats the color environment variables, matching the documented precedence of canonical config values.

tests.test_color.test_integrated_color_option(invoke, param, expecting_colors, ctx_color, assert_output_regex)[source]

Check effect of color option on all things colored, including verbosity option.

Also checks the color option in subcommands is inherited from parent context.

tests.test_color.test_color_settles_before_eager_help_and_version(invoke, args, expecting_colors)[source]

–color / –no-color colorize the eager –help and –version screens whatever their position on the command line.

Click processes eager options in command-line order, so a –color sitting after –help or –version would otherwise pin ctx.color only once the screen had already printed and exited. Command.parse_args settles the color options in a pre-pass to close that gap. See Command._resolve_color_eagerly.

tests.test_color.test_forced_color_sets_and_restores_env(monkeypatch)[source]

forced_color forces FORCE_COLOR and clears Click Extra’s disabling vars.

Inside the context the capture sees FORCE_COLOR=1 with every flag that would disable color (NO_COLOR, LLM, …) removed; on exit the prior environment, including any pre-existing values, is restored untouched.

tests.test_commands module

Test defaults of our custom commands, as well as their customizations and attached options, and how they interact with each others.

tests.test_commands.test_module_root_declarations()[source]

Verify click_extra.__all__ is a superset of click and cloup.

Sort order is enforced by ruff (RUF022).

tests.test_commands.all_command_cli()[source]

A CLI that is mixing all variations and flavors of subcommands.

tests.test_commands.test_unknown_option(invoke, all_command_cli)[source]
tests.test_commands.test_short_option_error_enhancement(invoke, cli_options, args, exit_code, expected_fragment)[source]

Command.parse_args improves error messages for single-dash multi-character tokens whose first character is not a registered short option. Vanilla Click would split -dbgwrong character by character and report “No such option: -d”; we re-raise with the full token and close-match suggestions instead.

The enhancement must not interfere with valid -abc-style combining or with the per-character diagnostic when a later character is unknown.

Upstream context: https://github.com/pallets/click/issues/2779

tests.test_commands.test_unknown_command(invoke, all_command_cli)[source]
tests.test_commands.test_required_command(invoke, all_command_cli, assert_output_regex)[source]
tests.test_commands.test_group_help(invoke, all_command_cli, param, exit_code, assert_output_regex)[source]
tests.test_commands.test_help_eagerness(invoke, all_command_cli, params, exit_code, expect_help, expect_empty_stderr, assert_output_regex)[source]

See: https://click.palletsprojects.com/en/stable/click-concepts/#callback-evaluation-order

tests.test_commands.test_help_custom_name(invoke)[source]

Removes the -h short option as we reserve it for a custom -h/--header option.

See: https://github.com/kdeldycke/mail-deduplicate/issues/762

tests.test_commands.test_subcommand_help(invoke, all_command_cli, cmd_id, param, assert_output_regex)[source]
tests.test_commands.test_subcommand_execution(invoke, all_command_cli, cmd_id)[source]
tests.test_commands.test_integrated_version_value(invoke, all_command_cli)[source]
tests.test_commands.test_colored_bare_help(invoke, cmd_decorator, param)[source]

Extra decorators are always colored.

Even when stripped of their default parameters, as reported in: https://github.com/kdeldycke/click-extra/issues/534 https://github.com/kdeldycke/click-extra/pull/543

tests.test_commands.test_duplicate_option(invoke)[source]

See: - https://kdeldycke.github.io/click-extra/commands.html#change-default-options - https://github.com/kdeldycke/click-extra/issues/232

tests.test_commands.test_no_option_leaks_between_subcommands(invoke, assert_output_regex)[source]

As reported in https://github.com/kdeldycke/click-extra/issues/489.

tests.test_commands.test_option_group_integration(invoke, assert_output_regex)[source]
tests.test_commands.test_show_envvar_parameter(invoke, cmd_decorator, ctx_settings, expected_help)[source]
tests.test_commands.test_show_choices_parameter(ctx_settings, expected)[source]

The show_choices context setting is forced on every option when set.

tests.test_commands.test_raw_args(invoke)[source]

Raw args are expected to be scoped in subcommands.

tests.test_commands.test_lazy_group(invoke, tmp_path, lazy_cmd_decorator, lazy_group_decorator)[source]

Test extends the snippet from Click documentation.

tests.test_commands.test_decorator_overrides()[source]

Ensure our decorators are not just alias of Click and Cloup ones.

tests.test_commands.test_decorator_cls_parameter(klass, should_raise)[source]

Decorators accept custom cls parameters.

tests.test_commands.test_help_shows_group_help(invoke)[source]

mycli help produces the same output as mycli --help.

tests.test_commands.test_help_shows_subcommand_help(invoke)[source]

mycli help greet matches mycli greet --help.

tests.test_commands.test_help_nested_group(invoke)[source]

mycli help sub leaf resolves through nested groups.

tests.test_commands.test_help_nonexistent_subcommand(invoke)[source]

mycli help nosuch reports an error.

tests.test_commands.test_help_subcommand_of_non_group(invoke)[source]

mycli help leaf deeper errors when leaf is not a group.

tests.test_commands.test_help_disabled(invoke)[source]

help_command=False suppresses auto-injection.

tests.test_commands.test_help_user_override(invoke)[source]

User-defined help subcommand replaces the auto-injected one.

tests.test_commands.test_help_appears_in_listing(invoke)[source]

The help subcommand is visible in the group’s command list.

mycli help --search term finds matching subcommands.

tests.test_commands.test_help_search_no_match(invoke)[source]

mycli help --search term with no matches.

tests.test_commands.test_help_in_all_command_cli(invoke, all_command_cli)[source]

The help subcommand works on the fixture CLI.

tests.test_commands.test_help_for_subcommand_in_all_command_cli(invoke, all_command_cli)[source]

help default-subcommand works on the fixture CLI.

tests.test_config module

tests.test_config.FULL_SEARCH_FLAGS = 285504

All search flags ConfigOption forces on, used as the baseline in tests.

tests.test_config.NO_DOTGLOB_FLAGS = 285440

FULL_SEARCH_FLAGS minus DOTGLOB, to exercise the dotfile warnings.

tests.test_config.simple_config_cli()[source]
tests.test_config.test_unset_conf(invoke, simple_config_cli)[source]
tests.test_config.test_unset_conf_debug_message(invoke, simple_config_cli, assert_output_regex)[source]
tests.test_config.test_conf_default_path(invoke, simple_config_cli)[source]
tests.test_config.test_conf_default_pathlib_type(invoke, create_config)[source]

Refs https://github.com/kdeldycke/click-extra/issues/1356

tests.test_config.test_conf_not_found(invoke, simple_config_cli, conf_path)[source]
tests.test_config.test_conf_unparsable(invoke, simple_config_cli, create_config)[source]

Explicit –config pointing to a file with garbage content.

tests.test_config.test_conf_empty_file(invoke, simple_config_cli, create_config)[source]

Explicit –config pointing to an empty file.

tests.test_config.test_no_config_option(invoke, simple_config_cli, create_config)[source]
tests.test_config.test_standalone_no_config_option(invoke)[source]

@no_config_option cannot work without @config_option.

tests.test_config.test_strict_conf(invoke, create_config, conf_text, expect_error)[source]

Strict mode rejects unknown params but accepts clean configs.

tests.test_config.test_conf_file_overrides_defaults(invoke, simple_config_cli, create_config, httpserver, conf_name, conf_text, conf_data, assert_output_regex)[source]
tests.test_config.test_auto_envvar_conf(invoke, simple_config_cli, create_config, httpserver, conf_name, conf_text, conf_data)[source]
tests.test_config.test_conf_file_overridden_by_cli_param(invoke, simple_config_cli, create_config, httpserver, conf_name, conf_text, conf_data)[source]
tests.test_config.test_conf_metadata(invoke, create_config, httpserver, conf_name, conf_text, conf_data)[source]
tests.test_config.test_conf_metadata_no_config(invoke)[source]

ctx.meta entries are not set when –no-config skips loading.

tests.test_config.test_default_map_populated(invoke, create_config)[source]

Verify default_map structure when config values match CLI parameters.

Complements test_conf_metadata which only checks the empty default_map case (where no config values match the CLI’s parameter structure).

tests.test_config.test_merge_default_map_standalone(invoke)[source]

merge_default_map filters a config into default_map on its own.

load_conf bypasses this method by installing the merged config the validation pipeline already produced, so it is exercised here directly to cover the standalone entry point external callers rely on.

tests.test_config.test_default_map_none_without_config(invoke)[source]

Verify default_map is left alone when –no-config is used.

tests.test_config.test_nested_subcommand_config(invoke, create_config)[source]

Config propagates through group -> subgroup -> leaf command.

tests.test_config.test_multiple_cli_shared_conf(invoke, create_config)[source]

Two CLIs sharing the same configuration file.

Refs: https://github.com/kdeldycke/click-extra/issues/1277

tests.test_config.test_params_template_not_mutated_across_invocations(invoke, create_config)[source]

Back-to-back invocations of the same CLI must not cross-contaminate via ConfigOption.params_template.

params_template is a @cached_property of the ConfigOption instance bound at decoration time, so it lives for the lifetime of the CLI object. _recursive_update mutates its first argument in place; without a defensive copy, the cached template would accumulate values from earlier --config loads and leak them into default_map on subsequent invocations.

tests.test_config.test_lazy_group_config(invoke, create_config, tmp_path)[source]

Test that lazy groups work with config files.

Refs: https://github.com/kdeldycke/click-extra/issues/1332

tests.test_config.test_lazy_group_config_no_config_flag(invoke, create_config, tmp_path)[source]

Test that –no-config works with lazy groups.

tests.test_config.test_file_pattern(file_format_patterns, expected_pattern)[source]

Test the file_pattern property with different file format configurations.

tests.test_config.test_default_pattern_roaming_force_posix(roaming, force_posix, current_platform, expected_path, monkeypatch)[source]

Test that roaming and force_posix affect the default pattern generation.

tests.test_config.test_default_pattern_xdg_config_home(force_posix, tmp_path, monkeypatch)[source]

Test that default_pattern respects XDG_CONFIG_HOME on Linux.

tests.test_config.test_parent_patterns(tmp_path, search_parents, subdirs, create_file, expected_start)[source]
tests.test_config.test_parent_patterns_with_magic_pattern(tmp_path, pattern_factory, expected_factory)[source]

Test parent_patterns with glob patterns containing magic characters.

Magic pattern with search_parents=False yields only the original.

tests.test_config.test_parent_patterns_relative_path(tmp_path)[source]

Test parent_patterns resolves relative paths to absolute.

tests.test_config.test_parent_patterns_stop_at_path(tmp_path)[source]

stop_at as a path limits the parent directory walk.

tests.test_config.test_parent_patterns_stop_at_vcs(tmp_path, has_vcs, expected_bounded)[source]

stop_at=VCS stops at VCS root, or walks to filesystem root if none.

tests.test_config.test_parent_patterns_inaccessible_directory(tmp_path)[source]

Walk stops at an inaccessible directory.

tests.test_config.test_find_vcs_root(tmp_path, vcs_dir, expected)[source]

Test _find_vcs_root with .git, .hg, and no VCS markers.

tests.test_config.test_config_option_default_no_config(invoke, create_config)[source]

ConfigOption with default=NO_CONFIG disables autodiscovery.

tests.test_config.test_no_config_explicit_with_default_no_config(invoke)[source]

–no-config still prints the skip message even when NO_CONFIG is the default.

tests.test_config.test_excluded_params(invoke, create_config)[source]

Custom excluded_params prevents config values from being applied.

tests.test_config.test_included_params(invoke, create_config)[source]

Only parameters in included_params are loaded from config.

tests.test_config.test_included_params_empty(invoke, create_config)[source]

An empty included_params excludes all params from config.

tests.test_config.test_included_and_excluded_params_conflict()[source]

Providing both included_params and excluded_params raises ValueError.

tests.test_config.test_multiple_files_matching_glob(invoke, create_config, tmp_path)[source]

When multiple files match a glob, only the first parseable one is used.

tests.test_config.test_forced_flags_warnings(caplog)[source]

Warnings fire when SPLIT, BRACE or NODIR flags are missing.

All format extensions are found in the search directory.

Regression test: before BRACE expansion, only the first format in the default pattern got the directory prefix: others were searched in CWD.

tests.test_config.test_root_dir_parent_search_finds_non_toml(invoke, tmp_path)[source]

Parent search with root_dir correctly finds non-TOML config in parents.

Before the root_dir refactoring, SPLIT patterns like *.toml|*.yaml only applied the directory prefix to the first sub-pattern. Now with root_dir, all sub-patterns are scoped to the correct directory.

tests.test_config.test_no_enabled_formats_raises()[source]

ValueError raised when all formats are disabled.

tests.test_config.test_pyproject_toml_in_defaults()[source]

ConfigOption() with default file_format_patterns includes PYPROJECT_TOML.

tests.test_config.test_pyproject_toml_tool_extraction(simple_config_cli)[source]

parse_conf with PYPROJECT_TOML returns the [tool] subsection.

tests.test_config.test_pyproject_toml_no_tool_section(simple_config_cli)[source]

pyproject.toml without [tool] returns empty dict.

tests.test_config.test_file_pattern_with_pyproject_toml()[source]

Explicit file_format_patterns with PYPROJECT_TOML works.

tests.test_config.test_pyproject_toml_overrides_defaults(invoke, create_config)[source]

End-to-end: a CLI with default formats reads from pyproject.toml.

tests.test_config.test_validate_config_valid(invoke, create_config)[source]

–validate-config with a valid config file exits 0.

tests.test_config.test_validate_config_invalid_keys(invoke, create_config)[source]

–validate-config with unrecognized keys exits 1.

tests.test_config.test_extensionless_config(invoke, create_config, default_pattern, expected_help_default)[source]

Both broad and exact default patterns resolve the same .commandrc file.

The default parameter is printed as-is on the help screen, so an exact path is more informative than a broad glob, but both locate the same file.

tests.test_config.test_validate_config_unparsable(invoke, create_config)[source]

–validate-config with garbage content exits 2.

tests.test_config.test_validate_config_missing_file(invoke, tmp_path)[source]

–validate-config with a nonexistent file is caught by Click’s Path(exists=True).

tests.test_config.test_validate_config_requires_config_option(invoke, tmp_path)[source]

–validate-config without @config_option raises RuntimeError.

tests.test_config.test_validate_config_pyproject_toml(invoke, create_config)[source]

–validate-config works with pyproject.toml [tool.*] sections.

tests.test_config.test_default_subcommand_selection(invoke, create_config, cli_subcmd, expected, unexpected)[source]

Config default is used when no subcommand given; CLI wins otherwise.

tests.test_config.test_default_subcommand_chained(invoke, create_config)[source]

chain=True group runs multiple config-listed subcommands in order.

tests.test_config.test_default_subcommand_config_errors(invoke, create_config, conf_value, error_fragment)[source]

Bad _default_subcommands values produce clear errors.

tests.test_config.test_default_subcommand_strict_mode_tolerance(invoke, create_config)[source]

strict=True config with _default_subcommands doesn’t raise.

tests.test_config.test_default_subcommand_validate_config_tolerance(invoke, create_config)[source]

–validate-config with _default_subcommands reports valid.

tests.test_config.test_default_subcommand_with_options(invoke, create_config)[source]

Default subcommand receives its config-provided options.

tests.test_config.test_default_subcommand_no_config(invoke)[source]

Normal behavior when no config file is loaded.

tests.test_config.test_default_subcommand_duplicates_warning(invoke, create_config)[source]

Duplicate entries in _default_subcommands are deduplicated with a warning.

tests.test_config.test_default_subcommand_cli_override_debug_log(invoke, create_config)[source]

Debug log emitted when CLI subcommands override config defaults.

tests.test_config.test_prepend_subcommand_selection(invoke, create_config, cli_subcmd, expected, unexpected)[source]

Prepend fires regardless of whether a CLI subcommand is given.

tests.test_config.test_prepend_subcommand_with_defaults(invoke, create_config, cli_subcmd, expect_backup)[source]

Prepend always applies; defaults only fire when no CLI subcommand given.

tests.test_config.test_prepend_subcommand_non_chained_error(invoke, create_config)[source]

Error on non-chained group.

tests.test_config.test_prepend_subcommand_config_errors(invoke, create_config, conf_value, error_fragment)[source]

Bad _prepend_subcommands values produce clear errors.

tests.test_config.test_prepend_subcommand_strict_mode_tolerance(invoke, create_config)[source]

strict=True config with _prepend_subcommands doesn’t raise.

tests.test_config.test_prepend_subcommand_validate_config_tolerance(invoke, create_config)[source]

–validate-config with _prepend_subcommands reports valid.

tests.test_config.test_prepend_subcommand_duplicates_warning(invoke, create_config)[source]

Duplicate entries in _prepend_subcommands are deduplicated with a warning.

tests.test_config.test_prepend_subcommand_info_log(invoke, create_config)[source]

INFO log emitted when _prepend_subcommands are injected.

tests.test_config.test_prepend_subcommand_multiple(invoke, create_config)[source]

Multiple prepend subcommands run in order.

tests.test_config.test_sanity_checks(caplog, default, file_format_patterns, flags, present, absent)[source]

_check_pattern_sanity emits (or suppresses) debug logs per pattern config.

tests.test_config.test_expand_dotted_keys(input_conf, expected)[source]
tests.test_config.test_dotted_keys_in_config(invoke, simple_config_cli, create_config, conf_name, conf_text)[source]

Dotted keys in config files are expanded into nested structures.

tests.test_config.test_expand_dotted_keys_conflict_warning(caplog, input_conf, warning_fragment)[source]

Scalar/dict conflicts on the same key emit a warning.

tests.test_config.test_expand_dotted_keys_empty_segments(caplog, input_conf)[source]

Dotted keys with empty segments are skipped with a warning.

tests.test_config.test_expand_dotted_keys_edge_cases(input_conf, expected)[source]
tests.test_config.test_expand_dotted_keys_strict_conflict(input_conf, error_fragment)[source]

Strict mode raises ValueError on type conflicts.

tests.test_config.test_expand_dotted_keys_strict_empty_segments(input_conf)[source]

Strict mode raises ValueError on dotted keys with empty segments.

tests.test_config.test_strict_conf_dotted_key_conflict(invoke, create_config)[source]

Strict mode rejects configs with dotted-key type conflicts.

tests.test_config_schema module

Tests for typed configuration schemas, validation, and extension points.

tests.test_config_schema.test_normalize_config_keys()[source]
tests.test_config_schema.test_config_schema_dataclass(invoke, create_config)[source]

Dataclass schemas are auto-detected and instantiated with normalized keys.

tests.test_config_schema.test_config_schema_callable(invoke, create_config)[source]

A plain callable can be used as config_schema.

tests.test_config_schema.test_config_schema_no_config_file(invoke)[source]

When no config file is found, schema defaults are used.

tests.test_config_schema.test_config_schema_dataclass_defaults(invoke, create_config)[source]

Dataclass defaults are used for fields not present in the config file.

tests.test_config_schema.test_fallback_sections(invoke, create_config)[source]

Legacy section names are recognized with a deprecation warning.

tests.test_config_schema.test_fallback_sections_prefers_current(invoke, create_config)[source]

When both current and legacy sections exist, current wins.

tests.test_config_schema.test_config_schema_multiple_formats(invoke, create_config, conf_name, conf_text)[source]

Config schema works with YAML and JSON, not just TOML.

tests.test_config_schema.test_config_schema_on_config_option_directly(invoke, create_config)[source]

Config schema can be set directly on ConfigOption via the decorator.

tests.test_config_schema.test_get_tool_config_defaults_to_current_context(invoke, create_config)[source]

get_tool_config() works without passing ctx explicitly.

tests.test_config_schema.test_flatten_config_keys()[source]
tests.test_config_schema.test_flatten_config_keys_with_normalize()[source]

flatten + normalize maps nested kebab-case config to flat snake_case fields.

tests.test_config_schema.test_config_schema_nested_toml(invoke, create_config)[source]

Nested TOML sub-tables map to flat dataclass fields via flattening.

tests.test_config_schema.test_config_schema_strict_rejects_unknown(invoke, create_config)[source]

schema_strict=True raises ValueError on unrecognized config keys.

tests.test_config_schema.test_config_schema_strict_passes_when_valid(invoke, create_config)[source]

schema_strict=True does not raise when all config keys are known.

tests.test_config_schema.test_config_schema_strict_with_nested(invoke, create_config)[source]

schema_strict=True validates flattened keys from nested sub-tables.

tests.test_config_schema.test_pyproject_toml_cwd_discovery(invoke, tmp_path, monkeypatch)[source]

pyproject.toml in CWD is discovered automatically without –config.

tests.test_config_schema.test_pyproject_toml_cwd_discovery_walks_up(invoke, tmp_path, monkeypatch)[source]

pyproject.toml discovery walks up from CWD to parent directories.

tests.test_config_schema.test_pyproject_toml_explicit_config_skips_cwd(invoke, create_config, tmp_path, monkeypatch)[source]

Explicit –config skips CWD pyproject.toml discovery.

tests.test_config_schema.test_pyproject_toml_cwd_skips_unrelated_tool_section(invoke, tmp_path, monkeypatch)[source]

A pyproject.toml without [tool.<cli_name>] is skipped.

Regression: a pyproject.toml carrying only unrelated [tool.X] sections (like a dotfiles repo’s [tool.ruff]) used to shadow the user’s app-dir config. It must now be ignored so the CLI falls back to its defaults instead of inheriting an unrelated project’s settings.

tests.test_config_schema.test_pyproject_toml_cwd_walks_past_unrelated_tool_section(invoke, tmp_path, monkeypatch)[source]

CWD walk continues past a pyproject.toml lacking [tool.<cli_name>].

A nearer pyproject.toml that only carries unrelated [tool.X] sections must not stop the upward walk: a parent pyproject.toml with a matching [tool.<cli_name>] section should still be discovered.

tests.test_config_schema.test_pyproject_toml_cwd_mixed_tool_sections(invoke, tmp_path, monkeypatch)[source]

[tool.<cli_name>] is picked from a pyproject.toml that also has others.

tests.test_config_schema.test_pyproject_toml_cwd_unrelated_does_not_shadow_app_dir(invoke, tmp_path, monkeypatch)[source]

An unrelated pyproject.toml falls through to the app-dir config.

Directly exercises the documented intent of the fix: when CWD contains a pyproject.toml whose only [tool.X] sections are unrelated to the CLI, the walk must give up so the standard app-dir search can find the user’s actual config and apply it.

tests.test_config_schema.test_flatten_config_keys_opaque()[source]

opaque_keys stops flattening at matching key boundaries.

tests.test_config_schema.test_flatten_config_keys_opaque_nested()[source]

opaque_keys works at deeper nesting levels.

tests.test_config_schema.test_schema_type_aware_flattening(invoke, create_config)[source]

dict-typed dataclass fields stop flattening automatically.

tests.test_config_schema.test_schema_field_metadata_config_path(invoke, create_config)[source]

click_extra.config_path extracts a value at a dotted TOML path.

tests.test_config_schema.test_schema_field_metadata_normalize_keys_true(invoke, create_config)[source]

click_extra.normalize_keys defaults to True: keys are normalized.

tests.test_config_schema.test_schema_nested_dataclass(invoke, create_config)[source]

Nested dataclass fields are recursively instantiated.

tests.test_config_schema.test_schema_nested_dataclass_with_opaque_fields(invoke, create_config)[source]

Nested dataclass with dict-typed fields preserves opaque keys.

tests.test_config_schema.test_schema_nested_dataclass_defaults(invoke, create_config)[source]

Nested dataclass uses defaults when config section is absent.

tests.test_config_schema.test_strict_skips_opaque_dict_field(invoke, create_config)[source]

Strict mode does not reject keys inside a dict[str, X] schema field.

A field typed as dict[str, dict] is user-controlled: the keys are data, not CLI flag names. Click-extra strips that sub-tree before running its unknown-key check so app extensions don’t trip strict mode.

tests.test_config_schema.test_strict_skips_opaque_metadata_field(invoke, create_config)[source]

Strict mode also honors the EXTENSION_METADATA_KEY marker on a field whose Python type is not a mapping.

tests.test_config_schema.test_validate_config_skips_opaque_field(invoke, create_config)[source]

–validate-config also skips opaque sub-trees, so a config that the runtime accepts also passes validation.

tests.test_config_schema.test_config_validator_runs_and_fails_under_validate_config(invoke, create_config)[source]

A registered ConfigValidator runs during --validate-config and surfaces its ValidationError with a path rooted at the config file.

tests.test_config_schema.test_config_validator_runs_during_normal_load(invoke, create_config)[source]

A misconfigured opaque sub-tree fails fast during normal config loading, not only under --validate-config.

tests.test_config_schema.test_config_validator_extension_path_strips_strict_check(invoke, create_config)[source]

A ConfigValidator(extension_path=…) registration alone is enough to skip strict-check on that path, even when the schema doesn’t have the field.

tests.test_config_schema.test_config_validator_collects_all_errors(invoke, create_config)[source]

--validate-config reports every detected error in one pass.

A config with both an unknown CLI flag key and a validator-flagged field should surface both messages before the run exits non-zero, so the user sees the full punch list.

tests.test_config_schema.test_collect_opaque_paths_from_schema()[source]

Schema introspection picks up dict-typed fields, metadata-marked fields, and nested-dataclass opaque fields with dotted prefixes.

tests.test_config_schema.test_schema_strict_honors_extension_metadata_on_non_mapping_field(invoke, create_config)[source]

schema_strict must not descend into an EXTENSION_METADATA_KEY-marked field whose Python type is not a mapping.

Before the opaque-path unification, the outer strip honored the marker but the dataclass adapter’s own flatten boundary inspected only the type hint, so the marked sub-tree was flattened into dotted keys and rejected as unknown.

tests.test_config_schema.test_run_config_validation_valid_document()[source]

A clean document yields an ok report with the schema instance built and every opaque sub-tree extracted.

tests.test_config_schema.test_run_config_validation_exposes_merged_conf()[source]

A passing strict check carries the template-filtered config as merged_conf, with recognized values merged in and unknown keys dropped.

tests.test_config_schema.test_run_config_validation_collects_all_then_short_circuits()[source]

collect_all=True gathers errors from every stage in order; collect_all=False stops after the first.

tests.test_config_schema.test_run_config_validation_wraps_schema_errors()[source]

A schema_strict failure is recorded as a ValidationError with the schema_error code, and the message is preserved verbatim.

tests.test_config_schema.test_run_config_validation_no_schema_no_template()[source]

With neither a template nor a schema, the report is ok and carries no schema instance.

tests.test_config_schema.test_make_schema_callable_coerces_dict_to_dataclass()[source]

The public make_schema_callable turns a raw config dict into a dataclass.

tests.test_context module

Tests for click_extra.context.

Covers four surfaces the module exposes:

  • The registry of ctx.meta key constants.

  • The get() / set() helpers that read and write them.

  • Context, Click Extra’s cloup.Context subclass.

  • _LazyMetaDict, the lazy ctx._meta proxy used by VersionOption.

tests.test_context.KEY_CONSTANTS: tuple[tuple[str, str], ...] = (('RAW_ARGS', 'click_extra.raw_args'), ('CONF_SOURCE', 'click_extra.conf_source'), ('CONF_FULL', 'click_extra.conf_full'), ('TOOL_CONFIG', 'click_extra.tool_config'), ('VERBOSITY_LEVEL', 'click_extra.verbosity_level'), ('VERBOSITY', 'click_extra.verbosity'), ('VERBOSE', 'click_extra.verbose'), ('QUIET', 'click_extra.quiet'), ('START_TIME', 'click_extra.start_time'), ('JOBS', 'click_extra.jobs'), ('TABLE_FORMAT', 'click_extra.table_format'), ('SORT_BY', 'click_extra.sort_by'), ('COLUMNS', 'click_extra.columns'), ('THEME', 'click_extra.theme.active'), ('THEME_OVERRIDES', 'click_extra.theme.overrides'), ('TELEMETRY', 'click_extra.telemetry'), ('PROGRESS', 'click_extra.progress'), ('ACCESSIBLE', 'click_extra.accessible'), ('ZERO_EXIT', 'click_extra.zero_exit'))

Pairs of (attribute name, raw string key) for every registered ctx.meta entry. Single source of truth used by every parametrized registry test below.

tests.test_context.test_key_constant_value(attr, expected)[source]

Each registered constant binds to its documented string value.

Pins the spelling of every key so a rename of the literal does not silently break downstream code that reads ctx.meta["click_extra.X"].

Return type:

None

tests.test_context.test_key_uses_namespace_prefix(key)[source]

Every registered key sits under META_NAMESPACE.

Return type:

None

tests.test_context.test_keys_are_unique()[source]

No two registry constants share a string value.

Return type:

None

tests.test_context.test_registry_covers_all_module_constants()[source]

KEY_CONSTANTS lists every public namespace-prefixed constant.

Drift detector: when a new ctx.meta key is added to click_extra.context but not to KEY_CONSTANTS, this test fails so the maintainer remembers to update both sides.

Return type:

None

tests.test_context.test_get_returns_default_for_missing_key()[source]

context.get() mirrors dict.get semantics.

Return type:

None

tests.test_context.test_set_then_get_round_trip()[source]

context.set() is observable through context.get().

Return type:

None

tests.test_context.test_context_uses_help_formatter()[source]

Context installs Click Extra’s colorized formatter.

Return type:

None

tests.test_context.test_context_meta_kwarg_seeds_ctx_meta()[source]

The meta= kwarg populates ctx.meta at construction time.

Return type:

None

tests.test_context.test_context_meta_kwarg_omitted_leaves_meta_empty()[source]

Without meta=, ctx.meta is an empty dict, not None.

Return type:

None

tests.test_context.test_context_color(monkeypatch, parent_color, child_color, expected)[source]

Context color resolution covers every parent/child path.

Root contexts without an explicit color= resolve the GNU auto default: with no color environment variable they stay at None (TTY detection). Child contexts inherit the parent’s color unless they override it explicitly.

Return type:

None

tests.test_context.test_context_posixly_correct(monkeypatch, env_present, expected)[source]

POSIXLY_CORRECT flips allow_interspersed_args off when present.

A plain click.Command defaults the flag to True, so the unset case leaves interspersing enabled and the set case disables it.

Return type:

None

tests.test_context.test_posixly_correct_presence_overrides_explicit_true(monkeypatch)[source]

An empty POSIXLY_CORRECT still wins over an explicit True.

Presence alone triggers POSIX parsing (matching GNU getopt), regardless of value, and it takes precedence over a developer-supplied allow_interspersed_args=True.

Return type:

None

tests.test_context.test_posixly_correct_stops_option_parsing_at_first_argument()[source]

End-to-end: parsing stops at the first positional under POSIXLY_CORRECT.

Without the variable, the option interleaves with arguments (GNU style). With it set, the first positional ends option parsing, so the option keeps its default and the remaining tokens fall into the variadic argument.

Return type:

None

tests.test_context.test_lazy_meta_dict_resolves_on_first_access()[source]

Reading a lazy key triggers exactly one source attribute access.

Return type:

None

tests.test_context.test_lazy_meta_dict_caches_after_resolution()[source]

Subsequent reads of the same lazy key do not re-hit the source.

Return type:

None

tests.test_context.test_lazy_meta_dict_get_resolves_lazy_key()[source]

.get(lazy_key) triggers resolution like __getitem__.

Return type:

None

tests.test_context.test_lazy_meta_dict_get_returns_default_for_unknown_key()[source]

.get(unknown, default) returns the default without touching source.

Return type:

None

tests.test_context.test_lazy_meta_dict_contains_includes_lazy_keys_without_resolving()[source]

in returns True for declared lazy keys before any resolution.

Return type:

None

tests.test_context.test_lazy_meta_dict_preserves_base_entries()[source]

Entries from the wrapped base dict remain accessible.

Return type:

None

tests.test_context.test_lazy_meta_dict_independent_keys_resolve_independently()[source]

Resolving one lazy key does not resolve sibling lazy keys.

Return type:

None

tests.test_context.test_pass_context_typed_for_enhanced_context(invoke)[source]

@pass_context accepts a handler typed with the enhanced Context.

Click’s own pass_context is typed for the base click.Context, so annotating the handler with click-extra’s Context (to reach its extra helpers) would fail static type checks by parameter contravariance. This must both type-check (mypy covers this file) and forward the active enhanced Context at runtime.

tests.test_envvar module

tests.test_envvar.test_merge_envvar_ids(envvars, result)[source]
tests.test_envvar.test_clean_envvar_id(env_name, clean_name)[source]
tests.test_envvar.test_show_auto_envvar_help(invoke, cmd_decorator, option_help)[source]

Check that the auto-generated envvar appears in the help screen with the extra variants.

Checks that https://github.com/pallets/click/issues/2483 is addressed.

tests.test_envvar.envvars_test_cases()[source]
tests.test_envvar.test_auto_envvar_parsing(invoke, cmd_decorator, envvars, expected_flag)[source]

This test highlights the way Click recognize and parse envvars.

It shows that the default behavior is not ideal, and covers how command improves the situation by normalizing the envvar name.

tests.test_envvar.test_env_copy()[source]

tests.test_execution module

Tests for the execution-control options: –jobs, –time and -0/–zero-exit.

tests.test_execution.test_standalone_jobs_option(invoke, cmd_decorator, option_decorator)[source]
tests.test_execution.test_default_value(invoke)[source]

Default is one fewer than available CPU cores.

tests.test_execution.test_keyword_resolution(invoke, keyword, expected)[source]

‘auto’ resolves to logical CPUs minus one, ‘max’ to all logical CPUs.

On a host with enough cores the resolution is silent; on a 1- or 2-core host the keyword collapses to a single (sequential) job with a warning, so the assertion adapts to the host running the suite.

tests.test_execution.test_parallel_keyword_collapses_to_sequential_warns(invoke, keyword, cpu_count, default_jobs, cpu_phrase)[source]

‘auto’/’max’ warn when too few logical CPUs force a single (sequential) job.

tests.test_execution.test_explicit_single_job_is_silent(invoke)[source]

An explicit ‘–jobs 1’ is a deliberate sequential choice: no warning.

tests.test_execution.test_default_collapses_to_sequential_warns(invoke)[source]

The bare default (‘auto’) also warns when it collapses to a single job.

This is the silent trap on a two-core host: no flag is passed, yet the default reserves one core and runs sequentially.

tests.test_execution.test_resolved_job_count_logged_at_info(invoke)[source]

The resolved job count and os.cpu_count() are logged at info level.

tests.test_execution.test_run_jobs_preserves_order(jobs)[source]

Results come back in submission order, sequential or parallel.

tests.test_execution.test_run_jobs_sequential_is_lazy()[source]

With one worker, items run lazily so a caller can stop early.

tests.test_execution.test_run_jobs_reads_jobs_from_context(invoke)[source]

Without an explicit count, run_jobs reads the resolved –jobs value.

tests.test_execution.test_run_jobs_without_context_runs_sequential()[source]

Outside any Click context and with no count, run_jobs falls back to 1.

tests.test_execution.test_invalid_value(invoke)[source]

Values that are neither an integer nor a known keyword are rejected.

tests.test_execution.test_jobs_shell_complete(incomplete, expected)[source]

–jobs completion suggests the auto/max keywords and never an integer.

tests.test_execution.test_clamp_to_one(invoke, value, warning)[source]

0 disables parallelism and negatives clamp: both run 1 job with a warning.

tests.test_execution.test_exceeds_cpu_count(invoke)[source]

Warn when requested jobs exceed available CPU cores.

tests.test_execution.test_no_warning_within_bounds(invoke)[source]

No warning when the value is within the valid range.

tests.test_execution.test_single_core_default()[source]

DEFAULT_JOBS is 1 when cpu_count is 1.

tests.test_execution.test_none_cpu_count_default()[source]

DEFAULT_JOBS is 1 when cpu_count returns None.

tests.test_execution.test_integrated_time_option(invoke, subcommand_id, time_min)[source]
tests.test_execution.test_integrated_notime_option(invoke, subcommand_id)[source]
tests.test_execution.test_standalone_timer_option(invoke, cmd_decorator, option_decorator, assert_output_regex)[source]
tests.test_execution.test_time_with_short_circuit_sibling_still_prints(invoke)[source]

--time --version still emits a duration.

--version is an eager option that calls ctx.exit() before the user command body runs, but --time is intentionally measured even on short-circuit paths so it can probe the cost of Click Extra’s own machinery (eager callbacks, config loading, option parsing).

tests.test_execution.test_standalone_zero_exit_option(invoke, cmd_decorator, option_decorator)[source]
tests.test_execution.test_zero_exit_auto_envvar(invoke)[source]

tests.test_highlight module

tests.test_highlight.test_theme_definition()[source]

Ensure we do not leave any property we would have inherited from cloup and logging primitives.

tests.test_highlight.test_extra_theme()[source]
class tests.test_highlight.HashType(*values)[source]

Bases: Enum

MD5 = 1
SHA1 = 2
BCRYPT = 3
class tests.test_highlight.Priority(*values)[source]

Bases: Enum

LOW = 'low-priority'
HIGH = 'high-priority'
class tests.test_highlight.Port(*values)[source]

Bases: IntEnum

HTTP = 80
HTTPS = 443
tests.test_highlight.test_option_highlight(opt, expected_outputs)[source]

Test highlighting of all option variations: types, defaults, ranges, envvars, choices, flags, metavars, deprecated messages.

tests.test_highlight.test_bracket_field_full_combination_styling()[source]

Document the runtime styling of a fully-loaded bracket field.

Covers the canonical [env var: NAME; default: VAL; required] block that combines all three trailing-field labels. The bracket slot styles only the structural tokens: the outer [ / ], the env var: `` / ``default: `` labels, and the ``; `` separators, while the value tokens (``NAME, VAL) and the required label get their own slot styling layered on top. This is the contract referenced in bracket’s docstring.

tests.test_highlight.test_range_field_does_not_leak_into_following_choice()[source]

A range field must not bleed its bracket styling into the next option.

Regression guard: the [x>=1] field emitted for an IntRange(min=1) option is immediately followed by a Choice option whose [apple|mango] metavar supplies a closing ]. When the range bound matched \S+ it absorbed the field’s own ], letting the bracket regex run past the field and dim everything up to the choice metavar’s ]. Assert the range field is self-contained and closed, and the following choice keeps its own styling.

tests.test_highlight.test_bracket_field_inner_slot_fallback_to_bracket()[source]

Inner bracket-field slots fall back to bracket when at identity.

Contract: a theme that styles only bracket and leaves envvar / default / required / range_label at identity should still render the whole bracket field with the bracket styling: value tokens inherit it rather than rendering plain. Specific inner slots override piecemeal.

tests.test_highlight.test_bracket_field_inner_slot_override_takes_precedence()[source]

When a theme sets one inner slot, only that token uses it.

Complements test_bracket_field_inner_slot_fallback_to_bracket(): a theme that styles bracket + only required (leaving envvar and default at identity) should render the required label with the dedicated style and fall back to bracket for the envvar and default values.

tests.test_highlight.test_skip_hidden_option()[source]

Ensure hidden options are not highlighted.

tests.test_highlight.test_cross_ref_highlight_disabled()[source]

When cross_ref_highlight is False, only structural elements are styled (bracket fields, deprecated messages, subcommands, choice metavars). Options, choices in free-form text, metavars, arguments, and CLI names are left plain.

tests.test_highlight.test_choice_does_not_override_default_style()[source]

Choice cross-ref highlighting must not restyle text inside bracket fields.

When a default value contains a substring that matches a choice keyword (like outline from rounded-outline), the choice style must not override the default value style. Regression test for the case where line-wrapping splits a hyphenated default so the second word starts a new line and passes the lookbehind.

tests.test_highlight.test_choice_collection_case(params, expected, forbidden)[source]

Choice keywords must use the original-case strings from the type definition, not the normalized (lowercased) forms produced by normalize_choice().

tests.test_highlight.test_argument_highlight(params, expected, forbidden)[source]

Argument metavars get the argument style, distinct from option metavars.

tests.test_highlight.test_no_false_positive_highlight(params, help_text, expected_present, expected_absent)[source]

Verify that highlighting does not leak into compound words, URLs, dotted names, already-styled regions, or partial-word matches.

tests.test_highlight.test_parent_keywords_highlighted_in_subcommand_help()[source]

Parent group names, options, and choices must be highlighted in subcommand help text.

tests.test_highlight.test_parent_choice_case_with_custom_metavar()[source]

Parent choices with custom metavar must use original-case strings in subcommand help, not normalized (lowercased) forms.

tests.test_highlight.test_command_aliases_collected()[source]

Command aliases are collected as keywords for highlighting.

tests.test_highlight.test_command_aliases_highlighted(invoke)[source]

Aliases inside parenthetical groups are highlighted with the subcommand style.

tests.test_highlight.test_single_alias_highlighted(invoke)[source]

A command with exactly one alias still gets highlighted.

tests.test_highlight.test_alias_no_false_positive_in_description(invoke)[source]

An alias name appearing in a description must not be highlighted when it does not sit inside alias parentheses.

tests.test_highlight.test_alias_substring_not_highlighted(invoke)[source]

An alias that is a substring of the subcommand name must not cause double-highlighting or partial matches.

tests.test_highlight.test_help_keywords_merge(base_kwargs, other_kwargs, checks)[source]

HelpKeywords.merge() unions every field.

tests.test_highlight.test_help_keywords_subtract(base_kwargs, removals_kwargs, checks)[source]

HelpKeywords.subtract() removes matching entries per field.

tests.test_highlight.test_extra_keywords_merged()[source]

extra_keywords injects additional strings into the collected set.

tests.test_highlight.test_excluded_keywords_preserved_in_collection()[source]

excluded_keywords does not remove from collect_keywords().

Exclusion is deferred to highlight_extra_keywords() so that choice metavars can be styled with the full choices set before the excluded choices are removed for cross-ref passes.

tests.test_highlight.test_excluded_keywords_via_constructor()[source]

excluded_keywords can be passed through the Command constructor.

tests.test_highlight.test_excluded_keywords_suppresses_highlighting()[source]

Excluded keywords do not appear styled in the rendered help text.

tests.test_highlight.test_style_choice_metavar(metavar, choices, expected)[source]

_style_choice_metavar styles known choices inside bracket-delimited metavar strings, styles non-choice parts (type placeholders) as metavars, and returns None for non-bracket strings.

tests.test_highlight.test_multiple_choice_options_metavar_styled()[source]

Each choice option gets its metavar individually styled.

tests.test_highlight.test_jobs_hybrid_metavar_highlights_keywords_and_placeholder()[source]

The hybrid --jobs [auto|max|INTEGER] metavar highlights every part.

Regression guard: JobCount is a custom ParamType, not a click.Choice, so the keyword collector learns its auto/max keywords only through the choices attribute it exposes. With no other option contributing these tokens (a bare JobsOption command pulls in no --color), auto/max can only be styled via JobCount, and the INTEGER placeholder renders as a metavar rather than staying plain.

tests.test_highlight.test_excluded_multiple_choices_styled_in_metavar_only()[source]

Multiple excluded choices appear styled in their own metavar but not in free-text descriptions.

tests.test_highlight.test_excluded_keywords_inheritance(parent_excluded, child_excluded, word, expect_styled)[source]

excluded_keywords propagate from parent groups to subcommands.

Parent choices are collected for subcommand help screens (cross-ref highlighting). The parent’s excluded_keywords must follow, otherwise excluded choices bleed into subcommand descriptions.

tests.test_highlight.test_excluded_keywords_grandparent_propagation()[source]

excluded_keywords propagate through multiple nesting levels.

tests.test_highlight.test_excluded_keywords_plain_click_group_parent()[source]

A plain click.Group parent without excluded_keywords does not crash.

tests.test_highlight.test_excluded_keywords_not_mutated()[source]

Calling format_help must not mutate the command’s excluded_keywords.

tests.test_highlight.test_keyword_collection(invoke, assert_output_regex)[source]
tests.test_highlight.test_substring_highlighting(content, patterns, expected, ignore_case)[source]
tests.test_highlight.test_standalone_help_option(invoke, cmd_decorator, cmd_type, option_decorator)[source]

tests.test_logging module

tests.test_logging.test_level_default_order()[source]
tests.test_logging.test_root_logger_defaults()[source]

Check our internal default is aligned to Python’s root logger.

tests.test_logging.test_integrated_verbosity_options(invoke, args, expected_level, assert_output_regex)[source]
tests.test_logging.test_custom_verbosity_option_name(invoke, args, assert_output_regex)[source]
tests.test_logging.test_custom_verbose_option_name(invoke, args, assert_output_regex)[source]
tests.test_logging.test_custom_quiet_option_name(invoke, args)[source]
tests.test_logging.test_unrecognized_verbosity_level(invoke, cmd_decorator, cmd_type)[source]
tests.test_logging.test_standalone_option_default_logger(invoke, cmd_decorator, option_decorator, args, expected_level, assert_output_regex)[source]

Checks: - option affect log level - the default logger is root - the default logger message format - level names are colored - log level is propagated to all other loggers

tests.test_logging.test_default_logger_param(invoke, logger_param, params)[source]

Passing a logger instance or name to the default_logger parameter works.

tests.test_logging.test_new_logger_name_passing(invoke)[source]

Test extra logger with custom format, passed to the option by its name.

tests.test_logging.test_new_logger_object_passing(invoke)[source]

Test extra logger with custom format, passed as an object to the option.

tests.test_logging.test_new_logger_root_config(invoke)[source]

Modify the root logger via new_logger()

tests.test_logging.test_logger_propagation(invoke)[source]

tests.test_man_page module

tests.test_man_page.test_render_manpage_header_and_sections()[source]
tests.test_man_page.test_option_names_are_escaped_and_bold()[source]
tests.test_man_page.test_choice_metavar_rendered()[source]
tests.test_man_page.test_no_rewrap_marker_becomes_no_fill()[source]

Click’s \b marker must produce a roff .nf / .fi block (click-man #9).

tests.test_man_page.test_no_rewrap_marker_does_not_leak_into_surrounding_paragraphs()[source]

A \b paragraph must not switch the whole DESCRIPTION to preformatted mode: prose before and after stays filled, only the marked paragraph lands between .nf / .fi.

tests.test_man_page.test_name_line_keeps_full_short_help()[source]

The NAME .SH line carries the canonical description without Click’s 45-char terminal truncation.

tests.test_man_page.test_inline_literal_in_short_help_renders_as_bold()[source]

Inline reST literals (..) in the first docstring line land on the NAME .SH line as \fB..\fR, not as raw backticks (which mandoc renders as quote characters).

tests.test_man_page.test_inline_literal_in_description_renders_as_bold()[source]

Inline reST literals in the description body become \fB..\fR so mandoc’s HTML output shows them in bold rather than wrapped in Unicode quote characters.

tests.test_man_page.test_boolean_flag_renders_both_spellings()[source]

--ascii / --no-ascii must both appear (click-man #41).

tests.test_man_page.test_hidden_option_skipped()[source]
tests.test_man_page.test_option_groups_become_subsections()[source]

An explicit option group renders as a roff .SS subsection of OPTIONS, with the ungrouped remainder gathered under Other options.

tests.test_man_page.test_ungrouped_command_has_no_subsections()[source]

A command with no explicit option group keeps a flat OPTIONS list.

tests.test_man_page.test_operand_documented()[source]
tests.test_man_page.test_render_manpages_tree_filenames()[source]
tests.test_man_page.test_subcommand_page_uses_full_path()[source]
tests.test_man_page.test_hidden_command_skipped()[source]
tests.test_man_page.test_dynamic_subcommand_discovered()[source]

Dynamically-resolved subcommands must be generated (click-man #14 / #56).

tests.test_man_page.test_environment_section_deduplicated()[source]

A shared env var (--config / --no-config) appears only once.

tests.test_man_page.test_files_section_from_config_option()[source]
tests.test_man_page.test_version_and_authors_overrides()[source]
tests.test_man_page.test_authors_omitted_without_metadata()[source]

No distribution matches the command name, so AUTHORS is dropped rather than synthesized from a fallback.

tests.test_man_page.test_source_date_epoch_is_honored(monkeypatch)[source]
tests.test_man_page.test_write_manpages(tmp_path)[source]
tests.test_man_page.test_man_option()[source]
tests.test_man_page.test_generated_roff_passes_groff_lint(cli, prog_name)[source]

Every generated page must parse cleanly under groff (no warnings).

forecast exercises the .SS option-group subsections.

tests.test_parameters module

class tests.test_parameters.Custom[source]

Bases: ParamType

A dummy custom type.

name: str = 'Custom'

the descriptive name of this type

convert(value, param, ctx)[source]

Convert the value to the correct type. This is not called if the value is None (the missing value).

This must accept string values from the command line, as well as values that are already the correct type. It may also convert other compatible types.

The param and ctx arguments may be None in certain situations, such as when converting prompt input.

If the value cannot be converted, call fail() with a descriptive message.

Parameters:
  • value – The value to convert.

  • param – The parameter that is using this type to convert its value. May be None.

  • ctx – The current context that arrived at this value. May be None.

tests.test_parameters.test_params_auto_types(invoke, option_decorator)[source]

Check parameters types and structure are properly derived from CLI.

tests.test_parameters.assert_table_content(output, expected_table, table_format=None)[source]

Helper to assert the content of a rendered table in the output.

Return type:

None

tests.test_parameters.test_standalone_show_params_option(invoke, cmd_decorator, option_decorator, assert_output_regex)[source]
tests.test_parameters.test_integrated_show_params_option(invoke, create_config)[source]
tests.test_parameters.test_show_params_table_format_ordering(invoke, args_order)[source]

--show-params respects --table-format regardless of CLI order.

tests.test_parameters.test_show_params_native_types(invoke, table_format)[source]

Serialization formats emit native types instead of styled glyphs.

tests.test_parameters.test_show_params_no_default_renders_none(invoke)[source]

A parameter with no default renders as None, not the UNSET sentinel.

Regression guard for Click 8.4’s UNSET sentinel leaking into the value and default columns through the --show-params re-parse path. See the RAW_ARGS dossier in click_extra.context.

tests.test_parameters.test_column_registry_is_consistent()[source]

TABLE_HEADERS exposes parallel column_labels() / column_ids().

tests.test_parameters.test_find_column_known_and_unknown()[source]
tests.test_parameters.test_render_doc_table_emits_markdown()[source]

render_doc_table returns a 2-column Markdown table covering every column.

tests.test_parameters.test_columns_option_projects_and_orders(invoke)[source]

–columns keeps only selected columns and preserves the user order.

tests.test_parameters.test_columns_option_rejects_unknown_id(invoke)[source]

An unknown column ID raises a UsageError with available IDs listed.

tests.test_parameters.test_recurse_subcommands(invoke)[source]
tests.test_parameters.test_subcommand_conflicts_with_parent_param(invoke)[source]

A subcommand whose name matches its direct parent’s param is skipped in the parameter tree (the config key would be ambiguous), but does not crash the CLI.

[root.alpha]
# "foo" is ambiguous: is it the --foo param or the [root.alpha.foo] subcommand?
foo = ???

See: https://github.com/kdeldycke/click-extra/pull/1286

tests.test_parameters.test_nested_subcommand_no_false_conflict_with_root_param(invoke)[source]

A nested subcommand can share a name with a root-level param without conflict.

The config paths are distinct (root.verbose vs root.alpha.verbose), so there is no ambiguity.

See: https://github.com/kdeldycke/click-extra/pull/1286

tests.test_parameters.test_standalone_table_rendering(invoke, opt1, opt2, table_format)[source]

Check all rendering styles of the table with standalone --show-params and --table-format option.

tests.test_parameters.test_standalone_no_color_rendering(invoke, opt1, opt2, opt3, table_format)[source]

Check that all rendering styles are responding to the --color option.

tests.test_pygments module

tests.test_pygments.lex(text)[source]

Shorthand: lex text and return (token_type, value) pairs.

Return type:

list[tuple]

tests.test_pygments.collect_classes(klass, prefix='Ansi')[source]

Returns all classes defined in click_extra.pygments that are a subclass of klass, and whose name starts with the provided prefix.

tests.test_pygments.get_pyproject_section(*section_path)[source]

Descends into the TOML tree of pyproject.toml to reach the value specified by section_path.

Return type:

dict[str, str]

tests.test_pygments.check_entry_points(entry_points, *section_path)[source]
Return type:

None

tests.test_pygments.test_ansi_lexers_candidates(tmp_path)[source]

Look into Pygments test suite to find all ANSI lexers candidates.

Good candidates for ANSI colorization are lexers that are producing Generic.Output tokens, which are often used by REPL-like and scripting terminal to render text in a console.

The list is manually maintained in Click Extra code, and this test is here to detect new candidates from new releases of Pygments.

Attention

The Pygments source code is downloaded from GitHub in the form of an archive, and extracted in a temporary folder.

The version of Pygments used for this test is the one installed in the current environment.

Danger

Security check While extracting the archive, we double check we are not fed an archive exploiting relative .. or . path attacks.

tests.test_pygments.test_formatter_entry_points()[source]
tests.test_pygments.test_filter_entry_points()[source]
tests.test_pygments.test_lexer_entry_points()[source]
tests.test_pygments.test_registered_formatters()[source]
tests.test_pygments.test_registered_filters()[source]
tests.test_pygments.test_registered_lexers()[source]
tests.test_pygments.test_plain_text(text, expected)[source]

Plain text without escape sequences passes through unchanged.

tests.test_pygments.test_sgr_fg_standard(code, color)[source]

SGR 30-37 set standard foreground colors.

tests.test_pygments.test_sgr_bg_standard(code, color)[source]

SGR 40-47 set standard background colors.

tests.test_pygments.test_sgr_fg_bright(code, color)[source]

SGR 90-97 set bright foreground colors.

tests.test_pygments.test_sgr_bg_bright(code, color)[source]

SGR 100-107 set bright background colors.

tests.test_pygments.test_sgr_text_attribute(code, attr)[source]

SGR attribute codes set the corresponding text styling.

tests.test_pygments.test_sgr_attribute_reset(set_code, reset_code, attr)[source]

Each attribute can be individually reset by its specific SGR code.

tests.test_pygments.test_sgr22_resets_bold_and_faint()[source]

SGR 22 (normal intensity) resets both bold and faint simultaneously.

tests.test_pygments.test_sgr0_resets_all()[source]

SGR 0 resets all attributes and colors.

tests.test_pygments.test_empty_sgr_is_reset()[source]

An empty SGR sequence (ESC [ m) is equivalent to SGR 0.

tests.test_pygments.test_sgr39_resets_foreground()[source]

SGR 39 resets foreground color to default.

tests.test_pygments.test_sgr49_resets_background()[source]

SGR 49 resets background color to default.

tests.test_pygments.test_sgr39_keeps_other_attributes()[source]

SGR 39 only resets foreground; other attributes persist.

tests.test_pygments.test_sgr_combined(params, expected_token)[source]

Multiple SGR codes in a single escape sequence are applied together.

tests.test_pygments.test_256color_fg(index)[source]

SGR 38;5;n sets foreground to 256-color index.

tests.test_pygments.test_256color_bg(index)[source]

SGR 48;5;n sets background to 256-color index.

tests.test_pygments.lex_quantized(text)[source]

Shorthand: lex text with explicit 256-color quantization opt-in.

Return type:

list[tuple]

tests.test_pygments.test_24bit_rgb_fg_quantized(r, g, b, expected_idx)[source]

SGR 38;2;r;g;b quantizes to nearest 256-color index when true_color=False.

tests.test_pygments.test_24bit_rgb_bg_quantized(r, g, b, expected_idx)[source]

SGR 48;2;r;g;b quantizes background to nearest 256-color index when true_color=False.

tests.test_pygments.lex_truecolor(text)[source]

Shorthand: lex text with true-color enabled.

Return type:

list[tuple]

tests.test_pygments.test_truecolor_fg_preserves_hex(r, g, b, expected_hex)[source]

With true_color=True, SGR 38;2;r;g;b emits Token.Ansi.FG_{hex}.

tests.test_pygments.test_truecolor_bg_preserves_hex(r, g, b, expected_hex)[source]

With true_color=True, SGR 48;2;r;g;b emits Token.Ansi.BG_{hex}.

tests.test_pygments.test_truecolor_combined_fg_and_bg()[source]

Foreground and background 24-bit RGB combine into a single compound token.

tests.test_pygments.test_truecolor_with_attribute()[source]

Bold + 24-bit RGB combines into a single compound token.

tests.test_pygments.test_truecolor_default_enabled()[source]

Default AnsiColorLexer() preserves 24-bit RGB as FG_{rrggbb} tokens.

tests.test_pygments.test_truecolor_explicit_disable_quantizes()[source]

AnsiColorLexer(true_color=False) quantizes RGB to the 256-color palette.

tests.test_pygments.test_truecolor_invalid_range_ignored()[source]

Out-of-range RGB values are skipped in true-color mode too.

tests.test_pygments.test_truecolor_truncated_params_ignored()[source]

Truncated RGB params are skipped in true-color mode too.

tests.test_pygments.test_truecolor_filter_forwards_flag()[source]

AnsiFilter(true_color=True) forwards the flag to its inner lexer.

tests.test_pygments.test_truecolor_session_lexer_forwards_flag()[source]

Lexer kwarg true_color=True flows through _AnsiFilterMixin.

tests.test_pygments.test_formatter_renders_truecolor_inline_style()[source]

AnsiHtmlFormatter emits inline style for FG_/BG_ tokens.

tests.test_pygments.test_formatter_renders_truecolor_background()[source]

Background 24-bit RGB renders as background-color inline style.

tests.test_pygments.test_formatter_truecolor_combined_with_class_styling()[source]

Bold + RGB renders both a CSS class for Bold and an inline style for the color.

tests.test_pygments.test_formatter_truecolor_fg_and_bg_nested_spans()[source]

Both fg and bg RGB on the same token produce two nested inline-style spans.

tests.test_pygments.test_formatter_quantize_path_no_inline_styles()[source]

When true_color=False is opted into, no inline styles are emitted.

tests.test_pygments.test_formatter_osc8_with_truecolor_coexist()[source]

OSC 8 hyperlink and 24-bit RGB tokens cooperate in the same span.

Both mechanisms inject Private Use Area markers into the token stream (_LINK_* for hyperlinks, _RGB_* for RGB colors) and rely on independent post-processing passes in format_unencoded. This test pins the contract that both rewrites happen and don’t interfere.

tests.test_pygments.test_nearest_256_quantization(r, g, b, expected)[source]

Verify RGB-to-256 quantization for representative values.

tests.test_pygments.test_extra_css_matches_sgr_attributes()[source]

EXTRA_ANSI_CSS keys match the attribute names in _SGR_ATTR_ON.

tests.test_pygments.test_non_sgr_csi_stripped()[source]

Non-SGR CSI sequences (cursor movement, etc.) are stripped.

tests.test_pygments.test_vt100_charset_stripped()[source]

VT100 charset selection escapes are stripped.

tests.test_pygments.test_vt100_charset_g1_stripped()[source]

ESC ) designator for G1 charset is stripped.

tests.test_pygments.test_unknown_escape_stripped()[source]

Unknown single-byte escape sequences are stripped.

tests.test_pygments.test_bare_escape_at_end()[source]

A lone ESC at the end of input is consumed without error.

tests.test_pygments.test_osc_sequence_stripped()[source]

Non-hyperlink OSC sequences are fully consumed and stripped.

tests.test_pygments.test_osc_st_terminated_stripped()[source]

OSC sequences terminated by ST (ESC ) are fully stripped.

OSC 8 hyperlinks emit link start/end tokens around the visible text.

tests.test_pygments.test_osc8_with_sgr()[source]

OSC 8 hyperlink combined with SGR color preserves both.

tests.test_pygments.test_osc8_unsafe_scheme_stripped(url)[source]

OSC 8 with unsafe or missing URL scheme is stripped silently.

tests.test_pygments.test_osc8_implicit_close()[source]

A new OSC 8 link implicitly closes the previous one.

tests.test_pygments.test_osc8_unclosed()[source]

An unclosed OSC 8 link is automatically closed at end of input.

tests.test_pygments.test_osc8_close_without_open()[source]

An OSC 8 close without a preceding open is silently ignored.

tests.test_pygments.test_color_persists_until_changed()[source]

A foreground color set in one sequence persists until explicitly changed.

tests.test_pygments.test_multiple_color_changes()[source]

Color can be changed multiple times without resetting.

tests.test_pygments.test_attribute_stacking()[source]

Attributes accumulate: bold then italic produces bold+italic.

tests.test_pygments.test_independent_fg_bg()[source]

Foreground and background colors are independent of each other.

tests.test_pygments.test_sgr_with_trailing_semicolons()[source]

Trailing semicolons in SGR parameters produce zero codes, which are resets.

tests.test_pygments.test_sgr_leading_semicolons()[source]

Leading semicolons produce 0 values (resets).

tests.test_pygments.test_sgr_double_semicolons()[source]

Double semicolons produce empty strings that cause the sequence to be skipped.

tests.test_pygments.test_sgr_unknown_codes_ignored()[source]

Unknown SGR codes are silently ignored; known codes still apply.

tests.test_pygments.test_256color_truncated_params()[source]

Truncated 256-color sequence (missing color index) leaves leftover codes.

38;5: code 38 triggers extended color handling but needs at least 2 more values (mode + index). Only 1 remains (5), so 38 skips. Then 5 is processed as SGR 5 (blink).

tests.test_pygments.test_256color_out_of_range()[source]

256-color index outside 0-255 is ignored.

tests.test_pygments.test_24bit_truncated_params()[source]

Truncated 24-bit RGB sequence (missing channels) is ignored.

tests.test_pygments.test_24bit_out_of_range()[source]

24-bit RGB values outside 0-255 are ignored.

tests.test_pygments.test_extended_color_unknown_mode()[source]

Extended color with unknown mode (not 5 or 2) skips the mode byte.

38;3;100: code 38 triggers extended color handling, mode 3 is unknown so mode and nothing else are consumed, then 100 is processed as SGR 100 (bright black background).

tests.test_pygments.test_non_numeric_sgr_params()[source]

Non-numeric characters in CSI params cause partial consumption.

ESC[abc;31m is not a valid SGR sequence. The regex consumes ESC[a as a CSI with a as the final byte, leaving bc;31mtext as plain text.

tests.test_pygments.test_consecutive_resets()[source]

Multiple consecutive resets are harmless.

tests.test_pygments.test_empty_text_between_sequences()[source]

Sequences with no text between them produce no empty tokens.

tests.test_pygments.test_newline_in_colored_text()[source]

Newlines within colored text are preserved in the token value.

tests.test_pygments.test_interleaved_text_and_escapes()[source]

Complex interleaving of plain text and escape sequences.

tests.test_pygments.test_lexer_resets_between_calls()[source]

Each call to get_tokens starts from a clean state.

tests.test_pygments.test_ansi_styles_has_all_named_colors()[source]

Style dict contains entries for all 16 named foreground and background colors.

tests.test_pygments.test_ansi_styles_has_256_palette()[source]

Style dict contains entries for all 256 foreground and background indices.

tests.test_pygments.test_ansi_styles_excludes_all_attributes()[source]

All text attribute tokens are absent from the style dict.

Furo’s dark-mode CSS generator adds color: #D0D0D0 to every token in the style dict. For attribute tokens, this overrides actual foreground colors on compound tokens when the attribute rule appears later in the CSS cascade. All attribute styling is handled by EXTRA_ANSI_CSS / custom.css instead.

tests.test_pygments.test_ansi_styles_count()[source]

Style dict has only color entries: 32 named + 512 indexed.

tests.test_pygments.test_formatter_no_color_on_attribute_css()[source]

CSS rules for attribute-only tokens must not set a color property.

When an attribute token (like -Ansi-Strikethrough) has a color property in its CSS rule, it can override the foreground color of a sibling color token (like -Ansi-Red) if the attribute rule appears later in the CSS cascade. This regression test catches the issue that Furo’s dark-mode generator exposed.

tests.test_pygments.test_palette_256_completeness()[source]

256-color palette has exactly 256 entries.

tests.test_pygments.test_palette_256_hex_format()[source]

All palette values are 7-character hex strings.

tests.test_pygments.test_lexer_map_completeness()[source]

LEXER_MAP has one entry per session lexer.

tests.test_pygments.test_ansi_filter_transforms_output_tokens()[source]

AnsiFilter converts Generic.Output tokens containing ANSI codes.

tests.test_pygments.test_ansi_filter_passes_through_other_tokens()[source]

AnsiFilter does not modify tokens that are not Generic.Output.

tests.test_pygments.test_formatter_css_classes_single_color()[source]

Single-color token gets the expected CSS classes.

tests.test_pygments.test_formatter_css_classes_compound()[source]

Compound token gets decomposed CSS classes.

tests.test_pygments.test_formatter_css_classes_256color()[source]

256-color tokens get the correct CSS class.

tests.test_pygments.test_formatter_style_defs_contain_ansi_colors()[source]

get_style_defs() includes CSS rules for ANSI color tokens.

OSC 8 hyperlink is rendered as an HTML <a> tag.

tests.test_pygments.test_formatter_osc8_with_color()[source]

OSC 8 hyperlink combined with SGR color renders both link and color.

tests.test_pygments.test_formatter_osc8_url_escaping()[source]

URLs with special HTML characters are properly escaped in href.

get_style_defs() includes CSS for hyperlink styling.

Unsafe URL schemes produce no <a> tag in formatted output.

tests.test_pygments.test_real_world_ansi(text, expected_tokens)[source]

Real-world ANSI patterns from terminal tools and documentation references.

tests.test_pyproject module

Consistency checks tying the test matrix to the declared dependencies.

The click version axis of the test matrix is the one dependency whose whole supported range is exercised release-by-release (a patch can change behavior mid-stream). These tests keep that axis in sync with the click dependency specifier, so a drift, like a new Click release or a raised floor, fails CI instead of silently rotting.

tests.test_pyproject.PYPROJECT = PosixPath('/home/runner/work/click-extra/click-extra/pyproject.toml')

Path to the project’s pyproject.toml, relative to this test file.

tests.test_pyproject.SENTINELS = ('released', 'stable', 'main')

Moving-reference values of the click-version axis: the lockfile-resolved release, and Click’s stable and main development branches. Everything else in the axis is a pinned release number.

tests.test_pyproject.load_click_matrix()[source]

Read the Click setup from pyproject.toml.

The click-version axis is spread across the forward-looking test-matrix.full-include rows (the pinned releases and the stable / main sentinels) and the released default declared in test-matrix.include.

Return type:

tuple[SpecifierSet, list[str], set[str]]

Returns:

the click dependency specifier, the collected click-version axis values, and the subset that are pinned release numbers (sentinels start with a letter, pinned versions with a digit).

tests.test_pyproject.stable_pypi_versions(package)[source]

Return all non-yanked, non-prerelease versions of package on PyPI.

Return type:

set[Version]

tests.test_pyproject.test_click_floor_is_pinned_and_sentinels_present()[source]

The matrix floor stays in sync with the dependency floor (hermetic).

The lowest pinned click-version must equal the lower bound of the click specifier, so raising or lowering the dependency floor without updating the matrix (or vice versa) fails here. Runs offline, unlike the PyPI cross-check below.

tests.test_pyproject.test_click_matrix_matches_authorized_releases()[source]

The pinned releases match exactly the Click releases the specifier allows.

Every release allowed by the click specifier must be referenced in the matrix: the newest through the released sentinel, every earlier one by an explicit pin. This asserts the set equality in both directions:

  • a missing pin means Click published a release the matrix has not caught up to (the previous newest is no longer covered by released);

  • a stale pin means a pinned version is no longer an authorized release (the floor was raised past it, or the release was yanked).

Either way, the matrix needs an edit, and this is the signal.

tests.test_pytest module

Test the Pytest helpers.

tests.test_pytest.test_aligned_colored_fixtures(uncolored, colored)[source]

tests.test_spinner module

class tests.test_spinner.TTYStringIO(initial_value='', newline='\n')[source]

Bases: StringIO

An in-memory text buffer that claims to be an interactive terminal.

isatty()[source]

Return whether this is an ‘interactive’ stream.

Return False if it can’t be determined.

Return type:

bool

tests.test_spinner.wait_until(predicate, timeout=3.0)[source]

Poll predicate until it is true or timeout seconds elapse.

Lets thread-driven assertions wait for an outcome instead of sleeping a fixed (and racy) amount.

Return type:

bool

tests.test_spinner.test_spinner_exported_from_root()[source]
tests.test_spinner.test_default_stream_is_stderr()[source]
tests.test_spinner.test_explicit_stream_is_honored()[source]
tests.test_spinner.test_resolve_enabled(enabled, stream, expected)[source]
tests.test_spinner.test_noop_on_non_tty_stream()[source]

A non-interactive stream produces no output and spawns no thread.

tests.test_spinner.test_delay_suppresses_quick_calls()[source]

A call shorter than the delay never draws anything.

tests.test_spinner.test_draws_and_cleans_up_when_enabled()[source]
tests.test_spinner.test_shown_false_when_not_drawn()[source]

shown stays False whenever no frame reaches the terminal.

tests.test_spinner.test_shown_true_after_drawing()[source]

shown flips to True once a frame is drawn, and stays True after stop.

tests.test_spinner.test_label_can_change_mid_spin()[source]
tests.test_spinner.test_hide_cursor_disabled()[source]
tests.test_spinner.test_ascii_frames()[source]
tests.test_spinner.test_stop_is_idempotent_and_safe_before_start()[source]
tests.test_spinner.test_suspend_and_resume()[source]

A spinner restarts cleanly after a stop, without re-using a dead thread.

tests.test_spinner.test_rotation_direction(reverse)[source]

Frames cycle forwards by default and backwards when reverse=True.

tests.test_spinner.test_beep_rings_bell_on_stop_when_enabled()[source]
tests.test_spinner.test_beep_silent_when_disabled()[source]

A disabled spinner never beeps, even with beep=True.

tests.test_spinner.test_echo_prints_above_running_spinner()[source]
tests.test_spinner.test_echo_degrades_to_plain_write_when_disabled()[source]

Off a TTY the message is still emitted, just without control codes.

tests.test_spinner.test_progress_option_is_a_default_option()[source]

ProgressOption ships in the default option set of every extra command.

tests.test_spinner.test_progress_option_resolution(invoke, args, expected)[source]

ctx.meta[PROGRESS] follows –progress and –accessible, never color.

tests.test_spinner.test_progressbar_follows_progress_flag(invoke, args, expected_hidden)[source]

click_extra.progressbar gates hidden on the resolved –progress flag.

tests.test_spinner.test_progressbar_explicit_hidden_overrides_flag(invoke, forced)[source]

An explicit hidden= wins over –no-progress, like echo(color=…).

tests.test_spinner.test_progressbar_shown_without_active_context()[source]

Outside a Click command the bar defaults to shown, like click.progressbar.

tests.test_spinner.test_progressbar_label_emission_off_tty(invoke, args, label_shown)[source]

Off a TTY a shown bar still emits its label once; a hidden one emits nothing.

tests.test_spinner.test_dumb_terminal_disables_spinner(monkeypatch, term)[source]

A cursor-less terminal self-disables the spinner even on a TTY.

tests.test_spinner.test_explicit_enabled_overrides_dumb_terminal(monkeypatch)[source]

An explicit enabled=True wins over the TERM=dumb auto-detection.

tests.test_spinner.test_decorator_runs_function_inside_spinner()[source]

@spinner animates while the wrapped function runs and returns its result.

tests.test_spinner.test_bare_decorator_without_parentheses()[source]

@Spinner with no parentheses wraps the function with default settings.

tests.test_spinner.test_resolve_color_enabled(monkeypatch, env, stream_factory, expected)[source]

Color follows FORCE_COLOR / dumb TERM / NO_COLOR then TTY, with no context.

tests.test_spinner.test_color_applied_on_tty(monkeypatch)[source]
tests.test_spinner.test_color_stripped_but_spinner_still_spins_when_disabled(monkeypatch)[source]

NO_COLOR strips the spinner’s color but never stops it spinning.

tests.test_spinner.test_style_applied_to_spinner(monkeypatch)[source]
tests.test_spinner.test_invalid_style_raises()[source]
tests.test_spinner.test_outcome_leaves_persistent_line(monkeypatch, outcome, glyph, color)[source]
tests.test_spinner.test_ok_degrades_to_plain_line_when_disabled(monkeypatch)[source]

Off a TTY the outcome is still recorded, without symbol color.

tests.test_spinner.test_timer_appended_to_frames_and_final_line()[source]
tests.test_spinner.test_timer_accepts_custom_formatter()[source]

A callable timer formats the elapsed seconds itself (yaspin #236).

tests.test_spinner.test_enable_windows_ansi_is_a_safe_noop()[source]

The Windows VT-enable never raises: off Windows, or on a stream with no usable console handle (the path the spinner exercises on every platform).

tests.test_spinner.test_elapsed_time_freezes_after_stop()[source]
tests.test_spinner.test_catalog_is_complete()[source]

The cli-spinners / ora catalog is present and well-formed.

tests.test_spinner.test_spinner_preset_supplies_frames_and_interval()[source]
tests.test_spinner.test_explicit_frames_and_interval_override_preset()[source]
tests.test_spinner.test_defaults_without_frames_or_preset()[source]
tests.test_spinner.test_multichar_preset_renders()[source]

A multi-character animation (which upstream b renderers drop) draws.

tests.test_spinner.test_demo_spinner_table_lists_selection(invoke)[source]

–table prints the catalog table; the tour stays TTY-only.

tests.test_spinner.test_demo_spinner_without_table_flag_shows_no_table(invoke)[source]

Off a TTY and without –table, the command renders no table.

tests.test_spinner.test_demo_spinner_tour_column_shows_three_cycle_time(invoke)[source]

The Tour column reports 3 × frames × interval (dots = 2.4s).

tests.test_spinner.test_demo_spinner_all_lists_full_catalog(invoke)[source]
tests.test_spinner.test_demo_spinner_select_filters_by_name(invoke)[source]
tests.test_spinner.test_demo_spinner_select_rejects_unknown(invoke)[source]
tests.test_spinner.test_demo_spinner_random_limits_count(invoke)[source]
tests.test_spinner.test_demo_spinner_options_are_mutually_exclusive(invoke)[source]
tests.test_spinner.test_tour_duration_bounds_dwell()[source]

The tour dwell aims for three cycles, clamped to [_TOUR_MIN, _TOUR_CAP], and never trims a huge spinner below one full cycle.

tests.test_styling module

Tests for click_extra.styling.Style extras.

tests.test_styling.test_hex_fg_converts_to_rgb_tuple(hex_str, expected_rgb)[source]
tests.test_styling.test_hex_bg_converts_to_rgb_tuple()[source]
tests.test_styling.test_hex_invalid_raises()[source]
tests.test_styling.test_named_color_string_passes_through()[source]

Plain named-color strings must not be touched by the hex shorthand.

tests.test_styling.test_or_right_operand_wins_on_conflicts()[source]
tests.test_styling.test_or_returns_subclass_instance()[source]
tests.test_styling.test_or_with_cloup_style_promotes_to_subclass()[source]
tests.test_styling.test_ror_with_cloup_left_operand()[source]

cloup_style | my_style is reached via __ror__.

tests.test_styling.test_or_with_non_style_returns_notimplemented()[source]

Style | int falls through to int.__ror__ and raises TypeError.

tests.test_styling.test_cascade_fills_unset_fields_from_base()[source]
tests.test_styling.test_cascade_keeps_subclass_identity()[source]
tests.test_styling.test_cascade_with_non_style_raises()[source]
tests.test_styling.test_to_dict_omits_unset_fields()[source]
tests.test_styling.test_to_dict_serializes_rgb_as_hex()[source]
tests.test_styling.test_to_dict_omits_none_only()[source]

False boolean attributes are kept; only None is filtered out.

tests.test_styling.test_from_dict_round_trip()[source]
tests.test_styling.test_from_dict_accepts_hex_or_rgb()[source]

Both hex strings and RGB tuples work as input.

tests.test_styling.test_str_returns_styled_sample()[source]
tests.test_styling.test_str_no_styling_has_no_color_codes()[source]

Style with no fields set produces only click’s bare reset suffix.

tests.test_styling.test_repr_compact_named_color()[source]
tests.test_styling.test_repr_compact_rgb_to_hex()[source]
tests.test_styling.test_repr_empty_style()[source]
tests.test_styling.test_repr_multiple_attrs()[source]
tests.test_styling.test_to_css_basic()[source]
tests.test_styling.test_to_css_named_color_passes_through()[source]
tests.test_styling.test_to_css_bright_named_color_resolves_to_rgb()[source]

Bright ANSI colors aren’t valid CSS keywords: convert to RGB.

tests.test_styling.test_to_css_text_decorations_combine()[source]
tests.test_styling.test_to_css_dim_emits_opacity()[source]
tests.test_styling.test_to_css_empty_style_returns_empty_string()[source]
tests.test_styling.test_from_ansi_parses_codes(escape, expected)[source]
tests.test_styling.test_from_ansi_round_trip_through_call()[source]

A Style can be parsed back from its own ANSI output.

tests.test_styling.test_from_ansi_invalid_raises()[source]
tests.test_styling.test_contrast_ratio_white_on_black_is_max()[source]
tests.test_styling.test_contrast_ratio_identical_colors_is_one()[source]
tests.test_styling.test_contrast_ratio_is_symmetric()[source]
tests.test_styling.test_contrast_ratio_meets_wcag_aa_for_dracula_default()[source]

Dracula’s default fg/bg pair clears WCAG AA (4.5).

tests.test_styling.test_contrast_ratio_requires_both_fgs()[source]
tests.test_styling.test_eq_ignores_style_kwargs_cache()[source]

Equality must not depend on cloup’s lazy _style_kwargs cache.

tests.test_styling.test_eq_with_cloup_style()[source]

A click-extra Style equals an equivalent cloup.Style.

tests.test_styling.test_supports_truecolor(monkeypatch, colorterm, term, expected)[source]
tests.test_styling.test_style_call_keeps_24bit_on_truecolor(monkeypatch)[source]

An RGB color emits a 24-bit sequence when the terminal supports truecolor.

tests.test_styling.test_style_call_quantizes_without_truecolor(monkeypatch)[source]

An RGB color downsamples to the nearest 256-index without truecolor.

tests.test_styling.test_style_call_leaves_named_and_indexed_colors(monkeypatch, colorterm)[source]

Named and palette-index colors never quantize, regardless of depth.

tests.test_styling.test_style_call_cache_survives_depth_flip(monkeypatch)[source]

The same style renders correctly when truecolor flips between calls.

Quantizing on a transient copy must not poison cloup’s lazy _style_kwargs cache on the shared (often singleton) style instance.

tests.test_table module

tests.test_table.test_table_formats_definition()[source]

Check all table formats are accounted for and properly named.

tests.test_table.test_unrecognized_format(invoke, cmd_decorator, cmd_type)[source]
tests.test_table.test_all_table_formats_have_test_rendering()[source]

Check all table formats have a rendering test fixture defined.

tests.test_table.test_all_table_rendering(invoke, cmd_decorator, option_decorator, format_name, expected)[source]
tests.test_table.test_markup_strips_ansi_by_default(invoke, format_id)[source]

Markup formats strip ANSI codes when --color is not forced.

tests.test_table.test_markup_preserves_ansi_with_color_flag(invoke, format_id)[source]

--color overrides ANSI stripping for markup formats.

tests.test_table.test_serialize_json_compatible(table_format, data)[source]
tests.test_table.test_serialize_roundtrip(table_format, data, loader)[source]
tests.test_table.test_serialize_toml_list_wrapping()[source]

Top-level lists are wrapped under a record key for TOML.

tests.test_table.test_serialize_strips_none(table_format)[source]

TOML and XML have no null type. None values are omitted.

tests.test_table.test_serialize_xml()[source]
tests.test_table.test_serialize_xml_list_wrapping()[source]

Top-level lists are wrapped under a record key for XML.

tests.test_table.test_serialize_xml_custom_root_element()[source]
tests.test_table.test_serialize_default_callback()[source]

Custom types are converted via the default callback.

tests.test_table.test_serialize_unsupported_format_raises()[source]
tests.test_table.test_strip_none(data, expected)[source]
tests.test_table.test_apply_default_native_types_unchanged()[source]
tests.test_table.test_apply_default_custom_type_converted(data, expected)[source]
tests.test_table.test_missing_dependency_clean_error(monkeypatch, func, args, kwargs, match)[source]

Missing optional dependency produces a clean error, no traceback.

tests.test_table.test_render_table_sort(data, headers, sort_key, expected_fruits)[source]
tests.test_table.test_render_table_header_edge_cases(headers, data, expected)[source]

Edge cases for header handling in structured format rendering.

tests.test_table.test_column_sort_key(header_defs, rows, sort_columns, cell_key, expected_first_col)[source]
tests.test_table.test_sort_by_option_choices_and_default(header_defs, expected_choices, expected_default)[source]

SortByOption choices and default are derived from column definitions.

tests.test_table.test_sort_by_option_wires_context(invoke)[source]

SortByOption replaces ctx.print_table with the sorted variant.

tests.test_table.test_sort_by_option_multi_column(invoke)[source]

Multiple –sort-by options define sort priority.

tests.test_telemetry module

tests.test_telemetry.test_standalone_telemetry_option(invoke, cmd_decorator, telemetry_help, option_decorator)[source]
tests.test_telemetry.test_multiple_envvars(invoke, cmd_decorator, telemetry_help)[source]

tests.test_test_plan module

Tests for the declarative CLI test-plan engine and its test-plan command.

Covers three surfaces:

The host Python interpreter stands in for the command under test, so cases stay fast and platform-neutral.

tests.test_test_plan.test_parse_returns_cases()[source]

A well-formed YAML plan yields one CLITestCase per entry.

tests.test_test_plan.test_parse_rejects_malformed(plan, exception)[source]

Empty, non-list, and unknown-directive plans raise.

tests.test_test_plan.test_case_normalizes_scalars()[source]

String scalars are coerced: exit_code to int, cli_parameters to a tuple.

tests.test_test_plan.test_output_contains_sees_merged_stream()[source]

output_contains matches substrings from both stdout and stderr.

tests.test_test_plan.test_output_regex_preserves_cross_stream_order()[source]

output_regex_matches sees stdout and stderr interleaved in write order.

tests.test_test_plan.test_output_directive_mismatch_fails()[source]

A substring absent from the merged stream fails the case.

tests.test_test_plan.test_output_and_stream_directives_are_mutually_exclusive()[source]

Combining output_* with stdout_*/stderr_* is rejected at construction.

tests.test_test_plan.test_run_counts_pass_and_fail(jobs)[source]

Pass/fail tallies match regardless of the worker count.

tests.test_test_plan.test_run_select_test_skips_others()[source]

select_test runs only the chosen 1-based cases; the rest count as skipped.

tests.test_test_plan.test_run_exit_on_error_bails_sequentially()[source]

With one worker, exit_on_error stops before later cases run.

tests.test_test_plan.test_run_stats_echoes_summary(capsys)[source]

stats prints the worker line up front and the result tally at the end.

tests.test_test_plan.test_run_no_stats_is_quiet(capsys)[source]

Without stats, neither the worker line nor the tally is printed.

tests.test_test_plan.test_cli_runs_default_plan(invoke)[source]

With no plan source, the subcommand runs the built-in default plan.

tests.test_test_plan.test_cli_runs_plan_file(invoke, tmp_path)[source]

A –plan-file is parsed and run against the target command.

tests.test_test_plan.test_cli_reports_failure_exit_code(invoke, tmp_path)[source]

A failing case makes the subcommand exit non-zero.

tests.test_test_plan.test_cli_rejects_non_integer_jobs(invoke)[source]

–jobs is click-extra’s JobsOption, so a non-numeric value is refused.

tests.test_test_plan.test_cli_requires_command(invoke)[source]

Without –command/–binary, the subcommand errors with a usage message.

tests.test_test_plan.test_cli_resolves_plan_from_config(invoke, tmp_path, monkeypatch)[source]

With no –plan-file, the plan comes from [tool.click-extra.test-plan].

tests.test_testing module

Test the testing utilities and the simulation of CLI execution.

tests.test_testing.test_real_fs()[source]

Check a simple test is not caught into the CLI runner fixture which is encapsulating all filesystem access into temporary directory structure.

tests.test_testing.test_temporary_fs(runner)[source]

Check the CLI runner fixture properly encapsulated the filesystem in temporary directory.

tests.test_testing.test_runner_output()[source]
tests.test_testing.check_default_colored_rendering(result)[source]
tests.test_testing.check_default_uncolored_rendering(result)[source]
tests.test_testing.check_forced_uncolored_rendering(result)[source]
tests.test_testing.test_invoke_optional_color(invoke)[source]
tests.test_testing.test_invoke_default_color(invoke)[source]
tests.test_testing.test_invoke_forced_color_stripping(invoke)[source]
tests.test_testing.test_invoke_color_keep(invoke)[source]
tests.test_testing.test_invoke_color_forced(invoke)[source]

Test colors are preserved while invoking, and forced to be rendered on Windows.

tests.test_testing.test_command_default_color()[source]

With @command and no color env var, Context and ColorOption resolve the GNU auto default (ctx.color=None), yet a forced runner still renders ANSI codes.

tests.test_testing.test_command_no_color_flag()[source]

Invoke with –no-color. Verify ctx.color=False and ANSI stripped from echo output.

tests.test_testing.test_force_color_attribute()[source]

CliRunner.force_color=True overrides color parameter.

tests.test_testing.test_no_color_envvar()[source]

NO_COLOR=1 env var causes ctx.color=False via ColorOption.

tests.test_testing.test_force_color_envvar()[source]

FORCE_COLOR=1 env var keeps ctx.color=True via ColorOption.

tests.test_testing.test_should_strip_ansi_non_tty()[source]

In a test runner (non-TTY), should_strip_ansi behaves based on color arg.

tests.test_testing.test_resolve_color_default_no_context()[source]

Outside any Click context, resolve_color_default returns None or passed value.

tests.test_theme module

Tests for click_extra.theme: in-process isolation, built-in TOML themes and [tool.<cli>.themes.<name>] config integration.

tests.test_theme.test_theme_does_not_leak_across_invocations()[source]

A –theme light invocation must not bleed into a later –help render.

Two back-to-back invocations of the same CLI in the same process:

  1. --theme light --help – selects the light palette for this call only.

  2. --help – no --theme argument, must fall back to the dark default.

The dark theme renders headings with \x1b[94m (bright blue); the light theme uses \x1b[35m (magenta, chosen to stay distinct from its blue options). If the second invocation picks up the first’s choice via process-wide state, it leaks the light palette and the assertion below fails.

tests.test_theme.test_theme_default_unchanged_after_invocation()[source]

An invocation with --theme light must not mutate the process-wide default.

tests.test_theme.test_theme_meta_key_matches_registry()[source]

get_current_theme() reads from the same key ThemeOption writes.

tests.test_theme.test_font_role_slots_are_known_and_disjoint()[source]

LITERAL_STYLES / REPLACEABLE_STYLES must classify real, distinct slots.

The two frozensets encode the man-pages(7) bold/italic font roles by slot name and are maintained by hand, so guard against drift: every name must be a real HelpTheme field, the two roles must not overlap, and the representative slots must keep their expected role.

tests.test_theme.test_themes_toml_tables_alphabetical()[source]

Top-level tables in themes.toml are declared alphabetically.

tests.test_theme.test_builtin_themes_alphabetical()[source]

BUILTIN_THEMES keys are alphabetical (matters for --theme choices).

tests.test_theme.test_builtin_themes_match_toml()[source]

Every TOML table maps to a BUILTIN_THEMES entry, and vice versa.

tests.test_theme.test_builtin_themes_are_helpextratheme_instances()[source]

Every BUILTIN_THEMES entry is a HelpTheme instance.

tests.test_theme.test_builtin_themes_follow_manpage_font_convention(theme_name)[source]

Every built-in theme bolds literal slots and italicizes replaceable ones.

Encodes the man-pages(7) typographic convention (LITERAL_STYLES bold, REPLACEABLE_STYLES italic) as an invariant across all palettes, so adding a theme or tweaking a slot can’t silently drop the literal/replaceable distinction. The manpage theme renders it with no color at all.

tests.test_theme.test_theme_round_trips_through_dict(theme_name)[source]

HelpTheme.to_dict/from_dict round-trips every built-in theme.

tests.test_theme.test_themes_toml_payload_matches_to_dict(theme_name)[source]

The TOML payload for each theme equals what to_dict would emit.

tests.test_theme.test_to_dict_omits_identity_slots()[source]

Slots left at the identity default do not appear in to_dict output.

tests.test_theme.test_to_dict_emits_cross_ref_highlight_only_when_overridden()[source]

cross_ref_highlight is emitted only when it differs from the default.

tests.test_theme.test_from_dict_rejects_unknown_keys()[source]

Typos like optoin raise TypeError instead of being silently dropped.

tests.test_theme.test_cascade_overrides_only_set_slots()[source]

cascade keeps base’s slots wherever the overlay leaves them at default.

tests.test_theme.test_cascade_returns_new_instance_when_overlay_changes_anything()[source]

Even a single-slot overlay produces a distinct theme instance.

tests.test_theme.test_cascade_round_trips_through_dict()[source]

self.to_dict() wins over base.to_dict() slot-by-slot.

tests.test_theme.test_cascade_rejects_non_theme_base()[source]

cascade rejects anything that is not a HelpTheme.

tests.test_theme.test_themechoice_choices_track_global_registry()[source]

Outside any context, choices reflects the module-level registry.

tests.test_theme.test_themechoice_choices_pick_up_context_overrides()[source]

A theme stashed under THEME_OVERRIDES shows up in choices.

tests.test_theme.test_branded_themes_meet_wcag_aa_large(theme_name, slot)[source]

Branded themes’ readable-text slots clear WCAG AA Large (3.0+).

Branded palettes are deliberate 24-bit RGB choices, so we can hold them to a real WCAG threshold. AA Large is the realistic floor: full AA (4.5+) is unattainable for some published themes (like Solarized’s accent blue on its base03 background sits at ~4.08).

A regression that drops one of these slots below 3.0 means the theme is less readable than what currently ships and warrants a deliberate palette tweak rather than a silent slip.

tests.test_theme.test_themes_meet_legibility_floor(theme_name)[source]

Every styled slot in every theme stays above the legibility floor.

Subdued slots (debug, bracket, default, …) are allowed to fall below WCAG AA Large by design, but no slot should be effectively invisible against the theme’s assumed background. Catches accidental palette tweaks like setting an attribute to nearly the background color.

tests.test_theme.test_load_builtin_themes_tolerates_missing_file(caplog, monkeypatch)[source]

A dropped themes.toml degrades to an empty mapping plus a warning.

tests.test_theme.test_themechoice_inert_when_registry_empty(monkeypatch)[source]

With no themes available, ThemeChoice ignores any value instead of failing.

Mirrors the runtime state once themes.toml is dropped: an empty registry means even the built-in dark default cannot resolve, so convert returns None rather than aborting the invocation.

tests.test_theme.test_cli_runs_with_empty_theme_registry(invoke, monkeypatch)[source]

A CLI still runs when no themes are available (themes.toml dropped).

The built-in –theme option defaults to dark; with an empty registry that default must stay inert instead of crashing the whole command.

tests.test_theme.test_themes_from_config_overrides_existing_theme()[source]

Known theme names cascade on top of the matching built-in palette.

tests.test_theme.test_themes_from_config_creates_standalone_theme()[source]

Unknown theme names build a stand-alone theme with unset slots at default.

tests.test_theme.test_themes_from_config_does_not_mutate_global_registry()[source]

themes_from_config is pure: the module-level registry is untouched.

tests.test_theme.test_get_theme_registry_falls_back_to_global_without_ctx()[source]

get_theme_registry(None) returns a copy of the module-level registry.

tests.test_theme.test_config_loads_new_theme(invoke, create_config)[source]

A [tool.<cli>.themes.<name>] table registers a new theme for the invocation.

tests.test_theme.test_config_overrides_existing_theme_palette(invoke, create_config)[source]

A [tool.<cli>.themes.dark] table cascades onto the built-in dark palette.

tests.test_theme.test_config_theme_appears_in_help_metavar(invoke, create_config)[source]

--help lists config-defined themes alongside the built-ins.

tests.test_theme.test_config_theme_validation_error(invoke, create_config)[source]

A malformed [tool.<cli>.themes.<name>] table is rejected with a rooted path.

tests.test_theme.test_config_theme_does_not_leak_across_invocations(invoke, create_config)[source]

Themes defined in invocation N are not visible in invocation N+1.

tests.test_theme.test_validate_config_catches_bad_theme(invoke, create_config)[source]

--validate-config surfaces the same ValidationError as the runtime path.

tests.test_types module

tests.test_types.test_click_choice_behavior()[source]

Lockdown the behavior of method inherited from Click’s Choice type.

Return type:

None

tests.test_types.test_enum_string_choices(enum_definition, choice_source, result)[source]
Return type:

None

tests.test_types.test_enum_choice_show_aliases(enum_definition, choice_source, show_aliases, result)[source]

Test that EnumChoice correctly handles Enum with aliases.

Return type:

None

class tests.test_types.MyEnum(*values)[source]

Bases: Enum

Produce different strings for keys/names, values and str().

FIRST_VALUE = 'first-value'
SECOND_VALUE = 'second-value'
tests.test_types.test_enum_choice_internals(source, expected_choices)[source]
Return type:

None

tests.test_types.test_enum_choice_case_sensitivity(case_sensitive)[source]
Return type:

None

tests.test_types.test_enum_choice_duplicate_string()[source]
Return type:

None

tests.test_types.test_enum_choice_command(invoke, cmd_decorator, opt_decorator, case_sensitive, valid_args, invalid_args)[source]

Test EnumChoice used within an option.

Return type:

None

tests.test_types.test_enum_choice_default_value(invoke, cmd_decorator, opt_decorator, opt_type, default_value)[source]

Test EnumChoice used within an option with a default value.

Return type:

None

tests.test_types.test_enum_choice_callback(invoke, cmd_decorator, opt_decorator, choice_source, cli_value)[source]

Test that option callbacks receive properly converted Enum members.

Return type:

None

tests.test_types.test_multi_choice_parses_input(raw, expected)[source]

convert() splits on the separator, strips whitespace, drops empties.

Return type:

None

tests.test_types.test_multi_choice_validates_against_choices()[source]

Unknown tokens raise BadParameter via self.fail().

Return type:

None

tests.test_types.test_multi_choice_case_insensitive_normalizes()[source]

case_sensitive=False matches case-insensitively and returns the canonical case.

Return type:

None

tests.test_types.test_multi_choice_metavar(choices, separator, expected)[source]
Return type:

None

tests.test_types.test_multi_choice_in_click_option()[source]

End-to-end: MultiChoice plugs into a Click option like Choice does.

Return type:

None

tests.test_version module

Test the --version option.

Todo

Test standalone scripts setting package name to filename and version to None.

Todo

Test standalone script fetching version from __version__ variable.

tests.test_version.test_standalone_version_option(invoke, cmd_decorator, option_decorator)[source]
tests.test_version.test_debug_output(invoke, cmd_decorator, option_decorator, assert_output_regex)[source]
tests.test_version.test_set_version(invoke)[source]
tests.test_version.test_custom_message(invoke, cmd_decorator, message, regex_stdout, assert_output_regex)[source]
tests.test_version.test_style_reset(invoke, cmd_decorator)[source]
tests.test_version.test_custom_message_style(invoke, cmd_decorator)[source]
tests.test_version.test_context_meta(invoke, cmd_decorator, assert_output_regex)[source]
tests.test_version.test_context_meta_laziness(invoke, cmd_decorator)[source]

Accessing a single field from ctx.meta must not evaluate unrelated fields.

Ensures that the _LazyVersionDict defers property evaluation: reading click_extra.version should not trigger expensive properties like env_info or git fields.

tests.test_version.test_module_version_parent_package_fallback(monkeypatch)[source]

module_version falls back to parent package’s __version__.

Simulates the Nuitka use-case: a CLI whose module is myapp.__main__ (no __version__), with the parent package myapp providing it.

tests.test_version.test_package_version_resolves_import_name_to_distribution(monkeypatch)[source]

When package_name is an import name that differs from its installed distribution name (PIL vs Pillow), package_version resolves it via packages_distributions() instead of returning None.

tests.test_version.test_package_version_ambiguous_import_name_returns_none(monkeypatch)[source]

When an import name maps to several installed distributions, package_version returns None rather than guessing.

tests.test_version.test_package_version_unknown_returns_none(monkeypatch)[source]

When the name resolves to no distribution, package_version returns None, the existing graceful behavior.

tests.test_version.test_cli_frame_fallback(monkeypatch)[source]

cli_frame() falls back to the outermost frame when all frames are from the Click ecosystem.

tests.test_version.test_integrated_version_option_precedence(invoke, params)[source]
tests.test_version.test_version_fields_forwarded_to_version_option(invoke)[source]

version_fields on @command forwards to VersionOption.

tests.test_version.test_version_fields_forwarded_on_group(invoke)[source]

version_fields works on @group too.

tests.test_version.test_version_fields_multiple(invoke)[source]

Multiple fields can be overridden at once.

tests.test_version.test_version_fields_rejects_unknown(invoke)[source]

Unknown field names raise TypeError.

tests.test_version.test_color_option_precedence(invoke)[source]

A plain click.command only decolors --version when --no-color precedes it on the command line.

Click evaluates eager parameters in the order the user typed them (callback evaluation order), so a --no-color placed after --version lands too late: the version screen has already rendered in color and exited.

click-extra’s own @command settles the color options in a pre-pass before any eager screen renders, so the color choice is honored whatever its position. See Command._resolve_color_eagerly and the order-independent test_color_settles_before_eager_help_and_version in test_color.py. This test pins the residual behavior of the plain-Click path, which that pre-pass does not reach.

tests.test_version.test_dev_version_appends_git_hash(invoke, cmd_decorator)[source]

A .dev version gets a +hash suffix appended (or not, if git is unavailable).

tests.test_version.test_prebaked_dev_version_not_double_suffixed(invoke, cmd_decorator)[source]

A version with an existing + is returned as-is: no second hash appended.

tests.test_version.test_release_version_unchanged(invoke, cmd_decorator)[source]

A non-dev version is never modified.

tests.test_version.init_file(tmp_path)[source]

Helper that creates a temporary __init__.py with the given content.

tests.test_version.test_prebake_cli_resolves_module_from_config(invoke, tmp_path, monkeypatch)[source]

click-extra prebake resolves its target from [tool.click-extra.prebake].

tests.test_version.test_prebake_dev_version(init_file)[source]

A .dev version gets +hash appended in the file.

tests.test_version.test_prebake_single_quotes(init_file)[source]

Single-quoted __version__ is also handled.

tests.test_version.test_prebake_already_baked_skipped(init_file)[source]

A version with existing + is left untouched.

tests.test_version.test_prebake_release_skipped(init_file)[source]

A release version (no .dev) is not modified.

tests.test_version.test_prebake_no_version_in_file(init_file)[source]

A file without __version__ returns None.

tests.test_version.test_prebake_missing_local_version_raises(init_file)[source]

Calling without local_version raises TypeError.

tests.test_version.test_prebake_idempotent(init_file)[source]

Running prebake twice does not double-suffix.

tests.test_version.test_prebake_preserves_surrounding_content(init_file)[source]

Content around __version__ is not disturbed.

tests.test_version.test_prebake_dunder_empty_replaced(init_file)[source]

An empty dunder variable gets replaced.

tests.test_version.test_prebake_dunder_single_quotes(init_file)[source]

Single-quoted empty dunder is also handled.

tests.test_version.test_prebake_dunder_nonempty_skipped(init_file)[source]

A dunder with an existing non-empty value is left untouched.

tests.test_version.test_prebake_dunder_not_found(init_file)[source]

A file without the target dunder returns None.

tests.test_version.test_prebake_dunder_idempotent(init_file)[source]

Running prebake_dunder twice does not overwrite.

tests.test_version.test_prebake_dunder_preserves_surrounding_content(init_file)[source]

Content around the target dunder is not disturbed.

tests.test_version.test_prebake_dunder_full_sha(init_file)[source]

A full 40-character SHA is handled correctly.

tests.test_version.test_discover_finds_init(tmp_path, monkeypatch)[source]

Discovers __init__.py from [project.scripts].

tests.test_version.test_discover_no_pyproject(tmp_path, monkeypatch)[source]

Returns empty list when pyproject.toml is missing.

tests.test_version.test_discover_no_scripts(tmp_path, monkeypatch)[source]

Returns empty list when [project.scripts] is absent.

tests.test_version.test_discover_deduplicates(tmp_path, monkeypatch)[source]

Multiple scripts from the same package yield one path.

tests.test_version.test_prebaked_git_branch()[source]

A pre-baked __git_branch__ dunder is used over subprocess.

tests.test_version.test_prebaked_git_long_hash()[source]

A pre-baked __git_long_hash__ dunder is used over subprocess.

tests.test_version.test_prebaked_git_tag_sha()[source]

A pre-baked __git_tag_sha__ dunder is resolved.

tests.test_version.test_prebaked_empty_dunder_ignored()[source]

An empty dunder is not treated as a pre-baked value.

tests.test_version.test_prebaked_non_string_ignored()[source]

A non-string dunder is not treated as a pre-baked value.

tests.test_version.test_prebaked_git_distance()[source]

A pre-baked __git_distance__ dunder is used over subprocess.

tests.test_version.test_prebaked_git_dirty()[source]

A pre-baked __git_dirty__ dunder is used over subprocess.

tests.test_version.test_resolve_git_distance(monkeypatch, describe_output, expected)[source]
tests.test_version.test_resolve_git_dirty(monkeypatch, status_output, expected)[source]
tests.test_version.test_archival_field_substituted(field_id, expected)[source]
tests.test_version.test_archival_field_unsubstituted_ignored(field_id)[source]

A raw checkout keeps the $Format placeholders, which must be ignored.

tests.test_version.test_archival_field_exact_tag_distance_zero()[source]

A bare describe-name (no -N-g suffix) means distance zero.

tests.test_version.test_read_archival_roundtrip(tmp_path)[source]
tests.test_version.test_read_archival_invalid_json(tmp_path)[source]
tests.test_version.test_find_archival_file_walks_up(tmp_path)[source]
tests.test_version.test_find_archival_file_absent(tmp_path)[source]
tests.test_version.test_archival_resolves_git_fields(tmp_path)[source]

Git fields resolve from .git_archival.json when there is no live git.

tests.test_version.test_version_dev_hash_assembly(module_version, expected)[source]

The git short hash is appended to dev releases only.