Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ chronological order. Releases follow [semantic versioning](https://semver.org/)
releases are available on [PyPI](https://pypi.org/project/pytask) and
[Anaconda.org](https://anaconda.org/conda-forge/pytask).

## 0.5.6 - 2025-xx-xx

- {pull}`703` fixes {issue}`701` by allowing `--capture tee-sys` again.

## 0.5.5 - 2025-07-25

- {pull}`692` documents how to use pytask with workspaces.
Expand Down
8 changes: 4 additions & 4 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ install:
uv sync --all-groups

# Run tests
test:
uv run --group test pytest
test *FLAGS:
uv run --group test pytest {{FLAGS}}

# Run tests with coverage
test-cov:
uv run --group test pytest --nbmake --cov=src --cov=tests --cov-report=xml -n auto
test-cov *FLAGS:
uv run --group test pytest --nbmake --cov=src --cov=tests --cov-report=xml -n auto {{FLAGS}}

# Run tests with notebook validation
test-nb:
Expand Down
66 changes: 27 additions & 39 deletions src/_pytask/click.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from typing import TYPE_CHECKING
from typing import Any
from typing import ClassVar
from typing import TypeVar

import click
from click import Choice
Expand All @@ -29,7 +28,6 @@
from _pytask.console import create_panel_title

if TYPE_CHECKING:
from collections.abc import Iterable
from collections.abc import Sequence


Expand All @@ -38,50 +36,40 @@

if importlib.metadata.version("click") < "8.2":
from click.parser import split_opt
else:
from click.parser import ( # type: ignore[attr-defined, no-redef, unused-ignore]
_split_opt as split_opt,
)

class EnumChoice(Choice): # type: ignore[type-arg, unused-ignore]
"""An enum-based choice type.

The implementation is copied from https://github.com/pallets/click/pull/2210 and
related discussion can be found in https://github.com/pallets/click/issues/605.

In contrast to using :class:`click.Choice`, using this type ensures that the
error message does not show the enum members.
class EnumChoice(Choice): # type: ignore[type-arg, unused-ignore]
"""An enum-based choice type.

In contrast to the proposed implementation in the PR, this implementation does
not use the members than rather the values of the enum.
The implementation is copied from https://github.com/pallets/click/pull/2210 and
related discussion can be found in https://github.com/pallets/click/issues/605.

"""
In contrast to using :class:`click.Choice`, using this type ensures that the
error message does not show the enum members.

def __init__(self, enum_type: type[Enum], case_sensitive: bool = True) -> None:
super().__init__(
choices=[element.value for element in enum_type],
case_sensitive=case_sensitive,
)
self.enum_type = enum_type

def convert(
self, value: Any, param: Parameter | None, ctx: Context | None
) -> Any:
if isinstance(value, Enum):
value = value.value
value = super().convert(value=value, param=param, ctx=ctx)
if value is None:
return None
return self.enum_type(value)
In contrast to the proposed implementation in the PR, this implementation does
not use the members than rather the values of the enum.

else:
from click.parser import ( # type: ignore[attr-defined, no-redef, unused-ignore]
_split_opt as split_opt,
)

ParamTypeValue = TypeVar("ParamTypeValue")
"""

class EnumChoice(Choice): # type: ignore[no-redef, type-arg, unused-ignore]
def __init__(
self, choices: Iterable[ParamTypeValue], case_sensitive: bool = False
) -> None:
super().__init__(choices=choices, case_sensitive=case_sensitive) # type: ignore[arg-type, unused-ignore]
def __init__(self, enum_type: type[Enum], case_sensitive: bool = True) -> None:
super().__init__(
choices=[element.value for element in enum_type],
case_sensitive=case_sensitive,
)
self.enum_type = enum_type

def convert(self, value: Any, param: Parameter | None, ctx: Context | None) -> Any:
if isinstance(value, Enum):
value = value.value
value = super().convert(value=value, param=param, ctx=ctx)
if value is None:
return None
return self.enum_type(value)


class _OptionHighlighter(RegexHighlighter):
Expand Down