Skip to content

Commit dc97f61

Browse files
authored
Merge pull request #372 from python-jsonschema/use-click-type-test
Apply click-type-test to check CLI annotations
2 parents 3e16eef + 43360e3 commit dc97f61

File tree

12 files changed

+71
-25
lines changed

12 files changed

+71
-25
lines changed

.github/workflows/build.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ jobs:
9797
9898
- name: test
9999
run: |
100-
python -m tox run-parallel -m ci -- --junitxml pytest.{envname}.xml
100+
python -m tox run -m ci -- -v --junitxml pytest.{envname}.xml
101101
python -m tox run -e cov
102102
103103
- uses: actions/upload-artifact@v4

scripts/aggregate-pytest-reports.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,45 @@ def main():
1010
args = parser.parse_args()
1111

1212
tests_by_name = defaultdict(dict)
13+
skipped_module_counts = defaultdict(int)
1314
for filename in args.FILES:
1415
tree = ElementTree.parse(filename)
1516
root = tree.getroot()
1617

1718
for testcase in root.findall("./testsuite/testcase"):
1819
classname = testcase.get("classname")
1920
name = testcase.get("name")
20-
nodename = f"{classname.replace('.', '/')}.py::{name}"
2121

2222
skip_node = testcase.find("skipped")
23-
if skip_node is not None:
24-
if "skipped" not in tests_by_name[nodename]:
25-
tests_by_name[nodename]["skipped"] = True
23+
24+
if classname:
25+
nodename = f"{classname.replace('.', '/')}.py::{name}"
26+
if skip_node is not None:
27+
if "skipped" not in tests_by_name[nodename]:
28+
tests_by_name[nodename]["skipped"] = True
29+
else:
30+
tests_by_name[nodename]["skipped"] = False
2631
else:
27-
tests_by_name[nodename]["skipped"] = False
32+
if skip_node is not None:
33+
skipped_module_counts[name] += 1
34+
35+
skipped_modules = {
36+
modname
37+
for modname, count in skipped_module_counts.items()
38+
if count == len(args.FILES)
39+
}
2840

2941
fail = False
3042
for nodename, attributes in tests_by_name.items():
3143
if attributes.get("skipped") is True:
3244
print(f"ALWAYS SKIPPED: {nodename}")
3345
fail = True
3446

47+
if skipped_modules:
48+
for modname in skipped_modules:
49+
print(f"ALWAYS SKIPPED MODULE: {modname}")
50+
fail = True
51+
3552
if fail:
3653
print("fail")
3754
sys.exit(1)

setup.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ console_scripts =
3939
[options.extras_require]
4040
dev =
4141
pytest<9
42+
click-type-test==0.0.7;python_version>="3.10"
4243
coverage<8
4344
pytest-xdist<4
4445
responses==0.24.1

src/check_jsonschema/cachedownloader.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ def _download(self) -> str:
127127
return dest
128128

129129
@contextlib.contextmanager
130-
def open(self) -> t.Generator[t.BinaryIO, None, None]:
130+
def open(self) -> t.Iterator[t.IO[bytes]]:
131131
if (not self._cache_dir) or self._disable_cache:
132132
yield io.BytesIO(self._get_request().content)
133133
else:

src/check_jsonschema/cli/main_command.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import os
4+
import sys
45
import textwrap
56
import typing as t
67

@@ -23,6 +24,11 @@
2324
from .param_types import CommaDelimitedList, LazyBinaryReadFile, ValidatorClassName
2425
from .parse_result import ParseResult, SchemaLoadingMode
2526

27+
if sys.version_info >= (3, 8):
28+
from typing import Literal
29+
else:
30+
from typing_extensions import Literal
31+
2632
BUILTIN_SCHEMA_NAMES = [f"vendor.{k}" for k in SCHEMA_CATALOG.keys()] + [
2733
f"custom.{k}" for k in CUSTOM_SCHEMA_NAMES
2834
]
@@ -232,16 +238,16 @@ def main(
232238
no_cache: bool,
233239
cache_filename: str | None,
234240
disable_formats: tuple[list[str], ...],
235-
format_regex: str,
236-
default_filetype: str,
237-
traceback_mode: str,
238-
data_transform: str | None,
241+
format_regex: Literal["python", "default"],
242+
default_filetype: Literal["json", "yaml", "toml", "json5"],
243+
traceback_mode: Literal["full", "short"],
244+
data_transform: Literal["azure-pipelines", "gitlab-ci"] | None,
239245
fill_defaults: bool,
240246
validator_class: type[jsonschema.protocols.Validator] | None,
241-
output_format: str,
247+
output_format: Literal["text", "json"],
242248
verbose: int,
243249
quiet: int,
244-
instancefiles: tuple[t.BinaryIO, ...],
250+
instancefiles: tuple[t.IO[bytes], ...],
245251
) -> None:
246252
args = ParseResult()
247253

src/check_jsonschema/cli/parse_result.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def __init__(self) -> None:
2222
self.schema_mode: SchemaLoadingMode = SchemaLoadingMode.filepath
2323
self.schema_path: str | None = None
2424
self.base_uri: str | None = None
25-
self.instancefiles: tuple[t.BinaryIO, ...] = ()
25+
self.instancefiles: tuple[t.IO[bytes], ...] = ()
2626
# cache controls
2727
self.disable_cache: bool = False
2828
self.cache_filename: str | None = None

src/check_jsonschema/instance_loader.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
class InstanceLoader:
1313
def __init__(
1414
self,
15-
files: t.Sequence[t.BinaryIO | CustomLazyFile],
15+
files: t.Sequence[t.IO[bytes] | CustomLazyFile],
1616
default_filetype: str = "json",
1717
data_transform: Transform | None = None,
1818
) -> None:
@@ -40,7 +40,7 @@ def iter_files(self) -> t.Iterator[tuple[str, ParseError | t.Any]]:
4040

4141
try:
4242
if isinstance(file, CustomLazyFile):
43-
stream: t.BinaryIO = t.cast(t.BinaryIO, file.open())
43+
stream: t.IO[bytes] = t.cast(t.IO[bytes], file.open())
4444
else:
4545
stream = file
4646

src/check_jsonschema/parsers/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from . import json5, toml, yaml
1212

1313
_PARSER_ERRORS: set[type[Exception]] = {json.JSONDecodeError, yaml.ParseError}
14-
DEFAULT_LOAD_FUNC_BY_TAG: dict[str, t.Callable[[t.BinaryIO], t.Any]] = {
14+
DEFAULT_LOAD_FUNC_BY_TAG: dict[str, t.Callable[[t.IO[bytes]], t.Any]] = {
1515
"json": json.load,
1616
}
1717
SUPPORTED_FILE_FORMATS = ["json", "yaml"]
@@ -67,7 +67,7 @@ def __init__(
6767

6868
def get(
6969
self, path: pathlib.Path | str, default_filetype: str
70-
) -> t.Callable[[t.BinaryIO], t.Any]:
70+
) -> t.Callable[[t.IO[bytes]], t.Any]:
7171
filetype = path_to_type(path, default_type=default_filetype)
7272

7373
if filetype in self._by_tag:
@@ -84,7 +84,7 @@ def get(
8484
)
8585

8686
def parse_data_with_path(
87-
self, data: t.BinaryIO | bytes, path: pathlib.Path | str, default_filetype: str
87+
self, data: t.IO[bytes] | bytes, path: pathlib.Path | str, default_filetype: str
8888
) -> t.Any:
8989
loadfunc = self.get(path, default_filetype)
9090
try:

src/check_jsonschema/parsers/json5.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@
2727
if _load is not None:
2828
_load_concrete: t.Callable = _load
2929

30-
def load(stream: t.BinaryIO) -> t.Any:
30+
def load(stream: t.IO[bytes]) -> t.Any:
3131
return _load_concrete(stream)
3232

3333
else:
3434

35-
def load(stream: t.BinaryIO) -> t.Any:
35+
def load(stream: t.IO[bytes]) -> t.Any:
3636
raise NotImplementedError
3737

3838

src/check_jsonschema/parsers/toml.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,14 @@ def _normalize(data: t.Any) -> t.Any:
6262
if ENABLED:
6363
ParseError: type[Exception] = toml_implementation.TOMLDecodeError
6464

65-
def load(stream: t.BinaryIO) -> t.Any:
65+
def load(stream: t.IO[bytes]) -> t.Any:
6666
data = toml_implementation.load(stream)
6767
return _normalize(data)
6868

6969
else:
7070
ParseError = ValueError
7171

72-
def load(stream: t.BinaryIO) -> t.Any:
72+
def load(stream: t.IO[bytes]) -> t.Any:
7373
raise NotImplementedError
7474

7575

0 commit comments

Comments
 (0)