linkml 1.8.5__py3-none-any.whl → 1.8.6__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.
- linkml/cli/main.py +2 -0
- linkml/generators/common/build.py +1 -2
- linkml/generators/common/lifecycle.py +18 -2
- linkml/generators/dbmlgen.py +173 -0
- linkml/generators/erdiagramgen.py +1 -0
- linkml/generators/jsonldcontextgen.py +7 -1
- linkml/generators/jsonschemagen.py +73 -53
- linkml/generators/linkmlgen.py +13 -1
- linkml/generators/owlgen.py +11 -1
- linkml/generators/plantumlgen.py +17 -10
- linkml/generators/pydanticgen/array.py +21 -61
- linkml/generators/pydanticgen/templates/attribute.py.jinja +1 -1
- linkml/generators/shaclgen.py +16 -5
- linkml/generators/typescriptgen.py +3 -1
- linkml/linter/rules.py +3 -1
- linkml/utils/converter.py +17 -0
- linkml/utils/deprecation.py +10 -0
- linkml/utils/helpers.py +65 -0
- linkml/utils/validation.py +2 -1
- linkml/validator/__init__.py +2 -2
- linkml/validator/plugins/jsonschema_validation_plugin.py +1 -0
- linkml/validator/report.py +4 -1
- linkml/validator/validator.py +4 -4
- linkml/validators/jsonschemavalidator.py +10 -0
- linkml/validators/sparqlvalidator.py +7 -0
- linkml/workspaces/example_runner.py +20 -1
- {linkml-1.8.5.dist-info → linkml-1.8.6.dist-info}/METADATA +2 -2
- {linkml-1.8.5.dist-info → linkml-1.8.6.dist-info}/RECORD +31 -30
- {linkml-1.8.5.dist-info → linkml-1.8.6.dist-info}/entry_points.txt +1 -0
- {linkml-1.8.5.dist-info → linkml-1.8.6.dist-info}/LICENSE +0 -0
- {linkml-1.8.5.dist-info → linkml-1.8.6.dist-info}/WHEEL +0 -0
@@ -1,7 +1,15 @@
|
|
1
1
|
import sys
|
2
2
|
from abc import ABC, abstractmethod
|
3
3
|
from enum import Enum
|
4
|
-
from typing import
|
4
|
+
from typing import (
|
5
|
+
ClassVar,
|
6
|
+
Iterable,
|
7
|
+
List,
|
8
|
+
Optional,
|
9
|
+
Type,
|
10
|
+
TypeVar,
|
11
|
+
Union,
|
12
|
+
)
|
5
13
|
|
6
14
|
from linkml_runtime.linkml_model import Element
|
7
15
|
from linkml_runtime.linkml_model.meta import ArrayExpression, DimensionExpression
|
@@ -9,20 +17,15 @@ from pydantic import VERSION as PYDANTIC_VERSION
|
|
9
17
|
|
10
18
|
from linkml.utils.deprecation import deprecation_warning
|
11
19
|
|
12
|
-
if int(PYDANTIC_VERSION[0])
|
13
|
-
from pydantic_core import core_schema
|
14
|
-
else:
|
20
|
+
if int(PYDANTIC_VERSION[0]) < 2:
|
15
21
|
# Support for having pydantic 1 installed in the same environment will be dropped in 1.9.0
|
16
22
|
deprecation_warning("pydantic-v1")
|
17
23
|
|
18
|
-
if
|
19
|
-
from
|
20
|
-
from pydantic_core import CoreSchema
|
21
|
-
|
22
|
-
if sys.version_info.minor <= 8:
|
23
|
-
from typing_extensions import Annotated
|
24
|
+
if sys.version_info.minor < 12:
|
25
|
+
from typing_extensions import TypeAliasType
|
24
26
|
else:
|
25
|
-
from typing import
|
27
|
+
from typing import TypeAliasType
|
28
|
+
|
26
29
|
|
27
30
|
from linkml.generators.pydanticgen.build import RangeResult
|
28
31
|
from linkml.generators.pydanticgen.template import ConditionalImport, Import, Imports, ObjectImport
|
@@ -37,75 +40,32 @@ class ArrayRepresentation(Enum):
|
|
37
40
|
_BOUNDED_ARRAY_FIELDS = ("exact_number_dimensions", "minimum_number_dimensions", "maximum_number_dimensions")
|
38
41
|
|
39
42
|
_T = TypeVar("_T")
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
class AnyShapeArrayType(Generic[_T]):
|
44
|
-
@classmethod
|
45
|
-
def __get_pydantic_core_schema__(cls, source_type: Any, handler: "GetCoreSchemaHandler") -> "CoreSchema":
|
46
|
-
# double-nested parameterized types here
|
47
|
-
# source_type: List[Union[T,List[...]]]
|
48
|
-
item_type = (Any,) if get_args(get_args(source_type)[0])[0] is _T else get_args(get_args(source_type)[0])[:-1]
|
49
|
-
|
50
|
-
if len(item_type) == 1:
|
51
|
-
item_schema = handler.generate_schema(item_type[0])
|
52
|
-
else:
|
53
|
-
item_schema = core_schema.union_schema([handler.generate_schema(i) for i in item_type])
|
54
|
-
|
55
|
-
if all([getattr(i, "__module__", "") == "builtins" and i is not Any for i in item_type]):
|
56
|
-
item_schema["strict"] = True
|
57
|
-
|
58
|
-
# Before python 3.11, `Any` type was a special object without a __name__
|
59
|
-
item_name = "_".join(["Any" if i is Any else i.__name__ for i in item_type])
|
60
|
-
|
61
|
-
array_ref = f"any-shape-array-{item_name}"
|
62
|
-
|
63
|
-
schema = core_schema.definitions_schema(
|
64
|
-
core_schema.list_schema(core_schema.definition_reference_schema(array_ref)),
|
65
|
-
[
|
66
|
-
core_schema.union_schema(
|
67
|
-
[
|
68
|
-
core_schema.list_schema(core_schema.definition_reference_schema(array_ref)),
|
69
|
-
item_schema,
|
70
|
-
],
|
71
|
-
ref=array_ref,
|
72
|
-
)
|
73
|
-
],
|
74
|
-
)
|
75
|
-
|
76
|
-
return schema
|
77
|
-
|
78
|
-
|
79
|
-
AnyShapeArray = Annotated[_RecursiveListType, AnyShapeArrayType]
|
43
|
+
AnyShapeArray = TypeAliasType("AnyShapeArray", Iterable[Union[_T, Iterable["AnyShapeArray[_T]"]]], type_params=(_T,))
|
80
44
|
|
81
45
|
_AnyShapeArrayImports = (
|
82
46
|
Imports()
|
83
47
|
+ Import(
|
84
48
|
module="typing",
|
85
49
|
objects=[
|
86
|
-
ObjectImport(name="Generic"),
|
87
50
|
ObjectImport(name="Iterable"),
|
88
51
|
ObjectImport(name="TypeVar"),
|
89
52
|
ObjectImport(name="Union"),
|
90
|
-
ObjectImport(name="get_args"),
|
91
53
|
],
|
92
54
|
)
|
93
55
|
+ ConditionalImport(
|
94
|
-
condition="sys.version_info.minor
|
56
|
+
condition="sys.version_info.minor >= 12",
|
95
57
|
module="typing",
|
96
|
-
objects=[ObjectImport(name="
|
97
|
-
alternative=Import(module="typing_extensions", objects=[ObjectImport(name="
|
58
|
+
objects=[ObjectImport(name="TypeAliasType")],
|
59
|
+
alternative=Import(module="typing_extensions", objects=[ObjectImport(name="TypeAliasType")]),
|
98
60
|
)
|
99
|
-
+ Import(module="pydantic", objects=[ObjectImport(name="GetCoreSchemaHandler")])
|
100
|
-
+ Import(module="pydantic_core", objects=[ObjectImport(name="CoreSchema"), ObjectImport(name="core_schema")])
|
101
61
|
)
|
102
62
|
|
103
63
|
# annotated types are special and inspect.getsource() can't stringify them
|
104
64
|
_AnyShapeArrayInjects = [
|
105
65
|
'_T = TypeVar("_T")',
|
106
|
-
|
107
|
-
|
108
|
-
|
66
|
+
"""AnyShapeArray = TypeAliasType(
|
67
|
+
"AnyShapeArray", Iterable[Union[_T, Iterable["AnyShapeArray[_T]"]]], type_params=(_T,)
|
68
|
+
)""",
|
109
69
|
]
|
110
70
|
|
111
71
|
_ConListImports = Imports() + Import(module="pydantic", objects=[ObjectImport(name="conlist")])
|
linkml/generators/shaclgen.py
CHANGED
@@ -116,10 +116,20 @@ class ShaclGenerator(Generator):
|
|
116
116
|
order += 1
|
117
117
|
prop_pv_literal(SH.name, s.title)
|
118
118
|
prop_pv_literal(SH.description, s.description)
|
119
|
-
|
120
|
-
|
121
|
-
|
119
|
+
# minCount
|
120
|
+
if s.minimum_cardinality:
|
121
|
+
prop_pv_literal(SH.minCount, s.minimum_cardinality)
|
122
|
+
elif s.exact_cardinality:
|
123
|
+
prop_pv_literal(SH.minCount, s.exact_cardinality)
|
124
|
+
elif s.required:
|
122
125
|
prop_pv_literal(SH.minCount, 1)
|
126
|
+
# maxCount
|
127
|
+
if s.maximum_cardinality:
|
128
|
+
prop_pv_literal(SH.maxCount, s.maximum_cardinality)
|
129
|
+
elif s.exact_cardinality:
|
130
|
+
prop_pv_literal(SH.maxCount, s.exact_cardinality)
|
131
|
+
elif not s.multivalued:
|
132
|
+
prop_pv_literal(SH.maxCount, 1)
|
123
133
|
prop_pv_literal(SH.minInclusive, s.minimum_value)
|
124
134
|
prop_pv_literal(SH.maxInclusive, s.maximum_value)
|
125
135
|
|
@@ -198,8 +208,6 @@ class ShaclGenerator(Generator):
|
|
198
208
|
add_simple_data_type(prop_pv, r)
|
199
209
|
if s.pattern:
|
200
210
|
prop_pv(SH.pattern, Literal(s.pattern))
|
201
|
-
if s.annotations and self.include_annotations:
|
202
|
-
self._add_annotations(prop_pv, s)
|
203
211
|
if s.equals_string:
|
204
212
|
# Map equal_string and equal_string_in to sh:in
|
205
213
|
self._and_equals_string(g, prop_pv, [s.equals_string])
|
@@ -207,6 +215,9 @@ class ShaclGenerator(Generator):
|
|
207
215
|
# Map equal_string and equal_string_in to sh:in
|
208
216
|
self._and_equals_string(g, prop_pv, s.equals_string_in)
|
209
217
|
|
218
|
+
if s.annotations and self.include_annotations:
|
219
|
+
self._add_annotations(prop_pv, s)
|
220
|
+
|
210
221
|
default_value = ifabsent_processor.process_slot(s, c)
|
211
222
|
if default_value:
|
212
223
|
prop_pv(SH.defaultValue, default_value)
|
@@ -280,7 +280,9 @@ def cli(yamlfile, gen_type_utils=False, include_induced_slots=False, output=None
|
|
280
280
|
gen = TypescriptGenerator(
|
281
281
|
yamlfile, gen_type_utils=gen_type_utils, include_induced_slots=include_induced_slots, **args
|
282
282
|
)
|
283
|
-
gen.serialize(output=output)
|
283
|
+
serialized = gen.serialize(output=output)
|
284
|
+
if output is None:
|
285
|
+
print(serialized)
|
284
286
|
|
285
287
|
|
286
288
|
if __name__ == "__main__":
|
linkml/linter/rules.py
CHANGED
@@ -134,7 +134,9 @@ class TreeRootClassRule(LinterRule):
|
|
134
134
|
if self.config.validate_existing_class_name:
|
135
135
|
for tree_root in tree_roots:
|
136
136
|
if str(tree_root.name) != self.config.root_class_name:
|
137
|
-
yield LinterProblem(message=f"Tree root class has name '{tree_root.name}'")
|
137
|
+
yield LinterProblem(message=f"Tree root class has an invalid name '{tree_root.name}'")
|
138
|
+
if len(tree_roots) > 1:
|
139
|
+
yield LinterProblem("Schema has more than one class with `tree_root: true`")
|
138
140
|
else:
|
139
141
|
if fix:
|
140
142
|
container = ClassDefinition(self.config.root_class_name, tree_root=True)
|
linkml/utils/converter.py
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
import logging
|
2
2
|
import os
|
3
|
+
import pathlib
|
3
4
|
import sys
|
4
5
|
|
5
6
|
import click
|
7
|
+
import yaml
|
6
8
|
from linkml_runtime.linkml_model import Prefix
|
7
9
|
from linkml_runtime.utils import inference_utils
|
8
10
|
from linkml_runtime.utils.compile_python import compile_python
|
@@ -55,6 +57,7 @@ logger = logging.getLogger(__name__)
|
|
55
57
|
@click.option("--index-slot", "-S", help="top level slot. Required for CSV dumping/loading")
|
56
58
|
@click.option("--schema", "-s", help="Path to schema specified as LinkML yaml")
|
57
59
|
@click.option("--prefix", "-P", multiple=True, help="Prefixmap base=URI pairs")
|
60
|
+
@click.option("--prefix-file", help="Path to yaml file containing base=URI pairs")
|
58
61
|
@click.option(
|
59
62
|
"--validate/--no-validate",
|
60
63
|
default=True,
|
@@ -79,6 +82,7 @@ def cli(
|
|
79
82
|
input_format=None,
|
80
83
|
output_format=None,
|
81
84
|
prefix=None,
|
85
|
+
prefix_file=None,
|
82
86
|
target_class_from_path=None,
|
83
87
|
schema=None,
|
84
88
|
validate=None,
|
@@ -99,6 +103,8 @@ def cli(
|
|
99
103
|
|
100
104
|
For more information, see https://linkml.io/linkml/data/index.html
|
101
105
|
"""
|
106
|
+
if prefix and prefix_file is not None:
|
107
|
+
raise Exception("Either set prefix OR prefix_file, not both.")
|
102
108
|
if prefix is None:
|
103
109
|
prefix = []
|
104
110
|
if module is None:
|
@@ -113,6 +119,17 @@ def cli(
|
|
113
119
|
for p in prefix:
|
114
120
|
base, uri = p.split("=")
|
115
121
|
prefix_map[base] = uri
|
122
|
+
if prefix_file is not None:
|
123
|
+
prefix_path = pathlib.Path(prefix_file).resolve()
|
124
|
+
if not prefix_path.exists():
|
125
|
+
raise Exception(f"Path {prefix_file} to prefix map does not exists.")
|
126
|
+
with open(prefix_path, "r") as prefix_stream:
|
127
|
+
raw_prefix_map = yaml.safe_load(prefix_stream)
|
128
|
+
prefix_file_map = raw_prefix_map.get("prefixes", None)
|
129
|
+
if prefix_file_map is None:
|
130
|
+
raise Exception("Provided prefix file does not contain the prefixes key.")
|
131
|
+
prefix_map = prefix_file_map
|
132
|
+
|
116
133
|
if schema is not None:
|
117
134
|
sv = SchemaView(schema)
|
118
135
|
if prefix_map:
|
linkml/utils/deprecation.py
CHANGED
@@ -222,6 +222,16 @@ DEPRECATIONS = (
|
|
222
222
|
recommendation="Update dependent packages to use pydantic>=2",
|
223
223
|
issue=1925,
|
224
224
|
),
|
225
|
+
Deprecation(
|
226
|
+
name="validators",
|
227
|
+
deprecated_in=SemVer.from_str("1.8.6"),
|
228
|
+
removed_in=SemVer.from_str("1.9.0"),
|
229
|
+
message=(
|
230
|
+
"linkml.validators and linkml.utils.validation are the older versions "
|
231
|
+
"of linkml.validator and have unmaintained, duplicated functionality"
|
232
|
+
),
|
233
|
+
recommendation="Update to use linkml.validator",
|
234
|
+
),
|
225
235
|
) # type: tuple[Deprecation, ...]
|
226
236
|
|
227
237
|
EMITTED = set() # type: set[str]
|
linkml/utils/helpers.py
CHANGED
@@ -1,4 +1,13 @@
|
|
1
1
|
import re
|
2
|
+
from functools import lru_cache
|
3
|
+
from typing import List, Tuple, Union
|
4
|
+
|
5
|
+
from linkml_runtime import SchemaView
|
6
|
+
from linkml_runtime.linkml_model.meta import (
|
7
|
+
ClassDefinition,
|
8
|
+
ElementName,
|
9
|
+
SlotDefinition,
|
10
|
+
)
|
2
11
|
|
3
12
|
|
4
13
|
def remove_duplicates(lst):
|
@@ -14,3 +23,59 @@ def write_to_file(file_path, data, mode="w", encoding="utf-8"):
|
|
14
23
|
def convert_to_snake_case(str):
|
15
24
|
str = re.sub(r"(?<=[a-z])(?=[A-Z])|[^a-zA-Z]", " ", str).strip().replace(" ", "_")
|
16
25
|
return "".join(str.lower())
|
26
|
+
|
27
|
+
|
28
|
+
@lru_cache(None)
|
29
|
+
def get_range_associated_slots(
|
30
|
+
schemaview: SchemaView, range_class: ClassDefinition
|
31
|
+
) -> Tuple[Union[SlotDefinition, None], Union[SlotDefinition, None], Union[List[SlotDefinition], None]]:
|
32
|
+
if isinstance(range_class, ElementName):
|
33
|
+
range_class = schemaview.get_class(range_class)
|
34
|
+
if range_class is None:
|
35
|
+
return None, None, None
|
36
|
+
|
37
|
+
range_class_id_slot = schemaview.get_identifier_slot(range_class.name, use_key=True)
|
38
|
+
if range_class_id_slot is None:
|
39
|
+
return None, None, None
|
40
|
+
|
41
|
+
non_id_slots = [s for s in schemaview.class_induced_slots(range_class.name) if s.name != range_class_id_slot.name]
|
42
|
+
non_id_required_slots = [s for s in non_id_slots if s.required]
|
43
|
+
|
44
|
+
# Some lists of objects can be serialized as SimpleDicts.
|
45
|
+
# A SimpleDict is serialized as simple key-value pairs where the value is atomic.
|
46
|
+
# The key must be declared as a key, and the value must satisfy one of the following conditions:
|
47
|
+
# 1. The value slot is the only other slot in the object other than the key
|
48
|
+
# 2. The value slot is explicitly annotated as a simple_dict_value
|
49
|
+
# 3. The value slot is the only non-key that is required
|
50
|
+
# See also: https://github.com/linkml/linkml/issues/1250
|
51
|
+
range_simple_dict_value_slot = None
|
52
|
+
if len(non_id_slots) == 1:
|
53
|
+
range_simple_dict_value_slot = non_id_slots[0]
|
54
|
+
elif len(non_id_slots) > 1:
|
55
|
+
candidate_non_id_slots = []
|
56
|
+
for non_id_slot in non_id_slots:
|
57
|
+
if isinstance(non_id_slot.annotations, dict):
|
58
|
+
is_simple_dict_value = non_id_slot.annotations.get("simple_dict_value", False)
|
59
|
+
else:
|
60
|
+
is_simple_dict_value = getattr(non_id_slot.annotations, "simple_dict_value", False)
|
61
|
+
if is_simple_dict_value:
|
62
|
+
candidate_non_id_slots.append(non_id_slot)
|
63
|
+
if len(candidate_non_id_slots) == 1:
|
64
|
+
range_simple_dict_value_slot = candidate_non_id_slots[0]
|
65
|
+
else:
|
66
|
+
candidate_non_id_slots = []
|
67
|
+
for non_id_slot in non_id_slots:
|
68
|
+
if non_id_slot.required:
|
69
|
+
candidate_non_id_slots.append(non_id_slot)
|
70
|
+
if len(candidate_non_id_slots) == 1:
|
71
|
+
range_simple_dict_value_slot = candidate_non_id_slots[0]
|
72
|
+
|
73
|
+
return range_class_id_slot, range_simple_dict_value_slot, non_id_required_slots
|
74
|
+
|
75
|
+
|
76
|
+
def is_simple_dict(schemaview: SchemaView, slot: SlotDefinition) -> bool:
|
77
|
+
if not slot.multivalued or not slot.inlined or slot.inlined_as_list:
|
78
|
+
return False
|
79
|
+
else:
|
80
|
+
_, range_simple_dict_value_slot, _ = get_range_associated_slots(schemaview, slot.range)
|
81
|
+
return range_simple_dict_value_slot is not None
|
linkml/utils/validation.py
CHANGED
@@ -7,6 +7,7 @@ from linkml_runtime.linkml_model import SchemaDefinition
|
|
7
7
|
from linkml_runtime.utils.yamlutils import YAMLRoot
|
8
8
|
|
9
9
|
from linkml.generators.jsonschemagen import JsonSchemaGenerator
|
10
|
+
from linkml.utils.deprecation import deprecation_warning
|
10
11
|
|
11
12
|
|
12
13
|
def _as_dict(inst):
|
@@ -16,7 +17,6 @@ def _as_dict(inst):
|
|
16
17
|
return inst_dict
|
17
18
|
|
18
19
|
|
19
|
-
# Deprecated: use validators module
|
20
20
|
def validate_object(
|
21
21
|
data: YAMLRoot,
|
22
22
|
schema: Union[str, TextIO, SchemaDefinition],
|
@@ -32,6 +32,7 @@ def validate_object(
|
|
32
32
|
:param closed:
|
33
33
|
:return:
|
34
34
|
"""
|
35
|
+
deprecation_warning("validators")
|
35
36
|
if target_class is None:
|
36
37
|
target_class = type(data)
|
37
38
|
inst_dict = _as_dict(data)
|
linkml/validator/__init__.py
CHANGED
@@ -60,7 +60,7 @@ def validate(
|
|
60
60
|
otherwise it should be a ``SchemaDefinition`` instance.
|
61
61
|
:param target_class: Name of the class within the schema to validate
|
62
62
|
against. If ``None``, the class will be inferred from the schema by
|
63
|
-
|
63
|
+
looking for a class with ``tree_root: true``. Defaults to ``None``.
|
64
64
|
:param strict: If ``True``, validation will stop after the first validation
|
65
65
|
error is found, Otherwise all validation problems will be reported.
|
66
66
|
Defaults to ``False``.
|
@@ -99,7 +99,7 @@ def validate_file(
|
|
99
99
|
otherwise it should be a ``SchemaDefinition`` instance.
|
100
100
|
:param target_class: Name of the class within the schema to validate
|
101
101
|
against. If ``None``, the class will be inferred from the schema by
|
102
|
-
|
102
|
+
looking for a class with ``tree_root: true``. Defaults to ``None``.
|
103
103
|
:param strict: If ``True``, validation will stop after the first validation
|
104
104
|
error is found, Otherwise all validation problems will be reported.
|
105
105
|
Defaults to ``False``.
|
linkml/validator/report.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
from enum import Enum
|
2
2
|
from typing import Any, List, Optional
|
3
3
|
|
4
|
-
from pydantic import BaseModel
|
4
|
+
from pydantic import BaseModel, Field
|
5
5
|
|
6
6
|
|
7
7
|
class Severity(str, Enum):
|
@@ -30,6 +30,9 @@ class ValidationResult(BaseModel):
|
|
30
30
|
instantiates: Optional[str] = None
|
31
31
|
context: List[str] = []
|
32
32
|
|
33
|
+
# The source object that caused this validation result
|
34
|
+
source: Any = Field(None, description="The source of this validation result", exclude=True)
|
35
|
+
|
33
36
|
|
34
37
|
class ValidationReport(BaseModel):
|
35
38
|
"""
|
linkml/validator/validator.py
CHANGED
@@ -48,7 +48,7 @@ class Validator:
|
|
48
48
|
:param instance: The instance to validate
|
49
49
|
:param target_class: Name of the class within the schema to validate
|
50
50
|
against. If ``None``, the class will be inferred from the schema by
|
51
|
-
|
51
|
+
looking for a class with ``tree_root: true``. Defaults to ``None``.
|
52
52
|
:return: A validation report
|
53
53
|
:rtype: ValidationReport
|
54
54
|
"""
|
@@ -61,7 +61,7 @@ class Validator:
|
|
61
61
|
which provides the instances to validate
|
62
62
|
:param target_class: Name of the class within the schema to validate
|
63
63
|
against. If ``None``, the class will be inferred from the schema by
|
64
|
-
|
64
|
+
looking for a class with ``tree_root: true``. Defaults to ``None``.
|
65
65
|
:return: A validation report
|
66
66
|
:rtype: ValidationReport
|
67
67
|
"""
|
@@ -73,7 +73,7 @@ class Validator:
|
|
73
73
|
:param instance: The instance to validate
|
74
74
|
:param target_class: Name of the class within the schema to validate
|
75
75
|
against. If ``None``, the class will be inferred from the schema by
|
76
|
-
|
76
|
+
looking for a class with ``tree_root: true``. Defaults to ``None``.
|
77
77
|
:return: Iterator over validation results
|
78
78
|
:rtype: Iterator[ValidationResult]
|
79
79
|
"""
|
@@ -89,7 +89,7 @@ class Validator:
|
|
89
89
|
which provides the instances to validate
|
90
90
|
:param target_class: Name of the class within the schema to validate
|
91
91
|
against. If ``None``, the class will be inferred from the schema by
|
92
|
-
|
92
|
+
looking for a class with ``tree_root: true``. Defaults to ``None``.
|
93
93
|
:return: Iterator over validation results
|
94
94
|
:rtype: Iterator[ValidationResult]
|
95
95
|
"""
|
@@ -18,6 +18,7 @@ from linkml.generators.jsonschemagen import JsonSchemaGenerator
|
|
18
18
|
from linkml.generators.pythongen import PythonGenerator
|
19
19
|
from linkml.utils import datautils
|
20
20
|
from linkml.utils.datavalidator import DataValidator
|
21
|
+
from linkml.utils.deprecation import deprecation_warning
|
21
22
|
|
22
23
|
logger = logging.getLogger(__name__)
|
23
24
|
|
@@ -29,6 +30,7 @@ class HashableSchemaDefinition(SchemaDefinition):
|
|
29
30
|
|
30
31
|
@lru_cache(maxsize=None)
|
31
32
|
def _generate_jsonschema(schema, top_class, closed, include_range_class_descendants):
|
33
|
+
deprecation_warning("validators")
|
32
34
|
logger.debug("Generating JSON Schema")
|
33
35
|
not_closed = not closed
|
34
36
|
return JsonSchemaGenerator(
|
@@ -42,6 +44,7 @@ def _generate_jsonschema(schema, top_class, closed, include_range_class_descenda
|
|
42
44
|
|
43
45
|
class JsonSchemaDataValidatorError(Exception):
|
44
46
|
def __init__(self, validation_messages: List[str]) -> None:
|
47
|
+
deprecation_warning("validators")
|
45
48
|
super().__init__("\n".join(validation_messages))
|
46
49
|
self.validation_messages = validation_messages
|
47
50
|
|
@@ -56,6 +59,7 @@ class JsonSchemaDataValidator(DataValidator):
|
|
56
59
|
_hashable_schema: Union[str, HashableSchemaDefinition] = field(init=False, repr=False)
|
57
60
|
|
58
61
|
def __setattr__(self, __name: str, __value: Any) -> None:
|
62
|
+
deprecation_warning("validators")
|
59
63
|
if __name == "schema":
|
60
64
|
if isinstance(__value, SchemaDefinition):
|
61
65
|
self._hashable_schema = HashableSchemaDefinition(**asdict(__value))
|
@@ -65,6 +69,7 @@ class JsonSchemaDataValidator(DataValidator):
|
|
65
69
|
|
66
70
|
def validate_file(self, input: str, format: str = "json", **kwargs):
|
67
71
|
# return self.validate_object(obj)
|
72
|
+
deprecation_warning("validators")
|
68
73
|
pass
|
69
74
|
|
70
75
|
def validate_object(self, data: YAMLRoot, target_class: Type[YAMLRoot] = None, closed: bool = True) -> None:
|
@@ -76,6 +81,7 @@ class JsonSchemaDataValidator(DataValidator):
|
|
76
81
|
:param closed:
|
77
82
|
:return:
|
78
83
|
"""
|
84
|
+
deprecation_warning("validators")
|
79
85
|
if target_class is None:
|
80
86
|
target_class = type(data)
|
81
87
|
inst_dict = as_simple_dict(data)
|
@@ -90,6 +96,7 @@ class JsonSchemaDataValidator(DataValidator):
|
|
90
96
|
:param closed:
|
91
97
|
:return:
|
92
98
|
"""
|
99
|
+
deprecation_warning("validators")
|
93
100
|
results = list(self.iter_validate_dict(data, target_class, closed))
|
94
101
|
if results:
|
95
102
|
raise JsonSchemaDataValidatorError(results)
|
@@ -97,6 +104,7 @@ class JsonSchemaDataValidator(DataValidator):
|
|
97
104
|
def iter_validate_dict(
|
98
105
|
self, data: dict, target_class_name: ClassDefinitionName = None, closed: bool = True
|
99
106
|
) -> Iterable[str]:
|
107
|
+
deprecation_warning("validators")
|
100
108
|
if self.schema is None:
|
101
109
|
raise ValueError("schema object must be set")
|
102
110
|
if target_class_name is None:
|
@@ -159,6 +167,8 @@ def cli(
|
|
159
167
|
"""
|
160
168
|
Validates instance data
|
161
169
|
"""
|
170
|
+
deprecation_warning("validators")
|
171
|
+
|
162
172
|
if module is None:
|
163
173
|
if schema is None:
|
164
174
|
raise Exception("must pass one of module OR schema")
|
@@ -14,11 +14,13 @@ from linkml.generators.sparqlgen import SparqlGenerator
|
|
14
14
|
from linkml.reporting import CheckResult, Report
|
15
15
|
from linkml.utils.datautils import _get_format, dumpers_loaders, get_dumper
|
16
16
|
from linkml.utils.datavalidator import DataValidator
|
17
|
+
from linkml.utils.deprecation import deprecation_warning
|
17
18
|
|
18
19
|
logger = logging.getLogger(__name__)
|
19
20
|
|
20
21
|
|
21
22
|
def sparqljson2dict(row: dict):
|
23
|
+
deprecation_warning("validators")
|
22
24
|
return {k: v["value"] for k, v in row.items()}
|
23
25
|
|
24
26
|
|
@@ -37,11 +39,13 @@ class SparqlDataValidator(DataValidator):
|
|
37
39
|
queries: dict = None
|
38
40
|
|
39
41
|
def validate_file(self, input: str, format: str = "turtle", **kwargs):
|
42
|
+
deprecation_warning("validators")
|
40
43
|
g = Graph()
|
41
44
|
g.parse(input, format=format)
|
42
45
|
return self.validate_graph(g, **kwargs)
|
43
46
|
|
44
47
|
def validate_graph(self, g: Graph, **kwargs):
|
48
|
+
deprecation_warning("validators")
|
45
49
|
if self.queries is None:
|
46
50
|
self.queries = SparqlGenerator(self.schema, **kwargs).queries
|
47
51
|
invalid = []
|
@@ -61,6 +65,7 @@ class SparqlDataValidator(DataValidator):
|
|
61
65
|
return invalid
|
62
66
|
|
63
67
|
def validate_endpoint(self, url: str, **kwargs):
|
68
|
+
deprecation_warning("validators")
|
64
69
|
if self.queries is None:
|
65
70
|
self.queries = SparqlGenerator(self.schema, **kwargs).queries
|
66
71
|
invalid = []
|
@@ -81,6 +86,7 @@ class SparqlDataValidator(DataValidator):
|
|
81
86
|
return report
|
82
87
|
|
83
88
|
def load_schema(self, schema: Union[str, SchemaDefinition]):
|
89
|
+
deprecation_warning("validators")
|
84
90
|
self.schemaview = SchemaView(schema)
|
85
91
|
self.schema = self.schemaview.schema
|
86
92
|
# self.schema = YAMLGenerator(schema).schema
|
@@ -124,6 +130,7 @@ def cli(
|
|
124
130
|
|
125
131
|
linkml-sparql-validate -U http://sparql.hegroup.org/sparql -s tests/test_validation/input/omo.yaml
|
126
132
|
"""
|
133
|
+
deprecation_warning("validators")
|
127
134
|
validator = SparqlDataValidator(schema)
|
128
135
|
if endpoint_url is not None:
|
129
136
|
results = validator.validate_endpoint(endpoint_url, limit=limit, named_graphs=named_graph)
|
@@ -22,6 +22,7 @@ from linkml_runtime.utils.formatutils import camelcase
|
|
22
22
|
|
23
23
|
from linkml._version import __version__
|
24
24
|
from linkml.generators.pythongen import PythonGenerator
|
25
|
+
from linkml.utils.helpers import get_range_associated_slots
|
25
26
|
from linkml.validator import Validator, _get_default_validator
|
26
27
|
|
27
28
|
logger = logging.getLogger(__name__)
|
@@ -244,10 +245,28 @@ class ExampleRunner:
|
|
244
245
|
raise ValueError(f"Cannot find unique class for URI {target_class}; got: {target_classes}")
|
245
246
|
target_class = target_classes[0]
|
246
247
|
new_dict_obj = {}
|
248
|
+
|
247
249
|
for k, v in dict_obj.items():
|
248
250
|
if v is not None:
|
249
251
|
islot = sv.induced_slot(k, target_class)
|
250
|
-
|
252
|
+
# if slot is a dictionary, repeat key in dictionary value object
|
253
|
+
if islot.multivalued and islot.inlined and not islot.inlined_as_list:
|
254
|
+
(range_id_slot, range_simple_dict_value_slot, _) = get_range_associated_slots(
|
255
|
+
self.schemaview, islot.range
|
256
|
+
)
|
257
|
+
v_as_list = []
|
258
|
+
for ik, iv in v.items():
|
259
|
+
# simple dictionaries can be simply created
|
260
|
+
if range_simple_dict_value_slot is not None:
|
261
|
+
value = {range_id_slot.name: ik, range_simple_dict_value_slot.name: iv}
|
262
|
+
# other dictionaries => simply add the identifier to the dictionary
|
263
|
+
else:
|
264
|
+
value = iv
|
265
|
+
value[range_id_slot.name] = ik
|
266
|
+
v_as_list.append(value)
|
267
|
+
v2 = self._load_from_dict(v_as_list, target_class=islot.range)
|
268
|
+
else:
|
269
|
+
v2 = self._load_from_dict(v, target_class=islot.range)
|
251
270
|
new_dict_obj[k] = v2
|
252
271
|
py_target_class = getattr(self.python_module, camelcase(target_class))
|
253
272
|
return py_target_class(**new_dict_obj)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: linkml
|
3
|
-
Version: 1.8.
|
3
|
+
Version: 1.8.6
|
4
4
|
Summary: Linked Open Data Modeling Language
|
5
5
|
Home-page: https://linkml.io/linkml/
|
6
6
|
Keywords: schema,linked data,data modeling,rdf,owl,biolink
|
@@ -53,7 +53,7 @@ Requires-Dist: pyyaml
|
|
53
53
|
Requires-Dist: rdflib (>=6.0.0)
|
54
54
|
Requires-Dist: requests (>=2.22)
|
55
55
|
Requires-Dist: sqlalchemy (>=1.4.31)
|
56
|
-
Requires-Dist: typing-extensions (>=4.
|
56
|
+
Requires-Dist: typing-extensions (>=4.6.0) ; python_version < "3.12"
|
57
57
|
Requires-Dist: watchdog (>=0.9.0)
|
58
58
|
Project-URL: Documentation, https://linkml.io/linkml/
|
59
59
|
Project-URL: Repository, https://github.com/linkml/linkml
|