Skip to content

Commit 7c3b3d9

Browse files
authored
Ensure compatibility with click v8.2. (#676)
1 parent f1495ca commit 7c3b3d9

File tree

7 files changed

+161
-112
lines changed

7 files changed

+161
-112
lines changed

.pre-commit-config.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
ci:
2+
skip: [type-checking]
3+
14
repos:
25
- repo: https://github.com/pre-commit/pre-commit-hooks
36
rev: v5.0.0
@@ -64,3 +67,10 @@ repos:
6467
- id: check-hooks-apply
6568
- id: check-useless-excludes
6669
# - id: identity # Prints all files passed to pre-commits. Debugging.
70+
- repo: local
71+
hooks:
72+
- id: type-checking
73+
name: type-checking
74+
entry: uv run --group typing mypy
75+
language: system
76+
pass_filenames: false

docs/source/changes.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ chronological order. Releases follow [semantic versioning](https://semver.org/)
55
releases are available on [PyPI](https://pypi.org/project/pytask) and
66
[Anaconda.org](https://anaconda.org/conda-forge/pytask).
77

8+
## 0.5.4 - 2025-xx-xx
9+
10+
- {pull}`676` ensures compatibility with click >8.2.0.
11+
- {pull}`680` uses uv everywhere.
12+
- {pull}`684` adds tests for lowest and highest dependency resolutions.
13+
814
## 0.5.3 - 2025-05-16
915

1016
- {pull}`650` allows to identify from which data catalog a node is coming from. Thanks

docs/source/how_to_guides/functional_interface.ipynb

Lines changed: 69 additions & 68 deletions
Large diffs are not rendered by default.

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ classifiers = [
2222
dynamic = ["version"]
2323
dependencies = [
2424
"attrs>=21.3.0",
25-
"click>=8.1.8,<8.2.0",
25+
"click>=8.1.8,!=8.2.0",
2626
"click-default-group>=1.2.4",
2727
"networkx>=2.4.0",
2828
"optree>=0.9.0",
@@ -51,6 +51,7 @@ dev = ["pygraphviz>=1.11;platform_system=='Linux'"]
5151
docs = [
5252
"furo>=2024.8.6",
5353
"ipython>=8.13.2",
54+
"ipywidgets>=8.1.6",
5455
"matplotlib>=3.5.0",
5556
"myst-parser>=3.0.0",
5657
"myst-nb>=1.2.0",

src/_pytask/cli.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
from __future__ import annotations
44

5+
import importlib.metadata
56
from typing import Any
67

78
import click
8-
from packaging.version import parse as parse_version
99

1010
from _pytask.click import ColoredGroup
1111
from _pytask.pluginmanager import storage
@@ -16,7 +16,7 @@
1616
}
1717

1818

19-
if parse_version(click.__version__) >= parse_version("8"): # pragma: no cover
19+
if importlib.metadata.version("click") >= "8": # pragma: no cover
2020
_VERSION_OPTION_KWARGS = {"package_name": "pytask"}
2121
else: # pragma: no cover
2222
_VERSION_OPTION_KWARGS = {}

src/_pytask/click.py

Lines changed: 52 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,24 @@
22

33
from __future__ import annotations
44

5+
import importlib.metadata
56
import inspect
67
from enum import Enum
78
from gettext import gettext as _
89
from gettext import ngettext
910
from typing import TYPE_CHECKING
1011
from typing import Any
1112
from typing import ClassVar
13+
from typing import TypeVar
1214

1315
import click
1416
from click import Choice
1517
from click import Command
1618
from click import Context
1719
from click import Parameter
18-
from click.parser import split_opt
1920
from click_default_group import DefaultGroup
2021
from rich.highlighter import RegexHighlighter
22+
from rich.padding import Padding
2123
from rich.panel import Panel
2224
from rich.table import Table
2325
from rich.text import Text
@@ -27,40 +29,59 @@
2729
from _pytask.console import create_panel_title
2830

2931
if TYPE_CHECKING:
32+
from collections.abc import Iterable
3033
from collections.abc import Sequence
3134

3235

3336
__all__ = ["ColoredCommand", "ColoredGroup", "EnumChoice"]
3437

3538

36-
class EnumChoice(Choice):
37-
"""An enum-based choice type.
39+
if importlib.metadata.version("click") < "8.2":
40+
from click.parser import split_opt
3841

39-
The implementation is copied from https://github.com/pallets/click/pull/2210 and
40-
related discussion can be found in https://github.com/pallets/click/issues/605.
42+
class EnumChoice(Choice):
43+
"""An enum-based choice type.
4144
42-
In contrast to using :class:`click.Choice`, using this type ensures that the error
43-
message does not show the enum members.
45+
The implementation is copied from https://github.com/pallets/click/pull/2210 and
46+
related discussion can be found in https://github.com/pallets/click/issues/605.
4447
45-
In contrast to the proposed implementation in the PR, this implementation does not
46-
use the members than rather the values of the enum.
48+
In contrast to using :class:`click.Choice`, using this type ensures that the
49+
error message does not show the enum members.
4750
48-
"""
51+
In contrast to the proposed implementation in the PR, this implementation does
52+
not use the members than rather the values of the enum.
4953
50-
def __init__(self, enum_type: type[Enum], case_sensitive: bool = True) -> None:
51-
super().__init__(
52-
choices=[element.value for element in enum_type],
53-
case_sensitive=case_sensitive,
54-
)
55-
self.enum_type = enum_type
54+
"""
55+
56+
def __init__(self, enum_type: type[Enum], case_sensitive: bool = True) -> None:
57+
super().__init__(
58+
choices=[element.value for element in enum_type],
59+
case_sensitive=case_sensitive,
60+
)
61+
self.enum_type = enum_type
62+
63+
def convert(
64+
self, value: Any, param: Parameter | None, ctx: Context | None
65+
) -> Any:
66+
if isinstance(value, Enum):
67+
value = value.value
68+
value = super().convert(value=value, param=param, ctx=ctx)
69+
if value is None:
70+
return None
71+
return self.enum_type(value)
72+
73+
else:
74+
from click.parser import ( # type: ignore[attr-defined, no-redef]
75+
_split_opt as split_opt,
76+
)
77+
78+
ParamTypeValue = TypeVar("ParamTypeValue")
5679

57-
def convert(self, value: Any, param: Parameter | None, ctx: Context | None) -> Any:
58-
if isinstance(value, Enum):
59-
value = value.value
60-
value = super().convert(value=value, param=param, ctx=ctx)
61-
if value is None:
62-
return None
63-
return self.enum_type(value)
80+
class EnumChoice(Choice): # type: ignore[no-redef]
81+
def __init__(
82+
self, choices: Iterable[ParamTypeValue], case_sensitive: bool = False
83+
) -> None:
84+
super().__init__(choices=choices, case_sensitive=case_sensitive) # type: ignore[arg-type]
6485

6586

6687
class _OptionHighlighter(RegexHighlighter):
@@ -119,7 +140,10 @@ def format_help(
119140
_print_options(self, ctx)
120141

121142
console.print(
122-
"[bold #FF0000]♥[/] [#f2f2f2]https://pytask-dev.readthedocs.io[/]",
143+
Padding(
144+
"[bold #FF0000]♥[/] [#f2f2f2]https://pytask-dev.readthedocs.io[/]",
145+
(0, 3, 0, 0),
146+
),
123147
justify="right",
124148
)
125149

@@ -197,7 +221,10 @@ def format_help(
197221
_print_options(self, ctx)
198222

199223
console.print(
200-
"[bold #FF0000]♥[/] [#f2f2f2]https://pytask-dev.readthedocs.io[/]",
224+
Padding(
225+
"[bold #FF0000]♥[/] [#f2f2f2]https://pytask-dev.readthedocs.io[/]",
226+
(0, 3, 0, 0),
227+
),
201228
justify="right",
202229
)
203230

uv.lock

Lines changed: 20 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)