Validate a KGM file.
This script performs both SHACL validation to ensure the Manifest is valid according to the KGM
specification (see https://prez.dev/manifest/) and checks that all the resources indicated by the Manifest
- whether local files/folders or remote resources on the Internet - are reachable.
~$ python validate.py {MANIFEST_FILE_PATH}
validate(manifest: Path) -> Graph
Validates a manifest and any assets listed in it with a conformance claim.
Uses validators known in the Semantic Background or supplied by the user.
Parameters:
| Name |
Type |
Description |
Default |
manifest
|
Path
|
|
required
|
Returns:
| Type |
Description |
Graph
|
Graph of validated manifest
|
Source code in kgm/validator.py
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150 | def validate(manifest: Path) -> Graph:
"""Validates a manifest and any assets listed in it with a conformance claim.
Uses validators known in the Semantic Background or supplied by the user.
Args:
manifest: path to a manifest file
Returns:
Graph of validated manifest
"""
if not Path(manifest).is_file():
raise ManifestValidationError("Manifest file does not exist")
# can't use get_manifest_paths_and_graph() here as that function uses validate()
manifest_path = manifest
manifest_root = Path(manifest).parent.resolve()
manifest_graph = load_graph(manifest)
ME = Path(__file__)
def literal_resolves_as_file_folder_or_url(lit: Literal):
l_str = str(lit)
if l_str.startswith("http") and "://" in l_str:
r = httpx.get(l_str)
if 200 <= r.status_code < 400:
pass
else:
raise ManifestValidationError(
f"Remote content link non-resolving: {l_str}"
)
elif "*" in l_str:
glob_parts = l_str.split("*")
dir = Path(manifest_root / Path(glob_parts[0]))
if not Path(dir).is_dir():
raise ManifestValidationError(
f"The content link {l_str} is not a directory"
)
else:
# It must be a local
if not (manifest_root / l_str).is_file():
raise ManifestValidationError(
f"Content link {manifest_root / l_str} is invalid - not a file"
)
# validate the manifest
if not kurra.shacl.check_validator_known("https://prez.dev/manifest-validator"):
kurra.shacl.sync_validators()
v = kurra.shacl.validate(
[manifest_graph, load_graph(ME.parent / "mrr.ttl")],
"https://prez.dev/manifest-validator",
)
if not v[0]:
raise ManifestValidationError(f"The manifest file is invalid:\n\n{v[2]}")
# get the background graph for merging into artifact graphs for validation
background_graph = get_background_graph(manifest)
# validate each resource with a conformance claim
# check all conformance claims validators indicated by IRI are known
# if any ar unknown, force a validator sync
for validator in manifest_graph.objects(subject=None, predicate=DCTERMS.conformsTo):
if not kurra.shacl.check_validator_known(str(validator)):
kurra.shacl.sync_validators()
# recheck all, in case we had a sync
for validator in manifest_graph.objects(subject=None, predicate=DCTERMS.conformsTo):
if str(validator).startswith("http"):
if not kurra.shacl.check_validator_known(str(validator)):
raise ManifestValidationError(
f"The validator <{validator}> indicated in the manifest file is not known to the Semantic Background"
)
# validate each file referenced in the Manifest
for s, resource in manifest_graph.subject_objects(PROF.hasResource):
for artifact in manifest_graph.objects(resource, PROF.hasArtifact):
if isinstance(artifact, BNode):
content_location = manifest_graph.value(
subject=artifact, predicate=SDO.contentLocation
)
# main_entity = manifest_graph.value(subject=artifact, predicate=SDO.mainEntity)
else:
content_location = artifact
# ensure the artifact resolves
literal_resolves_as_file_folder_or_url(content_location)
# validate each file in the artifact
for file in get_files_from_artifact(
(manifest_path, manifest_root, manifest_graph), content_location
):
# if there is a conformance claim validator for this artifact, use it
validator = manifest_graph.value(
subject=artifact, predicate=DCTERMS.conformsTo
)
# if not, check if the resource has one to use
if validator is None:
validator = manifest_graph.value(
subject=resource, predicate=DCTERMS.conformsTo
)
if validator is not None:
try:
data_graph = load_graph(manifest_root / file)
except SyntaxError as e:
raise SyntaxError(f"Failed to load {file}: {e}")
# all validators indicated in the Manifest will have been confirmed known at this point
# or are supplied
if isinstance(validator, URIRef):
pass
else: # must be a local file
validator = manifest_root / str(validator)
v = kurra.shacl.validate(
[data_graph, background_graph], str(validator)
)
if not v[0]:
raise ManifestValidationError(
f"the artifact {manifest_root / file} is invalid according to validator {validator}:\n\n{v[2]}"
)
return manifest_graph
|