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.
- linkml/__init__.py +2 -6
- linkml/_version.py +1 -1
- linkml/generators/PythonGenNotes.md +4 -4
- linkml/generators/__init__.py +26 -5
- linkml/generators/common/type_designators.py +27 -22
- linkml/generators/csvgen.py +4 -10
- linkml/generators/docgen/class.md.jinja2 +7 -0
- linkml/generators/docgen/class_diagram.md.jinja2 +0 -6
- linkml/generators/docgen/subset.md.jinja2 +54 -13
- linkml/generators/docgen.py +94 -92
- linkml/generators/dotgen.py +5 -9
- linkml/generators/erdiagramgen.py +58 -53
- linkml/generators/excelgen.py +10 -16
- linkml/generators/golanggen.py +11 -21
- linkml/generators/golrgen.py +4 -13
- linkml/generators/graphqlgen.py +3 -11
- linkml/generators/javagen.py +8 -15
- linkml/generators/jsonldcontextgen.py +7 -36
- linkml/generators/jsonldgen.py +14 -12
- linkml/generators/jsonschemagen.py +183 -136
- linkml/generators/linkmlgen.py +1 -1
- linkml/generators/markdowngen.py +40 -89
- linkml/generators/namespacegen.py +1 -2
- linkml/generators/oocodegen.py +22 -25
- linkml/generators/owlgen.py +48 -49
- linkml/generators/prefixmapgen.py +6 -14
- linkml/generators/projectgen.py +7 -14
- linkml/generators/protogen.py +3 -5
- linkml/generators/pydanticgen.py +85 -73
- linkml/generators/pythongen.py +89 -157
- linkml/generators/rdfgen.py +5 -11
- linkml/generators/shaclgen.py +32 -18
- linkml/generators/shexgen.py +19 -24
- linkml/generators/sparqlgen.py +5 -13
- linkml/generators/sqlalchemy/__init__.py +3 -2
- linkml/generators/sqlalchemy/sqlalchemy_declarative_template.py +7 -7
- linkml/generators/sqlalchemy/sqlalchemy_imperative_template.py +3 -3
- linkml/generators/sqlalchemygen.py +29 -27
- linkml/generators/sqlddlgen.py +34 -26
- linkml/generators/sqltablegen.py +21 -21
- linkml/generators/sssomgen.py +11 -13
- linkml/generators/summarygen.py +2 -4
- linkml/generators/terminusdbgen.py +7 -19
- linkml/generators/typescriptgen.py +10 -18
- linkml/generators/yamlgen.py +0 -2
- linkml/generators/yumlgen.py +23 -71
- linkml/linter/cli.py +4 -11
- linkml/linter/config/datamodel/config.py +17 -47
- linkml/linter/linter.py +2 -4
- linkml/linter/rules.py +34 -48
- linkml/reporting/__init__.py +2 -0
- linkml/reporting/model.py +9 -24
- linkml/transformers/relmodel_transformer.py +20 -33
- linkml/transformers/schema_renamer.py +14 -10
- linkml/utils/converter.py +15 -15
- linkml/utils/datautils.py +9 -24
- linkml/utils/datavalidator.py +2 -2
- linkml/utils/execute_tutorial.py +10 -12
- linkml/utils/generator.py +74 -92
- linkml/utils/helpers.py +4 -2
- linkml/utils/ifabsent_functions.py +23 -15
- linkml/utils/mergeutils.py +19 -35
- linkml/utils/rawloader.py +2 -6
- linkml/utils/schema_builder.py +31 -19
- linkml/utils/schema_fixer.py +28 -18
- linkml/utils/schemaloader.py +44 -89
- linkml/utils/schemasynopsis.py +50 -73
- linkml/utils/sqlutils.py +40 -30
- linkml/utils/typereferences.py +9 -6
- linkml/utils/validation.py +4 -5
- linkml/validators/__init__.py +2 -0
- linkml/validators/jsonschemavalidator.py +104 -53
- linkml/validators/sparqlvalidator.py +5 -15
- linkml/workspaces/datamodel/workspaces.py +13 -30
- linkml/workspaces/example_runner.py +75 -68
- {linkml-1.5.5.dist-info → linkml-1.5.7.dist-info}/METADATA +2 -2
- linkml-1.5.7.dist-info/RECORD +109 -0
- linkml-1.5.5.dist-info/RECORD +0 -109
- {linkml-1.5.5.dist-info → linkml-1.5.7.dist-info}/LICENSE +0 -0
- {linkml-1.5.5.dist-info → linkml-1.5.7.dist-info}/WHEEL +0 -0
- {linkml-1.5.5.dist-info → linkml-1.5.7.dist-info}/entry_points.txt +0 -0
linkml/utils/schema_fixer.py
CHANGED
@@ -1,15 +1,21 @@
|
|
1
1
|
import logging
|
2
2
|
import re
|
3
|
-
from copy import
|
3
|
+
from copy import copy
|
4
4
|
from dataclasses import dataclass
|
5
|
-
from typing import
|
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
|
11
|
-
from linkml_runtime.linkml_model import (
|
12
|
-
|
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(
|
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(
|
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(
|
345
|
-
|
346
|
-
|
347
|
-
|
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()
|
linkml/utils/schemaloader.py
CHANGED
@@ -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
|
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 (
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
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
|
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
|
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
|
158
|
-
#
|
159
|
-
#
|
160
|
-
#
|
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
|
-
|
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:
|
linkml/utils/schemasynopsis.py
CHANGED
@@ -1,24 +1,35 @@
|
|
1
|
-
import logging
|
2
1
|
from dataclasses import dataclass, field
|
3
|
-
from typing import Dict, List, Set, Union
|
4
|
-
|
5
|
-
from linkml_runtime.linkml_model.meta import (
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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 (
|
20
|
-
|
21
|
-
|
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
|
-
|
38
|
-
] = empty_dict() #
|
39
|
-
|
40
|
-
|
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
|
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
|
-
|
282
|
-
)
|
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
|
|