linkml 1.5.5__py3-none-any.whl → 1.5.7__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 (81) hide show
  1. linkml/__init__.py +2 -6
  2. linkml/_version.py +1 -1
  3. linkml/generators/PythonGenNotes.md +4 -4
  4. linkml/generators/__init__.py +26 -5
  5. linkml/generators/common/type_designators.py +27 -22
  6. linkml/generators/csvgen.py +4 -10
  7. linkml/generators/docgen/class.md.jinja2 +7 -0
  8. linkml/generators/docgen/class_diagram.md.jinja2 +0 -6
  9. linkml/generators/docgen/subset.md.jinja2 +54 -13
  10. linkml/generators/docgen.py +94 -92
  11. linkml/generators/dotgen.py +5 -9
  12. linkml/generators/erdiagramgen.py +58 -53
  13. linkml/generators/excelgen.py +10 -16
  14. linkml/generators/golanggen.py +11 -21
  15. linkml/generators/golrgen.py +4 -13
  16. linkml/generators/graphqlgen.py +3 -11
  17. linkml/generators/javagen.py +8 -15
  18. linkml/generators/jsonldcontextgen.py +7 -36
  19. linkml/generators/jsonldgen.py +14 -12
  20. linkml/generators/jsonschemagen.py +183 -136
  21. linkml/generators/linkmlgen.py +1 -1
  22. linkml/generators/markdowngen.py +40 -89
  23. linkml/generators/namespacegen.py +1 -2
  24. linkml/generators/oocodegen.py +22 -25
  25. linkml/generators/owlgen.py +48 -49
  26. linkml/generators/prefixmapgen.py +6 -14
  27. linkml/generators/projectgen.py +7 -14
  28. linkml/generators/protogen.py +3 -5
  29. linkml/generators/pydanticgen.py +85 -73
  30. linkml/generators/pythongen.py +89 -157
  31. linkml/generators/rdfgen.py +5 -11
  32. linkml/generators/shaclgen.py +32 -18
  33. linkml/generators/shexgen.py +19 -24
  34. linkml/generators/sparqlgen.py +5 -13
  35. linkml/generators/sqlalchemy/__init__.py +3 -2
  36. linkml/generators/sqlalchemy/sqlalchemy_declarative_template.py +7 -7
  37. linkml/generators/sqlalchemy/sqlalchemy_imperative_template.py +3 -3
  38. linkml/generators/sqlalchemygen.py +29 -27
  39. linkml/generators/sqlddlgen.py +34 -26
  40. linkml/generators/sqltablegen.py +21 -21
  41. linkml/generators/sssomgen.py +11 -13
  42. linkml/generators/summarygen.py +2 -4
  43. linkml/generators/terminusdbgen.py +7 -19
  44. linkml/generators/typescriptgen.py +10 -18
  45. linkml/generators/yamlgen.py +0 -2
  46. linkml/generators/yumlgen.py +23 -71
  47. linkml/linter/cli.py +4 -11
  48. linkml/linter/config/datamodel/config.py +17 -47
  49. linkml/linter/linter.py +2 -4
  50. linkml/linter/rules.py +34 -48
  51. linkml/reporting/__init__.py +2 -0
  52. linkml/reporting/model.py +9 -24
  53. linkml/transformers/relmodel_transformer.py +20 -33
  54. linkml/transformers/schema_renamer.py +14 -10
  55. linkml/utils/converter.py +15 -15
  56. linkml/utils/datautils.py +9 -24
  57. linkml/utils/datavalidator.py +2 -2
  58. linkml/utils/execute_tutorial.py +10 -12
  59. linkml/utils/generator.py +74 -92
  60. linkml/utils/helpers.py +4 -2
  61. linkml/utils/ifabsent_functions.py +23 -15
  62. linkml/utils/mergeutils.py +19 -35
  63. linkml/utils/rawloader.py +2 -6
  64. linkml/utils/schema_builder.py +31 -19
  65. linkml/utils/schema_fixer.py +28 -18
  66. linkml/utils/schemaloader.py +44 -89
  67. linkml/utils/schemasynopsis.py +50 -73
  68. linkml/utils/sqlutils.py +40 -30
  69. linkml/utils/typereferences.py +9 -6
  70. linkml/utils/validation.py +4 -5
  71. linkml/validators/__init__.py +2 -0
  72. linkml/validators/jsonschemavalidator.py +104 -53
  73. linkml/validators/sparqlvalidator.py +5 -15
  74. linkml/workspaces/datamodel/workspaces.py +13 -30
  75. linkml/workspaces/example_runner.py +75 -68
  76. {linkml-1.5.5.dist-info → linkml-1.5.7.dist-info}/METADATA +2 -2
  77. linkml-1.5.7.dist-info/RECORD +109 -0
  78. linkml-1.5.5.dist-info/RECORD +0 -109
  79. {linkml-1.5.5.dist-info → linkml-1.5.7.dist-info}/LICENSE +0 -0
  80. {linkml-1.5.5.dist-info → linkml-1.5.7.dist-info}/WHEEL +0 -0
  81. {linkml-1.5.5.dist-info → linkml-1.5.7.dist-info}/entry_points.txt +0 -0
linkml/linter/rules.py CHANGED
@@ -3,17 +3,24 @@ from abc import ABC, abstractmethod
3
3
  from functools import lru_cache
4
4
  from typing import Callable, Iterable, List
5
5
 
6
- from linkml_runtime.linkml_model import (ClassDefinition, ClassDefinitionName,
7
- Element, SlotDefinition)
6
+ from linkml_runtime.linkml_model import (
7
+ ClassDefinition,
8
+ ClassDefinitionName,
9
+ Element,
10
+ SlotDefinition,
11
+ )
8
12
  from linkml_runtime.utils.schemaview import SchemaView
9
13
  from prefixmaps.io.parser import load_multi_context
10
14
 
11
15
  from linkml import LOCAL_METAMODEL_YAML_FILE
12
16
 
13
- from .config.datamodel.config import (CanonicalPrefixesConfig,
14
- RecommendedRuleConfig, RuleConfig,
15
- StandardNamingConfig,
16
- TreeRootClassRuleConfig)
17
+ from .config.datamodel.config import (
18
+ CanonicalPrefixesConfig,
19
+ RecommendedRuleConfig,
20
+ RuleConfig,
21
+ StandardNamingConfig,
22
+ TreeRootClassRuleConfig,
23
+ )
17
24
  from .linter import LinterProblem
18
25
 
19
26
 
@@ -53,18 +60,14 @@ class LinterRule(ABC):
53
60
  class NoEmptyTitleRule(LinterRule):
54
61
  id = "no_empty_title"
55
62
 
56
- def check(
57
- self, schema_view: SchemaView, fix: bool = False
58
- ) -> Iterable[LinterProblem]:
63
+ def check(self, schema_view: SchemaView, fix: bool = False) -> Iterable[LinterProblem]:
59
64
  for e in schema_view.all_elements(imports=False).values():
60
65
  if fix and e.title is None:
61
66
  title = e.name.replace("_", " ")
62
67
  title = self.uncamel(title).lower()
63
68
  e.title = title
64
69
  if e.title is None:
65
- problem = LinterProblem(
66
- message=f"{self.format_element(e)} has no title"
67
- )
70
+ problem = LinterProblem(message=f"{self.format_element(e)} has no title")
68
71
  yield problem
69
72
 
70
73
 
@@ -77,17 +80,13 @@ class NoXsdIntTypeRule(LinterRule):
77
80
  if fix:
78
81
  type_definition.uri = "xsd:integer"
79
82
  else:
80
- yield LinterProblem(
81
- f"{self.format_element(type_definition)} has uri xsd:int"
82
- )
83
+ yield LinterProblem(f"{self.format_element(type_definition)} has uri xsd:int")
83
84
 
84
85
 
85
86
  class PermissibleValuesFormatRule(LinterRule):
86
87
  id = "permissible_values_format"
87
88
 
88
- def check(
89
- self, schema_view: SchemaView, fix: bool = False
90
- ) -> Iterable[LinterProblem]:
89
+ def check(self, schema_view: SchemaView, fix: bool = False) -> Iterable[LinterProblem]:
91
90
  pattern = self.PATTERNS.get(self.config.format, re.compile(self.config.format))
92
91
  for enum_def in schema_view.all_enums(imports=False).values():
93
92
  for value in enum_def.permissible_values.keys():
@@ -117,9 +116,7 @@ class RecommendedRule(LinterRule):
117
116
 
118
117
  def check(self, schema_view: SchemaView, fix: bool = False):
119
118
  recommended_meta_slots = _get_recommended_metamodel_slots()
120
- for element_name, element_definition in schema_view.all_elements(
121
- imports=False
122
- ).items():
119
+ for element_name, element_definition in schema_view.all_elements(imports=False).items():
123
120
  if self.config.include and element_name not in self.config.include:
124
121
  continue
125
122
  if element_name in self.config.exclude:
@@ -138,19 +135,13 @@ class TreeRootClassRule(LinterRule):
138
135
  def __init__(self, config: TreeRootClassRuleConfig) -> None:
139
136
  super().__init__(config)
140
137
 
141
- def check(
142
- self, schema_view: SchemaView, fix: bool = False
143
- ) -> Iterable[LinterProblem]:
144
- tree_roots = [
145
- c for c in schema_view.all_classes(imports=False).values() if c.tree_root
146
- ]
138
+ def check(self, schema_view: SchemaView, fix: bool = False) -> Iterable[LinterProblem]:
139
+ tree_roots = [c for c in schema_view.all_classes(imports=False).values() if c.tree_root]
147
140
  if len(tree_roots) > 0:
148
141
  if self.config.validate_existing_class_name:
149
142
  for tree_root in tree_roots:
150
143
  if str(tree_root.name) != self.config.root_class_name:
151
- yield LinterProblem(
152
- message=f"Tree root class has name '{tree_root.name}'"
153
- )
144
+ yield LinterProblem(message=f"Tree root class has name '{tree_root.name}'")
154
145
  else:
155
146
  if fix:
156
147
  container = ClassDefinition(self.config.root_class_name, tree_root=True)
@@ -190,9 +181,7 @@ class TreeRootClassRule(LinterRule):
190
181
  ]
191
182
  if must_have_identifier:
192
183
  top_level_classes = [
193
- c
194
- for c in top_level_classes
195
- if schema_view.get_identifier_slot(c.name) is not None
184
+ c for c in top_level_classes if schema_view.get_identifier_slot(c.name) is not None
196
185
  ]
197
186
  index_slots = []
198
187
  for c in top_level_classes:
@@ -220,12 +209,8 @@ class TreeRootClassRule(LinterRule):
220
209
  class NoInvalidSlotUsageRule(LinterRule):
221
210
  id = "no_invalid_slot_usage"
222
211
 
223
- def check(
224
- self, schema_view: SchemaView, fix: bool = False
225
- ) -> Iterable[LinterProblem]:
226
- for class_name, class_definition in schema_view.all_classes(
227
- imports=False
228
- ).items():
212
+ def check(self, schema_view: SchemaView, fix: bool = False) -> Iterable[LinterProblem]:
213
+ for class_name, class_definition in schema_view.all_classes(imports=False).items():
229
214
  slot_usage = class_definition.slot_usage
230
215
  if not slot_usage:
231
216
  continue
@@ -243,9 +228,7 @@ class StandardNamingRule(LinterRule):
243
228
  def __init__(self, config: StandardNamingConfig) -> None:
244
229
  self.config = config
245
230
 
246
- def check(
247
- self, schema_view: SchemaView, fix: bool = False
248
- ) -> Iterable[LinterProblem]:
231
+ def check(self, schema_view: SchemaView, fix: bool = False) -> Iterable[LinterProblem]:
249
232
  class_pattern = self.PATTERNS["uppercamel"]
250
233
  slot_pattern = self.PATTERNS["snake"]
251
234
  enum_pattern = self.PATTERNS["uppercamel"]
@@ -270,7 +253,8 @@ class StandardNamingRule(LinterRule):
270
253
  for permissible_value_name in enum_definition.permissible_values.keys():
271
254
  if permissible_value_pattern.fullmatch(permissible_value_name) is None:
272
255
  yield LinterProblem(
273
- f"Permissible value of {self.format_element(enum_definition)} has name '{permissible_value_name}'"
256
+ f"Permissible value of {self.format_element(enum_definition)} "
257
+ f"has name '{permissible_value_name}'"
274
258
  )
275
259
 
276
260
 
@@ -280,9 +264,7 @@ class CanonicalPrefixesRule(LinterRule):
280
264
  def __init__(self, config: CanonicalPrefixesConfig) -> None:
281
265
  self.config = config
282
266
 
283
- def check(
284
- self, schema_view: SchemaView, fix: bool = False
285
- ) -> Iterable[LinterProblem]:
267
+ def check(self, schema_view: SchemaView, fix: bool = False) -> Iterable[LinterProblem]:
286
268
  context = load_multi_context(self.config.prefixmaps_contexts)
287
269
  prefix_to_namespace = context.as_dict()
288
270
  namespace_to_prefix = context.as_inverted_dict()
@@ -290,10 +272,14 @@ class CanonicalPrefixesRule(LinterRule):
290
272
  if prefix.prefix_prefix in prefix_to_namespace:
291
273
  if prefix.prefix_reference != prefix_to_namespace[prefix.prefix_prefix]:
292
274
  yield LinterProblem(
293
- f"Schema maps prefix '{prefix.prefix_prefix}' to namespace '{prefix.prefix_reference}' instead of namespace '{prefix_to_namespace[prefix.prefix_prefix]}'"
275
+ f"Schema maps prefix '{prefix.prefix_prefix}' to namespace "
276
+ f"'{prefix.prefix_reference}' instead of namespace "
277
+ f"'{prefix_to_namespace[prefix.prefix_prefix]}'"
294
278
  )
295
279
  if prefix.prefix_reference in namespace_to_prefix:
296
280
  if prefix.prefix_prefix != namespace_to_prefix[prefix.prefix_reference]:
297
281
  yield LinterProblem(
298
- f"Schema maps prefix '{prefix.prefix_prefix}' to namespace '{prefix.prefix_reference}' instead of using prefix '{namespace_to_prefix[prefix.prefix_reference]}'"
282
+ f"Schema maps prefix '{prefix.prefix_prefix}' to namespace "
283
+ f"'{prefix.prefix_reference}' instead of using prefix "
284
+ f"'{namespace_to_prefix[prefix.prefix_reference]}'"
299
285
  )
@@ -1 +1,3 @@
1
1
  from linkml.reporting.model import CheckResult, Report
2
+
3
+ __all__ = ["CheckResult", "Report"]
linkml/reporting/model.py CHANGED
@@ -7,27 +7,18 @@
7
7
  # license: https://creativecommons.org/publicdomain/zero/1.0/
8
8
 
9
9
  import dataclasses
10
- import re
11
- import sys
12
10
  from dataclasses import dataclass
13
11
  from typing import Any, ClassVar, Dict, List, Optional, Union
14
12
 
15
- from jsonasobj2 import JsonObj, as_dict
16
- from linkml_runtime.linkml_model.meta import (EnumDefinition, PermissibleValue,
17
- PvFormulaOptions)
18
- from linkml_runtime.linkml_model.types import (Nodeidentifier, String,
19
- Uriorcurie)
13
+ from jsonasobj2 import as_dict
14
+ from linkml_runtime.linkml_model.meta import EnumDefinition, PermissibleValue
20
15
  from linkml_runtime.utils.curienamespace import CurieNamespace
21
- from linkml_runtime.utils.dataclass_extensions_376 import \
22
- dataclasses_init_fn_with_kwargs
16
+ from linkml_runtime.utils.dataclass_extensions_376 import dataclasses_init_fn_with_kwargs
23
17
  from linkml_runtime.utils.enumerations import EnumDefinitionImpl
24
- from linkml_runtime.utils.formatutils import camelcase, sfx, underscore
25
- from linkml_runtime.utils.metamodelcore import (NodeIdentifier, URIorCURIE,
26
- bnode, empty_dict, empty_list)
18
+ from linkml_runtime.utils.metamodelcore import NodeIdentifier, URIorCURIE, empty_list
27
19
  from linkml_runtime.utils.slot import Slot
28
- from linkml_runtime.utils.yamlutils import (YAMLRoot, extended_float,
29
- extended_int, extended_str)
30
- from rdflib import Namespace, URIRef
20
+ from linkml_runtime.utils.yamlutils import YAMLRoot
21
+ from rdflib import URIRef
31
22
 
32
23
  metamodel_version = "1.7.0"
33
24
 
@@ -73,8 +64,7 @@ class Report(YAMLRoot):
73
64
  if not isinstance(self.results, list):
74
65
  self.results = [self.results] if self.results is not None else []
75
66
  self.results = [
76
- v if isinstance(v, CheckResult) else CheckResult(**as_dict(v))
77
- for v in self.results
67
+ v if isinstance(v, CheckResult) else CheckResult(**as_dict(v)) for v in self.results
78
68
  ]
79
69
 
80
70
  super().__post_init__(**kwargs)
@@ -109,14 +99,10 @@ class CheckResult(YAMLRoot):
109
99
  if self.subject is not None and not isinstance(self.subject, NodeIdentifier):
110
100
  self.subject = NodeIdentifier(self.subject)
111
101
 
112
- if self.instantiates is not None and not isinstance(
113
- self.instantiates, NodeIdentifier
114
- ):
102
+ if self.instantiates is not None and not isinstance(self.instantiates, NodeIdentifier):
115
103
  self.instantiates = NodeIdentifier(self.instantiates)
116
104
 
117
- if self.predicate is not None and not isinstance(
118
- self.predicate, NodeIdentifier
119
- ):
105
+ if self.predicate is not None and not isinstance(self.predicate, NodeIdentifier):
120
106
  self.predicate = NodeIdentifier(self.predicate)
121
107
 
122
108
  if self.object is not None and not isinstance(self.object, NodeIdentifier):
@@ -184,7 +170,6 @@ class ProblemSlotMissing(Problem):
184
170
 
185
171
  # Enumerations
186
172
  class SeverityOptions(EnumDefinitionImpl):
187
-
188
173
  FATAL = PermissibleValue(text="FATAL")
189
174
  ERROR = PermissibleValue(text="ERROR")
190
175
  WARNING = PermissibleValue(text="WARNING")
@@ -1,16 +1,19 @@
1
1
  import logging
2
2
  from copy import copy
3
3
  from dataclasses import dataclass, field
4
- from enum import unique
5
4
  from typing import Dict, List, Optional
6
5
 
7
- from linkml_runtime.linkml_model import (Annotation, ClassDefinition,
8
- ClassDefinitionName, Definition,
9
- Prefix, SchemaDefinition,
10
- SlotDefinition)
11
- from linkml_runtime.utils.formatutils import camelcase, underscore
6
+ from linkml_runtime.linkml_model import (
7
+ Annotation,
8
+ ClassDefinition,
9
+ ClassDefinitionName,
10
+ Definition,
11
+ Prefix,
12
+ SchemaDefinition,
13
+ SlotDefinition,
14
+ )
12
15
  from linkml_runtime.utils.schemaview import SchemaView, SlotDefinitionName
13
- from sqlalchemy import *
16
+ from sqlalchemy import Enum
14
17
 
15
18
 
16
19
  class RelationalAnnotations(Enum):
@@ -99,9 +102,7 @@ class MultivaluedScalar(RelationalMapping):
99
102
  mapping_type: str = "MultivaluedScalar"
100
103
 
101
104
 
102
- def add_attribute(
103
- attributes: Dict[str, SlotDefinition], tgt_slot: SlotDefinition
104
- ) -> None:
105
+ def add_attribute(attributes: Dict[str, SlotDefinition], tgt_slot: SlotDefinition) -> None:
105
106
  attributes[tgt_slot.name] = tgt_slot
106
107
 
107
108
 
@@ -199,6 +200,7 @@ class RelationalModelTransformer:
199
200
  tree_root=c.tree_root,
200
201
  abstract=c.abstract,
201
202
  description=c.description,
203
+ unique_keys=c.unique_keys,
202
204
  )
203
205
  for slot in source_sv.class_induced_slots(cn):
204
206
  tgt_slot = copy(slot)
@@ -221,9 +223,7 @@ class RelationalModelTransformer:
221
223
  for cn in target_sv.all_classes():
222
224
  pk = self.get_direct_identifier_attribute(target_sv, cn)
223
225
  if self.foreign_key_policy == ForeignKeyPolicy.NO_FOREIGN_KEYS:
224
- logging.info(
225
- f"Will not inject any PKs, and policy == {self.foreign_key_policy}"
226
- )
226
+ logging.info(f"Will not inject any PKs, and policy == {self.foreign_key_policy}")
227
227
  else:
228
228
  if pk is None:
229
229
  pk = self.add_primary_key(cn, target_sv)
@@ -238,7 +238,6 @@ class RelationalModelTransformer:
238
238
  for cn, c in target_sv.all_classes().items():
239
239
  if self.foreign_key_policy == ForeignKeyPolicy.NO_FOREIGN_KEYS:
240
240
  continue
241
- incoming_links = [link for link in links if link.target_class == cn]
242
241
  pk_slot = self.get_direct_identifier_attribute(target_sv, cn)
243
242
  # if self.is_skip(c) and len(incoming_links) == 0:
244
243
  # logging.info(f'Skipping class: {c.name}')
@@ -248,24 +247,18 @@ class RelationalModelTransformer:
248
247
  slot = copy(src_slot)
249
248
  slot_range = slot.range
250
249
  slot_range_is_class = slot_range in target_sv.all_classes()
251
- links_to_range = [
252
- link for link in links if link.target_class == slot_range
253
- ]
254
- is_only_ref_to_range = len(links_to_range) == 1
255
250
  is_shared = slot_range_is_class and (
256
251
  slot.inlined or slot.inlined_as_list or "shared" in slot.annotations
257
252
  )
258
253
  if slot.multivalued:
259
254
  slot.multivalued = False
260
255
  slot_name = slot.name
261
- sn_singular = (
262
- slot.singular_name if slot.singular_name else slot.name
263
- )
256
+ sn_singular = slot.singular_name if slot.singular_name else slot.name
264
257
  if pk_slot is None:
265
258
  pk_slot = self.add_primary_key(c.name, target_sv)
266
259
  backref_slot = SlotDefinition(
267
260
  name=f"{c.name}_{pk_slot.name}",
268
- description=f"Autocreated FK slot",
261
+ description="Autocreated FK slot",
269
262
  range=c.name,
270
263
  slot_uri="rdf:subject",
271
264
  # close_mappings=[pk_slot.slot_uri],
@@ -361,13 +354,9 @@ class RelationalModelTransformer:
361
354
  if a.range in target.classes:
362
355
  tc = target.classes[a.range]
363
356
  # tc_pk_slot = target_sv.get_identifier_slot(tc.name)
364
- tc_pk_slot = self.get_direct_identifier_attribute(
365
- target_sv, tc.name
366
- )
357
+ tc_pk_slot = self.get_direct_identifier_attribute(target_sv, tc.name)
367
358
  if tc_pk_slot is None:
368
- raise ValueError(
369
- f"No PK for attribute {a.name} range {a.range}"
370
- )
359
+ raise ValueError(f"No PK for attribute {a.name} range {a.range}")
371
360
  is_inlined = a.inlined or not source_sv.get_identifier_slot(tc.name)
372
361
  if (
373
362
  fk_policy == ForeignKeyPolicy.INJECT_FK_FOR_NESTED
@@ -449,9 +438,7 @@ class RelationalModelTransformer:
449
438
  candidate_names = ["id", "uid", "identifier", "pk"]
450
439
  valid_candidate_names = [n for n in candidate_names if n not in c.attributes]
451
440
  if not valid_candidate_names:
452
- raise ValueError(
453
- f"Cannot add primary key to class {cn}: no valid candidate names"
454
- )
441
+ raise ValueError(f"Cannot add primary key to class {cn}: no valid candidate names")
455
442
  pk = SlotDefinition(name=valid_candidate_names[0], identifier=True, range="integer")
456
443
  add_annotation(pk, "dcterms:conformsTo", "rr:BlankNode")
457
444
  add_annotation(pk, "autoincrement", "true")
@@ -461,8 +448,8 @@ class RelationalModelTransformer:
461
448
  )
462
449
  # add PK to start of attributes
463
450
  atts = copy(c.attributes)
464
- c.attributes.clear() ## See https://github.com/linkml/linkml/issues/370
465
- add_attribute(c.attributes, pk) ## add to start
451
+ c.attributes.clear() # See https://github.com/linkml/linkml/issues/370
452
+ add_attribute(c.attributes, pk) # add to start
466
453
  c.attributes.update(atts)
467
454
  sv.set_modified()
468
455
  return pk
@@ -1,4 +1,3 @@
1
- import inspect
2
1
  from copy import deepcopy
3
2
  from dataclasses import dataclass, field
4
3
  from typing import Any, Callable, Dict, Type
@@ -6,11 +5,18 @@ from typing import Any, Callable, Dict, Type
6
5
  import click
7
6
  from jsonasobj2 import as_dict
8
7
  from linkml_runtime import SchemaView
9
- from linkml_runtime.linkml_model import (ClassDefinition, ClassDefinitionName,
10
- Element, ElementName, EnumDefinition,
11
- EnumDefinitionName, SchemaDefinition,
12
- SlotDefinition, SlotDefinitionName,
13
- TypeDefinition, TypeDefinitionName)
8
+ from linkml_runtime.linkml_model import (
9
+ ClassDefinition,
10
+ ClassDefinitionName,
11
+ ElementName,
12
+ EnumDefinition,
13
+ EnumDefinitionName,
14
+ SchemaDefinition,
15
+ SlotDefinition,
16
+ SlotDefinitionName,
17
+ TypeDefinition,
18
+ TypeDefinitionName,
19
+ )
14
20
  from linkml_runtime.utils.formatutils import camelcase, lcamelcase, underscore
15
21
  from linkml_runtime.utils.schema_as_dict import schema_as_yaml_dump
16
22
  from linkml_runtime.utils.yamlutils import YAMLRoot
@@ -92,9 +98,7 @@ class SchemaRenamer:
92
98
  return new_element
93
99
  else:
94
100
  try:
95
- element_vars = {
96
- k: v for k, v in vars(element).items() if not k.startswith("_")
97
- }
101
+ element_vars = {k: v for k, v in vars(element).items() if not k.startswith("_")}
98
102
  if len(element_vars) == 0:
99
103
  return element
100
104
  else:
@@ -134,7 +138,7 @@ def main(schema, output, class_names, slot_names):
134
138
  if slot_names:
135
139
  rename_map[SlotDefinition] = n2f(slot_names)
136
140
  if not rename_map.keys():
137
- raise ValueError(f"No transformations specified")
141
+ raise ValueError("No transformations specified")
138
142
  renamer = SchemaRenamer(rename_function_map=rename_map)
139
143
  rschema = renamer.rename_elements(sv.schema)
140
144
  ystr = schema_as_yaml_dump(rschema)
linkml/utils/converter.py CHANGED
@@ -1,7 +1,6 @@
1
1
  import logging
2
2
  import os
3
3
  import sys
4
- from pathlib import Path
5
4
  from typing import List
6
5
 
7
6
  import click
@@ -14,9 +13,16 @@ from linkml_runtime.utils.schemaview import SchemaView
14
13
  from linkml._version import __version__
15
14
  from linkml.generators.pythongen import PythonGenerator
16
15
  from linkml.utils import datautils, validation
17
- from linkml.utils.datautils import (_get_context, _get_format, _is_xsv,
18
- dumpers_loaders, get_dumper, get_loader,
19
- infer_index_slot, infer_root_class)
16
+ from linkml.utils.datautils import (
17
+ _get_context,
18
+ _get_format,
19
+ _is_xsv,
20
+ dumpers_loaders,
21
+ get_dumper,
22
+ get_loader,
23
+ infer_index_slot,
24
+ infer_root_class,
25
+ )
20
26
 
21
27
 
22
28
  @click.command()
@@ -45,9 +51,7 @@ from linkml.utils.datautils import (_get_context, _get_format, _is_xsv,
45
51
  show_default=True,
46
52
  help="Infer the target class from the filename, should be ClassName-<other-chars>.{yaml,json,...}",
47
53
  )
48
- @click.option(
49
- "--index-slot", "-S", help="top level slot. Required for CSV dumping/loading"
50
- )
54
+ @click.option("--index-slot", "-S", help="top level slot. Required for CSV dumping/loading")
51
55
  @click.option("--schema", "-s", help="Path to schema specified as LinkML yaml")
52
56
  @click.option("--prefix", "-P", multiple=True, help="Prefixmap base=URI pairs")
53
57
  @click.option(
@@ -118,7 +122,7 @@ def cli(
118
122
  if target_class is None:
119
123
  target_class = infer_root_class(sv)
120
124
  if target_class is None:
121
- raise Exception(f"target class not specified and could not be inferred")
125
+ raise Exception("target class not specified and could not be inferred")
122
126
  py_target_class = python_module.__dict__[target_class]
123
127
  input_format = _get_format(input, input_format)
124
128
  loader = get_loader(input_format)
@@ -127,7 +131,7 @@ def cli(
127
131
  outargs = {}
128
132
  if datautils._is_rdf_format(input_format):
129
133
  if sv is None:
130
- raise Exception(f"Must pass schema arg")
134
+ raise Exception("Must pass schema arg")
131
135
  inargs["schemaview"] = sv
132
136
  inargs["fmt"] = input_format
133
137
  if _is_xsv(input_format):
@@ -139,9 +143,7 @@ def cli(
139
143
  inargs["schema"] = schema
140
144
  obj = loader.load(source=input, target_class=py_target_class, **inargs)
141
145
  if infer:
142
- infer_config = inference_utils.Config(
143
- use_expressions=True, use_string_serialization=True
144
- )
146
+ infer_config = inference_utils.Config(use_expressions=True, use_string_serialization=True)
145
147
  infer_all_slot_values(obj, schemaview=sv, config=infer_config)
146
148
  if validate:
147
149
  if schema is None:
@@ -159,11 +161,9 @@ def cli(
159
161
  else:
160
162
  raise Exception("Must pass in context OR schema for RDF output")
161
163
  outargs["contexts"] = list(context)
162
- outargs["fmt"] = "json-ld"
163
- outargs["schemaview"] = sv
164
164
  if output_format == "rdf" or output_format == "ttl":
165
165
  if sv is None:
166
- raise Exception(f"Must pass schema arg")
166
+ raise Exception("Must pass schema arg")
167
167
  outargs["schemaview"] = sv
168
168
  if _is_xsv(output_format):
169
169
  if index_slot is None:
linkml/utils/datautils.py CHANGED
@@ -1,23 +1,12 @@
1
1
  import os
2
2
  from collections import defaultdict
3
- from typing import Optional, Union
4
-
5
- from linkml_runtime.dumpers.csv_dumper import CSVDumper
6
- from linkml_runtime.dumpers.json_dumper import JSONDumper
7
- from linkml_runtime.dumpers.rdf_dumper import RDFDumper
8
- from linkml_runtime.dumpers.rdflib_dumper import RDFLibDumper
9
- from linkml_runtime.dumpers.yaml_dumper import YAMLDumper
10
- from linkml_runtime.linkml_model.meta import (ClassDefinitionName,
11
- SchemaDefinition,
12
- SlotDefinitionName)
13
- from linkml_runtime.loaders.csv_loader import CSVLoader
14
- from linkml_runtime.loaders.json_loader import JSONLoader
3
+ from typing import Optional
4
+
5
+ from linkml_runtime.dumpers import CSVDumper, JSONDumper, RDFLibDumper, TSVDumper, YAMLDumper
6
+ from linkml_runtime.linkml_model.meta import ClassDefinitionName, SlotDefinitionName
7
+ from linkml_runtime.loaders import CSVLoader, JSONLoader, RDFLibLoader, TSVLoader, YAMLLoader
15
8
  from linkml_runtime.loaders.loader_root import Loader
16
- from linkml_runtime.loaders.rdf_loader import RDFLoader
17
- from linkml_runtime.loaders.rdflib_loader import RDFLibLoader
18
- from linkml_runtime.loaders.yaml_loader import YAMLLoader
19
9
  from linkml_runtime.utils.schemaview import SchemaView
20
- from linkml_runtime.utils.yamlutils import YAMLRoot
21
10
 
22
11
  from linkml.generators.jsonldcontextgen import ContextGenerator
23
12
 
@@ -27,9 +16,9 @@ dumpers_loaders = {
27
16
  "json": (JSONDumper, JSONLoader),
28
17
  "rdf": (RDFLibDumper, RDFLibLoader),
29
18
  "ttl": (RDFLibDumper, RDFLibLoader),
30
- "json-ld": (RDFLibDumper, RDFLibLoader),
19
+ "json-ld": (JSONDumper, JSONLoader),
31
20
  "csv": (CSVDumper, CSVLoader),
32
- "tsv": (CSVDumper, CSVLoader),
21
+ "tsv": (TSVDumper, TSVLoader),
33
22
  }
34
23
 
35
24
  aliases = {
@@ -42,9 +31,7 @@ def _get_format(path: str, specified_format: str = None, default=None):
42
31
  if specified_format is None:
43
32
  if path is None:
44
33
  if default is None:
45
- raise Exception(
46
- f"Must pass format option OR pass a filename with known file suffix"
47
- )
34
+ raise Exception("Must pass format option OR pass a filename with known file suffix")
48
35
  else:
49
36
  specified_format = default
50
37
  else:
@@ -52,9 +39,7 @@ def _get_format(path: str, specified_format: str = None, default=None):
52
39
  if ext is not None:
53
40
  specified_format = ext.replace(".", "")
54
41
  else:
55
- raise Exception(
56
- f"Must pass format option OR use known file suffix: {path}"
57
- )
42
+ raise Exception(f"Must pass format option OR use known file suffix: {path}")
58
43
  specified_format = specified_format.lower()
59
44
  if specified_format in aliases:
60
45
  specified_format = aliases[specified_format]
@@ -1,7 +1,7 @@
1
1
  from dataclasses import dataclass
2
2
  from typing import Union
3
3
 
4
- from linkml_runtime.linkml_model import SchemaDefinition, ClassDefinitionName
4
+ from linkml_runtime.linkml_model import ClassDefinitionName, SchemaDefinition
5
5
 
6
6
 
7
7
  @dataclass
@@ -16,6 +16,6 @@ class DataValidator:
16
16
  """
17
17
 
18
18
  def validate_dict(
19
- self, data: dict, target_class: ClassDefinitionName = None, closed: bool = True
19
+ self, data: dict, target_class: ClassDefinitionName = None, closed: bool = True
20
20
  ) -> None:
21
21
  raise NotImplementedError
@@ -10,15 +10,15 @@ import click
10
10
 
11
11
  from linkml._version import __version__
12
12
 
13
- re_decl = re.compile("^(\\S+):$")
14
- re_start_yaml = re.compile("^```(\w+)$")
15
- re_end_yaml = re.compile("^```$")
16
- re_html_comment = re.compile("^<!-- (.+) -->")
13
+ re_decl = re.compile(r"^(\S+):$")
14
+ re_start_yaml = re.compile(r"^```(\w+)$")
15
+ re_end_yaml = re.compile(r"^```$")
16
+ re_html_comment = re.compile(r"^<!-- (.+) -->")
17
17
 
18
18
 
19
19
  @dataclass
20
20
  class Block:
21
- category: str = None ## yaml, bash, python, ...
21
+ category: str = None # yaml, bash, python, ...
22
22
  title: str = None
23
23
  content: str = None
24
24
  output: str = None
@@ -71,9 +71,7 @@ def execute_blocks(directory: str, blocks: List[Block]) -> List[str]:
71
71
  outpath = cmd[pos + 1 :]
72
72
  cmd = cmd[0:pos]
73
73
  if len(outpath) > 1:
74
- raise Exception(
75
- f"Maximim 1 token after > in {block.content}. Got: {outpath}"
76
- )
74
+ raise Exception(f"Maximum 1 token after > in {block.content}. Got: {outpath}")
77
75
  outpath = str(Path(directory, *outpath))
78
76
  logging.info(f"OUTPATH = {outpath}")
79
77
  else:
@@ -91,7 +89,7 @@ def execute_blocks(directory: str, blocks: List[Block]) -> List[str]:
91
89
  if r.returncode == 0:
92
90
  err(f"Command unexpectedly succeeded: {cmd}")
93
91
  else:
94
- logging.info(f"Failed as expected")
92
+ logging.info("Failed as expected")
95
93
  if block.error:
96
94
  logging.info(f"ERR [sample] = ...{block.error[-200:]}")
97
95
  else:
@@ -100,7 +98,7 @@ def execute_blocks(directory: str, blocks: List[Block]) -> List[str]:
100
98
  if r.returncode != 0:
101
99
  err(f"Command failed: {cmd}")
102
100
  else:
103
- logging.info(f"Success!")
101
+ logging.info("Success!")
104
102
  elif block.is_stdout():
105
103
  if "compare_rdf" in block.annotations:
106
104
  logging.warning(
@@ -110,7 +108,7 @@ def execute_blocks(directory: str, blocks: List[Block]) -> List[str]:
110
108
  if last_block.output.strip() != block.content.strip():
111
109
  err(f"Mismatch: {str(last_block.output)} != {block.content}")
112
110
  else:
113
- logging.info(f"Hurray! Contents match!")
111
+ logging.info("Hurray! Contents match!")
114
112
  else:
115
113
  logging.info("No comparison performed")
116
114
  else:
@@ -197,7 +195,7 @@ def cli(inputs, directory):
197
195
  Example:
198
196
 
199
197
  export PYTHONPATH=`pwd`
200
- python -m linkml.utils.execute_tutorial -d /tmp/tutorial/ sphinx/intro/tutorial01.md
198
+ python -m linkml.utils.execute_tutorial -d /tmp/tutorial/ docs/intro/tutorial01.md
201
199
 
202
200
  """
203
201
  logging.basicConfig(level=logging.INFO)