python-hwpx-automation 6.0.3__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.
- hwpx_automation/__init__.py +61 -0
- hwpx_automation/__init__.pyi +27 -0
- hwpx_automation/__main__.py +8 -0
- hwpx_automation/agent_document.py +392 -0
- hwpx_automation/api.py +136 -0
- hwpx_automation/blind_eval.py +407 -0
- hwpx_automation/capabilities.py +110 -0
- hwpx_automation/compat.py +48 -0
- hwpx_automation/configuration.py +60 -0
- hwpx_automation/core/__init__.py +2 -0
- hwpx_automation/core/content.py +762 -0
- hwpx_automation/core/context.py +111 -0
- hwpx_automation/core/diff.py +53 -0
- hwpx_automation/core/document.py +37 -0
- hwpx_automation/core/formatting.py +513 -0
- hwpx_automation/core/handles.py +24 -0
- hwpx_automation/core/locations.py +205 -0
- hwpx_automation/core/locator.py +162 -0
- hwpx_automation/core/plan.py +680 -0
- hwpx_automation/core/resources.py +42 -0
- hwpx_automation/core/search.py +296 -0
- hwpx_automation/core/transactions.py +434 -0
- hwpx_automation/core/txn.py +48 -0
- hwpx_automation/document_state.py +95 -0
- hwpx_automation/errors.py +174 -0
- hwpx_automation/execution_lock.py +15 -0
- hwpx_automation/fastmcp_adapter.py +672 -0
- hwpx_automation/form_fill.py +1177 -0
- hwpx_automation/form_output_models.py +223 -0
- hwpx_automation/handlers/__init__.py +2 -0
- hwpx_automation/handlers/_shared.py +377 -0
- hwpx_automation/handlers/agent_document.py +257 -0
- hwpx_automation/handlers/authoring.py +750 -0
- hwpx_automation/handlers/content_edit.py +1078 -0
- hwpx_automation/handlers/form_fill.py +607 -0
- hwpx_automation/handlers/layout_style.py +660 -0
- hwpx_automation/handlers/quality_render.py +566 -0
- hwpx_automation/handlers/read_export.py +1295 -0
- hwpx_automation/handlers/specialized.py +624 -0
- hwpx_automation/handlers/tracked_changes.py +589 -0
- hwpx_automation/handlers/workflow.py +105 -0
- hwpx_automation/hwp_converter.py +227 -0
- hwpx_automation/hwp_support.py +94 -0
- hwpx_automation/hwpx_ops.py +1439 -0
- hwpx_automation/identity.json +263 -0
- hwpx_automation/identity.py +18 -0
- hwpx_automation/ingest_adapters.py +85 -0
- hwpx_automation/markdown_plan.py +216 -0
- hwpx_automation/mcp_cli.py +29 -0
- hwpx_automation/metadata/tools_meta.py +40 -0
- hwpx_automation/mixed_form.py +3007 -0
- hwpx_automation/mutation_models.py +401 -0
- hwpx_automation/network_policy.py +232 -0
- hwpx_automation/office/__init__.py +14 -0
- hwpx_automation/office/agent/__init__.py +125 -0
- hwpx_automation/office/agent/_batch_verification.py +383 -0
- hwpx_automation/office/agent/blueprint/__init__.py +58 -0
- hwpx_automation/office/agent/blueprint/bundle.py +282 -0
- hwpx_automation/office/agent/blueprint/catalog.py +136 -0
- hwpx_automation/office/agent/blueprint/dump.py +520 -0
- hwpx_automation/office/agent/blueprint/mapping.py +312 -0
- hwpx_automation/office/agent/blueprint/model.py +722 -0
- hwpx_automation/office/agent/blueprint/native.py +621 -0
- hwpx_automation/office/agent/blueprint/replay.py +622 -0
- hwpx_automation/office/agent/catalog.py +252 -0
- hwpx_automation/office/agent/cli.py +647 -0
- hwpx_automation/office/agent/commands.py +1383 -0
- hwpx_automation/office/agent/document.py +801 -0
- hwpx_automation/office/agent/form_plan.py +1760 -0
- hwpx_automation/office/agent/model.py +808 -0
- hwpx_automation/office/agent/path.py +155 -0
- hwpx_automation/office/agent/query.py +230 -0
- hwpx_automation/office/agent/story.py +207 -0
- hwpx_automation/office/authoring/__init__.py +3542 -0
- hwpx_automation/office/authoring/advanced_generators.py +154 -0
- hwpx_automation/office/authoring/builder/__init__.py +52 -0
- hwpx_automation/office/authoring/builder/core.py +996 -0
- hwpx_automation/office/authoring/builder/report.py +195 -0
- hwpx_automation/office/authoring/design/__init__.py +30 -0
- hwpx_automation/office/authoring/design/_support.py +144 -0
- hwpx_automation/office/authoring/design/composer.py +282 -0
- hwpx_automation/office/authoring/design/harvest.py +305 -0
- hwpx_automation/office/authoring/design/plan.py +69 -0
- hwpx_automation/office/authoring/design/profile.py +88 -0
- hwpx_automation/office/authoring/design/profiles/application_form/fragments/body.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/application_form/fragments/heading.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/application_form/fragments/info_table.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/application_form/fragments/title.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/application_form/profile.json +25 -0
- hwpx_automation/office/authoring/design/profiles/application_form/template.hwpx +0 -0
- hwpx_automation/office/authoring/design/profiles/home_notice/fragments/body.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/home_notice/fragments/heading.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/home_notice/fragments/title.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/home_notice/profile.json +24 -0
- hwpx_automation/office/authoring/design/profiles/home_notice/template.hwpx +0 -0
- hwpx_automation/office/authoring/design/profiles/official_notice/fragments/body.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/official_notice/fragments/heading.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/official_notice/fragments/info_table.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/official_notice/fragments/title.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/official_notice/profile.json +25 -0
- hwpx_automation/office/authoring/design/profiles/official_notice/template.hwpx +0 -0
- hwpx_automation/office/authoring/design/profiles/report/fragments/body.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/report/fragments/heading.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/report/fragments/info_table.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/report/fragments/title.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/report/profile.json +25 -0
- hwpx_automation/office/authoring/design/profiles/report/template.hwpx +0 -0
- hwpx_automation/office/authoring/design/validator.py +107 -0
- hwpx_automation/office/authoring/presets/__init__.py +22 -0
- hwpx_automation/office/authoring/presets/proposal.py +538 -0
- hwpx_automation/office/authoring/report_parser.py +141 -0
- hwpx_automation/office/authoring/style_profile.py +437 -0
- hwpx_automation/office/authoring/template_analyzer.py +657 -0
- hwpx_automation/office/compliance/__init__.py +38 -0
- hwpx_automation/office/compliance/official_lint.py +478 -0
- hwpx_automation/office/compliance/pii.py +388 -0
- hwpx_automation/office/document_ops/__init__.py +13 -0
- hwpx_automation/office/document_ops/comparison.py +62 -0
- hwpx_automation/office/document_ops/mail_merge.py +73 -0
- hwpx_automation/office/document_ops/redline.py +35 -0
- hwpx_automation/office/evalplan/__init__.py +36 -0
- hwpx_automation/office/evalplan/runtime.py +2762 -0
- hwpx_automation/office/exam/__init__.py +44 -0
- hwpx_automation/office/exam/compose.py +282 -0
- hwpx_automation/office/exam/ir.py +44 -0
- hwpx_automation/office/exam/measure.py +163 -0
- hwpx_automation/office/exam/parser.py +150 -0
- hwpx_automation/office/exam/profile.py +123 -0
- hwpx_automation/office/form_fill/__init__.py +66 -0
- hwpx_automation/office/form_fill/classification.py +108 -0
- hwpx_automation/office/form_fill/fill_residue.py +242 -0
- hwpx_automation/office/form_fill/fit/__init__.py +36 -0
- hwpx_automation/office/form_fill/fit/apply.py +24 -0
- hwpx_automation/office/form_fill/fit/engine.py +24 -0
- hwpx_automation/office/form_fill/fit/measure.py +50 -0
- hwpx_automation/office/form_fill/fit/policy.py +28 -0
- hwpx_automation/office/form_fill/fit/report.py +28 -0
- hwpx_automation/office/form_fill/fit/seal.py +457 -0
- hwpx_automation/office/form_fill/fit/wordbox.py +1343 -0
- hwpx_automation/office/form_fill/guidance.py +704 -0
- hwpx_automation/office/form_fill/quality.py +961 -0
- hwpx_automation/office/form_fill/split_run.py +333 -0
- hwpx_automation/office/form_fill/template_formfit.py +656 -0
- hwpx_automation/office/house_style/__init__.py +196 -0
- hwpx_automation/office/house_style/composition.py +68 -0
- hwpx_automation/office/house_style/data/bank.json +625 -0
- hwpx_automation/office/house_style/data/genres.json +43 -0
- hwpx_automation/office/quality/__init__.py +14 -0
- hwpx_automation/office/quality/page_guard.py +277 -0
- hwpx_automation/office/rendering/__init__.py +145 -0
- hwpx_automation/office/rendering/_hancom_open_rate.ps1 +374 -0
- hwpx_automation/office/rendering/_refresh_hwpx_mac.applescript +162 -0
- hwpx_automation/office/rendering/_render_hwpx.ps1 +72 -0
- hwpx_automation/office/rendering/_render_hwpx_mac.applescript +249 -0
- hwpx_automation/office/rendering/block_splits.py +76 -0
- hwpx_automation/office/rendering/detectors.py +151 -0
- hwpx_automation/office/rendering/diff.py +153 -0
- hwpx_automation/office/rendering/fixture_corpus.py +215 -0
- hwpx_automation/office/rendering/oracle.py +909 -0
- hwpx_automation/office/rendering/page_qa.py +245 -0
- hwpx_automation/office/rendering/qa_contracts.py +293 -0
- hwpx_automation/office/rendering/qa_metrics.py +241 -0
- hwpx_automation/office/rendering/worker.py +290 -0
- hwpx_automation/office/utilities/__init__.py +12 -0
- hwpx_automation/office/utilities/table_compute.py +477 -0
- hwpx_automation/ops_services/__init__.py +1 -0
- hwpx_automation/ops_services/_border_fill.py +283 -0
- hwpx_automation/ops_services/composition.py +55 -0
- hwpx_automation/ops_services/content_layout.py +322 -0
- hwpx_automation/ops_services/context.py +213 -0
- hwpx_automation/ops_services/form_fields.py +557 -0
- hwpx_automation/ops_services/media.py +178 -0
- hwpx_automation/ops_services/memo_style.py +477 -0
- hwpx_automation/ops_services/package_validation.py +166 -0
- hwpx_automation/ops_services/planning.py +201 -0
- hwpx_automation/ops_services/preview_export.py +585 -0
- hwpx_automation/ops_services/read_query.py +601 -0
- hwpx_automation/ops_services/save_policy.py +604 -0
- hwpx_automation/ops_services/tables.py +539 -0
- hwpx_automation/ops_services/transactions.py +616 -0
- hwpx_automation/preview_output_models.py +69 -0
- hwpx_automation/public-modules.json +206 -0
- hwpx_automation/py.typed +1 -0
- hwpx_automation/quality.py +351 -0
- hwpx_automation/quality_generation.py +725 -0
- hwpx_automation/runtime.py +321 -0
- hwpx_automation/runtime_services.py +100 -0
- hwpx_automation/server.py +259 -0
- hwpx_automation/storage.py +747 -0
- hwpx_automation/tool_bindings.py +170 -0
- hwpx_automation/tool_contract.py +982 -0
- hwpx_automation/upstream.py +755 -0
- hwpx_automation/utils/__init__.py +2 -0
- hwpx_automation/utils/helpers.py +29 -0
- hwpx_automation/visual_qa.py +667 -0
- hwpx_automation/workflow/__init__.py +55 -0
- hwpx_automation/workflow/adapters.py +482 -0
- hwpx_automation/workflow/dispatcher.py +213 -0
- hwpx_automation/workflow/models.py +243 -0
- hwpx_automation/workflow/policy.py +198 -0
- hwpx_automation/workflow/render_contracts.py +173 -0
- hwpx_automation/workflow/render_metrics.py +196 -0
- hwpx_automation/workflow/render_queue.py +482 -0
- hwpx_automation/workflow/render_security.py +172 -0
- hwpx_automation/workflow/render_transport.py +369 -0
- hwpx_automation/workflow/rendering.py +206 -0
- hwpx_automation/workflow/service.py +758 -0
- hwpx_automation/workflow/state_machine.py +65 -0
- hwpx_automation/workflow/store.py +747 -0
- hwpx_automation/workspace.py +1694 -0
- python_hwpx_automation-6.0.3.dist-info/METADATA +279 -0
- python_hwpx_automation-6.0.3.dist-info/RECORD +217 -0
- python_hwpx_automation-6.0.3.dist-info/WHEEL +5 -0
- python_hwpx_automation-6.0.3.dist-info/entry_points.txt +3 -0
- python_hwpx_automation-6.0.3.dist-info/licenses/LICENSE +178 -0
- python_hwpx_automation-6.0.3.dist-info/licenses/NOTICE +14 -0
- python_hwpx_automation-6.0.3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Versioned, hash-verified page-only fixture corpus loader."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from .qa_contracts import (
|
|
12
|
+
TAXONOMY_VERSION,
|
|
13
|
+
DefectCategory,
|
|
14
|
+
FindingSeverity,
|
|
15
|
+
NormalizedBBox,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
FIXTURE_MANIFEST_SCHEMA = "hwpx.visual-fixture-manifest/v1"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def sha256_file(path: str | Path) -> str:
|
|
23
|
+
digest = hashlib.sha256()
|
|
24
|
+
with Path(path).open("rb") as handle:
|
|
25
|
+
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
|
|
26
|
+
digest.update(chunk)
|
|
27
|
+
return digest.hexdigest()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True)
|
|
31
|
+
class FixtureAnnotation:
|
|
32
|
+
page: int
|
|
33
|
+
category: DefectCategory
|
|
34
|
+
severity: FindingSeverity
|
|
35
|
+
bbox: NormalizedBBox
|
|
36
|
+
labelers: tuple[str, ...]
|
|
37
|
+
label_status: str
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass(frozen=True)
|
|
41
|
+
class FixturePage:
|
|
42
|
+
page: int
|
|
43
|
+
path: Path
|
|
44
|
+
sha256: str
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass(frozen=True)
|
|
48
|
+
class FixtureCase:
|
|
49
|
+
case_id: str
|
|
50
|
+
classification: str
|
|
51
|
+
pages: tuple[FixturePage, ...]
|
|
52
|
+
annotations: tuple[FixtureAnnotation, ...]
|
|
53
|
+
provenance: dict[str, Any]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass(frozen=True)
|
|
57
|
+
class FixtureCorpus:
|
|
58
|
+
root: Path
|
|
59
|
+
schema: str
|
|
60
|
+
taxonomy_version: str
|
|
61
|
+
assurance: str
|
|
62
|
+
cases: tuple[FixtureCase, ...]
|
|
63
|
+
|
|
64
|
+
def receipt(self, case: FixtureCase) -> dict[str, Any]:
|
|
65
|
+
"""Return honest fixture evidence; it cannot claim a Hancom render."""
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
"receiptVersion": "hwpx.fixture-render-receipt/v1",
|
|
69
|
+
"renderSource": "deterministic_fixture",
|
|
70
|
+
"assurance": "fixture",
|
|
71
|
+
"renderChecked": False,
|
|
72
|
+
"realHancom": False,
|
|
73
|
+
"caseId": case.case_id,
|
|
74
|
+
"pages": [
|
|
75
|
+
{"page": page.page, "sha256": page.sha256, "path": page.path.name}
|
|
76
|
+
for page in case.pages
|
|
77
|
+
],
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _validate_fixture_manifest_header(raw: dict[str, Any]) -> None:
|
|
82
|
+
if raw.get("schema") != FIXTURE_MANIFEST_SCHEMA:
|
|
83
|
+
raise ValueError(f"unsupported fixture manifest schema: {raw.get('schema')!r}")
|
|
84
|
+
if raw.get("assurance") != "fixture":
|
|
85
|
+
raise ValueError("fixture manifest assurance must be exactly 'fixture'")
|
|
86
|
+
if raw.get("taxonomyVersion") != TAXONOMY_VERSION:
|
|
87
|
+
raise ValueError(f"unsupported taxonomy version: {raw.get('taxonomyVersion')!r}")
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _load_fixture_pages(
|
|
91
|
+
item: dict[str, Any], case_id: str, root: Path, verify_hashes: bool
|
|
92
|
+
) -> tuple[list[FixturePage], set[int]]:
|
|
93
|
+
pages: list[FixturePage] = []
|
|
94
|
+
page_numbers: set[int] = set()
|
|
95
|
+
for page_raw in item.get("pages", []):
|
|
96
|
+
page_no = int(page_raw["page"])
|
|
97
|
+
if page_no in page_numbers:
|
|
98
|
+
raise ValueError(f"duplicate page {page_no} in {case_id}")
|
|
99
|
+
page_numbers.add(page_no)
|
|
100
|
+
page_path = (root / str(page_raw["path"])).resolve()
|
|
101
|
+
if not page_path.is_relative_to(root) or page_path.suffix.lower() != ".png":
|
|
102
|
+
raise ValueError("fixture pages must be PNG files inside the corpus root")
|
|
103
|
+
expected_hash = str(page_raw["sha256"])
|
|
104
|
+
if verify_hashes and sha256_file(page_path) != expected_hash:
|
|
105
|
+
raise ValueError(f"fixture page hash mismatch: {page_path.name}")
|
|
106
|
+
pages.append(FixturePage(page_no, page_path, expected_hash))
|
|
107
|
+
if not pages:
|
|
108
|
+
raise ValueError(f"fixture case has no pages: {case_id}")
|
|
109
|
+
return pages, page_numbers
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _load_fixture_annotations(
|
|
113
|
+
item: dict[str, Any], case_id: str, page_numbers: set[int]
|
|
114
|
+
) -> list[FixtureAnnotation]:
|
|
115
|
+
annotations: list[FixtureAnnotation] = []
|
|
116
|
+
for ann in item.get("annotations", []):
|
|
117
|
+
page_no = int(ann["page"])
|
|
118
|
+
if page_no not in page_numbers:
|
|
119
|
+
raise ValueError(f"annotation references missing page {page_no} in {case_id}")
|
|
120
|
+
bbox = ann["bbox"]
|
|
121
|
+
labelers = tuple(str(value) for value in ann.get("labelers", []))
|
|
122
|
+
label_status = str(ann.get("labelStatus", "pending"))
|
|
123
|
+
if label_status not in {"pending", "disagreement", "adjudicated"}:
|
|
124
|
+
raise ValueError("unsupported annotation label status")
|
|
125
|
+
if label_status == "adjudicated" and len(set(labelers)) < 2:
|
|
126
|
+
raise ValueError("adjudicated ground truth requires two independent labelers")
|
|
127
|
+
annotations.append(
|
|
128
|
+
FixtureAnnotation(
|
|
129
|
+
page_no,
|
|
130
|
+
DefectCategory(str(ann["category"])),
|
|
131
|
+
FindingSeverity(str(ann["severity"])),
|
|
132
|
+
NormalizedBBox(*map(float, bbox)),
|
|
133
|
+
labelers,
|
|
134
|
+
label_status,
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
return annotations
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _validate_fixture_classification_annotations(
|
|
141
|
+
classification: str, annotations: list[FixtureAnnotation]
|
|
142
|
+
) -> None:
|
|
143
|
+
if classification == "clean" and annotations:
|
|
144
|
+
raise ValueError("clean fixture cannot contain defect annotations")
|
|
145
|
+
if classification != "clean" and not annotations:
|
|
146
|
+
raise ValueError("defect fixture must contain ground-truth annotations")
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _validate_fixture_case_provenance(
|
|
150
|
+
item: dict[str, Any], root: Path, verify_hashes: bool
|
|
151
|
+
) -> dict[str, Any]:
|
|
152
|
+
provenance = dict(item.get("provenance", {}))
|
|
153
|
+
source_document = provenance.get("sourceDocument")
|
|
154
|
+
source_hash = provenance.get("sourceSha256")
|
|
155
|
+
if source_document or source_hash:
|
|
156
|
+
if not isinstance(source_document, str) or not isinstance(source_hash, str):
|
|
157
|
+
raise ValueError("historical provenance requires sourceDocument and sourceSha256")
|
|
158
|
+
source_path = (root / source_document).resolve()
|
|
159
|
+
if verify_hashes and sha256_file(source_path) != source_hash:
|
|
160
|
+
raise ValueError(f"historical source hash mismatch: {source_path.name}")
|
|
161
|
+
return provenance
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _load_one_fixture_case(
|
|
165
|
+
item: dict[str, Any], root: Path, seen: set[str], verify_hashes: bool
|
|
166
|
+
) -> FixtureCase:
|
|
167
|
+
case_id = str(item["id"])
|
|
168
|
+
if case_id in seen:
|
|
169
|
+
raise ValueError(f"duplicate fixture case id: {case_id}")
|
|
170
|
+
seen.add(case_id)
|
|
171
|
+
classification = str(item["classification"])
|
|
172
|
+
if classification not in {"clean", "natural", "injected"}:
|
|
173
|
+
raise ValueError(f"unsupported fixture classification: {classification}")
|
|
174
|
+
pages, page_numbers = _load_fixture_pages(item, case_id, root, verify_hashes)
|
|
175
|
+
annotations = _load_fixture_annotations(item, case_id, page_numbers)
|
|
176
|
+
_validate_fixture_classification_annotations(classification, annotations)
|
|
177
|
+
provenance = _validate_fixture_case_provenance(item, root, verify_hashes)
|
|
178
|
+
return FixtureCase(
|
|
179
|
+
case_id,
|
|
180
|
+
classification,
|
|
181
|
+
tuple(sorted(pages, key=lambda page: page.page)),
|
|
182
|
+
tuple(annotations),
|
|
183
|
+
provenance,
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def load_fixture_manifest(path: str | Path, *, verify_hashes: bool = True) -> FixtureCorpus:
|
|
188
|
+
manifest_path = Path(path).resolve()
|
|
189
|
+
root = manifest_path.parent
|
|
190
|
+
raw = json.loads(manifest_path.read_text(encoding="utf-8"))
|
|
191
|
+
_validate_fixture_manifest_header(raw)
|
|
192
|
+
cases: list[FixtureCase] = []
|
|
193
|
+
seen: set[str] = set()
|
|
194
|
+
for item in raw.get("cases", []):
|
|
195
|
+
cases.append(_load_one_fixture_case(item, root, seen, verify_hashes))
|
|
196
|
+
if not cases:
|
|
197
|
+
raise ValueError("fixture manifest contains no cases")
|
|
198
|
+
return FixtureCorpus(
|
|
199
|
+
root=root,
|
|
200
|
+
schema=FIXTURE_MANIFEST_SCHEMA,
|
|
201
|
+
taxonomy_version=str(raw["taxonomyVersion"]),
|
|
202
|
+
assurance="fixture",
|
|
203
|
+
cases=tuple(cases),
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
__all__ = [
|
|
208
|
+
"FIXTURE_MANIFEST_SCHEMA",
|
|
209
|
+
"FixtureAnnotation",
|
|
210
|
+
"FixturePage",
|
|
211
|
+
"FixtureCase",
|
|
212
|
+
"FixtureCorpus",
|
|
213
|
+
"load_fixture_manifest",
|
|
214
|
+
"sha256_file",
|
|
215
|
+
]
|