bitexoft-verify 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bitexoft_verify/__init__.py +3 -0
- bitexoft_verify/__main__.py +4 -0
- bitexoft_verify/checks/__init__.py +1 -0
- bitexoft_verify/checks/a1.py +396 -0
- bitexoft_verify/checks/common.py +300 -0
- bitexoft_verify/cli.py +120 -0
- bitexoft_verify/codes.py +68 -0
- bitexoft_verify/data/README.md +13 -0
- bitexoft_verify/data/a1_impossible_sample.csv +31 -0
- bitexoft_verify/data/a1_match_sample.csv +31 -0
- bitexoft_verify/data/a2_isolated_sample.csv +31 -0
- bitexoft_verify/data/issue-codes.json +57 -0
- bitexoft_verify/manifest.py +103 -0
- bitexoft_verify/report.py +234 -0
- bitexoft_verify/runner.py +101 -0
- bitexoft_verify-0.2.0.dist-info/METADATA +139 -0
- bitexoft_verify-0.2.0.dist-info/RECORD +19 -0
- bitexoft_verify-0.2.0.dist-info/WHEEL +4 -0
- bitexoft_verify-0.2.0.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
"""Checks reused across deliveries and projects.
|
|
2
|
+
|
|
3
|
+
House rule: a check never fails for something the person cannot fix. If an optional
|
|
4
|
+
dependency or an external fixture is missing, it is SKIPPED with a message saying what
|
|
5
|
+
to do; failing would be lying about their code.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import ast
|
|
11
|
+
import importlib
|
|
12
|
+
import subprocess
|
|
13
|
+
import sys
|
|
14
|
+
from dataclasses import dataclass
|
|
15
|
+
from enum import Enum
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
from ..manifest import Manifest
|
|
20
|
+
from ..report import Check, Problems, issue
|
|
21
|
+
|
|
22
|
+
FRAMEWORKS = (
|
|
23
|
+
"fastapi",
|
|
24
|
+
"pydantic",
|
|
25
|
+
"pydantic_settings",
|
|
26
|
+
"starlette",
|
|
27
|
+
"uvicorn",
|
|
28
|
+
"matplotlib",
|
|
29
|
+
"sqlalchemy",
|
|
30
|
+
"alembic",
|
|
31
|
+
"psycopg",
|
|
32
|
+
"flask",
|
|
33
|
+
"django",
|
|
34
|
+
"httpx",
|
|
35
|
+
"requests",
|
|
36
|
+
"numpy",
|
|
37
|
+
"pandas",
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# Input and output, in every shape the domain might reach for. The domain answers what
|
|
41
|
+
# the problem *is*; how data arrives and leaves is somebody else's job.
|
|
42
|
+
IO_MODULES = ("csv", "json", "argparse", "sys", "pathlib", "os", "io", "logging", "sqlite3", "pickle", "configparser")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass(slots=True)
|
|
46
|
+
class Context:
|
|
47
|
+
root: Path
|
|
48
|
+
manifest: Manifest
|
|
49
|
+
timeout: int = 180
|
|
50
|
+
|
|
51
|
+
def run(self, args: list[str], timeout: int | None = None) -> subprocess.CompletedProcess[str]:
|
|
52
|
+
"""Runs a command at the root of the learner's repository."""
|
|
53
|
+
return subprocess.run(
|
|
54
|
+
args,
|
|
55
|
+
cwd=self.root,
|
|
56
|
+
capture_output=True,
|
|
57
|
+
text=True,
|
|
58
|
+
timeout=timeout or self.timeout,
|
|
59
|
+
check=False,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def python(self, args: list[str], timeout: int | None = None) -> subprocess.CompletedProcess[str]:
|
|
63
|
+
return self.run([sys.executable, *args], timeout=timeout)
|
|
64
|
+
|
|
65
|
+
def cli(self, args: list[str], timeout: int | None = None) -> subprocess.CompletedProcess[str]:
|
|
66
|
+
"""Runs the learner's entry point, however they declared it."""
|
|
67
|
+
module = self.manifest.get("cli.module")
|
|
68
|
+
if isinstance(module, str) and module.strip():
|
|
69
|
+
return self.python(["-m", module, *args], timeout=timeout)
|
|
70
|
+
return self.python([self.manifest.require("cli.main"), *args], timeout=timeout)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def import_symbol(reference: str) -> Any:
|
|
74
|
+
"""`package.module:Symbol` -> the object. Raises if it cannot be imported."""
|
|
75
|
+
module_name, _, symbol = reference.partition(":")
|
|
76
|
+
module = importlib.import_module(module_name)
|
|
77
|
+
if not symbol:
|
|
78
|
+
return module
|
|
79
|
+
if not hasattr(module, symbol):
|
|
80
|
+
raise AttributeError(f"{module_name} does not export '{symbol}'")
|
|
81
|
+
return getattr(module, symbol)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def check_manifest_keys(ctx: Context, keys: list[str], check_id: str = "manifest") -> Check:
|
|
85
|
+
missing = ctx.manifest.missing(keys)
|
|
86
|
+
title = "bitexoft.toml declares what is needed"
|
|
87
|
+
if missing:
|
|
88
|
+
return Check(
|
|
89
|
+
check_id,
|
|
90
|
+
title,
|
|
91
|
+
"fail",
|
|
92
|
+
f"missing keys: {', '.join(missing)}",
|
|
93
|
+
(issue("manifest.missing_keys", keys=list(missing)),),
|
|
94
|
+
)
|
|
95
|
+
return Check(check_id, title, "pass")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def check_symbol(
|
|
99
|
+
ctx: Context,
|
|
100
|
+
reference: str,
|
|
101
|
+
*,
|
|
102
|
+
shape: str | None = None,
|
|
103
|
+
members: list[str] | None = None,
|
|
104
|
+
check_id: str | None = None,
|
|
105
|
+
) -> Check:
|
|
106
|
+
"""Checks that a symbol exists and has the shape the contract asks for."""
|
|
107
|
+
resolved = ctx.manifest.resolve(reference)
|
|
108
|
+
identifier = check_id or resolved.replace(":", ".").replace(".", "_").lower()
|
|
109
|
+
title = f"{resolved} exists with the required shape"
|
|
110
|
+
try:
|
|
111
|
+
obj = import_symbol(resolved)
|
|
112
|
+
except Exception as exc: # noqa: BLE001 - any import failure is a contract failure
|
|
113
|
+
return Check(
|
|
114
|
+
identifier,
|
|
115
|
+
title,
|
|
116
|
+
"fail",
|
|
117
|
+
f"could not import: {type(exc).__name__}: {exc}",
|
|
118
|
+
(issue("symbol.import_failed", symbol=resolved, error=type(exc).__name__, detail=str(exc)),),
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
problems = Problems()
|
|
122
|
+
|
|
123
|
+
if shape == "frozen-dataclass":
|
|
124
|
+
params = getattr(obj, "__dataclass_params__", None)
|
|
125
|
+
if params is None:
|
|
126
|
+
problems.add("not a dataclass", "symbol.not_dataclass", symbol=resolved)
|
|
127
|
+
elif not getattr(params, "frozen", False):
|
|
128
|
+
problems.add("the dataclass is not immutable (frozen=True)", "symbol.not_frozen", symbol=resolved)
|
|
129
|
+
elif shape == "enum":
|
|
130
|
+
if not (isinstance(obj, type) and issubclass(obj, Enum)):
|
|
131
|
+
problems.add("not an Enum", "symbol.not_enum", symbol=resolved)
|
|
132
|
+
elif shape == "exception":
|
|
133
|
+
if not (isinstance(obj, type) and issubclass(obj, BaseException)):
|
|
134
|
+
problems.add("not an exception class", "symbol.not_exception", symbol=resolved)
|
|
135
|
+
elif shape == "protocol-or-abc":
|
|
136
|
+
is_protocol = bool(getattr(obj, "_is_protocol", False))
|
|
137
|
+
is_abc = isinstance(obj, type) and bool(getattr(obj, "__abstractmethods__", frozenset()))
|
|
138
|
+
if not (is_protocol or is_abc):
|
|
139
|
+
problems.add(
|
|
140
|
+
"neither a Protocol nor an abstract base class", "symbol.not_protocol_or_abc", symbol=resolved
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
for member in members or []:
|
|
144
|
+
if isinstance(obj, type) and issubclass(obj, Enum):
|
|
145
|
+
present = member in obj.__members__
|
|
146
|
+
else:
|
|
147
|
+
present = hasattr(obj, member) or member in getattr(obj, "__annotations__", {})
|
|
148
|
+
if not present:
|
|
149
|
+
problems.add(f"'{member}' is missing", "symbol.member_missing", symbol=resolved, member=member)
|
|
150
|
+
|
|
151
|
+
if problems:
|
|
152
|
+
return problems.failing(identifier, title, "; ")
|
|
153
|
+
return Check(identifier, title, "pass")
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def module_imports(path: Path) -> list[str]:
|
|
157
|
+
try:
|
|
158
|
+
tree = ast.parse(path.read_text(encoding="utf-8"))
|
|
159
|
+
except (SyntaxError, UnicodeDecodeError):
|
|
160
|
+
return []
|
|
161
|
+
names: list[str] = []
|
|
162
|
+
for node in ast.walk(tree):
|
|
163
|
+
if isinstance(node, ast.Import):
|
|
164
|
+
names += [alias.name for alias in node.names]
|
|
165
|
+
elif isinstance(node, ast.ImportFrom) and node.module and node.level == 0:
|
|
166
|
+
names.append(node.module)
|
|
167
|
+
return names
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def check_domain_has_no_io(ctx: Context, check_id: str = "boundary") -> Check:
|
|
171
|
+
"""
|
|
172
|
+
The boundary, read from the syntax tree. Nothing is executed: what counts is what
|
|
173
|
+
the code says it depends on. This is the check that keeps the cost of every later
|
|
174
|
+
delivery constant instead of growing.
|
|
175
|
+
"""
|
|
176
|
+
title = "The domain has no input or output"
|
|
177
|
+
folder = ctx.manifest.layer_path("domain")
|
|
178
|
+
if folder is None:
|
|
179
|
+
return Check(
|
|
180
|
+
check_id,
|
|
181
|
+
title,
|
|
182
|
+
"fail",
|
|
183
|
+
"I cannot find the domain folder declared in bitexoft.toml",
|
|
184
|
+
(issue("boundary.no_domain_folder"),),
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
forbidden = (*IO_MODULES, *FRAMEWORKS)
|
|
188
|
+
offenders = Problems()
|
|
189
|
+
checked = 0
|
|
190
|
+
for module in sorted(folder.rglob("*.py")):
|
|
191
|
+
checked += 1
|
|
192
|
+
for name in module_imports(module):
|
|
193
|
+
if any(name == entry or name.startswith(entry + ".") for entry in forbidden):
|
|
194
|
+
relative = module.relative_to(ctx.root)
|
|
195
|
+
offenders.add(
|
|
196
|
+
f"{relative} imports {name}",
|
|
197
|
+
"boundary.imports_io",
|
|
198
|
+
module=relative.as_posix(),
|
|
199
|
+
imported=name,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
if checked == 0:
|
|
203
|
+
return Check(
|
|
204
|
+
check_id,
|
|
205
|
+
title,
|
|
206
|
+
"fail",
|
|
207
|
+
"the domain folder has no Python modules",
|
|
208
|
+
(issue("boundary.no_modules"),),
|
|
209
|
+
)
|
|
210
|
+
if offenders:
|
|
211
|
+
return offenders.failing(check_id, title, "; ", limit=8)
|
|
212
|
+
return Check(check_id, title, "pass")
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def check_tests_pass(ctx: Context, check_id: str = "tests") -> Check:
|
|
216
|
+
title = "Your test suite passes"
|
|
217
|
+
try:
|
|
218
|
+
result = ctx.python(["-m", "pytest", "-q", "--no-header", "-x"], timeout=ctx.timeout)
|
|
219
|
+
except subprocess.TimeoutExpired:
|
|
220
|
+
return Check(
|
|
221
|
+
check_id,
|
|
222
|
+
title,
|
|
223
|
+
"fail",
|
|
224
|
+
f"the tests did not finish within {ctx.timeout}s",
|
|
225
|
+
(issue("tests.timeout", seconds=ctx.timeout),),
|
|
226
|
+
)
|
|
227
|
+
if result.returncode == 5:
|
|
228
|
+
return Check(check_id, title, "fail", "no tests were collected", (issue("tests.none_collected"),))
|
|
229
|
+
if result.returncode != 0:
|
|
230
|
+
tail = (result.stdout or result.stderr).strip().splitlines()[-3:]
|
|
231
|
+
return Check(
|
|
232
|
+
check_id,
|
|
233
|
+
title,
|
|
234
|
+
"fail",
|
|
235
|
+
" / ".join(tail) or "pytest returned a non-zero exit code",
|
|
236
|
+
(issue("tests.failed", detail=" / ".join(tail)),),
|
|
237
|
+
)
|
|
238
|
+
return Check(check_id, title, "pass")
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _test_files(ctx: Context) -> list[Path]:
|
|
242
|
+
seen: list[Path] = []
|
|
243
|
+
for pattern in ("test_*.py", "*_test.py", "conftest.py"):
|
|
244
|
+
seen += [
|
|
245
|
+
path
|
|
246
|
+
for path in ctx.root.rglob(pattern)
|
|
247
|
+
if ".venv" not in path.parts and "node_modules" not in path.parts
|
|
248
|
+
]
|
|
249
|
+
return seen
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def check_tests_cover_invariants(ctx: Context, error_names: list[str], check_id: str = "invariant_tests") -> Check:
|
|
253
|
+
"""A test that builds something impossible and expects a refusal."""
|
|
254
|
+
title = "At least one test builds an impossible object"
|
|
255
|
+
files = _test_files(ctx)
|
|
256
|
+
if not files:
|
|
257
|
+
return Check(check_id, title, "fail", "I cannot find any test files", (issue("tests.no_files"),))
|
|
258
|
+
for path in files:
|
|
259
|
+
text = path.read_text(encoding="utf-8", errors="ignore")
|
|
260
|
+
if "raises" in text and any(name in text for name in error_names):
|
|
261
|
+
return Check(check_id, title, "pass")
|
|
262
|
+
return Check(
|
|
263
|
+
check_id,
|
|
264
|
+
title,
|
|
265
|
+
"fail",
|
|
266
|
+
f"no test expects {' or '.join(error_names)}: an invariant nobody tests is a comment, not a guarantee",
|
|
267
|
+
(issue("tests.no_invariant_test", errors=list(error_names)),),
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def check_determinism(ctx: Context, args: list[str], check_id: str = "determinism") -> Check:
|
|
272
|
+
title = "Same input, same output"
|
|
273
|
+
try:
|
|
274
|
+
first = ctx.cli(args, timeout=120)
|
|
275
|
+
second = ctx.cli(args, timeout=120)
|
|
276
|
+
except subprocess.TimeoutExpired:
|
|
277
|
+
return Check(
|
|
278
|
+
check_id,
|
|
279
|
+
title,
|
|
280
|
+
"fail",
|
|
281
|
+
"one of the two runs did not finish within 120s",
|
|
282
|
+
(issue("determinism.timeout", seconds=120),),
|
|
283
|
+
)
|
|
284
|
+
if first.returncode != 0 or second.returncode != 0:
|
|
285
|
+
return Check(
|
|
286
|
+
check_id,
|
|
287
|
+
title,
|
|
288
|
+
"skip",
|
|
289
|
+
"cannot be checked: the program does not exit cleanly",
|
|
290
|
+
(issue("determinism.not_checkable"),),
|
|
291
|
+
)
|
|
292
|
+
if first.stdout != second.stdout:
|
|
293
|
+
return Check(
|
|
294
|
+
check_id,
|
|
295
|
+
title,
|
|
296
|
+
"fail",
|
|
297
|
+
"two runs over the same file produce different output",
|
|
298
|
+
(issue("determinism.differs"),),
|
|
299
|
+
)
|
|
300
|
+
return Check(check_id, title, "pass")
|
bitexoft_verify/cli.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""Command line.
|
|
2
|
+
|
|
3
|
+
bitexoft-verify check A1 # verifies A1 (and everything before it)
|
|
4
|
+
bitexoft-verify check A3 --only # just A3
|
|
5
|
+
bitexoft-verify check A1 --repo ../mine # another directory
|
|
6
|
+
bitexoft-verify deliveries # what this version can verify today
|
|
7
|
+
|
|
8
|
+
Exit codes: 0 all green, 1 something fails, 2 could not even start (missing manifest,
|
|
9
|
+
unknown delivery). All three matter: CI reads them.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import argparse
|
|
15
|
+
import sys
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
from . import __version__
|
|
19
|
+
from .manifest import ManifestError
|
|
20
|
+
from .report import Check, Gate, Report, detect_surface, issue
|
|
21
|
+
from .runner import UnknownDelivery, available_deliveries, run
|
|
22
|
+
|
|
23
|
+
GREEN, RED, DIM, YELLOW, RESET = "\033[32m", "\033[31m", "\033[2m", "\033[33m", "\033[0m"
|
|
24
|
+
MARKS = {"pass": "ok ", "fail": "FAIL", "skip": "-- "}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _colour(status: str, text: str, tty: bool) -> str:
|
|
28
|
+
if not tty:
|
|
29
|
+
return text
|
|
30
|
+
return {"pass": GREEN, "fail": RED, "skip": YELLOW}.get(status, "") + text + RESET
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _print(report: Report, tty: bool) -> None:
|
|
34
|
+
for gate in report.gates:
|
|
35
|
+
print(f"\n{gate.id}")
|
|
36
|
+
for check in gate.checks:
|
|
37
|
+
print(f" {_colour(check.status, MARKS[check.status], tty)} {check.title}")
|
|
38
|
+
if check.message:
|
|
39
|
+
print(f" {DIM if tty else ''}{check.message}{RESET if tty else ''}")
|
|
40
|
+
print(
|
|
41
|
+
f"\n{report.passed} green, {report.failed} red, {report.skipped} not checked "
|
|
42
|
+
f"(delivery {report.delivery}, {'CI' if report.surface == 'ci' else 'local'})"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def main(argv: list[str] | None = None) -> int:
|
|
47
|
+
parser = argparse.ArgumentParser(
|
|
48
|
+
prog="bitexoft-verify", description="Verifies a delivery of a Bitexoft project."
|
|
49
|
+
)
|
|
50
|
+
parser.add_argument("--version", action="version", version=f"bitexoft-verify {__version__}")
|
|
51
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
52
|
+
|
|
53
|
+
check = sub.add_parser("check", help="verify a delivery")
|
|
54
|
+
check.add_argument("delivery", help="delivery identifier, such as A1")
|
|
55
|
+
check.add_argument("--repo", type=Path, default=Path.cwd(), help="repository root (defaults to the current one)")
|
|
56
|
+
check.add_argument("--out", type=Path, default=Path("verify-report.json"), help="where to write the report")
|
|
57
|
+
check.add_argument("--surface", choices=["auto", "local", "ci"], default="auto")
|
|
58
|
+
check.add_argument("--only", action="store_true", help="do not also verify the earlier deliveries")
|
|
59
|
+
check.add_argument("--timeout", type=int, default=180, help="seconds per command executed")
|
|
60
|
+
check.add_argument("--quiet", action="store_true")
|
|
61
|
+
|
|
62
|
+
sub.add_parser("deliveries", help="list the deliveries this version can verify")
|
|
63
|
+
|
|
64
|
+
args = parser.parse_args(argv)
|
|
65
|
+
tty = sys.stdout.isatty()
|
|
66
|
+
|
|
67
|
+
if args.command == "deliveries":
|
|
68
|
+
print("deliveries this version can verify: " + ", ".join(available_deliveries()))
|
|
69
|
+
return 0
|
|
70
|
+
|
|
71
|
+
root = args.repo.resolve()
|
|
72
|
+
delivery_id = args.delivery.strip().upper()
|
|
73
|
+
surface = detect_surface(None if args.surface == "auto" else args.surface)
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
report = run(
|
|
77
|
+
root,
|
|
78
|
+
delivery_id,
|
|
79
|
+
cli_version=__version__,
|
|
80
|
+
surface=surface,
|
|
81
|
+
cumulative=not args.only,
|
|
82
|
+
timeout=args.timeout,
|
|
83
|
+
)
|
|
84
|
+
except (ManifestError, UnknownDelivery) as exc:
|
|
85
|
+
print(f"{RED if tty else ''}{exc}{RESET if tty else ''}", file=sys.stderr)
|
|
86
|
+
return 2
|
|
87
|
+
except Exception as exc: # noqa: BLE001 - an unexpected failure must not lose the report
|
|
88
|
+
report = Report(
|
|
89
|
+
project="unknown",
|
|
90
|
+
delivery=delivery_id,
|
|
91
|
+
cli_version=__version__,
|
|
92
|
+
gates=[
|
|
93
|
+
Gate(
|
|
94
|
+
"acceptance",
|
|
95
|
+
[
|
|
96
|
+
Check(
|
|
97
|
+
"runner",
|
|
98
|
+
"Verification could run",
|
|
99
|
+
"fail",
|
|
100
|
+
f"{type(exc).__name__}: {exc}",
|
|
101
|
+
(issue("runner.crashed", error=type(exc).__name__, detail=str(exc)),),
|
|
102
|
+
)
|
|
103
|
+
],
|
|
104
|
+
)
|
|
105
|
+
],
|
|
106
|
+
surface=surface, # type: ignore[arg-type]
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
written = report.write(args.out)
|
|
110
|
+
if not args.quiet:
|
|
111
|
+
_print(report, tty)
|
|
112
|
+
print(f"report: {written}")
|
|
113
|
+
if report.surface == "local":
|
|
114
|
+
print("this report is informative; the one that counts is produced by CI")
|
|
115
|
+
|
|
116
|
+
return 0 if report.ok else 1
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
if __name__ == "__main__":
|
|
120
|
+
raise SystemExit(main())
|
bitexoft_verify/codes.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""The codes a check can attach to a problem (ADR 0028).
|
|
2
|
+
|
|
3
|
+
A code names a *kind of problem*, not a check: `symbol.not_frozen`, not `position`.
|
|
4
|
+
It is a public identifier. The platform keeps one sentence per code and language (ADR 0027), and a
|
|
5
|
+
learner's saved report keeps the code it was written with, so a code is never renamed and never
|
|
6
|
+
reused. A wrong code is retired and a new one added.
|
|
7
|
+
|
|
8
|
+
Only the codes something emits are listed. A1 is frozen (ADR 0009) and its checks emit none: the codes
|
|
9
|
+
that were drafted for its own checks were taken out, and a delivery from A2 on adds the ones it needs.
|
|
10
|
+
|
|
11
|
+
Each code lists the parameters it may carry. `expected` and `found` are the values to compare, when
|
|
12
|
+
there are two. `detail` is raw text from the learner's own code, such as an exception message: it is
|
|
13
|
+
shown as it is, never translated.
|
|
14
|
+
|
|
15
|
+
`python -m bitexoft_verify.codes` writes `data/issue-codes.json`, the list the platform's tests read
|
|
16
|
+
to make sure every code has a sentence in every language. A test keeps that file in step with this one.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import json
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
ISSUE_CODES: dict[str, tuple[str, ...]] = {
|
|
25
|
+
# --- the manifest
|
|
26
|
+
"manifest.missing_keys": ("keys",),
|
|
27
|
+
# --- a symbol the contract names
|
|
28
|
+
"symbol.import_failed": ("symbol", "error", "detail"),
|
|
29
|
+
"symbol.not_dataclass": ("symbol",),
|
|
30
|
+
"symbol.not_frozen": ("symbol",),
|
|
31
|
+
"symbol.not_enum": ("symbol",),
|
|
32
|
+
"symbol.not_exception": ("symbol",),
|
|
33
|
+
"symbol.not_protocol_or_abc": ("symbol",),
|
|
34
|
+
"symbol.member_missing": ("symbol", "member"),
|
|
35
|
+
# --- the boundary
|
|
36
|
+
"boundary.no_domain_folder": (),
|
|
37
|
+
"boundary.no_modules": (),
|
|
38
|
+
"boundary.imports_io": ("module", "imported"),
|
|
39
|
+
# --- the learner's tests
|
|
40
|
+
"tests.timeout": ("seconds",),
|
|
41
|
+
"tests.none_collected": (),
|
|
42
|
+
"tests.failed": ("detail",),
|
|
43
|
+
"tests.no_files": (),
|
|
44
|
+
"tests.no_invariant_test": ("errors",),
|
|
45
|
+
# --- determinism
|
|
46
|
+
"determinism.timeout": ("seconds",),
|
|
47
|
+
"determinism.not_checkable": (),
|
|
48
|
+
"determinism.differs": (),
|
|
49
|
+
# --- the runner itself
|
|
50
|
+
"runner.crashed": ("error", "detail"),
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
CATALOGUE = Path(__file__).resolve().parent / "data" / "issue-codes.json"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def catalogue() -> dict:
|
|
57
|
+
"""What the platform reads: every code and the parameters it may carry."""
|
|
58
|
+
return {"codes": {code: list(params) for code, params in sorted(ISSUE_CODES.items())}}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def main() -> int:
|
|
62
|
+
CATALOGUE.write_text(json.dumps(catalogue(), indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
|
63
|
+
print(f"{len(ISSUE_CODES)} codes written to {CATALOGUE}")
|
|
64
|
+
return 0
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
if __name__ == "__main__":
|
|
68
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Sample files
|
|
2
|
+
|
|
3
|
+
Synthetic, generated by `verify/tools/make_fixtures.py`. No third-party data ships here.
|
|
4
|
+
|
|
5
|
+
`a1_match_sample.csv` holds 30 instants in metres on a 105x68 pitch. Players sit on a ring
|
|
6
|
+
around the centroid, so the expected spread of each instant is that ring's radius: between
|
|
7
|
+
12 m and 32 m, crossing the default thresholds in both directions.
|
|
8
|
+
|
|
9
|
+
`a1_impossible_sample.csv` is the same file with one coordinate off the pitch.
|
|
10
|
+
|
|
11
|
+
`a2_isolated_sample.csv` holds ten players on a tight ring plus one stranded on the far
|
|
12
|
+
wing. The mean distance to the centroid stays low while the furthest player is far away, so
|
|
13
|
+
the two indicators of A2 reach opposite verdicts. That conflict is the point of the file.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
t,p1_x,p1_y,p2_x,p2_y,p3_x,p3_y,p4_x,p4_y,p5_x,p5_y,p6_x,p6_y,p7_x,p7_y,p8_x,p8_y,p9_x,p9_y,p10_x,p10_y,p11_x,p11_y,ball_x,ball_y
|
|
2
|
+
0.0,64.443,32.944,62.394,40.111,56.601,44.150,51.281,44.744,45.648,42.867,41.004,37.097,41.124,30.563,45.468,25.249,49.647,23.510,56.331,23.765,62.945,28.683,52.500,34.000
|
|
3
|
+
1.0,64.991,34.427,62.109,41.195,56.784,44.569,50.822,45.148,44.523,42.662,40.040,34.899,40.428,31.081,43.738,25.977,49.603,23.056,57.779,23.803,63.094,28.028,52.600,34.000
|
|
4
|
+
2.0,65.495,33.677,63.471,40.276,57.641,44.746,49.940,45.471,43.159,42.137,39.967,37.107,39.632,32.339,44.551,24.742,49.204,22.682,57.738,23.292,62.534,26.561,52.700,34.000
|
|
5
|
+
3.0,66.500,34.002,64.726,40.139,58.097,45.549,46.960,45.572,43.373,43.554,38.832,36.728,38.726,31.748,43.091,24.670,51.244,21.451,58.221,22.500,64.497,27.505,52.800,34.000
|
|
6
|
+
4.0,67.999,34.115,66.171,40.574,58.297,46.938,51.295,47.908,40.716,43.062,37.816,38.468,37.890,29.341,42.352,23.456,51.468,20.081,58.253,21.046,66.167,27.419,52.900,34.000
|
|
7
|
+
5.0,69.499,33.848,65.801,43.528,58.781,48.217,49.891,49.119,42.191,46.165,35.925,37.401,36.379,29.143,43.146,21.224,49.574,18.928,60.241,20.378,67.163,26.258,53.000,34.000
|
|
8
|
+
6.0,71.495,34.387,68.310,43.484,60.140,49.657,50.699,51.023,41.263,47.789,34.449,39.336,34.543,28.413,41.880,19.821,51.273,16.936,61.819,19.098,69.091,25.667,53.100,34.000
|
|
9
|
+
7.0,73.467,95.000,69.998,44.450,60.029,51.644,46.602,52.139,39.169,48.603,32.710,40.323,33.522,25.909,39.061,19.477,51.133,15.140,62.537,17.398,69.063,22.381,53.200,34.000
|
|
10
|
+
8.0,75.066,38.004,69.893,47.544,61.757,52.949,47.863,54.275,37.263,49.506,30.945,41.221,30.374,28.346,37.965,17.957,51.269,13.330,63.226,15.689,71.908,22.892,53.300,34.000
|
|
11
|
+
9.0,76.856,36.385,73.878,44.771,61.245,54.598,46.872,55.460,36.893,50.997,30.219,43.169,28.554,29.337,36.810,17.065,50.040,12.061,65.353,15.228,71.981,20.629,53.400,34.000
|
|
12
|
+
10.0,78.500,34.004,73.614,47.655,63.598,55.161,53.716,57.374,34.242,50.660,26.632,36.351,28.222,25.627,34.079,17.487,47.953,10.961,63.719,12.890,76.721,25.493,53.500,34.000
|
|
13
|
+
11.0,79.467,35.200,75.080,47.323,60.577,57.187,48.832,58.075,31.987,49.801,27.049,42.113,26.324,28.044,36.024,14.749,51.721,9.710,64.985,12.454,75.559,21.359,53.600,34.000
|
|
14
|
+
12.0,79.709,30.406,76.444,46.172,65.132,55.984,49.752,58.626,33.536,51.924,26.331,41.607,26.654,25.546,34.629,15.189,47.863,9.604,64.160,11.585,76.131,21.341,53.700,34.000
|
|
15
|
+
13.0,81.432,35.790,77.477,47.262,65.036,57.535,51.523,60.085,30.861,51.376,23.915,38.402,24.561,27.006,32.684,14.943,45.560,8.658,64.904,10.408,77.152,20.254,53.800,34.000
|
|
16
|
+
14.0,82.537,38.767,77.828,49.293,66.871,58.212,54.109,61.412,30.060,52.591,23.109,41.334,22.374,29.712,32.861,12.997,44.321,7.555,72.308,13.126,79.530,21.283,53.900,34.000
|
|
17
|
+
15.0,83.967,35.298,79.235,48.992,70.275,57.405,48.333,62.101,34.984,57.563,21.887,40.681,21.206,30.761,30.199,13.978,51.076,5.679,69.230,9.979,77.274,16.491,54.000,34.000
|
|
18
|
+
16.0,84.259,30.475,78.137,51.235,63.777,60.952,50.457,62.741,32.919,56.779,22.344,43.635,21.079,28.548,30.260,13.292,51.133,5.226,67.378,8.502,81.890,22.608,54.100,34.000
|
|
19
|
+
17.0,83.499,33.807,78.759,48.829,67.552,58.390,48.392,61.654,34.768,56.885,24.294,45.575,23.289,24.660,35.027,10.954,50.151,6.180,68.305,9.998,78.912,19.393,54.200,34.000
|
|
20
|
+
18.0,81.951,32.475,77.206,48.508,65.552,57.810,50.911,60.511,36.368,56.228,25.377,44.442,24.191,26.531,33.452,13.727,47.196,7.883,67.736,11.265,77.543,19.967,54.300,34.000
|
|
21
|
+
19.0,79.471,35.133,73.685,49.065,65.302,55.395,52.899,58.297,33.623,51.374,27.052,42.119,27.532,24.751,35.376,15.212,50.820,9.747,65.195,12.554,72.481,17.657,54.400,34.000
|
|
22
|
+
20.0,77.459,35.294,74.228,45.128,64.241,53.864,49.805,56.369,34.999,50.067,28.075,38.797,29.160,25.937,33.771,19.096,47.777,11.905,58.403,12.136,73.420,21.682,54.500,34.000
|
|
23
|
+
21.0,75.973,35.015,73.009,44.326,61.842,53.407,48.726,54.875,35.731,48.817,30.435,41.278,29.563,29.399,36.056,18.891,52.439,12.850,64.088,15.600,72.143,22.390,54.600,34.000
|
|
24
|
+
22.0,74.996,34.365,71.316,45.103,61.513,52.554,49.670,54.089,38.225,49.653,31.657,41.626,30.615,29.300,40.132,17.083,50.257,13.851,61.953,15.624,71.216,22.761,54.700,34.000
|
|
25
|
+
23.0,73.500,34.095,69.838,44.663,62.199,50.764,49.185,52.663,38.446,48.043,32.741,40.401,32.018,29.830,37.525,20.750,47.756,15.589,62.560,17.410,67.721,20.979,54.800,34.000
|
|
26
|
+
24.0,71.496,33.635,68.967,42.531,61.240,49.183,49.649,50.906,38.814,45.862,34.237,38.717,34.211,29.367,39.369,21.641,49.602,17.100,61.632,19.005,68.152,24.306,54.900,34.000
|
|
27
|
+
25.0,69.429,32.599,67.656,40.930,60.902,47.301,51.963,49.292,40.282,44.639,36.059,37.892,36.101,29.969,42.940,21.349,49.199,18.991,60.480,20.491,67.360,26.569,55.000,34.000
|
|
28
|
+
26.0,67.385,32.334,66.029,39.831,59.726,45.830,49.295,47.188,41.557,43.233,38.591,39.055,38.512,29.125,42.334,24.073,49.899,20.704,58.124,21.485,64.904,26.409,55.100,34.000
|
|
29
|
+
27.0,65.857,32.239,64.405,39.730,59.293,44.500,50.056,45.949,42.085,41.731,39.030,34.814,39.449,30.892,42.343,25.997,50.145,22.036,57.170,22.600,64.867,29.129,55.200,34.000
|
|
30
|
+
28.0,64.987,34.514,63.392,39.520,56.828,44.554,50.192,45.057,45.608,43.386,40.667,37.625,40.441,31.038,44.329,25.487,50.852,22.848,57.382,23.644,63.117,28.062,55.300,34.000
|
|
31
|
+
29.0,64.487,33.494,63.034,39.173,56.128,44.295,50.451,44.641,44.329,41.909,41.510,38.337,40.942,31.095,45.252,25.392,49.577,23.525,58.423,24.607,62.388,27.881,55.400,34.000
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
t,p1_x,p1_y,p2_x,p2_y,p3_x,p3_y,p4_x,p4_y,p5_x,p5_y,p6_x,p6_y,p7_x,p7_y,p8_x,p8_y,p9_x,p9_y,p10_x,p10_y,p11_x,p11_y,ball_x,ball_y
|
|
2
|
+
0.0,64.443,32.944,62.394,40.111,56.601,44.150,51.281,44.744,45.648,42.867,41.004,37.097,41.124,30.563,45.468,25.249,49.647,23.510,56.331,23.765,62.945,28.683,52.500,34.000
|
|
3
|
+
1.0,64.991,34.427,62.109,41.195,56.784,44.569,50.822,45.148,44.523,42.662,40.040,34.899,40.428,31.081,43.738,25.977,49.603,23.056,57.779,23.803,63.094,28.028,52.600,34.000
|
|
4
|
+
2.0,65.495,33.677,63.471,40.276,57.641,44.746,49.940,45.471,43.159,42.137,39.967,37.107,39.632,32.339,44.551,24.742,49.204,22.682,57.738,23.292,62.534,26.561,52.700,34.000
|
|
5
|
+
3.0,66.500,34.002,64.726,40.139,58.097,45.549,46.960,45.572,43.373,43.554,38.832,36.728,38.726,31.748,43.091,24.670,51.244,21.451,58.221,22.500,64.497,27.505,52.800,34.000
|
|
6
|
+
4.0,67.999,34.115,66.171,40.574,58.297,46.938,51.295,47.908,40.716,43.062,37.816,38.468,37.890,29.341,42.352,23.456,51.468,20.081,58.253,21.046,66.167,27.419,52.900,34.000
|
|
7
|
+
5.0,69.499,33.848,65.801,43.528,58.781,48.217,49.891,49.119,42.191,46.165,35.925,37.401,36.379,29.143,43.146,21.224,49.574,18.928,60.241,20.378,67.163,26.258,53.000,34.000
|
|
8
|
+
6.0,71.495,34.387,68.310,43.484,60.140,49.657,50.699,51.023,41.263,47.789,34.449,39.336,34.543,28.413,41.880,19.821,51.273,16.936,61.819,19.098,69.091,25.667,53.100,34.000
|
|
9
|
+
7.0,73.467,35.056,69.998,44.450,60.029,51.644,46.602,52.139,39.169,48.603,32.710,40.323,33.522,25.909,39.061,19.477,51.133,15.140,62.537,17.398,69.063,22.381,53.200,34.000
|
|
10
|
+
8.0,75.066,38.004,69.893,47.544,61.757,52.949,47.863,54.275,37.263,49.506,30.945,41.221,30.374,28.346,37.965,17.957,51.269,13.330,63.226,15.689,71.908,22.892,53.300,34.000
|
|
11
|
+
9.0,76.856,36.385,73.878,44.771,61.245,54.598,46.872,55.460,36.893,50.997,30.219,43.169,28.554,29.337,36.810,17.065,50.040,12.061,65.353,15.228,71.981,20.629,53.400,34.000
|
|
12
|
+
10.0,78.500,34.004,73.614,47.655,63.598,55.161,53.716,57.374,34.242,50.660,26.632,36.351,28.222,25.627,34.079,17.487,47.953,10.961,63.719,12.890,76.721,25.493,53.500,34.000
|
|
13
|
+
11.0,79.467,35.200,75.080,47.323,60.577,57.187,48.832,58.075,31.987,49.801,27.049,42.113,26.324,28.044,36.024,14.749,51.721,9.710,64.985,12.454,75.559,21.359,53.600,34.000
|
|
14
|
+
12.0,79.709,30.406,76.444,46.172,65.132,55.984,49.752,58.626,33.536,51.924,26.331,41.607,26.654,25.546,34.629,15.189,47.863,9.604,64.160,11.585,76.131,21.341,53.700,34.000
|
|
15
|
+
13.0,81.432,35.790,77.477,47.262,65.036,57.535,51.523,60.085,30.861,51.376,23.915,38.402,24.561,27.006,32.684,14.943,45.560,8.658,64.904,10.408,77.152,20.254,53.800,34.000
|
|
16
|
+
14.0,82.537,38.767,77.828,49.293,66.871,58.212,54.109,61.412,30.060,52.591,23.109,41.334,22.374,29.712,32.861,12.997,44.321,7.555,72.308,13.126,79.530,21.283,53.900,34.000
|
|
17
|
+
15.0,83.967,35.298,79.235,48.992,70.275,57.405,48.333,62.101,34.984,57.563,21.887,40.681,21.206,30.761,30.199,13.978,51.076,5.679,69.230,9.979,77.274,16.491,54.000,34.000
|
|
18
|
+
16.0,84.259,30.475,78.137,51.235,63.777,60.952,50.457,62.741,32.919,56.779,22.344,43.635,21.079,28.548,30.260,13.292,51.133,5.226,67.378,8.502,81.890,22.608,54.100,34.000
|
|
19
|
+
17.0,83.499,33.807,78.759,48.829,67.552,58.390,48.392,61.654,34.768,56.885,24.294,45.575,23.289,24.660,35.027,10.954,50.151,6.180,68.305,9.998,78.912,19.393,54.200,34.000
|
|
20
|
+
18.0,81.951,32.475,77.206,48.508,65.552,57.810,50.911,60.511,36.368,56.228,25.377,44.442,24.191,26.531,33.452,13.727,47.196,7.883,67.736,11.265,77.543,19.967,54.300,34.000
|
|
21
|
+
19.0,79.471,35.133,73.685,49.065,65.302,55.395,52.899,58.297,33.623,51.374,27.052,42.119,27.532,24.751,35.376,15.212,50.820,9.747,65.195,12.554,72.481,17.657,54.400,34.000
|
|
22
|
+
20.0,77.459,35.294,74.228,45.128,64.241,53.864,49.805,56.369,34.999,50.067,28.075,38.797,29.160,25.937,33.771,19.096,47.777,11.905,58.403,12.136,73.420,21.682,54.500,34.000
|
|
23
|
+
21.0,75.973,35.015,73.009,44.326,61.842,53.407,48.726,54.875,35.731,48.817,30.435,41.278,29.563,29.399,36.056,18.891,52.439,12.850,64.088,15.600,72.143,22.390,54.600,34.000
|
|
24
|
+
22.0,74.996,34.365,71.316,45.103,61.513,52.554,49.670,54.089,38.225,49.653,31.657,41.626,30.615,29.300,40.132,17.083,50.257,13.851,61.953,15.624,71.216,22.761,54.700,34.000
|
|
25
|
+
23.0,73.500,34.095,69.838,44.663,62.199,50.764,49.185,52.663,38.446,48.043,32.741,40.401,32.018,29.830,37.525,20.750,47.756,15.589,62.560,17.410,67.721,20.979,54.800,34.000
|
|
26
|
+
24.0,71.496,33.635,68.967,42.531,61.240,49.183,49.649,50.906,38.814,45.862,34.237,38.717,34.211,29.367,39.369,21.641,49.602,17.100,61.632,19.005,68.152,24.306,54.900,34.000
|
|
27
|
+
25.0,69.429,32.599,67.656,40.930,60.902,47.301,51.963,49.292,40.282,44.639,36.059,37.892,36.101,29.969,42.940,21.349,49.199,18.991,60.480,20.491,67.360,26.569,55.000,34.000
|
|
28
|
+
26.0,67.385,32.334,66.029,39.831,59.726,45.830,49.295,47.188,41.557,43.233,38.591,39.055,38.512,29.125,42.334,24.073,49.899,20.704,58.124,21.485,64.904,26.409,55.100,34.000
|
|
29
|
+
27.0,65.857,32.239,64.405,39.730,59.293,44.500,50.056,45.949,42.085,41.731,39.030,34.814,39.449,30.892,42.343,25.997,50.145,22.036,57.170,22.600,64.867,29.129,55.200,34.000
|
|
30
|
+
28.0,64.987,34.514,63.392,39.520,56.828,44.554,50.192,45.057,45.608,43.386,40.667,37.625,40.441,31.038,44.329,25.487,50.852,22.848,57.382,23.644,63.117,28.062,55.300,34.000
|
|
31
|
+
29.0,64.487,33.494,63.034,39.173,56.128,44.295,50.451,44.641,44.329,41.909,41.510,38.337,40.942,31.095,45.252,25.392,49.577,23.525,58.423,24.607,62.388,27.881,55.400,34.000
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
t,p1_x,p1_y,p2_x,p2_y,p3_x,p3_y,p4_x,p4_y,p5_x,p5_y,p6_x,p6_y,p7_x,p7_y,p8_x,p8_y,p9_x,p9_y,p10_x,p10_y,p11_x,p11_y,ball_x,ball_y
|
|
2
|
+
0.0,47.880,35.244,46.863,37.700,42.273,40.903,37.687,40.893,33.874,38.631,32.008,34.330,33.338,30.013,37.468,27.170,42.706,27.224,46.832,30.253,88.000,34.000,40.000,34.000
|
|
3
|
+
1.0,47.999,34.115,46.041,38.721,42.216,40.918,37.488,40.836,33.627,38.352,32.026,34.583,32.974,30.558,37.449,27.176,42.339,27.115,46.571,29.893,88.200,34.000,40.000,34.000
|
|
4
|
+
2.0,47.998,34.159,46.927,37.602,42.931,40.699,38.247,41.025,33.195,37.786,32.001,34.100,33.550,29.741,36.953,27.343,41.655,26.956,46.222,29.474,88.400,34.000,40.000,34.000
|
|
5
|
+
3.0,47.999,33.904,46.323,38.411,43.406,40.515,37.371,40.800,33.890,38.648,32.009,34.338,33.382,29.955,37.229,27.246,42.704,27.224,46.758,30.147,88.600,34.000,40.000,34.000
|
|
6
|
+
4.0,47.998,34.153,46.692,37.946,42.262,40.906,37.346,40.792,33.248,37.861,32.001,33.888,32.961,30.578,37.035,27.313,42.186,27.074,46.773,30.168,88.800,34.000,40.000,34.000
|
|
7
|
+
5.0,47.999,33.882,46.147,38.608,42.339,40.885,38.295,41.035,33.537,38.244,32.006,33.726,33.654,29.615,36.616,27.476,42.684,27.217,46.219,29.471,89.000,34.000,40.000,34.000
|
|
8
|
+
6.0,47.882,32.768,46.701,37.932,43.661,40.402,38.348,41.045,33.059,37.580,32.142,32.650,33.914,29.327,37.746,27.092,43.539,27.543,46.571,29.893,89.200,34.000,40.000,34.000
|
|
9
|
+
7.0,47.972,33.403,47.275,36.995,41.965,40.979,38.958,41.139,34.779,39.455,32.013,34.412,33.526,29.771,36.974,27.335,43.049,27.343,46.413,29.696,89.400,34.000,40.000,34.000
|
|
10
|
+
8.0,48.000,34.080,46.329,38.404,42.862,40.723,37.443,40.822,32.793,37.125,32.025,34.572,33.565,29.722,37.807,27.076,42.104,27.054,46.430,29.717,89.600,34.000,40.000,34.000
|
|
11
|
+
9.0,47.921,32.988,46.654,37.998,42.484,40.844,36.900,40.638,33.213,37.811,32.010,34.364,33.119,30.327,38.622,26.908,42.970,27.315,45.660,28.912,89.800,34.000,40.000,34.000
|
|
12
|
+
10.0,47.996,33.773,46.812,37.776,42.870,40.721,38.622,41.092,33.710,38.449,32.008,34.327,33.421,29.903,38.135,26.998,42.577,27.184,45.964,29.201,90.000,34.000,40.000,34.000
|
|
13
|
+
11.0,47.919,32.978,46.682,37.958,41.796,41.016,36.993,40.672,33.775,38.522,32.032,33.360,33.676,29.590,37.136,27.277,42.706,27.224,46.426,29.712,90.200,34.000,40.000,34.000
|
|
14
|
+
12.0,47.966,33.335,46.646,38.008,42.399,40.869,36.765,40.585,33.955,38.716,32.001,33.885,33.521,29.776,37.475,27.168,41.695,26.964,46.901,30.358,90.400,34.000,40.000,34.000
|
|
15
|
+
13.0,47.959,34.726,45.835,38.926,42.206,40.921,36.665,40.545,33.342,37.992,32.024,33.443,33.393,29.940,37.646,27.119,42.254,27.092,46.039,29.278,90.600,34.000,40.000,34.000
|
|
16
|
+
14.0,47.994,33.732,46.878,37.677,43.159,40.615,37.785,40.919,33.774,38.522,32.107,32.828,34.248,28.996,38.179,26.989,42.934,27.302,46.122,29.365,90.800,34.000,40.000,34.000
|
|
17
|
+
15.0,47.998,34.145,46.981,37.517,42.868,40.721,37.840,40.933,33.679,38.413,32.018,33.517,33.235,30.157,39.025,26.854,42.143,27.063,46.770,30.165,91.000,34.000,40.000,34.000
|
|
18
|
+
16.0,47.934,33.074,46.121,38.636,43.224,40.589,37.420,40.815,33.538,38.245,32.017,33.536,33.758,29.496,37.755,27.089,42.368,27.123,47.124,30.723,91.200,34.000,40.000,34.000
|
|
19
|
+
17.0,47.994,33.722,46.475,38.229,42.771,40.754,37.456,40.826,33.920,38.679,32.005,34.251,33.716,29.544,37.164,27.267,42.742,27.236,46.236,29.490,91.400,34.000,40.000,34.000
|
|
20
|
+
18.0,47.949,34.810,46.530,38.160,41.387,41.091,37.237,40.757,33.586,38.303,32.022,33.461,33.989,29.249,38.468,26.933,43.296,27.439,46.787,30.188,91.600,34.000,40.000,34.000
|
|
21
|
+
19.0,47.963,33.312,46.109,38.649,42.538,40.828,37.292,40.775,33.089,37.626,32.014,34.427,34.060,29.177,39.021,26.854,42.798,27.255,46.344,29.614,91.800,34.000,40.000,34.000
|
|
22
|
+
20.0,47.952,34.784,46.410,38.308,41.831,41.009,36.784,40.593,34.155,38.916,32.002,33.824,32.995,30.523,37.089,27.294,42.829,27.265,45.982,29.219,92.000,34.000,40.000,34.000
|
|
23
|
+
21.0,47.986,33.570,46.649,38.004,42.293,40.898,37.329,40.787,33.373,38.033,32.007,34.306,33.247,30.140,37.766,27.087,41.917,27.010,46.140,29.385,92.200,34.000,40.000,34.000
|
|
24
|
+
22.0,47.988,34.390,46.769,37.837,43.036,40.661,36.865,40.624,33.556,38.267,32.132,35.305,33.244,30.144,38.728,26.892,44.113,27.824,46.345,29.615,92.400,34.000,40.000,34.000
|
|
25
|
+
23.0,47.988,34.387,46.195,38.556,42.299,40.896,38.686,41.102,33.182,37.767,32.002,33.858,33.427,29.896,36.540,27.508,42.060,27.043,46.415,29.698,92.600,34.000,40.000,34.000
|
|
26
|
+
24.0,47.998,33.833,46.399,38.321,42.728,40.768,36.947,40.655,33.313,37.952,32.037,34.688,33.294,30.074,37.763,27.087,42.108,27.054,46.947,30.429,92.800,34.000,40.000,34.000
|
|
27
|
+
25.0,47.991,33.666,46.393,38.328,42.123,40.942,38.255,41.027,33.102,37.647,32.002,34.145,33.255,30.128,37.083,27.296,41.391,26.910,45.902,29.139,93.000,34.000,40.000,34.000
|
|
28
|
+
26.0,47.945,34.839,46.536,38.151,42.670,40.787,37.435,40.820,33.333,37.979,32.018,34.484,33.434,29.887,37.669,27.112,43.056,27.346,46.551,29.867,93.200,34.000,40.000,34.000
|
|
29
|
+
27.0,47.933,34.932,46.568,38.110,42.702,40.777,38.417,41.058,33.561,38.273,32.009,33.654,33.421,29.904,37.396,27.192,41.842,26.993,46.226,29.479,93.400,34.000,40.000,34.000
|
|
30
|
+
28.0,47.994,33.717,46.522,38.169,41.836,41.008,37.246,40.760,33.215,37.815,32.033,33.347,33.342,30.008,38.329,26.959,42.473,27.153,45.921,29.158,93.600,34.000,40.000,34.000
|
|
31
|
+
29.0,47.989,33.626,46.521,38.171,43.414,40.512,36.198,40.335,33.564,38.276,32.017,33.532,33.089,30.373,37.560,27.143,42.601,27.191,46.154,29.400,93.800,34.000,40.000,34.000
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"codes": {
|
|
3
|
+
"boundary.imports_io": [
|
|
4
|
+
"module",
|
|
5
|
+
"imported"
|
|
6
|
+
],
|
|
7
|
+
"boundary.no_domain_folder": [],
|
|
8
|
+
"boundary.no_modules": [],
|
|
9
|
+
"determinism.differs": [],
|
|
10
|
+
"determinism.not_checkable": [],
|
|
11
|
+
"determinism.timeout": [
|
|
12
|
+
"seconds"
|
|
13
|
+
],
|
|
14
|
+
"manifest.missing_keys": [
|
|
15
|
+
"keys"
|
|
16
|
+
],
|
|
17
|
+
"runner.crashed": [
|
|
18
|
+
"error",
|
|
19
|
+
"detail"
|
|
20
|
+
],
|
|
21
|
+
"symbol.import_failed": [
|
|
22
|
+
"symbol",
|
|
23
|
+
"error",
|
|
24
|
+
"detail"
|
|
25
|
+
],
|
|
26
|
+
"symbol.member_missing": [
|
|
27
|
+
"symbol",
|
|
28
|
+
"member"
|
|
29
|
+
],
|
|
30
|
+
"symbol.not_dataclass": [
|
|
31
|
+
"symbol"
|
|
32
|
+
],
|
|
33
|
+
"symbol.not_enum": [
|
|
34
|
+
"symbol"
|
|
35
|
+
],
|
|
36
|
+
"symbol.not_exception": [
|
|
37
|
+
"symbol"
|
|
38
|
+
],
|
|
39
|
+
"symbol.not_frozen": [
|
|
40
|
+
"symbol"
|
|
41
|
+
],
|
|
42
|
+
"symbol.not_protocol_or_abc": [
|
|
43
|
+
"symbol"
|
|
44
|
+
],
|
|
45
|
+
"tests.failed": [
|
|
46
|
+
"detail"
|
|
47
|
+
],
|
|
48
|
+
"tests.no_files": [],
|
|
49
|
+
"tests.no_invariant_test": [
|
|
50
|
+
"errors"
|
|
51
|
+
],
|
|
52
|
+
"tests.none_collected": [],
|
|
53
|
+
"tests.timeout": [
|
|
54
|
+
"seconds"
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
}
|