click-extra
click-extra copied to clipboard
Tweak callback evaluation order of eagerness options
Eager parameters are evaluated in the order as they were provided on the command line by the user as explained in: https://click.palletsprojects.com/en/8.0.x/advanced/#callback-evaluation-order
This means a call to:
-
--no-color --versionwill output a plain, uncoloured version string, while -
--version --no-colorwill output a coloured string.
This is highlighted by a test at: https://github.com/kdeldycke/click-extra/blob/d589907ff7cdb198699a8be5aba86fa5ade97bef/click_extra/tests/test_colorize.py#L318-L346
It could be great to have --no-color (and its NO_COLOR env var) be respected whatever its order.
I'm using this to workaround the env var issue:
import os
from collections.abc import Callable
from configparser import RawConfigParser
from functools import partial
from typing import TYPE_CHECKING, Any, ParamSpec, TypeVar, cast
import click
import cloup
from click_extra.colorize import (
HelpExtraFormatter,
color_envvars,
default_theme,
nocolor_theme,
)
from click_extra.commands import (
ExtraCommand,
ExtraGroup,
)
from click_extra.commands import (
default_extra_params as _default_extra_params,
)
Param = ParamSpec("Param")
T = TypeVar("T")
def _wrap_impl(
func: Callable[Param, T],
lazy_params: Callable[[], list[click.Parameter]],
lazy_context: Callable[[], dict[str, Any]] = dict,
*default_args: Param.args,
**default_kwargs: Param.kwargs,
) -> Callable[Param, T]:
func_partial = partial(func, *default_args, **default_kwargs)
def wrapped(*args: Param.args, **kwargs: Param.kwargs) -> T:
if "params" not in kwargs:
kwargs["params"] = lazy_params()
if "context_settings" not in kwargs:
kwargs["context_settings"] = lazy_context()
return func_partial(*args, **kwargs)
return wrapped
if TYPE_CHECKING:
_wrap = partial
else:
_wrap = _wrap_impl
def default_extra_params() -> list[click.Parameter]:
return cast(list[click.Parameter], _default_extra_params())
def clicked_extra_context() -> dict[str, Any]:
color: bool = True
# Collect all colorize flags in environment variables we recognize.
colorize_from_env: set[bool] = set()
for var, default in color_envvars.items():
if var in os.environ:
# Presence of the variable in the environment without a value encodes
# for an activation, hence the default to True.
var_value = os.environ.get(var, "true")
# `os.environ` is a dict whose all values are strings. Here we normalize
# these string into booleans. If we can't, we fallback to True, in the
# same spirit as above.
var_boolean = RawConfigParser.BOOLEAN_STATES.get(
var_value.lower(),
True,
)
colorize_from_env.add(default ^ (not var_boolean))
# Re-interpret the provided value against the recognized environment variables.
if colorize_from_env:
# The environment can only override the provided value if it comes from
# the default value or the config file.
color = True in colorize_from_env
# XXX: mypy thinks click_extra.HelpFormatter (imported via star)
# is not the same as cloup.HelpFormatter.
# this is not needed for pyright.
help_formatter = cast(cloup.HelpFormatter, HelpExtraFormatter)
return {
"color": color,
"formatter_settings": help_formatter.settings(
theme=default_theme if color else nocolor_theme,
),
}
extra_command = _wrap(
cloup.command,
lazy_params=default_extra_params,
lazy_context=clicked_extra_context,
cls=ExtraCommand,
)
extra_group = _wrap(
cloup.group,
lazy_params=default_extra_params,
lazy_context=clicked_extra_context,
cls=ExtraGroup,
)
I'm using this to workaround the env var issue:
@mochaaP Which env var issue are you talking about? 😅