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
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Unreleased

- Update vendored schemas: bitbucket-pipelines, circle-ci, compose-spec, dependabot,
github-workflows, gitlab-ci, mergify, renovate, woodpecker-ci (2025-05-11)
- Fix: support ``click==8.2.0``

0.33.0
------
Expand Down
16 changes: 15 additions & 1 deletion src/check_jsonschema/cli/param_types.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import functools
import importlib
import os
import re
Expand All @@ -10,6 +11,18 @@
import jsonschema
from click._compat import open_stream

C = t.TypeVar("C", bound=t.Callable[..., t.Any])


def _shim_click_8_2_get_metavar(func: C) -> C:
@functools.wraps(func)
def wrapper(*args: t.Any, **kwargs: t.Any) -> None:
if len(args) > 1 or "ctx" in kwargs:
return func(*args, **kwargs)
return func(*args, ctx=None, **kwargs)

return wrapper # type: ignore[return-value]


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

def get_metavar(self, param: click.Parameter) -> str:
@_shim_click_8_2_get_metavar
def get_metavar(self, param: click.Parameter, ctx: click.Context | None) -> str:
if self.choices is not None:
return "{" + ",".join(self.choices) + "}"
return "TEXT,TEXT,..."
Expand Down
6 changes: 0 additions & 6 deletions tests/acceptance/conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import textwrap

import pytest
from click.testing import CliRunner

from check_jsonschema import main as cli_main

Expand All @@ -16,11 +15,6 @@ def _render_result(result):
"""


@pytest.fixture
def cli_runner():
return CliRunner(mix_stderr=False)


@pytest.fixture
def run_line(cli_runner):
def func(cli_args, *args, **kwargs):
Expand Down
11 changes: 11 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import inspect
import os
import pathlib
import sys

import pytest
import responses
from click.testing import CliRunner


@pytest.fixture
def cli_runner():
# compatibility for click==8.2.0 vs click<=8.1
sig = inspect.signature(CliRunner)
if "mix_stderr" in sig.parameters:
return CliRunner(mix_stderr=False)
return CliRunner()


@pytest.fixture(autouse=True)
Expand Down
7 changes: 0 additions & 7 deletions tests/unit/cli/conftest.py

This file was deleted.

12 changes: 6 additions & 6 deletions tests/unit/cli/test_callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,30 +22,30 @@ def mycli(bar, baz):
print(baz)


def test_deprecation_warning_callback_on_missing_opts(runner):
result = runner.invoke(mycli, [])
def test_deprecation_warning_callback_on_missing_opts(cli_runner):
result = cli_runner.invoke(mycli, [])
assert result.exit_code == 0
assert result.stdout == "False\n"


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


def test_deprecation_warning_callback_added_message(runner):
def test_deprecation_warning_callback_added_message(cli_runner):
with pytest.warns(
UserWarning,
match=(
"'--baz' is deprecated and will be removed in a future release. "
"Use --frob instead!"
),
):
result = runner.invoke(mycli, ["--baz", "ok"], catch_exceptions=False)
result = cli_runner.invoke(mycli, ["--baz", "ok"], catch_exceptions=False)
assert result.exit_code == 0, result.stdout
assert result.stdout == "False\nok\n"
76 changes: 42 additions & 34 deletions tests/unit/cli/test_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,16 @@ def test_parse_result_set_schema(
assert args.schema_path is None


def test_requires_some_args(runner):
result = runner.invoke(cli_main, [])
def test_requires_some_args(cli_runner):
result = cli_runner.invoke(cli_main, [])
assert result.exit_code == 2


def test_schemafile_and_instancefile(runner, mock_parse_result, in_tmp_dir, tmp_path):
def test_schemafile_and_instancefile(
cli_runner, mock_parse_result, in_tmp_dir, tmp_path
):
touch_files(tmp_path, "foo.json")
runner.invoke(cli_main, ["--schemafile", "schema.json", "foo.json"])
cli_runner.invoke(cli_main, ["--schemafile", "schema.json", "foo.json"])
assert mock_parse_result.schema_mode == SchemaLoadingMode.filepath
assert mock_parse_result.schema_path == "schema.json"
assert isinstance(mock_parse_result.instancefiles, tuple)
Expand All @@ -83,25 +85,27 @@ def test_schemafile_and_instancefile(runner, mock_parse_result, in_tmp_dir, tmp_
assert tuple(f.name for f in mock_parse_result.instancefiles) == ("foo.json",)


def test_requires_at_least_one_instancefile(runner):
result = runner.invoke(cli_main, ["--schemafile", "schema.json"])
def test_requires_at_least_one_instancefile(cli_runner):
result = cli_runner.invoke(cli_main, ["--schemafile", "schema.json"])
assert result.exit_code == 2


def test_requires_schemafile(runner, in_tmp_dir, tmp_path):
def test_requires_schemafile(cli_runner, in_tmp_dir, tmp_path):
touch_files(tmp_path, "foo.json")
result = runner.invoke(cli_main, ["foo.json"])
result = cli_runner.invoke(cli_main, ["foo.json"])
assert result.exit_code == 2


def test_no_cache_defaults_false(runner, mock_parse_result):
runner.invoke(cli_main, ["--schemafile", "schema.json", "foo.json"])
def test_no_cache_defaults_false(cli_runner, mock_parse_result):
cli_runner.invoke(cli_main, ["--schemafile", "schema.json", "foo.json"])
assert mock_parse_result.disable_cache is False


def test_no_cache_flag_is_true(runner, mock_parse_result, in_tmp_dir, tmp_path):
def test_no_cache_flag_is_true(cli_runner, mock_parse_result, in_tmp_dir, tmp_path):
touch_files(tmp_path, "foo.json")
runner.invoke(cli_main, ["--schemafile", "schema.json", "foo.json", "--no-cache"])
cli_runner.invoke(
cli_main, ["--schemafile", "schema.json", "foo.json", "--no-cache"]
)
assert mock_parse_result.disable_cache is True


Expand Down Expand Up @@ -133,9 +137,9 @@ def test_no_cache_flag_is_true(runner, mock_parse_result, in_tmp_dir, tmp_path):
],
],
)
def test_mutex_schema_opts(runner, cmd_args, in_tmp_dir, tmp_path):
def test_mutex_schema_opts(cli_runner, cmd_args, in_tmp_dir, tmp_path):
touch_files(tmp_path, "foo.json")
result = runner.invoke(cli_main, cmd_args + ["foo.json"])
result = cli_runner.invoke(cli_main, cmd_args + ["foo.json"])
assert result.exit_code == 2
assert "are mutually exclusive" in result.stderr

Expand All @@ -148,24 +152,24 @@ def test_mutex_schema_opts(runner, cmd_args, in_tmp_dir, tmp_path):
["-h"],
],
)
def test_supports_common_option(runner, cmd_args):
result = runner.invoke(cli_main, cmd_args)
def test_supports_common_option(cli_runner, cmd_args):
result = cli_runner.invoke(cli_main, cmd_args)
assert result.exit_code == 0


@pytest.mark.parametrize(
"setting,expect_value", [(None, None), ("1", False), ("0", False)]
)
def test_no_color_env_var(
runner, monkeypatch, setting, expect_value, boxed_context, in_tmp_dir, tmp_path
cli_runner, monkeypatch, setting, expect_value, boxed_context, in_tmp_dir, tmp_path
):
if setting is None:
monkeypatch.delenv("NO_COLOR", raising=False)
else:
monkeypatch.setenv("NO_COLOR", setting)

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


Expand All @@ -174,22 +178,22 @@ def test_no_color_env_var(
[(None, None), ("auto", None), ("always", True), ("never", False)],
)
def test_color_cli_option(
runner, setting, expected_value, boxed_context, in_tmp_dir, tmp_path
cli_runner, setting, expected_value, boxed_context, in_tmp_dir, tmp_path
):
args = ["--schemafile", "schema.json", "foo.json"]
if setting:
args.extend(("--color", setting))
touch_files(tmp_path, "foo.json")
runner.invoke(cli_main, args)
cli_runner.invoke(cli_main, args)
assert boxed_context.ref.color == expected_value


def test_no_color_env_var_overrides_cli_option(
runner, monkeypatch, mock_cli_exec, boxed_context, in_tmp_dir, tmp_path
cli_runner, monkeypatch, mock_cli_exec, boxed_context, in_tmp_dir, tmp_path
):
monkeypatch.setenv("NO_COLOR", "1")
touch_files(tmp_path, "foo.json")
runner.invoke(
cli_runner.invoke(
cli_main, ["--color=always", "--schemafile", "schema.json", "foo.json"]
)
assert boxed_context.ref.color is False
Expand All @@ -200,21 +204,23 @@ def test_no_color_env_var_overrides_cli_option(
[("auto", 0), ("always", 0), ("never", 0), ("anything_else", 2)],
)
def test_color_cli_option_is_choice(
runner, setting, expected_value, in_tmp_dir, tmp_path
cli_runner, setting, expected_value, in_tmp_dir, tmp_path
):
touch_files(tmp_path, "foo.json")
assert (
runner.invoke(
cli_runner.invoke(
cli_main,
["--color", setting, "--schemafile", "schema.json", "foo.json"],
).exit_code
== expected_value
)


def test_formats_default_to_enabled(runner, mock_parse_result, in_tmp_dir, tmp_path):
def test_formats_default_to_enabled(
cli_runner, mock_parse_result, in_tmp_dir, tmp_path
):
touch_files(tmp_path, "foo.json")
runner.invoke(cli_main, ["--schemafile", "schema.json", "foo.json"])
cli_runner.invoke(cli_main, ["--schemafile", "schema.json", "foo.json"])
assert mock_parse_result.disable_all_formats is False
assert mock_parse_result.disable_formats == ()

Expand All @@ -232,10 +238,10 @@ def test_formats_default_to_enabled(runner, mock_parse_result, in_tmp_dir, tmp_p
),
)
def test_disable_selected_formats(
runner, mock_parse_result, addargs, in_tmp_dir, tmp_path
cli_runner, mock_parse_result, addargs, in_tmp_dir, tmp_path
):
touch_files(tmp_path, "foo.json")
runner.invoke(
cli_runner.invoke(
cli_main,
[
"--schemafile",
Expand Down Expand Up @@ -263,10 +269,12 @@ def test_disable_selected_formats(
["--disable-formats", "*,email"],
),
)
def test_disable_all_formats(runner, mock_parse_result, addargs, in_tmp_dir, tmp_path):
def test_disable_all_formats(
cli_runner, mock_parse_result, addargs, in_tmp_dir, tmp_path
):
touch_files(tmp_path, "foo.json")
# this should be an override, with or without other args
runner.invoke(
cli_runner.invoke(
cli_main,
[
"--schemafile",
Expand All @@ -279,13 +287,13 @@ def test_disable_all_formats(runner, mock_parse_result, addargs, in_tmp_dir, tmp


def test_can_specify_custom_validator_class(
runner, mock_parse_result, mock_module, in_tmp_dir, tmp_path
cli_runner, mock_parse_result, mock_module, in_tmp_dir, tmp_path
):
mock_module("foo.py", "class MyValidator: pass")
import foo

touch_files(tmp_path, "foo.json")
result = runner.invoke(
result = cli_runner.invoke(
cli_main,
[
"--schemafile",
Expand All @@ -303,7 +311,7 @@ def test_can_specify_custom_validator_class(
"failmode", ("syntax", "import", "attr", "function", "non_callable")
)
def test_custom_validator_class_fails(
runner, mock_parse_result, mock_module, failmode, in_tmp_dir, tmp_path
cli_runner, mock_parse_result, mock_module, failmode, in_tmp_dir, tmp_path
):
mock_module(
"foo.py",
Expand Down Expand Up @@ -331,7 +339,7 @@ def validator_func(*args, **kwargs):
raise NotImplementedError

touch_files(tmp_path, "foo.json")
result = runner.invoke(
result = cli_runner.invoke(
cli_main,
["--schemafile", "schema.json", "foo.json", "--validator-class", arg],
)
Expand Down
10 changes: 2 additions & 8 deletions tests/unit/test_lazy_file_handling.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,15 @@
import platform

import pytest
from click.testing import CliRunner

from check_jsonschema.cli.main_command import build_checker
from check_jsonschema.cli.main_command import main as cli_main


@pytest.fixture
def runner() -> CliRunner:
return CliRunner(mix_stderr=False)


@pytest.mark.skipif(
platform.system() != "Linux", reason="test requires /proc/self/ mechanism"
)
def test_open_file_usage_never_exceeds_1000(runner, monkeypatch, tmp_path):
def test_open_file_usage_never_exceeds_1000(cli_runner, monkeypatch, tmp_path):
schema_path = tmp_path / "schema.json"
schema_path.write_text("{}")

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

monkeypatch.setattr("check_jsonschema.cli.main_command.execute", fake_execute)
res = runner.invoke(cli_main, args)
res = cli_runner.invoke(cli_main, args)
assert res.exit_code == 0, res.stderr

assert checker is not None
Expand Down
Loading