Skip to content

Commit 7649a91

Browse files
committed
Introduce _Case in the test suite loader.
(Should help on the way to preserving subject information in tests).
1 parent 9a4043d commit 7649a91

File tree

1 file changed

+68
-37
lines changed

1 file changed

+68
-37
lines changed

jsonschema/tests/_suite.py

Lines changed: 68 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from __future__ import annotations
55

66
from collections.abc import Iterable, Mapping
7-
from functools import partial
7+
from functools import cache, partial
88
from pathlib import Path
99
from typing import TYPE_CHECKING, Any
1010
import json
@@ -22,6 +22,8 @@
2222
from jsonschema.validators import _VALIDATORS
2323
import jsonschema
2424

25+
_DELIMITERS = re.compile(r"[\W\- ]+")
26+
2527

2628
def _find_suite():
2729
root = os.environ.get("JSON_SCHEMA_TEST_SUITE")
@@ -46,6 +48,8 @@ class Suite:
4648

4749
_root: Path = field(factory=_find_suite)
4850

51+
@property
52+
@cache # noqa: B019
4953
def _remotes(self) -> Mapping[str, Mapping[str, Any] | bool]:
5054
jsonschema_suite = self._root.joinpath("bin", "jsonschema_suite")
5155
remotes = subprocess.check_output(
@@ -63,8 +67,8 @@ def benchmark(self, runner: pyperf.Runner): # pragma: no cover
6367
def version(self, name) -> Version:
6468
return Version(
6569
name=name,
66-
path=self._root.joinpath("tests", name),
67-
remotes=self._remotes(),
70+
path=self._root / "tests" / name,
71+
remotes=self._remotes, # type: ignore # python/mypy#5858
6872
)
6973

7074

@@ -76,30 +80,29 @@ class Version:
7680

7781
name: str
7882

79-
def benchmark(self, runner: pyperf.Runner, **kwargs): # pragma: no cover
83+
def benchmark(self, **kwargs): # pragma: no cover
8084
for case in self.cases():
81-
for test in case:
82-
runner.bench_func(
83-
test.fully_qualified_name,
84-
partial(test.validate_ignoring_errors, **kwargs),
85-
)
85+
case.benchmark(**kwargs)
8686

87-
def cases(self) -> Iterable[Iterable[_Test]]:
87+
def cases(self) -> Iterable[_Case]:
8888
return self._cases_in(paths=self._path.glob("*.json"))
8989

90-
def format_cases(self) -> Iterable[Iterable[_Test]]:
90+
def format_cases(self) -> Iterable[_Case]:
9191
return self._cases_in(paths=self._path.glob("optional/format/*.json"))
9292

93-
def optional_cases_of(self, name: str) -> Iterable[Iterable[_Test]]:
93+
def optional_cases_of(self, name: str) -> Iterable[_Case]:
9494
return self._cases_in(paths=[self._path / "optional" / f"{name}.json"])
9595

9696
def to_unittest_testcase(self, *groups, **kwargs):
9797
name = kwargs.pop("name", "Test" + self.name.title().replace("-", ""))
9898
methods = {
99-
test.method_name: test.to_unittest_method(**kwargs)
100-
for group in groups
101-
for case in group
102-
for test in case
99+
method.__name__: method
100+
for method in (
101+
test.to_unittest_method(**kwargs)
102+
for group in groups
103+
for case in group
104+
for test in case.tests
105+
)
103106
}
104107
cls = type(name, (unittest.TestCase,), methods)
105108

@@ -113,21 +116,51 @@ def to_unittest_testcase(self, *groups, **kwargs):
113116

114117
return cls
115118

116-
def _cases_in(self, paths: Iterable[Path]) -> Iterable[Iterable[_Test]]:
119+
def _cases_in(self, paths: Iterable[Path]) -> Iterable[_Case]:
117120
for path in paths:
118121
for case in json.loads(path.read_text(encoding="utf-8")):
119-
yield (
120-
_Test(
121-
version=self,
122-
subject=path.stem,
123-
case_description=case["description"],
124-
schema=case["schema"],
125-
remotes=self._remotes,
126-
**test,
127-
) for test in case["tests"]
122+
yield _Case.from_dict(
123+
case,
124+
version=self,
125+
subject=path.stem,
126+
remotes=self._remotes,
128127
)
129128

130129

130+
@frozen
131+
class _Case:
132+
133+
version: Version
134+
135+
subject: str
136+
description: str
137+
schema: Mapping[str, Any] | bool
138+
tests: list[_Test]
139+
comment: str | None = None
140+
141+
@classmethod
142+
def from_dict(cls, data, remotes, **kwargs):
143+
data.update(kwargs)
144+
tests = [
145+
_Test(
146+
version=data["version"],
147+
subject=data["subject"],
148+
case_description=data["description"],
149+
schema=data["schema"],
150+
remotes=remotes,
151+
**test,
152+
) for test in data.pop("tests")
153+
]
154+
return cls(tests=tests, **data)
155+
156+
def benchmark(self, runner: pyperf.Runner, **kwargs): # pragma: no cover
157+
for test in self.tests:
158+
runner.bench_func(
159+
test.fully_qualified_name,
160+
partial(test.validate_ignoring_errors, **kwargs),
161+
)
162+
163+
131164
@frozen(repr=False)
132165
class _Test:
133166

@@ -147,7 +180,7 @@ class _Test:
147180
comment: str | None = None
148181

149182
def __repr__(self): # pragma: no cover
150-
return "<Test {}>".format(self.fully_qualified_name)
183+
return f"<Test {self.fully_qualified_name}>"
151184

152185
@property
153186
def fully_qualified_name(self): # pragma: no cover
@@ -160,15 +193,6 @@ def fully_qualified_name(self): # pragma: no cover
160193
],
161194
)
162195

163-
@property
164-
def method_name(self):
165-
delimiters = r"[\W\- ]+"
166-
return "test_{}_{}_{}".format(
167-
re.sub(delimiters, "_", self.subject),
168-
re.sub(delimiters, "_", self.case_description),
169-
re.sub(delimiters, "_", self.description),
170-
)
171-
172196
def to_unittest_method(self, skip=lambda test: None, **kwargs):
173197
if self.valid:
174198
def fn(this):
@@ -178,7 +202,14 @@ def fn(this):
178202
with this.assertRaises(jsonschema.ValidationError):
179203
self.validate(**kwargs)
180204

181-
fn.__name__ = self.method_name
205+
fn.__name__ = "_".join(
206+
[
207+
"test",
208+
_DELIMITERS.sub("_", self.subject),
209+
_DELIMITERS.sub("_", self.case_description),
210+
_DELIMITERS.sub("_", self.description),
211+
],
212+
)
182213
reason = skip(self)
183214
if reason is None or os.environ.get("JSON_SCHEMA_DEBUG", "0") != "0":
184215
return fn

0 commit comments

Comments
 (0)