linkml 1.9.0rc1__py3-none-any.whl → 1.9.1__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/generators/common/build.py +1 -7
- linkml/generators/common/ifabsent_processor.py +20 -20
- linkml/generators/common/lifecycle.py +2 -1
- linkml/generators/common/naming.py +1 -1
- linkml/generators/common/template.py +5 -5
- linkml/generators/common/type_designators.py +1 -3
- linkml/generators/csvgen.py +3 -3
- linkml/generators/docgen.py +20 -25
- linkml/generators/dotgen.py +4 -4
- linkml/generators/erdiagramgen.py +7 -7
- linkml/generators/excelgen.py +2 -3
- linkml/generators/golanggen.py +2 -2
- linkml/generators/golrgen.py +3 -3
- linkml/generators/jsonldcontextgen.py +4 -4
- linkml/generators/jsonschemagen.py +5 -5
- linkml/generators/markdowngen.py +8 -10
- linkml/generators/mermaidclassdiagramgen.py +2 -2
- linkml/generators/oocodegen.py +10 -10
- linkml/generators/owlgen.py +19 -18
- linkml/generators/plantumlgen.py +15 -15
- linkml/generators/prefixmapgen.py +5 -5
- linkml/generators/projectgen.py +10 -10
- linkml/generators/pydanticgen/array.py +15 -21
- linkml/generators/pydanticgen/build.py +4 -4
- linkml/generators/pydanticgen/includes.py +1 -1
- linkml/generators/pydanticgen/pydanticgen.py +24 -28
- linkml/generators/pydanticgen/template.py +36 -36
- linkml/generators/pythongen.py +21 -29
- linkml/generators/rdfgen.py +2 -2
- linkml/generators/shaclgen.py +9 -9
- linkml/generators/shexgen.py +3 -3
- linkml/generators/sparqlgen.py +3 -3
- linkml/generators/sqlalchemygen.py +2 -2
- linkml/generators/terminusdbgen.py +2 -3
- linkml/generators/typescriptgen.py +3 -3
- linkml/generators/yumlgen.py +13 -13
- linkml/linter/cli.py +1 -1
- linkml/linter/config/datamodel/config.py +207 -213
- linkml/linter/config/datamodel/config.yaml +51 -3
- linkml/linter/config/default.yaml +3 -0
- linkml/linter/formatters/markdown_formatter.py +2 -2
- linkml/linter/linter.py +4 -3
- linkml/linter/rules.py +38 -19
- linkml/reporting/model.py +11 -15
- linkml/transformers/logical_model_transformer.py +9 -8
- linkml/transformers/relmodel_transformer.py +6 -6
- linkml/transformers/schema_renamer.py +2 -2
- linkml/utils/converter.py +1 -1
- linkml/utils/deprecation.py +3 -3
- linkml/utils/execute_tutorial.py +5 -6
- linkml/utils/generator.py +17 -16
- linkml/utils/helpers.py +2 -2
- linkml/utils/logictools.py +5 -4
- linkml/utils/mergeutils.py +51 -5
- linkml/utils/schema_builder.py +8 -8
- linkml/utils/schema_fixer.py +8 -8
- linkml/utils/schemaloader.py +16 -15
- linkml/utils/schemasynopsis.py +29 -29
- linkml/utils/sqlutils.py +5 -5
- linkml/utils/typereferences.py +5 -6
- linkml/utils/validation.py +2 -2
- linkml/validator/cli.py +7 -6
- linkml/validator/loaders/delimited_file_loader.py +2 -1
- linkml/validator/loaders/json_loader.py +2 -1
- linkml/validator/loaders/loader.py +2 -1
- linkml/validator/loaders/passthrough_loader.py +2 -1
- linkml/validator/loaders/yaml_loader.py +2 -1
- linkml/validator/plugins/jsonschema_validation_plugin.py +2 -1
- linkml/validator/plugins/pydantic_validation_plugin.py +2 -1
- linkml/validator/plugins/recommended_slots_plugin.py +3 -2
- linkml/validator/plugins/shacl_validation_plugin.py +2 -1
- linkml/validator/plugins/validation_plugin.py +1 -1
- linkml/validator/report.py +3 -3
- linkml/validator/validator.py +3 -2
- linkml/validators/jsonschemavalidator.py +6 -5
- linkml/workspaces/datamodel/workspaces.py +21 -26
- linkml/workspaces/example_runner.py +7 -6
- {linkml-1.9.0rc1.dist-info → linkml-1.9.1.dist-info}/METADATA +6 -9
- {linkml-1.9.0rc1.dist-info → linkml-1.9.1.dist-info}/RECORD +82 -82
- {linkml-1.9.0rc1.dist-info → linkml-1.9.1.dist-info}/WHEEL +1 -1
- {linkml-1.9.0rc1.dist-info → linkml-1.9.1.dist-info}/LICENSE +0 -0
- {linkml-1.9.0rc1.dist-info → linkml-1.9.1.dist-info}/entry_points.txt +0 -0
@@ -38,7 +38,7 @@ classes:
|
|
38
38
|
configured by a configuration file.
|
39
39
|
attributes:
|
40
40
|
no_empty_title:
|
41
|
-
range:
|
41
|
+
range: NoEmptyTitleConfig
|
42
42
|
description: >-
|
43
43
|
Disallow empty titles on schema elements. Autofix will transform the element's
|
44
44
|
name into a title.
|
@@ -141,8 +141,14 @@ classes:
|
|
141
141
|
multivalued: true
|
142
142
|
description: >-
|
143
143
|
Default: []
|
144
|
-
All elements except the ones with names specified in this list will be checked
|
145
|
-
|
144
|
+
All elements except the ones with names specified in this list will be checked
|
145
|
+
exclude_type:
|
146
|
+
range: MetamodelElementTypeEnum
|
147
|
+
multivalued: true
|
148
|
+
description: >-
|
149
|
+
Default: []
|
150
|
+
Elements of all types (ClassDefinition, SlotDefinition, EnumDefinition, PermissibleValue, etc.) except the types specified in this list will be checked
|
151
|
+
|
146
152
|
StandardNamingConfig:
|
147
153
|
description: >-
|
148
154
|
Additional configuration options for the `standard_naming` rule
|
@@ -153,6 +159,24 @@ classes:
|
|
153
159
|
description: >-
|
154
160
|
Default: false
|
155
161
|
If true, permissible values will be checked for snake_case, otherwise UPPER_SNAKE
|
162
|
+
exclude_type:
|
163
|
+
range: MetamodelElementTypeEnum
|
164
|
+
multivalued: true
|
165
|
+
description: >-
|
166
|
+
Default: []
|
167
|
+
Elements of all types (ClassDefinition, SlotDefinition, EnumDefinition, PermissibleValue, etc.) except the types specified in this list will be checked
|
168
|
+
class_pattern:
|
169
|
+
range: string
|
170
|
+
required: false
|
171
|
+
description: >
|
172
|
+
If specified, permissible format pattern for classes can be provided either as one of the following:
|
173
|
+
snake, uppersnake, camel, uppercamel, kebab or as regular expression (e.g. "[a-z][_a-z0-9]+" for snake case)
|
174
|
+
slot_pattern:
|
175
|
+
range: string
|
176
|
+
required: false
|
177
|
+
description: >
|
178
|
+
If specified, permissible format pattern for slots can be provided either as one of the following:
|
179
|
+
snake, uppersnake, camel, uppercamel, kebab or as regular expression (e.g. "[a-z][_a-z0-9]+" for snake case)
|
156
180
|
|
157
181
|
CanonicalPrefixesConfig:
|
158
182
|
description: >-
|
@@ -168,6 +192,18 @@ classes:
|
|
168
192
|
validation. The order of names is meaningful and will be preserved. See:
|
169
193
|
https://github.com/linkml/prefixmaps#usage
|
170
194
|
|
195
|
+
NoEmptyTitleConfig:
|
196
|
+
description: >-
|
197
|
+
Additional configuration options for the no_empty_title rule
|
198
|
+
is_a: RuleConfig
|
199
|
+
attributes:
|
200
|
+
exclude_type:
|
201
|
+
range: MetamodelElementTypeEnum
|
202
|
+
multivalued: true
|
203
|
+
description: >-
|
204
|
+
Default: []
|
205
|
+
Elements of all types (ClassDefinition, SlotDefinition, EnumDefinition, PermissibleValue, etc.) except the types specified in this list will be checked
|
206
|
+
|
171
207
|
enums:
|
172
208
|
ExtendableConfigs:
|
173
209
|
description: The permissible values for the `extends` field of a config file
|
@@ -184,3 +220,15 @@ enums:
|
|
184
220
|
description: A violation of a rule at this level is a minor issue that should be fixed
|
185
221
|
error:
|
186
222
|
description: A violation of a rule at this level is a major issue that must be fixed
|
223
|
+
MetamodelElementTypeEnum:
|
224
|
+
description: >-
|
225
|
+
The permissible values for the exclude_type slot
|
226
|
+
permissible_values:
|
227
|
+
class_definition:
|
228
|
+
meaning: linkml:ClassDefinition
|
229
|
+
enum_definition:
|
230
|
+
meaning: linkml:EnumDefinition
|
231
|
+
permissible_value:
|
232
|
+
meaning: linkml:PermissibleValue
|
233
|
+
slot_definition:
|
234
|
+
meaning: linkml:SlotDefinition
|
@@ -1,6 +1,7 @@
|
|
1
1
|
rules:
|
2
2
|
no_empty_title:
|
3
3
|
level: disabled
|
4
|
+
exclude_type: []
|
4
5
|
permissible_values_format:
|
5
6
|
level: disabled
|
6
7
|
format: uppersnake
|
@@ -12,6 +13,7 @@ rules:
|
|
12
13
|
level: disabled
|
13
14
|
include: []
|
14
15
|
exclude: []
|
16
|
+
exclude_type: []
|
15
17
|
no_xsd_int_type:
|
16
18
|
level: disabled
|
17
19
|
no_invalid_slot_usage:
|
@@ -19,6 +21,7 @@ rules:
|
|
19
21
|
standard_naming:
|
20
22
|
level: disabled
|
21
23
|
permissible_values_upper_case: false
|
24
|
+
exclude_type: []
|
22
25
|
canonical_prefixes:
|
23
26
|
level: disabled
|
24
27
|
prefixmaps_contexts:
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from collections import defaultdict
|
2
|
-
from typing import IO, Any,
|
2
|
+
from typing import IO, Any, Optional
|
3
3
|
|
4
4
|
from linkml.linter.config.datamodel.config import RuleLevel
|
5
5
|
from linkml.linter.linter import LinterProblem
|
@@ -53,7 +53,7 @@ class MarkdownFormatter(Formatter):
|
|
53
53
|
self.write(f"### {name}")
|
54
54
|
self.write_schema_problems(problems)
|
55
55
|
|
56
|
-
def write_schema_problems(self, problems:
|
56
|
+
def write_schema_problems(self, problems: list[LinterProblem]):
|
57
57
|
errors = [p for p in problems if str(p.level) == RuleLevel.error.text]
|
58
58
|
warnings = [p for p in problems if str(p.level) == RuleLevel.warning.text]
|
59
59
|
if errors:
|
linkml/linter/linter.py
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
import inspect
|
2
|
+
from collections.abc import Iterable
|
2
3
|
from copy import deepcopy
|
3
4
|
from dataclasses import dataclass
|
4
5
|
from functools import lru_cache
|
5
6
|
from pathlib import Path
|
6
|
-
from typing import Any,
|
7
|
+
from typing import Any, Union
|
7
8
|
|
8
9
|
import jsonschema
|
9
10
|
import yaml
|
@@ -29,7 +30,7 @@ class LinterProblem:
|
|
29
30
|
|
30
31
|
|
31
32
|
@lru_cache
|
32
|
-
def get_named_config(name: str) ->
|
33
|
+
def get_named_config(name: str) -> dict[str, Any]:
|
33
34
|
config_path = str(Path(__file__).parent / f"config/{name}.yaml")
|
34
35
|
with open(config_path) as config_file:
|
35
36
|
return yaml.safe_load(config_file)
|
@@ -67,7 +68,7 @@ def _format_path(path):
|
|
67
68
|
|
68
69
|
|
69
70
|
class Linter:
|
70
|
-
def __init__(self, config:
|
71
|
+
def __init__(self, config: dict[str, Any] = {}) -> None:
|
71
72
|
default_config = deepcopy(get_named_config("default"))
|
72
73
|
merged_config = config
|
73
74
|
if config.get("extends") == ExtendableConfigs.recommended.text:
|
linkml/linter/rules.py
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
import re
|
2
2
|
from abc import ABC, abstractmethod
|
3
|
-
from
|
4
|
-
from
|
3
|
+
from collections.abc import Iterable
|
4
|
+
from functools import cache
|
5
|
+
from typing import Callable
|
5
6
|
|
6
7
|
from linkml_runtime.linkml_model import ClassDefinition, ClassDefinitionName, Element, SlotDefinition
|
7
8
|
from linkml_runtime.utils.schemaview import SchemaView
|
@@ -55,8 +56,14 @@ class LinterRule(ABC):
|
|
55
56
|
class NoEmptyTitleRule(LinterRule):
|
56
57
|
id = "no_empty_title"
|
57
58
|
|
59
|
+
# todo PVs are not checked for titles yet
|
60
|
+
|
58
61
|
def check(self, schema_view: SchemaView, fix: bool = False) -> Iterable[LinterProblem]:
|
62
|
+
excluded_types = [t.text if hasattr(t, "text") else str(t) for t in getattr(self.config, "exclude_type", [])]
|
59
63
|
for e in schema_view.all_elements(imports=False).values():
|
64
|
+
element_type_name = type(e).class_name
|
65
|
+
if element_type_name in excluded_types:
|
66
|
+
continue
|
60
67
|
if fix and e.title is None:
|
61
68
|
title = e.name.replace("_", " ")
|
62
69
|
title = self.uncamel(title).lower()
|
@@ -89,8 +96,8 @@ class PermissibleValuesFormatRule(LinterRule):
|
|
89
96
|
yield LinterProblem(f"{self.format_element(enum_def)} has permissible value '{value}'")
|
90
97
|
|
91
98
|
|
92
|
-
@
|
93
|
-
def _get_recommended_metamodel_slots() ->
|
99
|
+
@cache
|
100
|
+
def _get_recommended_metamodel_slots() -> list[str]:
|
94
101
|
meta_schema_view = SchemaView(LOCAL_METAMODEL_YAML_FILE)
|
95
102
|
recommended_meta_slots = []
|
96
103
|
for class_name in meta_schema_view.all_classes(imports=False).keys():
|
@@ -104,16 +111,22 @@ def _get_recommended_metamodel_slots() -> List[str]:
|
|
104
111
|
class RecommendedRule(LinterRule):
|
105
112
|
id = "recommended"
|
106
113
|
|
114
|
+
# todo PVs are not checked for recommended fields yet
|
115
|
+
|
107
116
|
def __init__(self, config: RecommendedRuleConfig) -> None:
|
108
117
|
self.config = config
|
109
118
|
|
110
119
|
def check(self, schema_view: SchemaView, fix: bool = False):
|
111
120
|
recommended_meta_slots = _get_recommended_metamodel_slots()
|
121
|
+
excluded_types = [t.text if hasattr(t, "text") else str(t) for t in getattr(self.config, "exclude_type", [])]
|
112
122
|
for element_name, element_definition in schema_view.all_elements(imports=False).items():
|
123
|
+
element_type_name = type(element_definition).class_name
|
113
124
|
if self.config.include and element_name not in self.config.include:
|
114
125
|
continue
|
115
126
|
if element_name in self.config.exclude:
|
116
127
|
continue
|
128
|
+
if element_type_name in excluded_types:
|
129
|
+
continue
|
117
130
|
for meta_slot_name, meta_slot_value in vars(element_definition).items():
|
118
131
|
key = f"{element_definition.class_name}__{meta_slot_name}"
|
119
132
|
if key in recommended_meta_slots and not meta_slot_value:
|
@@ -153,7 +166,7 @@ class TreeRootClassRule(LinterRule):
|
|
153
166
|
must_have_identifier=False,
|
154
167
|
slot_name_func: Callable = None,
|
155
168
|
convert_camel_case=False,
|
156
|
-
) ->
|
169
|
+
) -> list[SlotDefinition]:
|
157
170
|
"""
|
158
171
|
Adds index slots to a container pointing at all top-level classes
|
159
172
|
|
@@ -216,6 +229,7 @@ class StandardNamingRule(LinterRule):
|
|
216
229
|
self.config = config
|
217
230
|
|
218
231
|
def check(self, schema_view: SchemaView, fix: bool = False) -> Iterable[LinterProblem]:
|
232
|
+
excluded_types = [t.text if hasattr(t, "text") else str(t) for t in getattr(self.config, "exclude_type", [])]
|
219
233
|
class_pattern = (
|
220
234
|
self.PATTERNS["uppercamel"]
|
221
235
|
if not self.config.class_pattern
|
@@ -232,24 +246,29 @@ class StandardNamingRule(LinterRule):
|
|
232
246
|
self.PATTERNS["uppersnake"] if self.config.permissible_values_upper_case else self.PATTERNS["snake"]
|
233
247
|
)
|
234
248
|
|
235
|
-
|
236
|
-
|
237
|
-
|
249
|
+
if "class_definition" not in excluded_types:
|
250
|
+
for class_name in schema_view.all_classes(imports=False).keys():
|
251
|
+
if class_pattern.fullmatch(class_name) is None:
|
252
|
+
yield LinterProblem(f"Class has name '{class_name}'")
|
238
253
|
|
239
|
-
|
240
|
-
|
241
|
-
|
254
|
+
if "slot_definition" not in excluded_types:
|
255
|
+
for slot_name in schema_view.all_slots(imports=False).keys():
|
256
|
+
if slot_pattern.fullmatch(slot_name) is None:
|
257
|
+
yield LinterProblem(f"Slot has name '{slot_name}'")
|
242
258
|
|
243
259
|
for enum_name, enum_definition in schema_view.all_enums(imports=False).items():
|
244
|
-
if enum_pattern.fullmatch(enum_name) is None:
|
245
|
-
yield LinterProblem(f"Enum has name '{enum_name}'")
|
246
260
|
|
247
|
-
|
248
|
-
if
|
249
|
-
yield LinterProblem(
|
250
|
-
|
251
|
-
|
252
|
-
|
261
|
+
if "enum_definition" not in excluded_types:
|
262
|
+
if enum_pattern.fullmatch(enum_name) is None:
|
263
|
+
yield LinterProblem(f"Enum has name '{enum_name}'")
|
264
|
+
|
265
|
+
if "permissible_value" not in excluded_types:
|
266
|
+
for permissible_value_name in enum_definition.permissible_values.keys():
|
267
|
+
if permissible_value_pattern.fullmatch(permissible_value_name) is None:
|
268
|
+
yield LinterProblem(
|
269
|
+
f"Permissible value of {self.format_element(enum_definition)} "
|
270
|
+
f"has name '{permissible_value_name}'"
|
271
|
+
)
|
253
272
|
|
254
273
|
|
255
274
|
class CanonicalPrefixesRule(LinterRule):
|
linkml/reporting/model.py
CHANGED
@@ -6,14 +6,12 @@
|
|
6
6
|
# description: A datamodel for reports on data
|
7
7
|
# license: https://creativecommons.org/publicdomain/zero/1.0/
|
8
8
|
|
9
|
-
import dataclasses
|
10
9
|
from dataclasses import dataclass
|
11
|
-
from typing import Any, ClassVar,
|
10
|
+
from typing import Any, ClassVar, Optional, Union
|
12
11
|
|
13
12
|
from jsonasobj2 import as_dict
|
14
13
|
from linkml_runtime.linkml_model.meta import EnumDefinition, PermissibleValue
|
15
14
|
from linkml_runtime.utils.curienamespace import CurieNamespace
|
16
|
-
from linkml_runtime.utils.dataclass_extensions_376 import dataclasses_init_fn_with_kwargs
|
17
15
|
from linkml_runtime.utils.enumerations import EnumDefinitionImpl
|
18
16
|
from linkml_runtime.utils.metamodelcore import NodeIdentifier, URIorCURIE, empty_list
|
19
17
|
from linkml_runtime.utils.slot import Slot
|
@@ -22,8 +20,6 @@ from rdflib import URIRef
|
|
22
20
|
|
23
21
|
metamodel_version = "1.7.0"
|
24
22
|
|
25
|
-
# Overwrite dataclasses _init_fn to add **kwargs in __init__
|
26
|
-
dataclasses._init_fn = dataclasses_init_fn_with_kwargs
|
27
23
|
|
28
24
|
# Namespaces
|
29
25
|
LINKML = CurieNamespace("linkml", "https://w3id.org/linkml/")
|
@@ -49,16 +45,16 @@ class Report(YAMLRoot):
|
|
49
45
|
A report object
|
50
46
|
"""
|
51
47
|
|
52
|
-
_inherited_slots: ClassVar[
|
48
|
+
_inherited_slots: ClassVar[list[str]] = []
|
53
49
|
|
54
50
|
class_class_uri: ClassVar[URIRef] = REPORTING.Report
|
55
51
|
class_class_curie: ClassVar[str] = "reporting:Report"
|
56
52
|
class_name: ClassVar[str] = "report"
|
57
53
|
class_model_uri: ClassVar[URIRef] = REPORTING.Report
|
58
54
|
|
59
|
-
results: Optional[Union[Union[dict, "CheckResult"],
|
55
|
+
results: Optional[Union[Union[dict, "CheckResult"], list[Union[dict, "CheckResult"]]]] = empty_list()
|
60
56
|
|
61
|
-
def __post_init__(self, *_:
|
57
|
+
def __post_init__(self, *_: list[str], **kwargs: dict[str, Any]):
|
62
58
|
if not isinstance(self.results, list):
|
63
59
|
self.results = [self.results] if self.results is not None else []
|
64
60
|
self.results = [v if isinstance(v, CheckResult) else CheckResult(**as_dict(v)) for v in self.results]
|
@@ -72,7 +68,7 @@ class CheckResult(YAMLRoot):
|
|
72
68
|
An individual check
|
73
69
|
"""
|
74
70
|
|
75
|
-
_inherited_slots: ClassVar[
|
71
|
+
_inherited_slots: ClassVar[list[str]] = []
|
76
72
|
|
77
73
|
class_class_uri: ClassVar[URIRef] = REPORTING.CheckResult
|
78
74
|
class_class_curie: ClassVar[str] = "reporting:CheckResult"
|
@@ -88,7 +84,7 @@ class CheckResult(YAMLRoot):
|
|
88
84
|
source: Optional[Union[str, NodeIdentifier]] = None
|
89
85
|
info: Optional[str] = None
|
90
86
|
|
91
|
-
def __post_init__(self, *_:
|
87
|
+
def __post_init__(self, *_: list[str], **kwargs: dict[str, Any]):
|
92
88
|
if self.type is not None and not isinstance(self.type, URIorCURIE):
|
93
89
|
self.type = URIorCURIE(self.type)
|
94
90
|
|
@@ -117,7 +113,7 @@ class CheckResult(YAMLRoot):
|
|
117
113
|
|
118
114
|
|
119
115
|
class Problem(CheckResult):
|
120
|
-
_inherited_slots: ClassVar[
|
116
|
+
_inherited_slots: ClassVar[list[str]] = []
|
121
117
|
|
122
118
|
class_class_uri: ClassVar[URIRef] = REPORTING.Problem
|
123
119
|
class_class_curie: ClassVar[str] = "reporting:Problem"
|
@@ -130,7 +126,7 @@ class ProblemSlotUndeclared(Problem):
|
|
130
126
|
A problem in which an undeclared slot is used
|
131
127
|
"""
|
132
128
|
|
133
|
-
_inherited_slots: ClassVar[
|
129
|
+
_inherited_slots: ClassVar[list[str]] = []
|
134
130
|
|
135
131
|
class_class_uri: ClassVar[URIRef] = REPORTING.ProblemSlotUndeclared
|
136
132
|
class_class_curie: ClassVar[str] = "reporting:ProblemSlotUndeclared"
|
@@ -143,7 +139,7 @@ class ProblemSlotInapplicable(Problem):
|
|
143
139
|
A problem in which a slot is used in an instance of a class where the slot is not applicable for that class
|
144
140
|
"""
|
145
141
|
|
146
|
-
_inherited_slots: ClassVar[
|
142
|
+
_inherited_slots: ClassVar[list[str]] = []
|
147
143
|
|
148
144
|
class_class_uri: ClassVar[URIRef] = REPORTING.ProblemSlotInapplicable
|
149
145
|
class_class_curie: ClassVar[str] = "reporting:ProblemSlotInapplicable"
|
@@ -156,7 +152,7 @@ class ProblemSlotMissing(Problem):
|
|
156
152
|
A problem in which an instance of a class has a required slot which is not filled in
|
157
153
|
"""
|
158
154
|
|
159
|
-
_inherited_slots: ClassVar[
|
155
|
+
_inherited_slots: ClassVar[list[str]] = []
|
160
156
|
|
161
157
|
class_class_uri: ClassVar[URIRef] = REPORTING.ProblemSlotMissing
|
162
158
|
class_class_curie: ClassVar[str] = "reporting:ProblemSlotMissing"
|
@@ -268,5 +264,5 @@ slots.report__results = Slot(
|
|
268
264
|
curie=REPORTING.curie("results"),
|
269
265
|
model_uri=REPORTING.report__results,
|
270
266
|
domain=None,
|
271
|
-
range=Optional[Union[Union[dict, CheckResult],
|
267
|
+
range=Optional[Union[Union[dict, CheckResult], list[Union[dict, CheckResult]]]],
|
272
268
|
)
|
@@ -57,9 +57,10 @@ See logictools.py for the symbolic reasoning engine.
|
|
57
57
|
"""
|
58
58
|
|
59
59
|
import logging
|
60
|
+
from collections.abc import Iterator
|
60
61
|
from copy import deepcopy
|
61
62
|
from dataclasses import dataclass
|
62
|
-
from typing import Any, Callable,
|
63
|
+
from typing import Any, Callable, Optional, Union
|
63
64
|
|
64
65
|
from linkml_runtime import SchemaView
|
65
66
|
from linkml_runtime.dumpers import json_dumper
|
@@ -340,7 +341,7 @@ class LogicalModelTransformer(ModelTransformer):
|
|
340
341
|
a lack of range assignment.
|
341
342
|
"""
|
342
343
|
|
343
|
-
reason_over_metamodel_slots: Optional[
|
344
|
+
reason_over_metamodel_slots: Optional[list[str]] = None
|
344
345
|
"""
|
345
346
|
If set, only reason over the specified metamodel slots.
|
346
347
|
"""
|
@@ -407,10 +408,10 @@ class LogicalModelTransformer(ModelTransformer):
|
|
407
408
|
self,
|
408
409
|
target_schema: SchemaDefinition,
|
409
410
|
target_class_name: ClassDefinitionName,
|
410
|
-
ancestors:
|
411
|
+
ancestors: list[ClassDefinitionName],
|
411
412
|
):
|
412
413
|
anc_classes = [self.schemaview.get_class(ancestor) for ancestor in ancestors]
|
413
|
-
attributes:
|
414
|
+
attributes: dict[SlotDefinitionName, SlotDefinition] = {}
|
414
415
|
for ancestor_class in anc_classes:
|
415
416
|
top_level_slots = [(s, target_schema.slots[s]) for s in ancestor_class.slots]
|
416
417
|
for slot_name, slot_expr in (
|
@@ -543,7 +544,7 @@ class LogicalModelTransformer(ModelTransformer):
|
|
543
544
|
elif isinstance(expr, logictools.Not):
|
544
545
|
self._simplify_member_ofs(expr.operand)
|
545
546
|
|
546
|
-
def _remove_redundant(self, elements:
|
547
|
+
def _remove_redundant(self, elements: list[str]) -> list[str]:
|
547
548
|
sv = self.schemaview
|
548
549
|
redundant = set()
|
549
550
|
if not self.preserve_class_mixins:
|
@@ -568,7 +569,7 @@ class LogicalModelTransformer(ModelTransformer):
|
|
568
569
|
logger.warning(f"Unknown class {x} in {elements}")
|
569
570
|
return [x for x in elements if x not in redundant]
|
570
571
|
|
571
|
-
def _type_descendants(self, type_name: str, imports=True, reflexive=True, depth_first=True) ->
|
572
|
+
def _type_descendants(self, type_name: str, imports=True, reflexive=True, depth_first=True) -> list[str]:
|
572
573
|
# TODO: move this to schemaview
|
573
574
|
sv = self.schemaview
|
574
575
|
from linkml_runtime.utils.schemaview import _closure
|
@@ -584,7 +585,7 @@ class LogicalModelTransformer(ModelTransformer):
|
|
584
585
|
depth_first=depth_first,
|
585
586
|
)
|
586
587
|
|
587
|
-
def _enum_descendants(self, enum_name: str, imports=True, reflexive=True, depth_first=True) ->
|
588
|
+
def _enum_descendants(self, enum_name: str, imports=True, reflexive=True, depth_first=True) -> list[str]:
|
588
589
|
# TODO: move this to schemaview
|
589
590
|
sv = self.schemaview
|
590
591
|
from linkml_runtime.utils.schemaview import _closure
|
@@ -754,7 +755,7 @@ class LogicalModelTransformer(ModelTransformer):
|
|
754
755
|
self,
|
755
756
|
attribute: Union[SlotDefinition, AnonymousSlotExpression],
|
756
757
|
root_slot: SlotDefinition = None,
|
757
|
-
stack:
|
758
|
+
stack: list = None,
|
758
759
|
) -> str:
|
759
760
|
# Note: in future versions of the metamodel, multivalued may move to the expression
|
760
761
|
if stack is None:
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import logging
|
2
2
|
from copy import copy
|
3
3
|
from dataclasses import dataclass, field
|
4
|
-
from typing import
|
4
|
+
from typing import Optional
|
5
5
|
|
6
6
|
from linkml_runtime.linkml_model import (
|
7
7
|
Annotation,
|
@@ -105,7 +105,7 @@ class MultivaluedScalar(RelationalMapping):
|
|
105
105
|
mapping_type: str = "MultivaluedScalar"
|
106
106
|
|
107
107
|
|
108
|
-
def add_attribute(attributes:
|
108
|
+
def add_attribute(attributes: dict[str, SlotDefinition], tgt_slot: SlotDefinition) -> None:
|
109
109
|
attributes[tgt_slot.name] = tgt_slot
|
110
110
|
|
111
111
|
|
@@ -114,11 +114,11 @@ def add_annotation(element: Definition, tag: str, value: str) -> None:
|
|
114
114
|
element.annotations[ann.tag] = ann
|
115
115
|
|
116
116
|
|
117
|
-
def get_primary_key_attributes(cls: ClassDefinition) ->
|
117
|
+
def get_primary_key_attributes(cls: ClassDefinition) -> list[SlotDefinitionName]:
|
118
118
|
return [a.name for a in cls.attributes.values() if RelationalAnnotations.PRIMARY_KEY in a.annotations]
|
119
119
|
|
120
120
|
|
121
|
-
def get_foreign_key_map(cls: ClassDefinition) ->
|
121
|
+
def get_foreign_key_map(cls: ClassDefinition) -> dict[SlotDefinitionName, str]:
|
122
122
|
return {
|
123
123
|
a.name: a.annotations[RelationalAnnotations.FOREIGN_KEY].value
|
124
124
|
for a in cls.attributes.values()
|
@@ -133,7 +133,7 @@ class TransformationResult:
|
|
133
133
|
"""
|
134
134
|
|
135
135
|
schema: SchemaDefinition
|
136
|
-
mappings:
|
136
|
+
mappings: list[RelationalMapping]
|
137
137
|
|
138
138
|
|
139
139
|
@dataclass
|
@@ -412,7 +412,7 @@ class RelationalModelTransformer:
|
|
412
412
|
return a
|
413
413
|
return None
|
414
414
|
|
415
|
-
def get_reference_map(self) ->
|
415
|
+
def get_reference_map(self) -> list[Link]:
|
416
416
|
"""
|
417
417
|
Extract all class-slot-range references
|
418
418
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from copy import deepcopy
|
2
2
|
from dataclasses import dataclass, field
|
3
|
-
from typing import Any, Callable
|
3
|
+
from typing import Any, Callable
|
4
4
|
|
5
5
|
import click
|
6
6
|
from jsonasobj2 import as_dict
|
@@ -35,7 +35,7 @@ class SchemaRenamer:
|
|
35
35
|
Renames schema elements
|
36
36
|
"""
|
37
37
|
|
38
|
-
rename_function_map:
|
38
|
+
rename_function_map: dict[type, Callable] = field(default_factory=lambda: {})
|
39
39
|
schema: SchemaDefinition = None
|
40
40
|
|
41
41
|
def rename_elements(self, schema: SchemaDefinition) -> SchemaDefinition:
|
linkml/utils/converter.py
CHANGED
@@ -123,7 +123,7 @@ def cli(
|
|
123
123
|
prefix_path = pathlib.Path(prefix_file).resolve()
|
124
124
|
if not prefix_path.exists():
|
125
125
|
raise Exception(f"Path {prefix_file} to prefix map does not exists.")
|
126
|
-
with open(prefix_path
|
126
|
+
with open(prefix_path) as prefix_stream:
|
127
127
|
raw_prefix_map = yaml.safe_load(prefix_stream)
|
128
128
|
prefix_file_map = raw_prefix_map.get("prefixes", None)
|
129
129
|
if prefix_file_map is None:
|
linkml/utils/deprecation.py
CHANGED
@@ -206,7 +206,7 @@ DEPRECATIONS = (
|
|
206
206
|
Deprecation(
|
207
207
|
name="pydanticgen-v1",
|
208
208
|
deprecated_in=SemVer.from_str("1.7.5"),
|
209
|
-
removed_in=SemVer.from_str("1.
|
209
|
+
removed_in=SemVer.from_str("1.10.0"),
|
210
210
|
message="Support for generating Pydantic v1.*.* models with pydanticgen is deprecated",
|
211
211
|
recommendation="Migrate any existing models to Pydantic v2",
|
212
212
|
issue=1925,
|
@@ -214,7 +214,7 @@ DEPRECATIONS = (
|
|
214
214
|
Deprecation(
|
215
215
|
name="pydantic-v1",
|
216
216
|
deprecated_in=SemVer.from_str("1.7.5"),
|
217
|
-
removed_in=SemVer.from_str("1.
|
217
|
+
removed_in=SemVer.from_str("1.10.0"),
|
218
218
|
message=(
|
219
219
|
"LinkML will set a dependency of pydantic>=2 and become incompatible "
|
220
220
|
"with packages with pydantic<2 as a runtime dependency"
|
@@ -225,7 +225,7 @@ DEPRECATIONS = (
|
|
225
225
|
Deprecation(
|
226
226
|
name="validators",
|
227
227
|
deprecated_in=SemVer.from_str("1.8.6"),
|
228
|
-
removed_in=SemVer.from_str("1.
|
228
|
+
removed_in=SemVer.from_str("1.10.0"),
|
229
229
|
message=(
|
230
230
|
"linkml.validators and linkml.utils.validation are the older versions "
|
231
231
|
"of linkml.validator and have unmaintained, duplicated functionality"
|
linkml/utils/execute_tutorial.py
CHANGED
@@ -4,7 +4,6 @@ import subprocess
|
|
4
4
|
import sys
|
5
5
|
from dataclasses import dataclass
|
6
6
|
from pathlib import Path, PurePath
|
7
|
-
from typing import List
|
8
7
|
|
9
8
|
import click
|
10
9
|
|
@@ -26,8 +25,8 @@ class Block:
|
|
26
25
|
output: str = None
|
27
26
|
error: str = None
|
28
27
|
expected_fail: bool = None
|
29
|
-
prior_lines:
|
30
|
-
annotations:
|
28
|
+
prior_lines: list[str] = None
|
29
|
+
annotations: list[str] = None
|
31
30
|
|
32
31
|
def is_file_block(self) -> bool:
|
33
32
|
return self.title and "." in self.title
|
@@ -39,7 +38,7 @@ class Block:
|
|
39
38
|
return self.category == "bash"
|
40
39
|
|
41
40
|
|
42
|
-
def execute_blocks(directory: str, blocks:
|
41
|
+
def execute_blocks(directory: str, blocks: list[Block]) -> list[str]:
|
43
42
|
"""
|
44
43
|
Execute the code blocks embedded in a tutorial
|
45
44
|
|
@@ -117,12 +116,12 @@ def execute_blocks(directory: str, blocks: List[Block]) -> List[str]:
|
|
117
116
|
return errs
|
118
117
|
|
119
118
|
|
120
|
-
def write_lines(lines:
|
119
|
+
def write_lines(lines: list[str]) -> None:
|
121
120
|
for line in lines:
|
122
121
|
print(f"+++ {line}")
|
123
122
|
|
124
123
|
|
125
|
-
def parse_file_to_blocks(input) ->
|
124
|
+
def parse_file_to_blocks(input) -> list[Block]:
|
126
125
|
"""
|
127
126
|
Parses a markdown tutorial file to code blacks to be executed
|
128
127
|
|