linkml 1.8.4__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.
Files changed (38) hide show
  1. linkml/cli/main.py +2 -0
  2. linkml/generators/common/build.py +1 -2
  3. linkml/generators/common/ifabsent_processor.py +98 -21
  4. linkml/generators/common/lifecycle.py +18 -2
  5. linkml/generators/common/naming.py +106 -0
  6. linkml/generators/dbmlgen.py +173 -0
  7. linkml/generators/docgen.py +16 -7
  8. linkml/generators/erdiagramgen.py +1 -0
  9. linkml/generators/graphqlgen.py +34 -2
  10. linkml/generators/jsonldcontextgen.py +7 -1
  11. linkml/generators/jsonschemagen.py +73 -53
  12. linkml/generators/linkmlgen.py +13 -1
  13. linkml/generators/owlgen.py +11 -1
  14. linkml/generators/plantumlgen.py +17 -10
  15. linkml/generators/pydanticgen/array.py +21 -61
  16. linkml/generators/pydanticgen/template.py +12 -1
  17. linkml/generators/pydanticgen/templates/attribute.py.jinja +1 -1
  18. linkml/generators/python/python_ifabsent_processor.py +1 -1
  19. linkml/generators/pythongen.py +123 -21
  20. linkml/generators/shaclgen.py +16 -5
  21. linkml/generators/typescriptgen.py +3 -1
  22. linkml/linter/rules.py +3 -1
  23. linkml/utils/converter.py +17 -0
  24. linkml/utils/deprecation.py +10 -0
  25. linkml/utils/helpers.py +65 -0
  26. linkml/utils/validation.py +2 -1
  27. linkml/validator/__init__.py +2 -2
  28. linkml/validator/plugins/jsonschema_validation_plugin.py +1 -0
  29. linkml/validator/report.py +4 -1
  30. linkml/validator/validator.py +4 -4
  31. linkml/validators/jsonschemavalidator.py +10 -0
  32. linkml/validators/sparqlvalidator.py +7 -0
  33. linkml/workspaces/example_runner.py +20 -1
  34. {linkml-1.8.4.dist-info → linkml-1.8.6.dist-info}/METADATA +2 -2
  35. {linkml-1.8.4.dist-info → linkml-1.8.6.dist-info}/RECORD +38 -36
  36. {linkml-1.8.4.dist-info → linkml-1.8.6.dist-info}/entry_points.txt +1 -0
  37. {linkml-1.8.4.dist-info → linkml-1.8.6.dist-info}/LICENSE +0 -0
  38. {linkml-1.8.4.dist-info → linkml-1.8.6.dist-info}/WHEEL +0 -0
@@ -29,6 +29,7 @@ from rdflib import URIRef
29
29
 
30
30
  import linkml
31
31
  from linkml._version import __version__
32
+ from linkml.generators.pydanticgen.template import Import, Imports, ObjectImport
32
33
  from linkml.generators.python.python_ifabsent_processor import PythonIfAbsentProcessor
33
34
  from linkml.utils.generator import Generator, shared_arguments
34
35
 
@@ -127,13 +128,120 @@ class PythonGenerator(Generator):
127
128
  self.emit_prefixes.add(type_prefix)
128
129
 
129
130
  def gen_schema(self) -> str:
131
+ all_imports = Imports()
132
+ # generic imports
133
+ all_imports = (
134
+ all_imports
135
+ + Import(module="dataclasses")
136
+ + Import(module="re")
137
+ + Import(
138
+ module="jsonasobj2",
139
+ objects=[
140
+ ObjectImport(name="JsonObj"),
141
+ ObjectImport(name="as_dict"),
142
+ ],
143
+ )
144
+ + Import(
145
+ module="typing",
146
+ objects=[
147
+ ObjectImport(name="Optional"),
148
+ ObjectImport(name="List"),
149
+ ObjectImport(name="Union"),
150
+ ObjectImport(name="Dict"),
151
+ ObjectImport(name="ClassVar"),
152
+ ObjectImport(name="Any"),
153
+ ],
154
+ )
155
+ + Import(
156
+ module="dataclasses",
157
+ objects=[
158
+ ObjectImport(name="dataclass"),
159
+ ],
160
+ )
161
+ + Import(
162
+ module="datetime",
163
+ objects=[
164
+ ObjectImport(name="date"),
165
+ ObjectImport(name="datetime"),
166
+ ObjectImport(name="time"),
167
+ ],
168
+ )
169
+ )
170
+
130
171
  # The metamodel uses Enumerations to define itself, so don't import if we are generating the metamodel
131
- enumimports = (
132
- ""
133
- if self.genmeta
134
- else "from linkml_runtime.linkml_model.meta import EnumDefinition, PermissibleValue, PvFormulaOptions\n"
172
+ if not self.genmeta:
173
+ all_imports = all_imports + Import(
174
+ module="linkml_runtime.linkml_model.meta",
175
+ objects=[
176
+ ObjectImport(name="EnumDefinition"),
177
+ ObjectImport(name="PermissibleValue"),
178
+ ObjectImport(name="PvFormulaOptions"),
179
+ ],
180
+ )
181
+ # linkml imports
182
+ all_imports = (
183
+ all_imports
184
+ + Import(
185
+ module="linkml_runtime.utils.slot",
186
+ objects=[
187
+ ObjectImport(name="Slot"),
188
+ ],
189
+ )
190
+ + Import(
191
+ module="linkml_runtime.utils.metamodelcore",
192
+ objects=[
193
+ ObjectImport(name="empty_list"),
194
+ ObjectImport(name="empty_dict"),
195
+ ObjectImport(name="bnode"),
196
+ ],
197
+ )
198
+ + Import(
199
+ module="linkml_runtime.utils.yamlutils",
200
+ objects=[
201
+ ObjectImport(name="YAMLRoot"),
202
+ ObjectImport(name="extended_str"),
203
+ ObjectImport(name="extended_float"),
204
+ ObjectImport(name="extended_int"),
205
+ ],
206
+ )
207
+ + Import(
208
+ module="linkml_runtime.utils.dataclass_extensions_376",
209
+ objects=[
210
+ ObjectImport(name="dataclasses_init_fn_with_kwargs"),
211
+ ],
212
+ )
213
+ + Import(
214
+ module="linkml_runtime.utils.formatutils",
215
+ objects=[
216
+ ObjectImport(name="camelcase"),
217
+ ObjectImport(name="underscore"),
218
+ ObjectImport(name="sfx"),
219
+ ],
220
+ )
221
+ )
222
+
223
+ # handler import
224
+ all_imports = all_imports + Import(
225
+ module="linkml_runtime.utils.enumerations", objects=[ObjectImport(name="EnumDefinitionImpl")]
226
+ )
227
+ # other imports
228
+ all_imports = (
229
+ all_imports
230
+ + Import(
231
+ module="rdflib",
232
+ objects=[
233
+ ObjectImport(name="Namespace"),
234
+ ObjectImport(name="URIRef"),
235
+ ],
236
+ )
237
+ + Import(
238
+ module="linkml_runtime.utils.curienamespace",
239
+ objects=[
240
+ ObjectImport(name="CurieNamespace"),
241
+ ],
242
+ )
135
243
  )
136
- handlerimport = "from linkml_runtime.utils.enumerations import EnumDefinitionImpl"
244
+
137
245
  split_description = ""
138
246
  if self.schema.description:
139
247
  split_description = "\n# ".join(d for d in self.schema.description.split("\n") if d is not None)
@@ -151,21 +259,7 @@ class PythonGenerator(Generator):
151
259
  # description: {split_description}
152
260
  # license: {be(self.schema.license)}
153
261
 
154
- import dataclasses
155
- import re
156
- from jsonasobj2 import JsonObj, as_dict
157
- from typing import Optional, List, Union, Dict, ClassVar, Any
158
- from dataclasses import dataclass
159
- from datetime import date, datetime, time
160
- {enumimports}
161
- from linkml_runtime.utils.slot import Slot
162
- from linkml_runtime.utils.metamodelcore import empty_list, empty_dict, bnode
163
- from linkml_runtime.utils.yamlutils import YAMLRoot, extended_str, extended_float, extended_int
164
- from linkml_runtime.utils.dataclass_extensions_376 import dataclasses_init_fn_with_kwargs
165
- from linkml_runtime.utils.formatutils import camelcase, underscore, sfx
166
- {handlerimport}
167
- from rdflib import Namespace, URIRef
168
- from linkml_runtime.utils.curienamespace import CurieNamespace
262
+ {all_imports.render()}
169
263
  {self.gen_imports()}
170
264
 
171
265
  metamodel_version = "{self.schema.metamodel_version}"
@@ -836,7 +930,15 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
836
930
  ):
837
931
  rlines.append(f"\tself.{aliased_slot_name} = {base_type_name}()")
838
932
  else:
839
- if (
933
+ if slot.range in self.schema.enums and slot.ifabsent:
934
+ # `ifabsent` for an enumeration cannot be assigned to
935
+ # the dataclass field default, because it would be a
936
+ # mutable. `python_ifabsent_processor.py` can specify
937
+ # the default as string and here that string gets
938
+ # converted into an object attribute invocation
939
+ # TODO: fix according https://github.com/linkml/linkml/pull/2329#discussion_r1797534588
940
+ rlines.append(f"\tself.{aliased_slot_name} = getattr({slot.range}, self.{aliased_slot_name})")
941
+ elif (
840
942
  (self.class_identifier(slot.range) and not slot.inlined)
841
943
  or slot.range in self.schema.types
842
944
  or slot.range in self.schema.enums
@@ -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
- if not s.multivalued:
120
- prop_pv_literal(SH.maxCount, 1)
121
- if s.required:
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:
@@ -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
@@ -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)
@@ -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
- looked for a class with ``tree_root: true``. Defaults to ``None``.
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
- looked for a class with ``tree_root: true``. Defaults to ``None``.
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``.
@@ -55,4 +55,5 @@ class JsonschemaValidationPlugin(ValidationPlugin):
55
55
  instantiates=context.target_class,
56
56
  message=f"{best_error.message} in /{'/'.join(str(p) for p in best_error.absolute_path)}",
57
57
  context=error_context,
58
+ source=best_error,
58
59
  )
@@ -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
  """
@@ -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
- looked for a class with ``tree_root: true``. Defaults to ``None``.
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
- looked for a class with ``tree_root: true``. Defaults to ``None``.
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
- looked for a class with ``tree_root: true``. Defaults to ``None``.
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
- looked for a class with ``tree_root: true``. Defaults to ``None``.
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
- v2 = self._load_from_dict(v, target_class=islot.range)
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.4
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.4.0) ; python_version < "3.9"
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