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
@@ -1,15 +1,21 @@
1
1
  import logging
2
2
  import re
3
- from copy import deepcopy, copy
3
+ from copy import copy
4
4
  from dataclasses import dataclass
5
- from typing import Callable, List, Dict, Any, Union
5
+ from typing import Any, Callable, Dict, List, Union
6
6
 
7
7
  import click
8
8
  import yaml
9
9
  from linkml_runtime import SchemaView
10
- from linkml_runtime.dumpers import json_dumper, yaml_dumper
11
- from linkml_runtime.linkml_model import (ClassDefinition, ClassDefinitionName,
12
- SchemaDefinition, SlotDefinition, EnumDefinition, TypeDefinition)
10
+ from linkml_runtime.dumpers import json_dumper
11
+ from linkml_runtime.linkml_model import (
12
+ ClassDefinition,
13
+ ClassDefinitionName,
14
+ EnumDefinition,
15
+ SchemaDefinition,
16
+ SlotDefinition,
17
+ TypeDefinition,
18
+ )
13
19
  from linkml_runtime.utils.formatutils import camelcase, underscore
14
20
  from linkml_runtime.utils.yamlutils import YAMLRoot
15
21
 
@@ -87,7 +93,7 @@ class SchemaFixer:
87
93
  tree_roots = [c for c in sv.all_classes().values() if c.tree_root]
88
94
  if len(tree_roots) > 0:
89
95
  if force:
90
- logging.info(f"Forcing addition of containers")
96
+ logging.info("Forcing addition of containers")
91
97
  else:
92
98
  raise ValueError(f"Schema already has containers: {tree_roots}")
93
99
  container = ClassDefinition(class_name, tree_root=True)
@@ -122,15 +128,11 @@ class SchemaFixer:
122
128
  for s in sv.class_induced_slots(cn):
123
129
  ranges.add(s.range)
124
130
  top_level_classes = [
125
- c
126
- for c in sv.all_classes().values()
127
- if not c.tree_root and c.name not in ranges
131
+ c for c in sv.all_classes().values() if not c.tree_root and c.name not in ranges
128
132
  ]
129
133
  if must_have_identifier:
130
134
  top_level_classes = [
131
- c
132
- for c in top_level_classes
133
- if sv.get_identifier_slot(c.name) is not None
135
+ c for c in top_level_classes if sv.get_identifier_slot(c.name) is not None
134
136
  ]
135
137
  index_slots = []
136
138
  for c in top_level_classes:
@@ -290,7 +292,13 @@ class SchemaFixer:
290
292
  self.history = []
291
293
  self.history.append(txt)
292
294
 
293
- def fix_element_names(self, schema: SchemaDefinition, schema_dict: Dict[str, Any] = None, rules: Dict[str, Callable] = None, imports=False) -> Union[YAMLRoot, Dict]:
295
+ def fix_element_names(
296
+ self,
297
+ schema: SchemaDefinition,
298
+ schema_dict: Dict[str, Any] = None,
299
+ rules: Dict[str, Callable] = None,
300
+ imports=False,
301
+ ) -> Union[YAMLRoot, Dict]:
294
302
  """
295
303
  Changes element names to conform to naming conventions.
296
304
 
@@ -341,10 +349,12 @@ def main(verbose: int, quiet: bool):
341
349
 
342
350
  @main.command()
343
351
  @click.argument("input_schema")
344
- @click.option("--imports/--no-imports",
345
- default=False,
346
- show_default=True,
347
- help="Apply fix to referenced elements from modules")
352
+ @click.option(
353
+ "--imports/--no-imports",
354
+ default=False,
355
+ show_default=True,
356
+ help="Apply fix to referenced elements from modules",
357
+ )
348
358
  def fix_name(input_schema, **kwargs):
349
359
  """Fix element names to conform to naming conventions"""
350
360
  with open(input_schema) as f:
@@ -356,4 +366,4 @@ def fix_name(input_schema, **kwargs):
356
366
 
357
367
 
358
368
  if __name__ == "__main__":
359
- main()
369
+ main()
@@ -2,29 +2,29 @@ import logging
2
2
  import os
3
3
  from collections import OrderedDict
4
4
  from copy import deepcopy
5
- from typing import (Dict, Iterator, List, Mapping, Optional, Set, TextIO,
6
- Tuple, Union, cast)
5
+ from typing import Dict, Iterator, List, Mapping, Optional, Set, TextIO, Tuple, Union, cast
7
6
  from urllib.parse import urlparse
8
7
 
9
8
  from jsonasobj2 import values
10
- from linkml_runtime.linkml_model.meta import (ClassDefinition,
11
- ClassDefinitionName, ElementName,
12
- EnumDefinition,
13
- EnumDefinitionName,
14
- SchemaDefinition, SlotDefinition,
15
- SlotDefinitionName,
16
- TypeDefinition,
17
- TypeDefinitionName)
9
+ from linkml_runtime.linkml_model.meta import (
10
+ ClassDefinition,
11
+ ClassDefinitionName,
12
+ ElementName,
13
+ EnumDefinition,
14
+ EnumDefinitionName,
15
+ SchemaDefinition,
16
+ SlotDefinition,
17
+ SlotDefinitionName,
18
+ TypeDefinition,
19
+ TypeDefinitionName,
20
+ )
18
21
  from linkml_runtime.utils.context_utils import parse_import_map
19
- from linkml_runtime.utils.formatutils import (camelcase,
20
- mangled_attribute_name, sfx,
21
- underscore)
22
+ from linkml_runtime.utils.formatutils import camelcase, mangled_attribute_name, sfx, underscore
22
23
  from linkml_runtime.utils.metamodelcore import Bool
23
24
  from linkml_runtime.utils.namespaces import Namespaces
24
25
  from linkml_runtime.utils.yamlutils import TypedNode
25
26
 
26
- from linkml.utils.mergeutils import (merge_classes, merge_schemas, merge_slots,
27
- slot_usage_name)
27
+ from linkml.utils.mergeutils import merge_classes, merge_schemas, merge_slots, slot_usage_name
28
28
  from linkml.utils.rawloader import load_raw_schema
29
29
  from linkml.utils.schemasynopsis import SchemaSynopsis
30
30
 
@@ -56,9 +56,7 @@ class SchemaLoader:
56
56
  :param source_file_date: modification of source file
57
57
  :param source_file_size: size of source file
58
58
  """
59
- self.logger = (
60
- logger if logger is not None else logging.getLogger(self.__class__.__name__)
61
- )
59
+ self.logger = logger if logger is not None else logging.getLogger(self.__class__.__name__)
62
60
  if isinstance(data, SchemaDefinition):
63
61
  self.schema = data
64
62
  else:
@@ -77,17 +75,13 @@ class SchemaLoader:
77
75
  self.namespaces = namespaces if namespaces else Namespaces()
78
76
  self.useuris = useuris if useuris is not None else True
79
77
  self.importmap = (
80
- parse_import_map(importmap, self.base_dir)
81
- if importmap is not None
82
- else dict()
78
+ parse_import_map(importmap, self.base_dir) if importmap is not None else dict()
83
79
  )
84
80
  self.source_file_date = source_file_date
85
81
  self.source_file_size = source_file_size
86
82
  self.synopsis: Optional[SchemaSynopsis] = None
87
83
  self.schema_location: Optional[str] = None
88
- self.schema_defaults: Dict[
89
- str, str
90
- ] = {} # Map from schema URI to default namespace
84
+ self.schema_defaults: Dict[str, str] = {} # Map from schema URI to default namespace
91
85
  self.merge_modules = mergeimports
92
86
  self.emit_metadata = emit_metadata
93
87
 
@@ -135,9 +129,7 @@ class SchemaLoader:
135
129
  sname = os.path.join(self.importmap[pfx], ":".join(toks[1:]))
136
130
  else:
137
131
  sname = self.namespaces.uri_for(sname)
138
- sname = self.importmap.get(
139
- str(sname), sname
140
- ) # It may also use URI or other forms
132
+ sname = self.importmap.get(str(sname), sname) # It may also use URI or other forms
141
133
  import_schemadefinition = load_raw_schema(
142
134
  sname + ".yaml",
143
135
  base_dir=os.path.dirname(self.schema.source_file)
@@ -154,10 +146,12 @@ class SchemaLoader:
154
146
  f"Schema {import_schemadefinition.name} - version mismatch",
155
147
  import_schemadefinition.name,
156
148
  )
157
- # Note: for debugging purposes we also check whether the version came from the same spot. This should
158
- # be loosened to version only once we're sure that everything is working
159
- # TODO: The test below needs review -- there are cases where it fails because self.loaded[...][0] has the
160
- # full path name and loaded_schema[0] is just the local name
149
+ # Note: for debugging purposes we also check whether the version
150
+ # came from the same spot. This should be loosened to
151
+ # version only once we're sure that everything is working
152
+ # TODO: The test below needs review -- there are cases where it
153
+ # fails because self.loaded[...][0] has the full path name
154
+ # and loaded_schema[0] is just the local name
161
155
  # if self.loaded[import_schemadefinition.id] != loaded_schema:
162
156
  # self.raise_value_error(f"Schema imported from different files: "
163
157
  # f"{self.loaded[import_schemadefinition.id][0]} : {loaded_schema[0]}")
@@ -270,7 +264,7 @@ class SchemaLoader:
270
264
  from_schema = cls.from_schema
271
265
  if from_schema is None:
272
266
  from_schema = self.schema.id
273
- #if cls.from_schema is None:
267
+ # if cls.from_schema is None:
274
268
  # raise Exception(f"Class has no from_schema: {cls}")
275
269
  suffixed_cls_schema = sfx(from_schema)
276
270
  cls.class_uri = self.namespaces.uri_or_curie_for(
@@ -308,7 +302,6 @@ class SchemaLoader:
308
302
  slot.range = self.schema.default_range
309
303
 
310
304
  # Update enums
311
- merged_enums: List[EnumDefinitionName] = []
312
305
  for enum in self.schema.enums.values():
313
306
  if not enum.from_schema:
314
307
  enum.from_schema = self.schema.id
@@ -335,9 +328,7 @@ class SchemaLoader:
335
328
  typ.name,
336
329
  )
337
330
  if not typ.typeof and not typ.uri:
338
- self.raise_value_error(
339
- f'type "{typ.name}" does not declare a URI', typ.name
340
- )
331
+ self.raise_value_error(f'type "{typ.name}" does not declare a URI', typ.name)
341
332
  self.merge_type(typ, merged_types)
342
333
  if not typ.from_schema:
343
334
  typ.from_schema = self.schema.id
@@ -366,10 +357,7 @@ class SchemaLoader:
366
357
 
367
358
  # Propagate domain to containing class
368
359
  if slot.domain and slot.domain in self.schema.classes:
369
- if (
370
- slot.name not in self.schema.classes[slot.domain].slots
371
- and not slot.owner
372
- ):
360
+ if slot.name not in self.schema.classes[slot.domain].slots and not slot.owner:
373
361
  slot.owner = slot.name
374
362
  # Slot domains to not appear
375
363
  # self.schema.classes[slot.domain].slots.append(slot.name)
@@ -459,9 +447,7 @@ class SchemaLoader:
459
447
  )
460
448
 
461
449
  # Evaluate any slot inverses
462
- def domain_range_alignment(
463
- fwd_slot: SlotDefinition, inverse_slot: SlotDefinition
464
- ) -> bool:
450
+ def domain_range_alignment(fwd_slot: SlotDefinition, inverse_slot: SlotDefinition) -> bool:
465
451
  """Determine whether the range of fwd_slot is compatible with the domain of inverse_slot"""
466
452
  # TODO: Determine what to do about class and slot hierarchy
467
453
  if fwd_slot.range and fwd_slot.range not in self.schema.classes:
@@ -507,9 +493,7 @@ class SchemaLoader:
507
493
  )
508
494
 
509
495
  # Check for duplicate class and type names
510
- def check_dups(
511
- s1: Set[ElementName], s2: Set[ElementName]
512
- ) -> Tuple[List[ElementName], str]:
496
+ def check_dups(s1: Set[ElementName], s2: Set[ElementName]) -> Tuple[List[ElementName], str]:
513
497
  if s1.isdisjoint(s2):
514
498
  return [], ""
515
499
 
@@ -536,8 +520,7 @@ class SchemaLoader:
536
520
 
537
521
  # Check that the default range is valid
538
522
  default_range_needed = any(
539
- slot.range == self.schema.default_range
540
- for slot in self.schema.slots.values()
523
+ slot.range == self.schema.default_range for slot in self.schema.slots.values()
541
524
  )
542
525
  if (
543
526
  default_range_needed
@@ -648,9 +631,7 @@ class SchemaLoader:
648
631
  for cls in self.schema.classes.values():
649
632
  if cls.tree_root:
650
633
  if tree_root is not None:
651
- self.logger.warning(
652
- f"Duplicate tree_root: {cls.name} with {tree_root}"
653
- )
634
+ self.logger.warning(f"Duplicate tree_root: {cls.name} with {tree_root}")
654
635
  else:
655
636
  tree_root = cls.name
656
637
 
@@ -674,9 +655,7 @@ class SchemaLoader:
674
655
  f'{typ}: "{name}" - ":" not allowed in identifier', name
675
656
  )
676
657
 
677
- def merge_enum(
678
- self, enum: EnumDefinition, merged_enums: List[EnumDefinitionName]
679
- ) -> None:
658
+ def merge_enum(self, enum: EnumDefinition, merged_enums: List[EnumDefinitionName]) -> None:
680
659
  """
681
660
  Merge parent enumeration information into target enum
682
661
 
@@ -695,9 +674,7 @@ class SchemaLoader:
695
674
  enum.is_a,
696
675
  )
697
676
 
698
- def merge_slot(
699
- self, slot: SlotDefinition, merged_slots: List[SlotDefinitionName]
700
- ) -> None:
677
+ def merge_slot(self, slot: SlotDefinition, merged_slots: List[SlotDefinitionName]) -> None:
701
678
  """
702
679
  Merge parent slot information into target slot
703
680
 
@@ -731,9 +708,7 @@ class SchemaLoader:
731
708
  )
732
709
  merged_slots.append(slot.name)
733
710
 
734
- def merge_class(
735
- self, cls: ClassDefinition, merged_classes: List[ClassDefinitionName]
736
- ) -> None:
711
+ def merge_class(self, cls: ClassDefinition, merged_classes: List[ClassDefinitionName]) -> None:
737
712
  """
738
713
  Merge parent class information into target class
739
714
 
@@ -745,9 +720,7 @@ class SchemaLoader:
745
720
  if cls.is_a:
746
721
  if cls.is_a in self.schema.classes:
747
722
  self.merge_class(self.schema.classes[cls.is_a], merged_classes)
748
- merge_classes(
749
- self.schema, cls, self.schema.classes[cls.is_a], False
750
- )
723
+ merge_classes(self.schema, cls, self.schema.classes[cls.is_a], False)
751
724
  else:
752
725
  self.raise_value_error(
753
726
  f'Class: "{cls.name}" - unknown is_a reference: {cls.is_a}',
@@ -769,9 +742,7 @@ class SchemaLoader:
769
742
  definitions that are introduced strictly as usages and add them to the slots component
770
743
  """
771
744
  visited: Set[ClassDefinitionName] = set()
772
- visited_usages: Set[
773
- SlotDefinitionName
774
- ] = set() # Slots that are or will be mangled
745
+ visited_usages: Set[SlotDefinitionName] = set() # Slots that are or will be mangled
775
746
 
776
747
  def located_aliased_parent_slot(
777
748
  owning_class: ClassDefinition, usage_slot: SlotDefinition
@@ -779,26 +750,16 @@ class SchemaLoader:
779
750
  """Determine whether we are overriding an attributes style slot in the parent class
780
751
  Preconditions: usage_slot is NOT in schema.slots
781
752
  """
782
- usage_attribute_name = mangled_attribute_name(
783
- owning_class.name, usage_slot.name
784
- )
753
+ usage_attribute_name = mangled_attribute_name(owning_class.name, usage_slot.name)
785
754
  if owning_class.is_a:
786
- parent_slot_name = mangled_attribute_name(
787
- owning_class.is_a, usage_slot.name
788
- )
789
- if (
790
- parent_slot_name in self.schema.slots
791
- or parent_slot_name in visited_usages
792
- ):
755
+ parent_slot_name = mangled_attribute_name(owning_class.is_a, usage_slot.name)
756
+ if parent_slot_name in self.schema.slots or parent_slot_name in visited_usages:
793
757
  usage_slot.is_a = parent_slot_name
794
758
  visited_usages.add(usage_attribute_name)
795
759
  return True
796
760
  for mixin in owning_class.mixins:
797
761
  mixin_slot_name = mangled_attribute_name(mixin, usage_slot.name)
798
- if (
799
- mixin_slot_name in self.schema.slots
800
- or mixin_slot_name in visited_usages
801
- ):
762
+ if mixin_slot_name in self.schema.slots or mixin_slot_name in visited_usages:
802
763
  usage_slot.is_a = mixin_slot_name
803
764
  visited_usages.add(usage_attribute_name)
804
765
  return True
@@ -921,9 +882,7 @@ class SchemaLoader:
921
882
  if new_val:
922
883
  setattr(new_slot, metaslot_name, new_val)
923
884
 
924
- def merge_type(
925
- self, typ: TypeDefinition, merged_types: List[TypeDefinitionName]
926
- ) -> None:
885
+ def merge_type(self, typ: TypeDefinition, merged_types: List[TypeDefinitionName]) -> None:
927
886
  """
928
887
  Merge parent type information into target type
929
888
  :param typ: target type
@@ -1031,9 +990,7 @@ class SchemaLoader:
1031
990
  return underscore(slot.alias if slot.alias else slot.name)
1032
991
 
1033
992
  @staticmethod
1034
- def raise_value_error(
1035
- error: str, loc_str: Optional[Union[TypedNode, str]] = None
1036
- ) -> None:
993
+ def raise_value_error(error: str, loc_str: Optional[Union[TypedNode, str]] = None) -> None:
1037
994
  SchemaLoader.raise_value_errors(error, loc_str)
1038
995
 
1039
996
  @staticmethod
@@ -1055,9 +1012,7 @@ class SchemaLoader:
1055
1012
  locs = "\n\t".join(TypedNode.yaml_loc(e, suffix="") for e in loc_str)
1056
1013
  self.logger.warning(f"{warning}\n\t{locs}")
1057
1014
  else:
1058
- self.logger.warning(
1059
- f'{warning}\n\t{TypedNode.yaml_loc(loc_str, suffix="")}'
1060
- )
1015
+ self.logger.warning(f'{warning}\n\t{TypedNode.yaml_loc(loc_str, suffix="")}')
1061
1016
 
1062
1017
  def _get_base_dir(self, stated_base: str) -> Optional[str]:
1063
1018
  if stated_base:
@@ -1,24 +1,35 @@
1
- import logging
2
1
  from dataclasses import dataclass, field
3
- from typing import Dict, List, Set, Union, cast
4
-
5
- from linkml_runtime.linkml_model.meta import (ClassDefinition,
6
- ClassDefinitionName, Definition,
7
- DefinitionName, Element,
8
- ElementName, EnumDefinition,
9
- EnumDefinitionName,
10
- SchemaDefinition, SlotDefinition,
11
- SlotDefinitionName,
12
- SubsetDefinitionName,
13
- TypeDefinition,
14
- TypeDefinitionName)
2
+ from typing import Dict, List, Set, Union
3
+
4
+ from linkml_runtime.linkml_model.meta import (
5
+ ClassDefinition,
6
+ ClassDefinitionName,
7
+ Definition,
8
+ DefinitionName,
9
+ Element,
10
+ ElementName,
11
+ EnumDefinition,
12
+ EnumDefinitionName,
13
+ SchemaDefinition,
14
+ SlotDefinition,
15
+ SlotDefinitionName,
16
+ SubsetDefinitionName,
17
+ TypeDefinition,
18
+ TypeDefinitionName,
19
+ )
15
20
  from linkml_runtime.utils.metamodelcore import empty_dict
16
21
  from linkml_runtime.utils.yamlutils import TypedNode
17
22
  from rdflib import URIRef
18
23
 
19
- from linkml.utils.typereferences import (ClassType, EnumType, References,
20
- RefType, SlotType, SubsetType,
21
- TypeType)
24
+ from linkml.utils.typereferences import (
25
+ ClassType,
26
+ EnumType,
27
+ References,
28
+ RefType,
29
+ SlotType,
30
+ SubsetType,
31
+ TypeType,
32
+ )
22
33
 
23
34
 
24
35
  def empty_references() -> field:
@@ -33,29 +44,17 @@ class SchemaSynopsis:
33
44
  schema: SchemaDefinition = field(repr=False, compare=False)
34
45
 
35
46
  # References by type -- set by add_ref
36
- typerefs: Dict[
37
- TypeDefinitionName, References
38
- ] = empty_dict() # Type name to all references
39
- slotrefs: Dict[
40
- SlotDefinitionName, References
41
- ] = empty_dict() # Slot name to all references
42
- classrefs: Dict[
43
- ClassDefinitionName, References
44
- ] = empty_dict() # Class name to all references
45
- subsetrefs: Dict[
46
- SubsetDefinitionName, References
47
- ] = empty_dict() # Subset name to references
48
- enumrefs: Dict[
49
- EnumDefinitionName, References
50
- ] = empty_dict() # Enum name to references
47
+ typerefs: Dict[TypeDefinitionName, References] = empty_dict() # Type name to all references
48
+ slotrefs: Dict[SlotDefinitionName, References] = empty_dict() # Slot name to all references
49
+ classrefs: Dict[ClassDefinitionName, References] = empty_dict() # Class name to all references
50
+ subsetrefs: Dict[SubsetDefinitionName, References] = empty_dict() # Subset name to references
51
+ enumrefs: Dict[EnumDefinitionName, References] = empty_dict() # Enum name to references
51
52
 
52
53
  # Type specific
53
54
  typebases: Dict[
54
55
  str, Set[TypeDefinitionName]
55
56
  ] = empty_dict() # Base referencing types (direct and indirect)
56
- typeofs: Dict[
57
- TypeDefinitionName, TypeDefinitionName
58
- ] = empty_dict() # Type to specializations
57
+ typeofs: Dict[TypeDefinitionName, TypeDefinitionName] = empty_dict() # Type to specializations
59
58
 
60
59
  # Slot specific
61
60
  slotclasses: Dict[
@@ -70,9 +69,7 @@ class SchemaSynopsis:
70
69
  owners: Dict[
71
70
  SlotDefinitionName, Set[ClassDefinitionName]
72
71
  ] = empty_dict() # Slot to owning classes (sb. 1)
73
- inverses: Dict[
74
- str, Set[str]
75
- ] = empty_dict() # Slots declared as inverses of other slots
72
+ inverses: Dict[str, Set[str]] = empty_dict() # Slots declared as inverses of other slots
76
73
 
77
74
  # Class specific
78
75
  ownslots: Dict[
@@ -88,18 +85,14 @@ class SchemaSynopsis:
88
85
 
89
86
  # Slot or Class (Definition) specific
90
87
  roots: References = empty_references() # Definitions with no parents
91
- isarefs: Dict[
92
- DefinitionName, References
93
- ] = empty_dict() # Definition to isa references
88
+ isarefs: Dict[DefinitionName, References] = empty_dict() # Definition to isa references
94
89
  mixinrefs: Dict[
95
90
  DefinitionName, References
96
91
  ] = empty_dict() # Mixin to referencing classes or slots
97
92
  mixins: References = empty_references() # Definitions declared as mixin
98
93
  abstracts: References = empty_references() # Definitions declared as abstract
99
94
  applytos: References = empty_references() # Definitions that include applytos
100
- applytorefs: Dict[
101
- DefinitionName, References
102
- ] = empty_dict() # Definition to applyier
95
+ applytorefs: Dict[DefinitionName, References] = empty_dict() # Definition to applyier
103
96
 
104
97
  # Slot or Type specific
105
98
  rangerefs: Dict[
@@ -131,9 +124,7 @@ class SchemaSynopsis:
131
124
  for slotname in owned_slots:
132
125
  self.owners.setdefault(slotname, set()).add(cls.name)
133
126
 
134
- def summarize_slot_definition(
135
- self, k: SlotDefinitionName, v: SlotDefinition
136
- ) -> None:
127
+ def summarize_slot_definition(self, k: SlotDefinitionName, v: SlotDefinition) -> None:
137
128
  """
138
129
  Summarize a slot definition
139
130
  :param k: slot name
@@ -172,9 +163,7 @@ class SchemaSynopsis:
172
163
  if v.base:
173
164
  self.typebases.setdefault(v.base, set()).add(k)
174
165
 
175
- def summarize_class_definition(
176
- self, k: ClassDefinitionName, v: ClassDefinition
177
- ) -> None:
166
+ def summarize_class_definition(self, k: ClassDefinitionName, v: ClassDefinition) -> None:
178
167
  """
179
168
  Summarize class definition element
180
169
 
@@ -203,9 +192,7 @@ class SchemaSynopsis:
203
192
  """
204
193
  self.summarize_element(EnumType, k, v)
205
194
 
206
- def summarize_definition(
207
- self, typ: RefType, k: DefinitionName, v: Definition
208
- ) -> None:
195
+ def summarize_definition(self, typ: RefType, k: DefinitionName, v: Definition) -> None:
209
196
  """
210
197
  Summarize slot and class definitions
211
198
 
@@ -239,13 +226,11 @@ class SchemaSynopsis:
239
226
 
240
227
  :param typ: element type
241
228
  :param k: element name
242
- :param v: element deffinition
229
+ :param v: element definition
243
230
  :return:
244
231
  """
245
232
  if k != v.name:
246
- raise ValueError(
247
- "{typ} name mismatch: {k} != {v.name}"
248
- ) # should never happen
233
+ raise ValueError("{typ} name mismatch: {k} != {v.name}") # should never happen
249
234
  for subset in v.in_subset:
250
235
  self.add_ref(typ, k, SubsetType, subset)
251
236
 
@@ -277,9 +262,9 @@ class SchemaSynopsis:
277
262
  fromtype, fromname
278
263
  )
279
264
  elif totype is SubsetType:
280
- self.subsetrefs.setdefault(
281
- SubsetDefinitionName(toname), References()
282
- ).addref(fromtype, fromname)
265
+ self.subsetrefs.setdefault(SubsetDefinitionName(toname), References()).addref(
266
+ fromtype, fromname
267
+ )
283
268
  elif totype is EnumType:
284
269
  self.enumrefs.setdefault(SlotDefinitionName(toname), References()).addref(
285
270
  fromtype, fromname
@@ -289,8 +274,7 @@ class SchemaSynopsis:
289
274
 
290
275
  def _ancestor_is_owned(self, slot: SlotDefinition) -> bool:
291
276
  return bool(slot.is_a) and (
292
- slot.is_a in self.owners
293
- or self._ancestor_is_owned(self.schema.slots[slot.is_a])
277
+ slot.is_a in self.owners or self._ancestor_is_owned(self.schema.slots[slot.is_a])
294
278
  )
295
279
 
296
280
  def errors(self) -> List[str]:
@@ -307,18 +291,14 @@ class SchemaSynopsis:
307
291
  undefined_slots = set(self.slotrefs.keys()) - set(self.schema.slots.keys())
308
292
  if undefined_slots:
309
293
  rval += [
310
- f"\tUndefined slot references: "
311
- f"{', '.join(format_undefineds(undefined_slots))}"
294
+ f"\tUndefined slot references: " f"{', '.join(format_undefineds(undefined_slots))}"
312
295
  ]
313
296
  undefined_types = set(self.typerefs.keys()) - set(self.schema.types.keys())
314
297
  if undefined_types:
315
298
  rval += [
316
- f"\tUndefined type references: "
317
- f"{', '.join(format_undefineds(undefined_types))}"
299
+ f"\tUndefined type references: " f"{', '.join(format_undefineds(undefined_types))}"
318
300
  ]
319
- undefined_subsets = set(self.subsetrefs.keys()) - set(
320
- self.schema.subsets.keys()
321
- )
301
+ undefined_subsets = set(self.subsetrefs.keys()) - set(self.schema.subsets.keys())
322
302
  if undefined_subsets:
323
303
  rval += [
324
304
  f"\tUndefined subset references: "
@@ -327,8 +307,7 @@ class SchemaSynopsis:
327
307
  undefined_enums = set(self.enumrefs.keys()) - set(self.schema.enums.keys())
328
308
  if undefined_enums:
329
309
  rval += [
330
- f"\tUndefined enun references: "
331
- f"{', '.join(format_undefineds(undefined_enums))}"
310
+ f"\tUndefined enun references: " f"{', '.join(format_undefineds(undefined_enums))}"
332
311
  ]
333
312
 
334
313
  # Inlined slots must be multivalued (not a inviolable rule, but we make assumptions about this elsewhere in
@@ -416,9 +395,7 @@ class SchemaSynopsis:
416
395
  else:
417
396
  unowned_slots.add(slotname)
418
397
  if n_unreferenced_descendants:
419
- rval += [
420
- f"\tUnreferenced descendants of owned slots: {n_unreferenced_descendants}"
421
- ]
398
+ rval += [f"\tUnreferenced descendants of owned slots: {n_unreferenced_descendants}"]
422
399
  if unowned_slots:
423
400
  rval += [f"\t* Unowned slots: {', '.join(sorted(unowned_slots))}"]
424
401