4
4
from __future__ import annotations
5
5
6
6
from collections .abc import Iterable , Mapping
7
- from functools import partial
7
+ from functools import cache , partial
8
8
from pathlib import Path
9
9
from typing import TYPE_CHECKING , Any
10
10
import json
22
22
from jsonschema .validators import _VALIDATORS
23
23
import jsonschema
24
24
25
+ _DELIMITERS = re .compile (r"[\W\- ]+" )
26
+
25
27
26
28
def _find_suite ():
27
29
root = os .environ .get ("JSON_SCHEMA_TEST_SUITE" )
@@ -46,6 +48,8 @@ class Suite:
46
48
47
49
_root : Path = field (factory = _find_suite )
48
50
51
+ @property
52
+ @cache # noqa: B019
49
53
def _remotes (self ) -> Mapping [str , Mapping [str , Any ] | bool ]:
50
54
jsonschema_suite = self ._root .joinpath ("bin" , "jsonschema_suite" )
51
55
remotes = subprocess .check_output (
@@ -63,8 +67,8 @@ def benchmark(self, runner: pyperf.Runner): # pragma: no cover
63
67
def version (self , name ) -> Version :
64
68
return Version (
65
69
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
68
72
)
69
73
70
74
@@ -76,30 +80,29 @@ class Version:
76
80
77
81
name : str
78
82
79
- def benchmark (self , runner : pyperf . Runner , ** kwargs ): # pragma: no cover
83
+ def benchmark (self , ** kwargs ): # pragma: no cover
80
84
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 )
86
86
87
- def cases (self ) -> Iterable [Iterable [ _Test ] ]:
87
+ def cases (self ) -> Iterable [_Case ]:
88
88
return self ._cases_in (paths = self ._path .glob ("*.json" ))
89
89
90
- def format_cases (self ) -> Iterable [Iterable [ _Test ] ]:
90
+ def format_cases (self ) -> Iterable [_Case ]:
91
91
return self ._cases_in (paths = self ._path .glob ("optional/format/*.json" ))
92
92
93
- def optional_cases_of (self , name : str ) -> Iterable [Iterable [ _Test ] ]:
93
+ def optional_cases_of (self , name : str ) -> Iterable [_Case ]:
94
94
return self ._cases_in (paths = [self ._path / "optional" / f"{ name } .json" ])
95
95
96
96
def to_unittest_testcase (self , * groups , ** kwargs ):
97
97
name = kwargs .pop ("name" , "Test" + self .name .title ().replace ("-" , "" ))
98
98
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
+ )
103
106
}
104
107
cls = type (name , (unittest .TestCase ,), methods )
105
108
@@ -113,21 +116,51 @@ def to_unittest_testcase(self, *groups, **kwargs):
113
116
114
117
return cls
115
118
116
- def _cases_in (self , paths : Iterable [Path ]) -> Iterable [Iterable [ _Test ] ]:
119
+ def _cases_in (self , paths : Iterable [Path ]) -> Iterable [_Case ]:
117
120
for path in paths :
118
121
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 ,
128
127
)
129
128
130
129
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
+
131
164
@frozen (repr = False )
132
165
class _Test :
133
166
@@ -147,7 +180,7 @@ class _Test:
147
180
comment : str | None = None
148
181
149
182
def __repr__ (self ): # pragma: no cover
150
- return "<Test {}>" . format ( self .fully_qualified_name )
183
+ return f "<Test { self .fully_qualified_name } >"
151
184
152
185
@property
153
186
def fully_qualified_name (self ): # pragma: no cover
@@ -160,15 +193,6 @@ def fully_qualified_name(self): # pragma: no cover
160
193
],
161
194
)
162
195
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
-
172
196
def to_unittest_method (self , skip = lambda test : None , ** kwargs ):
173
197
if self .valid :
174
198
def fn (this ):
@@ -178,7 +202,14 @@ def fn(this):
178
202
with this .assertRaises (jsonschema .ValidationError ):
179
203
self .validate (** kwargs )
180
204
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
+ )
182
213
reason = skip (self )
183
214
if reason is None or os .environ .get ("JSON_SCHEMA_DEBUG" , "0" ) != "0" :
184
215
return fn
0 commit comments