Skip to content

Commit dec917d

Browse files
committed
Add compat code to work with click v8.2.0
1 parent c8d7e75 commit dec917d

File tree

8 files changed

+77
-62
lines changed

8 files changed

+77
-62
lines changed

CHANGELOG.rst

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Unreleased
1111
.. vendor-insert-here
1212
1313
- Update vendored schemas: compose-spec, mergify, renovate, woodpecker-ci (2025-04-27)
14+
- Fix: support ``click==8.2.0``
1415

1516
0.33.0
1617
------

src/check_jsonschema/cli/param_types.py

+15-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import functools
34
import importlib
45
import os
56
import re
@@ -10,6 +11,18 @@
1011
import jsonschema
1112
from click._compat import open_stream
1213

14+
C = t.TypeVar("C", bound=t.Callable[..., t.Any])
15+
16+
17+
def _shim_click_8_2_get_metavar(func: C) -> C:
18+
@functools.wraps(func)
19+
def wrapper(*args: t.Any, **kwargs: t.Any) -> None:
20+
if len(args) > 1 or "ctx" in kwargs:
21+
return func(*args, **kwargs)
22+
return func(*args, ctx=None, **kwargs)
23+
24+
return wrapper # type: ignore[return-value]
25+
1326

1427
class CommaDelimitedList(click.ParamType):
1528
name = "comma_delimited"
@@ -24,7 +37,8 @@ def __init__(
2437
self.convert_values = convert_values
2538
self.choices = list(choices) if choices is not None else None
2639

27-
def get_metavar(self, param: click.Parameter) -> str:
40+
@_shim_click_8_2_get_metavar
41+
def get_metavar(self, param: click.Parameter, ctx: click.Context | None) -> str:
2842
if self.choices is not None:
2943
return "{" + ",".join(self.choices) + "}"
3044
return "TEXT,TEXT,..."

tests/acceptance/conftest.py

-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import textwrap
22

33
import pytest
4-
from click.testing import CliRunner
54

65
from check_jsonschema import main as cli_main
76

@@ -16,11 +15,6 @@ def _render_result(result):
1615
"""
1716

1817

19-
@pytest.fixture
20-
def cli_runner():
21-
return CliRunner(mix_stderr=False)
22-
23-
2418
@pytest.fixture
2519
def run_line(cli_runner):
2620
def func(cli_args, *args, **kwargs):

tests/conftest.py

+11
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
1+
import inspect
12
import os
23
import pathlib
34
import sys
45

56
import pytest
67
import responses
8+
from click.testing import CliRunner
9+
10+
11+
@pytest.fixture
12+
def cli_runner():
13+
# compatibility for click==8.2.0 vs click<=8.1
14+
sig = inspect.signature(CliRunner)
15+
if "mix_stderr" in sig.parameters:
16+
return CliRunner(mix_stderr=False)
17+
return CliRunner()
718

819

920
@pytest.fixture(autouse=True)

tests/unit/cli/conftest.py

-7
This file was deleted.

tests/unit/cli/test_callbacks.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -22,30 +22,30 @@ def mycli(bar, baz):
2222
print(baz)
2323

2424

25-
def test_deprecation_warning_callback_on_missing_opts(runner):
26-
result = runner.invoke(mycli, [])
25+
def test_deprecation_warning_callback_on_missing_opts(cli_runner):
26+
result = cli_runner.invoke(mycli, [])
2727
assert result.exit_code == 0
2828
assert result.stdout == "False\n"
2929

3030

31-
def test_deprecation_warning_callback_on_flag(runner):
31+
def test_deprecation_warning_callback_on_flag(cli_runner):
3232
with pytest.warns(
3333
UserWarning,
3434
match="'--bar' is deprecated and will be removed in a future release",
3535
):
36-
result = runner.invoke(mycli, ["--bar"], catch_exceptions=False)
36+
result = cli_runner.invoke(mycli, ["--bar"], catch_exceptions=False)
3737
assert result.exit_code == 0, result.stdout
3838
assert result.stdout == "True\n"
3939

4040

41-
def test_deprecation_warning_callback_added_message(runner):
41+
def test_deprecation_warning_callback_added_message(cli_runner):
4242
with pytest.warns(
4343
UserWarning,
4444
match=(
4545
"'--baz' is deprecated and will be removed in a future release. "
4646
"Use --frob instead!"
4747
),
4848
):
49-
result = runner.invoke(mycli, ["--baz", "ok"], catch_exceptions=False)
49+
result = cli_runner.invoke(mycli, ["--baz", "ok"], catch_exceptions=False)
5050
assert result.exit_code == 0, result.stdout
5151
assert result.stdout == "False\nok\n"

tests/unit/cli/test_parse.py

+42-34
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,16 @@ def test_parse_result_set_schema(
6767
assert args.schema_path is None
6868

6969

70-
def test_requires_some_args(runner):
71-
result = runner.invoke(cli_main, [])
70+
def test_requires_some_args(cli_runner):
71+
result = cli_runner.invoke(cli_main, [])
7272
assert result.exit_code == 2
7373

7474

75-
def test_schemafile_and_instancefile(runner, mock_parse_result, in_tmp_dir, tmp_path):
75+
def test_schemafile_and_instancefile(
76+
cli_runner, mock_parse_result, in_tmp_dir, tmp_path
77+
):
7678
touch_files(tmp_path, "foo.json")
77-
runner.invoke(cli_main, ["--schemafile", "schema.json", "foo.json"])
79+
cli_runner.invoke(cli_main, ["--schemafile", "schema.json", "foo.json"])
7880
assert mock_parse_result.schema_mode == SchemaLoadingMode.filepath
7981
assert mock_parse_result.schema_path == "schema.json"
8082
assert isinstance(mock_parse_result.instancefiles, tuple)
@@ -83,25 +85,27 @@ def test_schemafile_and_instancefile(runner, mock_parse_result, in_tmp_dir, tmp_
8385
assert tuple(f.name for f in mock_parse_result.instancefiles) == ("foo.json",)
8486

8587

86-
def test_requires_at_least_one_instancefile(runner):
87-
result = runner.invoke(cli_main, ["--schemafile", "schema.json"])
88+
def test_requires_at_least_one_instancefile(cli_runner):
89+
result = cli_runner.invoke(cli_main, ["--schemafile", "schema.json"])
8890
assert result.exit_code == 2
8991

9092

91-
def test_requires_schemafile(runner, in_tmp_dir, tmp_path):
93+
def test_requires_schemafile(cli_runner, in_tmp_dir, tmp_path):
9294
touch_files(tmp_path, "foo.json")
93-
result = runner.invoke(cli_main, ["foo.json"])
95+
result = cli_runner.invoke(cli_main, ["foo.json"])
9496
assert result.exit_code == 2
9597

9698

97-
def test_no_cache_defaults_false(runner, mock_parse_result):
98-
runner.invoke(cli_main, ["--schemafile", "schema.json", "foo.json"])
99+
def test_no_cache_defaults_false(cli_runner, mock_parse_result):
100+
cli_runner.invoke(cli_main, ["--schemafile", "schema.json", "foo.json"])
99101
assert mock_parse_result.disable_cache is False
100102

101103

102-
def test_no_cache_flag_is_true(runner, mock_parse_result, in_tmp_dir, tmp_path):
104+
def test_no_cache_flag_is_true(cli_runner, mock_parse_result, in_tmp_dir, tmp_path):
103105
touch_files(tmp_path, "foo.json")
104-
runner.invoke(cli_main, ["--schemafile", "schema.json", "foo.json", "--no-cache"])
106+
cli_runner.invoke(
107+
cli_main, ["--schemafile", "schema.json", "foo.json", "--no-cache"]
108+
)
105109
assert mock_parse_result.disable_cache is True
106110

107111

@@ -133,9 +137,9 @@ def test_no_cache_flag_is_true(runner, mock_parse_result, in_tmp_dir, tmp_path):
133137
],
134138
],
135139
)
136-
def test_mutex_schema_opts(runner, cmd_args, in_tmp_dir, tmp_path):
140+
def test_mutex_schema_opts(cli_runner, cmd_args, in_tmp_dir, tmp_path):
137141
touch_files(tmp_path, "foo.json")
138-
result = runner.invoke(cli_main, cmd_args + ["foo.json"])
142+
result = cli_runner.invoke(cli_main, cmd_args + ["foo.json"])
139143
assert result.exit_code == 2
140144
assert "are mutually exclusive" in result.stderr
141145

@@ -148,24 +152,24 @@ def test_mutex_schema_opts(runner, cmd_args, in_tmp_dir, tmp_path):
148152
["-h"],
149153
],
150154
)
151-
def test_supports_common_option(runner, cmd_args):
152-
result = runner.invoke(cli_main, cmd_args)
155+
def test_supports_common_option(cli_runner, cmd_args):
156+
result = cli_runner.invoke(cli_main, cmd_args)
153157
assert result.exit_code == 0
154158

155159

156160
@pytest.mark.parametrize(
157161
"setting,expect_value", [(None, None), ("1", False), ("0", False)]
158162
)
159163
def test_no_color_env_var(
160-
runner, monkeypatch, setting, expect_value, boxed_context, in_tmp_dir, tmp_path
164+
cli_runner, monkeypatch, setting, expect_value, boxed_context, in_tmp_dir, tmp_path
161165
):
162166
if setting is None:
163167
monkeypatch.delenv("NO_COLOR", raising=False)
164168
else:
165169
monkeypatch.setenv("NO_COLOR", setting)
166170

167171
touch_files(tmp_path, "foo.json")
168-
runner.invoke(cli_main, ["--schemafile", "schema.json", "foo.json"])
172+
cli_runner.invoke(cli_main, ["--schemafile", "schema.json", "foo.json"])
169173
assert boxed_context.ref.color == expect_value
170174

171175

@@ -174,22 +178,22 @@ def test_no_color_env_var(
174178
[(None, None), ("auto", None), ("always", True), ("never", False)],
175179
)
176180
def test_color_cli_option(
177-
runner, setting, expected_value, boxed_context, in_tmp_dir, tmp_path
181+
cli_runner, setting, expected_value, boxed_context, in_tmp_dir, tmp_path
178182
):
179183
args = ["--schemafile", "schema.json", "foo.json"]
180184
if setting:
181185
args.extend(("--color", setting))
182186
touch_files(tmp_path, "foo.json")
183-
runner.invoke(cli_main, args)
187+
cli_runner.invoke(cli_main, args)
184188
assert boxed_context.ref.color == expected_value
185189

186190

187191
def test_no_color_env_var_overrides_cli_option(
188-
runner, monkeypatch, mock_cli_exec, boxed_context, in_tmp_dir, tmp_path
192+
cli_runner, monkeypatch, mock_cli_exec, boxed_context, in_tmp_dir, tmp_path
189193
):
190194
monkeypatch.setenv("NO_COLOR", "1")
191195
touch_files(tmp_path, "foo.json")
192-
runner.invoke(
196+
cli_runner.invoke(
193197
cli_main, ["--color=always", "--schemafile", "schema.json", "foo.json"]
194198
)
195199
assert boxed_context.ref.color is False
@@ -200,21 +204,23 @@ def test_no_color_env_var_overrides_cli_option(
200204
[("auto", 0), ("always", 0), ("never", 0), ("anything_else", 2)],
201205
)
202206
def test_color_cli_option_is_choice(
203-
runner, setting, expected_value, in_tmp_dir, tmp_path
207+
cli_runner, setting, expected_value, in_tmp_dir, tmp_path
204208
):
205209
touch_files(tmp_path, "foo.json")
206210
assert (
207-
runner.invoke(
211+
cli_runner.invoke(
208212
cli_main,
209213
["--color", setting, "--schemafile", "schema.json", "foo.json"],
210214
).exit_code
211215
== expected_value
212216
)
213217

214218

215-
def test_formats_default_to_enabled(runner, mock_parse_result, in_tmp_dir, tmp_path):
219+
def test_formats_default_to_enabled(
220+
cli_runner, mock_parse_result, in_tmp_dir, tmp_path
221+
):
216222
touch_files(tmp_path, "foo.json")
217-
runner.invoke(cli_main, ["--schemafile", "schema.json", "foo.json"])
223+
cli_runner.invoke(cli_main, ["--schemafile", "schema.json", "foo.json"])
218224
assert mock_parse_result.disable_all_formats is False
219225
assert mock_parse_result.disable_formats == ()
220226

@@ -232,10 +238,10 @@ def test_formats_default_to_enabled(runner, mock_parse_result, in_tmp_dir, tmp_p
232238
),
233239
)
234240
def test_disable_selected_formats(
235-
runner, mock_parse_result, addargs, in_tmp_dir, tmp_path
241+
cli_runner, mock_parse_result, addargs, in_tmp_dir, tmp_path
236242
):
237243
touch_files(tmp_path, "foo.json")
238-
runner.invoke(
244+
cli_runner.invoke(
239245
cli_main,
240246
[
241247
"--schemafile",
@@ -263,10 +269,12 @@ def test_disable_selected_formats(
263269
["--disable-formats", "*,email"],
264270
),
265271
)
266-
def test_disable_all_formats(runner, mock_parse_result, addargs, in_tmp_dir, tmp_path):
272+
def test_disable_all_formats(
273+
cli_runner, mock_parse_result, addargs, in_tmp_dir, tmp_path
274+
):
267275
touch_files(tmp_path, "foo.json")
268276
# this should be an override, with or without other args
269-
runner.invoke(
277+
cli_runner.invoke(
270278
cli_main,
271279
[
272280
"--schemafile",
@@ -279,13 +287,13 @@ def test_disable_all_formats(runner, mock_parse_result, addargs, in_tmp_dir, tmp
279287

280288

281289
def test_can_specify_custom_validator_class(
282-
runner, mock_parse_result, mock_module, in_tmp_dir, tmp_path
290+
cli_runner, mock_parse_result, mock_module, in_tmp_dir, tmp_path
283291
):
284292
mock_module("foo.py", "class MyValidator: pass")
285293
import foo
286294

287295
touch_files(tmp_path, "foo.json")
288-
result = runner.invoke(
296+
result = cli_runner.invoke(
289297
cli_main,
290298
[
291299
"--schemafile",
@@ -303,7 +311,7 @@ def test_can_specify_custom_validator_class(
303311
"failmode", ("syntax", "import", "attr", "function", "non_callable")
304312
)
305313
def test_custom_validator_class_fails(
306-
runner, mock_parse_result, mock_module, failmode, in_tmp_dir, tmp_path
314+
cli_runner, mock_parse_result, mock_module, failmode, in_tmp_dir, tmp_path
307315
):
308316
mock_module(
309317
"foo.py",
@@ -331,7 +339,7 @@ def validator_func(*args, **kwargs):
331339
raise NotImplementedError
332340

333341
touch_files(tmp_path, "foo.json")
334-
result = runner.invoke(
342+
result = cli_runner.invoke(
335343
cli_main,
336344
["--schemafile", "schema.json", "foo.json", "--validator-class", arg],
337345
)

tests/unit/test_lazy_file_handling.py

+2-8
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,15 @@
22
import platform
33

44
import pytest
5-
from click.testing import CliRunner
65

76
from check_jsonschema.cli.main_command import build_checker
87
from check_jsonschema.cli.main_command import main as cli_main
98

109

11-
@pytest.fixture
12-
def runner() -> CliRunner:
13-
return CliRunner(mix_stderr=False)
14-
15-
1610
@pytest.mark.skipif(
1711
platform.system() != "Linux", reason="test requires /proc/self/ mechanism"
1812
)
19-
def test_open_file_usage_never_exceeds_1000(runner, monkeypatch, tmp_path):
13+
def test_open_file_usage_never_exceeds_1000(cli_runner, monkeypatch, tmp_path):
2014
schema_path = tmp_path / "schema.json"
2115
schema_path.write_text("{}")
2216

@@ -37,7 +31,7 @@ def fake_execute(argv):
3731
checker = build_checker(argv)
3832

3933
monkeypatch.setattr("check_jsonschema.cli.main_command.execute", fake_execute)
40-
res = runner.invoke(cli_main, args)
34+
res = cli_runner.invoke(cli_main, args)
4135
assert res.exit_code == 0, res.stderr
4236

4337
assert checker is not None

0 commit comments

Comments
 (0)