linkml 1.9.0rc1__py3-none-any.whl → 1.9.1rc3__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.1rc3.dist-info}/METADATA +6 -9
- {linkml-1.9.0rc1.dist-info → linkml-1.9.1rc3.dist-info}/RECORD +82 -82
- {linkml-1.9.0rc1.dist-info → linkml-1.9.1rc3.dist-info}/WHEEL +1 -1
- {linkml-1.9.0rc1.dist-info → linkml-1.9.1rc3.dist-info}/LICENSE +0 -0
- {linkml-1.9.0rc1.dist-info → linkml-1.9.1rc3.dist-info}/entry_points.txt +0 -0
linkml/generators/oocodegen.py
CHANGED
@@ -2,7 +2,7 @@ import abc
|
|
2
2
|
import re
|
3
3
|
import unicodedata
|
4
4
|
from dataclasses import dataclass, field
|
5
|
-
from typing import
|
5
|
+
from typing import Optional
|
6
6
|
|
7
7
|
from linkml_runtime.linkml_model.meta import (
|
8
8
|
ClassDefinition,
|
@@ -32,8 +32,8 @@ class OODocument:
|
|
32
32
|
name: SAFE_NAME
|
33
33
|
package: PACKAGE = None
|
34
34
|
source_schema: SchemaDefinition = None
|
35
|
-
classes:
|
36
|
-
imports:
|
35
|
+
classes: list["OOClass"] = field(default_factory=lambda: [])
|
36
|
+
imports: list[str] = field(default_factory=lambda: [])
|
37
37
|
|
38
38
|
|
39
39
|
@dataclass
|
@@ -45,7 +45,7 @@ class OOField:
|
|
45
45
|
name: SAFE_NAME
|
46
46
|
range: TYPE_EXPRESSION = None
|
47
47
|
default_value: str = None
|
48
|
-
annotations:
|
48
|
+
annotations: list[ANNOTATION] = field(default_factory=lambda: [])
|
49
49
|
source_slot: SlotDefinition = field(default_factory=lambda: [])
|
50
50
|
|
51
51
|
|
@@ -61,10 +61,10 @@ class OOClass:
|
|
61
61
|
is_a: Optional[SAFE_NAME] = None
|
62
62
|
mixin: Optional[bool] = None
|
63
63
|
abstract: Optional[bool] = None
|
64
|
-
mixins:
|
65
|
-
fields:
|
66
|
-
all_fields:
|
67
|
-
annotations:
|
64
|
+
mixins: list[SAFE_NAME] = field(default_factory=lambda: [])
|
65
|
+
fields: list[OOField] = field(default_factory=lambda: [])
|
66
|
+
all_fields: list[OOField] = field(default_factory=lambda: [])
|
67
|
+
annotations: list[ANNOTATION] = field(default_factory=lambda: [])
|
68
68
|
package: PACKAGE = None
|
69
69
|
source_class: ClassDefinition = None
|
70
70
|
|
@@ -135,7 +135,7 @@ class OOCodeGenerator(Generator):
|
|
135
135
|
|
136
136
|
return safe_label
|
137
137
|
|
138
|
-
def generate_enums(self, all_enums:
|
138
|
+
def generate_enums(self, all_enums: dict[EnumDefinitionName, EnumDefinition]) -> dict:
|
139
139
|
# TODO: make an explicit class to represent how an enum is passed to the template
|
140
140
|
enums = {}
|
141
141
|
for enum_name, enum_original in all_enums.items():
|
@@ -161,7 +161,7 @@ class OOCodeGenerator(Generator):
|
|
161
161
|
|
162
162
|
return enums
|
163
163
|
|
164
|
-
def create_documents(self) ->
|
164
|
+
def create_documents(self) -> list[OODocument]:
|
165
165
|
"""
|
166
166
|
Currently hardcoded for java-style
|
167
167
|
:return:
|
linkml/generators/owlgen.py
CHANGED
@@ -3,10 +3,11 @@
|
|
3
3
|
import logging
|
4
4
|
import os
|
5
5
|
from collections import defaultdict
|
6
|
+
from collections.abc import Mapping
|
6
7
|
from copy import copy
|
7
8
|
from dataclasses import dataclass, field
|
8
9
|
from enum import Enum, unique
|
9
|
-
from typing import Any,
|
10
|
+
from typing import Any, Optional, Union
|
10
11
|
|
11
12
|
import click
|
12
13
|
import rdflib
|
@@ -117,7 +118,7 @@ class OwlSchemaGenerator(Generator):
|
|
117
118
|
metadata_profile: MetadataProfile = None
|
118
119
|
"""Deprecated - use metadata_profiles."""
|
119
120
|
|
120
|
-
metadata_profiles:
|
121
|
+
metadata_profiles: list[MetadataProfile] = field(default_factory=lambda: [])
|
121
122
|
"""By default, use the linkml metadata profile,
|
122
123
|
this allows for overrides."""
|
123
124
|
|
@@ -151,10 +152,10 @@ class OwlSchemaGenerator(Generator):
|
|
151
152
|
|
152
153
|
default_permissible_value_type: Union[str, URIRef] = field(default_factory=lambda: OWL.Class)
|
153
154
|
|
154
|
-
slot_is_literal_map: Mapping[str,
|
155
|
+
slot_is_literal_map: Mapping[str, set[bool]] = field(default_factory=lambda: defaultdict(set))
|
155
156
|
"""DEPRECATED: use node_owltypes"""
|
156
157
|
|
157
|
-
node_owltypes: Mapping[Union[BNode, URIRef],
|
158
|
+
node_owltypes: Mapping[Union[BNode, URIRef], set[OWL_TYPE]] = field(default_factory=lambda: defaultdict(set))
|
158
159
|
"""rdfs:Datatype, owl:Thing"""
|
159
160
|
|
160
161
|
simplify: bool = True
|
@@ -425,7 +426,7 @@ class OwlSchemaGenerator(Generator):
|
|
425
426
|
else:
|
426
427
|
self.graph.add((subject_expr, RDFS.subClassOf, superclass_expr))
|
427
428
|
|
428
|
-
def get_own_slots(self, cls: Union[ClassDefinition, AnonymousClassExpression]) ->
|
429
|
+
def get_own_slots(self, cls: Union[ClassDefinition, AnonymousClassExpression]) -> list[SlotDefinition]:
|
429
430
|
"""
|
430
431
|
Get the slots that are defined on a class, excluding those that are inherited.
|
431
432
|
|
@@ -561,7 +562,7 @@ class OwlSchemaGenerator(Generator):
|
|
561
562
|
self,
|
562
563
|
slot: Union[SlotDefinition, AnonymousSlotExpression],
|
563
564
|
owning_class: Optional[Union[ClassDefinition, AnonymousClassExpression]] = None,
|
564
|
-
) ->
|
565
|
+
) -> set[URIRef]:
|
565
566
|
"""
|
566
567
|
Determine the OWL types of a named slot or slot expression
|
567
568
|
|
@@ -594,7 +595,7 @@ class OwlSchemaGenerator(Generator):
|
|
594
595
|
cls: Optional[Union[ClassDefinition, AnonymousClassExpression]],
|
595
596
|
slot: Union[SlotDefinition, AnonymousSlotExpression],
|
596
597
|
main_slot: SlotDefinition = None,
|
597
|
-
owl_types:
|
598
|
+
owl_types: set[OWL_TYPE] = None,
|
598
599
|
) -> Optional[Union[BNode, URIRef]]:
|
599
600
|
"""
|
600
601
|
Take a ClassExpression and SlotExpression combination and transform to a node.
|
@@ -685,7 +686,7 @@ class OwlSchemaGenerator(Generator):
|
|
685
686
|
self,
|
686
687
|
element: Union[SlotDefinition, AnonymousSlotExpression, TypeDefinition, AnonymousTypeExpression],
|
687
688
|
is_literal: Optional[bool] = None,
|
688
|
-
) ->
|
689
|
+
) -> tuple[list[BNode], set[OWL_TYPE]]:
|
689
690
|
owl_types = set()
|
690
691
|
owl_exprs = []
|
691
692
|
graph = self.graph
|
@@ -978,7 +979,7 @@ class OwlSchemaGenerator(Generator):
|
|
978
979
|
subject: Union[URIRef, BNode],
|
979
980
|
condition: AnonymousClassExpression,
|
980
981
|
cls: ClassDefinition,
|
981
|
-
) ->
|
982
|
+
) -> list[BNode]:
|
982
983
|
for slot_name, expr in condition.slot_conditions.items():
|
983
984
|
var = self._swrl_var(slot_name)
|
984
985
|
if expr.maximum_value is not None:
|
@@ -996,7 +997,7 @@ class OwlSchemaGenerator(Generator):
|
|
996
997
|
return True
|
997
998
|
return profile in self.metadata_profiles or profile == self.metadata_profile
|
998
999
|
|
999
|
-
def _get_owltypes(self, current:
|
1000
|
+
def _get_owltypes(self, current: set[OWL_TYPE], exprs: list[Union[BNode, URIRef]]) -> set[OWL_TYPE]:
|
1000
1001
|
"""
|
1001
1002
|
Gets the OWL types of specified expressions plus current owl types.
|
1002
1003
|
|
@@ -1076,7 +1077,7 @@ class OwlSchemaGenerator(Generator):
|
|
1076
1077
|
return URIRef("https://w3id.org/linkml/" + name)
|
1077
1078
|
|
1078
1079
|
def _complement_of_union_of(
|
1079
|
-
self, exprs:
|
1080
|
+
self, exprs: list[Union[BNode, URIRef]], owl_types: set[OWL_TYPE] = None, **kwargs
|
1080
1081
|
) -> Optional[Union[BNode, URIRef]]:
|
1081
1082
|
if not exprs:
|
1082
1083
|
raise ValueError("Must pass at least one")
|
@@ -1092,16 +1093,16 @@ class OwlSchemaGenerator(Generator):
|
|
1092
1093
|
|
1093
1094
|
return neg_expr
|
1094
1095
|
|
1095
|
-
def _intersection_of(self, exprs:
|
1096
|
+
def _intersection_of(self, exprs: list[Union[BNode, URIRef]], **kwargs) -> Optional[Union[BNode, URIRef]]:
|
1096
1097
|
return self._boolean_expression(exprs, OWL.intersectionOf, **kwargs)
|
1097
1098
|
|
1098
|
-
def _union_of(self, exprs:
|
1099
|
+
def _union_of(self, exprs: list[Union[BNode, URIRef]], **kwargs) -> Optional[Union[BNode, URIRef]]:
|
1099
1100
|
return self._boolean_expression(exprs, OWL.unionOf, **kwargs)
|
1100
1101
|
|
1101
|
-
def _object_one_of(self, exprs:
|
1102
|
+
def _object_one_of(self, exprs: list[Union[BNode, URIRef]], **kwargs) -> Optional[Union[BNode, URIRef]]:
|
1102
1103
|
return self._boolean_expression(exprs, OWL.oneOf, **kwargs)
|
1103
1104
|
|
1104
|
-
def _exactly_one_of(self, exprs:
|
1105
|
+
def _exactly_one_of(self, exprs: list[Union[BNode, URIRef]]) -> Optional[Union[BNode, URIRef]]:
|
1105
1106
|
if not exprs:
|
1106
1107
|
raise ValueError("Must pass at least one")
|
1107
1108
|
if len(exprs) == 1:
|
@@ -1113,7 +1114,7 @@ class OwlSchemaGenerator(Generator):
|
|
1113
1114
|
sub_exprs.append(self._intersection_of([x, neg_expr]))
|
1114
1115
|
return self._union_of(sub_exprs)
|
1115
1116
|
|
1116
|
-
def _datatype_restriction(self, datatype: URIRef, facets:
|
1117
|
+
def _datatype_restriction(self, datatype: URIRef, facets: list[Union[BNode, URIRef]]) -> BNode:
|
1117
1118
|
node = BNode()
|
1118
1119
|
graph = self.graph
|
1119
1120
|
graph.add((node, RDF.type, RDFS.Datatype))
|
@@ -1132,10 +1133,10 @@ class OwlSchemaGenerator(Generator):
|
|
1132
1133
|
|
1133
1134
|
def _boolean_expression(
|
1134
1135
|
self,
|
1135
|
-
exprs:
|
1136
|
+
exprs: list[Union[BNode, URIRef, Literal]],
|
1136
1137
|
predicate: URIRef,
|
1137
1138
|
node: Optional[URIRef] = None,
|
1138
|
-
owl_types:
|
1139
|
+
owl_types: set[OWL_TYPE] = None,
|
1139
1140
|
) -> Optional[Union[BNode, URIRef]]:
|
1140
1141
|
graph = self.graph
|
1141
1142
|
if [x for x in exprs if x is None]:
|
linkml/generators/plantumlgen.py
CHANGED
@@ -8,7 +8,7 @@ import base64
|
|
8
8
|
import os
|
9
9
|
import zlib
|
10
10
|
from dataclasses import dataclass
|
11
|
-
from typing import Callable,
|
11
|
+
from typing import Callable, Optional, cast
|
12
12
|
|
13
13
|
import click
|
14
14
|
import requests
|
@@ -38,15 +38,15 @@ class PlantumlGenerator(Generator):
|
|
38
38
|
valid_formats = ["puml", "plantuml", "png", "pdf", "jpg", "json", "svg"]
|
39
39
|
visit_all_class_slots = False
|
40
40
|
|
41
|
-
referenced: Optional[
|
42
|
-
generated: Optional[
|
43
|
-
class_generated: Optional[
|
44
|
-
associations_generated: Optional[
|
45
|
-
focus_classes: Optional[
|
46
|
-
gen_classes: Optional[
|
41
|
+
referenced: Optional[set[ClassDefinitionName]] = None # List of classes that have to be emitted
|
42
|
+
generated: Optional[set[ClassDefinitionName]] = None # List of classes that have been emitted
|
43
|
+
class_generated: Optional[set[ClassDefinitionName]] = None # Class definitions that have been emitted
|
44
|
+
associations_generated: Optional[set[ClassDefinitionName]] = None # Classes with associations generated
|
45
|
+
focus_classes: Optional[set[ClassDefinitionName]] = None # Classes to be completely filled
|
46
|
+
gen_classes: Optional[set[ClassDefinitionName]] = None # Classes to be generated
|
47
47
|
output_file_name: Optional[str] = None # Location of output file if directory used
|
48
48
|
|
49
|
-
classes:
|
49
|
+
classes: set[ClassDefinitionName] = None
|
50
50
|
directory: Optional[str] = None
|
51
51
|
kroki_server: Optional[str] = "https://kroki.io"
|
52
52
|
tooltips_flag: bool = False
|
@@ -54,7 +54,7 @@ class PlantumlGenerator(Generator):
|
|
54
54
|
|
55
55
|
def visit_schema(
|
56
56
|
self,
|
57
|
-
classes:
|
57
|
+
classes: set[ClassDefinitionName] = None,
|
58
58
|
directory: Optional[str] = None,
|
59
59
|
**_,
|
60
60
|
) -> Optional[str]:
|
@@ -76,7 +76,7 @@ class PlantumlGenerator(Generator):
|
|
76
76
|
self.gen_classes = self.synopsis.roots.classrefs
|
77
77
|
self.referenced = self.gen_classes
|
78
78
|
self.generated = set()
|
79
|
-
plantumlclassdef:
|
79
|
+
plantumlclassdef: list[str] = []
|
80
80
|
while self.referenced.difference(self.generated):
|
81
81
|
cn = sorted(list(self.referenced.difference(self.generated)), reverse=True)[0]
|
82
82
|
self.generated.add(cn)
|
@@ -127,7 +127,7 @@ class PlantumlGenerator(Generator):
|
|
127
127
|
@param cn:
|
128
128
|
@return:
|
129
129
|
"""
|
130
|
-
slot_defs:
|
130
|
+
slot_defs: list[str] = []
|
131
131
|
if cn not in self.class_generated and (not self.focus_classes or cn in self.focus_classes):
|
132
132
|
cls = self.schema.classes[cn]
|
133
133
|
for slot in self.filtered_cls_slots(cn, all_slots=True, filtr=lambda s: s.range not in self.schema.classes):
|
@@ -168,8 +168,8 @@ class PlantumlGenerator(Generator):
|
|
168
168
|
@return: PLANTUML representation of the association
|
169
169
|
"""
|
170
170
|
|
171
|
-
classes:
|
172
|
-
assocs:
|
171
|
+
classes: list[str] = []
|
172
|
+
assocs: list[str] = []
|
173
173
|
if cn not in self.associations_generated and (not self.focus_classes or cn in self.focus_classes):
|
174
174
|
cls = self.schema.classes[cn]
|
175
175
|
|
@@ -271,7 +271,7 @@ class PlantumlGenerator(Generator):
|
|
271
271
|
if cn not in self.class_generated:
|
272
272
|
classes.append(self.add_class(cn))
|
273
273
|
assocs.append('"' + cls.is_a + '" ' + plantuml_is_a + ' "' + cn + '"')
|
274
|
-
entries:
|
274
|
+
entries: list[str] = []
|
275
275
|
entries.extend(classes)
|
276
276
|
entries.extend(assocs)
|
277
277
|
return entries
|
@@ -294,7 +294,7 @@ class PlantumlGenerator(Generator):
|
|
294
294
|
cn: ClassDefinitionName,
|
295
295
|
all_slots: bool = True,
|
296
296
|
filtr: Callable[[SlotDefinition], bool] = None,
|
297
|
-
) ->
|
297
|
+
) -> list[SlotDefinition]:
|
298
298
|
"""Return the set of slots associated with the class that meet the filter criteria. Slots will be returned
|
299
299
|
in defining order, with class slots returned last
|
300
300
|
|
@@ -5,7 +5,7 @@ Generate JSON-LD contexts
|
|
5
5
|
|
6
6
|
import os
|
7
7
|
from dataclasses import dataclass, field
|
8
|
-
from typing import
|
8
|
+
from typing import Optional, Union
|
9
9
|
|
10
10
|
import click
|
11
11
|
from jsonasobj2 import JsonObj, as_json
|
@@ -30,10 +30,10 @@ class PrefixGenerator(Generator):
|
|
30
30
|
uses_schemaloader = True
|
31
31
|
|
32
32
|
# ObjectVars
|
33
|
-
emit_prefixes:
|
33
|
+
emit_prefixes: set[str] = field(default_factory=lambda: set())
|
34
34
|
default_ns: str = None
|
35
|
-
context_body:
|
36
|
-
slot_class_maps:
|
35
|
+
context_body: dict = field(default_factory=lambda: dict())
|
36
|
+
slot_class_maps: dict = field(default_factory=lambda: dict())
|
37
37
|
base: Optional[Union[str, Namespace]] = None
|
38
38
|
|
39
39
|
def __post_init__(self):
|
@@ -74,7 +74,7 @@ class PrefixGenerator(Generator):
|
|
74
74
|
context[k] = v
|
75
75
|
|
76
76
|
if self.format == "tsv":
|
77
|
-
mapping:
|
77
|
+
mapping: dict = {} # prefix to IRI mapping
|
78
78
|
for prefix in sorted(self.emit_prefixes):
|
79
79
|
mapping[prefix] = self.namespaces[prefix]
|
80
80
|
|
linkml/generators/projectgen.py
CHANGED
@@ -4,7 +4,7 @@ from collections import defaultdict
|
|
4
4
|
from dataclasses import dataclass, field
|
5
5
|
from functools import lru_cache
|
6
6
|
from pathlib import Path
|
7
|
-
from typing import Any
|
7
|
+
from typing import Any
|
8
8
|
|
9
9
|
import click
|
10
10
|
import yaml
|
@@ -30,9 +30,9 @@ logger = logging.getLogger(__name__)
|
|
30
30
|
|
31
31
|
PATH_FSTRING = str
|
32
32
|
GENERATOR_NAME = str
|
33
|
-
ARG_DICT =
|
34
|
-
CONFIG_TUPLE =
|
35
|
-
GEN_MAP:
|
33
|
+
ARG_DICT = dict[str, Any]
|
34
|
+
CONFIG_TUPLE = tuple[type[Generator], PATH_FSTRING, ARG_DICT]
|
35
|
+
GEN_MAP: dict[GENERATOR_NAME, CONFIG_TUPLE]
|
36
36
|
GEN_MAP = {
|
37
37
|
"graphql": (GraphqlGenerator, "graphql/{name}.graphql", {}),
|
38
38
|
"jsonldcontext": (ContextGenerator, "jsonld/{name}.context.jsonld", {}),
|
@@ -63,7 +63,7 @@ GEN_MAP = {
|
|
63
63
|
}
|
64
64
|
|
65
65
|
|
66
|
-
@lru_cache
|
66
|
+
@lru_cache
|
67
67
|
def get_local_imports(schema_path: str, dir: str):
|
68
68
|
logger.info(f"GETTING IMPORTS = {schema_path}")
|
69
69
|
all_imports = [schema_path]
|
@@ -85,9 +85,9 @@ class ProjectConfiguration:
|
|
85
85
|
"""
|
86
86
|
|
87
87
|
directory: str = "tmp"
|
88
|
-
generator_args:
|
89
|
-
includes:
|
90
|
-
excludes:
|
88
|
+
generator_args: dict[GENERATOR_NAME, ARG_DICT] = field(default_factory=lambda: defaultdict(dict))
|
89
|
+
includes: list[str] = None
|
90
|
+
excludes: list[str] = None
|
91
91
|
mergeimports: bool = None
|
92
92
|
|
93
93
|
|
@@ -188,8 +188,8 @@ class ProjectGenerator:
|
|
188
188
|
def cli(
|
189
189
|
yamlfile,
|
190
190
|
dir,
|
191
|
-
exclude:
|
192
|
-
include:
|
191
|
+
exclude: list[str],
|
192
|
+
include: list[str],
|
193
193
|
config_file,
|
194
194
|
mergeimports,
|
195
195
|
generator_arguments: str,
|
@@ -3,10 +3,7 @@ from abc import ABC, abstractmethod
|
|
3
3
|
from enum import Enum
|
4
4
|
from typing import (
|
5
5
|
ClassVar,
|
6
|
-
Iterable,
|
7
|
-
List,
|
8
6
|
Optional,
|
9
|
-
Type,
|
10
7
|
TypeVar,
|
11
8
|
Union,
|
12
9
|
)
|
@@ -40,14 +37,13 @@ class ArrayRepresentation(Enum):
|
|
40
37
|
_BOUNDED_ARRAY_FIELDS = ("exact_number_dimensions", "minimum_number_dimensions", "maximum_number_dimensions")
|
41
38
|
|
42
39
|
_T = TypeVar("_T")
|
43
|
-
AnyShapeArray = TypeAliasType("AnyShapeArray",
|
40
|
+
AnyShapeArray = TypeAliasType("AnyShapeArray", list[Union[_T, "AnyShapeArray[_T]"]], type_params=(_T,))
|
44
41
|
|
45
42
|
_AnyShapeArrayImports = (
|
46
43
|
Imports()
|
47
44
|
+ Import(
|
48
45
|
module="typing",
|
49
46
|
objects=[
|
50
|
-
ObjectImport(name="Iterable"),
|
51
47
|
ObjectImport(name="TypeVar"),
|
52
48
|
ObjectImport(name="Union"),
|
53
49
|
],
|
@@ -64,7 +60,7 @@ _AnyShapeArrayImports = (
|
|
64
60
|
_AnyShapeArrayInjects = [
|
65
61
|
'_T = TypeVar("_T")',
|
66
62
|
"""AnyShapeArray = TypeAliasType(
|
67
|
-
"AnyShapeArray",
|
63
|
+
"AnyShapeArray", list[Union[_T, "AnyShapeArray[_T]"]], type_params=(_T,)
|
68
64
|
)""",
|
69
65
|
]
|
70
66
|
|
@@ -161,11 +157,9 @@ class ArrayValidator:
|
|
161
157
|
"""
|
162
158
|
if array.minimum_number_dimensions is not None and array.maximum_number_dimensions is None and array.dimensions:
|
163
159
|
raise ValidationError(
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
"maximum_number_dimensions explicitly to False for unbounded dimensions"
|
168
|
-
)
|
160
|
+
"Cannot specify a minimum_number_dimensions while maximum is None while using labeled dimensions - "
|
161
|
+
"either use exact_number_dimensions > len(dimensions) for extra parameterized dimensions or set "
|
162
|
+
"maximum_number_dimensions explicitly to False for unbounded dimensions"
|
169
163
|
)
|
170
164
|
|
171
165
|
@staticmethod
|
@@ -256,7 +250,7 @@ class ArrayRangeGenerator(ABC):
|
|
256
250
|
return any([getattr(self.array, arr_field, None) is not None for arr_field in _BOUNDED_ARRAY_FIELDS])
|
257
251
|
|
258
252
|
@classmethod
|
259
|
-
def get_generator(cls, repr: ArrayRepresentation) ->
|
253
|
+
def get_generator(cls, repr: ArrayRepresentation) -> type["ArrayRangeGenerator"]:
|
260
254
|
"""Get the generator class for a given array representation"""
|
261
255
|
for subclass in cls.__subclasses__():
|
262
256
|
if repr in (subclass.REPR, subclass.REPR.value):
|
@@ -293,7 +287,7 @@ class ListOfListsArray(ArrayRangeGenerator):
|
|
293
287
|
|
294
288
|
@staticmethod
|
295
289
|
def _list_of_lists(dimensions: int, dtype: str) -> str:
|
296
|
-
return ("
|
290
|
+
return ("list[" * dimensions) + dtype + ("]" * dimensions)
|
297
291
|
|
298
292
|
@staticmethod
|
299
293
|
def _parameterized_dimension(dimension: DimensionExpression, dtype: str) -> RangeResult:
|
@@ -306,7 +300,7 @@ class ListOfListsArray(ArrayRangeGenerator):
|
|
306
300
|
dmax = dimension.maximum_cardinality
|
307
301
|
else:
|
308
302
|
# TODO: handle labels for labeled but unshaped arrays
|
309
|
-
return RangeResult(range="
|
303
|
+
return RangeResult(range="list[" + dtype + "]")
|
310
304
|
|
311
305
|
items = []
|
312
306
|
if dmin is not None:
|
@@ -341,7 +335,7 @@ class ListOfListsArray(ArrayRangeGenerator):
|
|
341
335
|
|
342
336
|
def _bounded_dimensions(self, array: ArrayExpression) -> RangeResult:
|
343
337
|
"""
|
344
|
-
A nested series of ``
|
338
|
+
A nested series of ``list[]`` ranges with :attr:`.dtype` at the center.
|
345
339
|
|
346
340
|
When an array expression allows for a range of dimensions, each set of ``List`` s is joined by a ``Union`` .
|
347
341
|
"""
|
@@ -357,13 +351,13 @@ class ListOfListsArray(ArrayRangeGenerator):
|
|
357
351
|
):
|
358
352
|
return self._any_shape()
|
359
353
|
elif array.maximum_number_dimensions:
|
360
|
-
# e.g., if min = 2, max = 3, range = Union[
|
354
|
+
# e.g., if min = 2, max = 3, range = Union[list[list[dtype]], list[list[list[dtype]]]]
|
361
355
|
min_dims = array.minimum_number_dimensions if array.minimum_number_dimensions is not None else 1
|
362
356
|
ranges = [self._list_of_lists(i, self.dtype) for i in range(min_dims, array.maximum_number_dimensions + 1)]
|
363
357
|
return RangeResult(range="Union[" + ", ".join(ranges) + "]")
|
364
358
|
else:
|
365
359
|
# min specified with no max
|
366
|
-
# e.g., if min = 3, range =
|
360
|
+
# e.g., if min = 3, range = list[list[AnyShapeArray[dtype]]]
|
367
361
|
return RangeResult(
|
368
362
|
range=self._list_of_lists(array.minimum_number_dimensions - 1, self._any_shape().range),
|
369
363
|
injected_classes=_AnyShapeArrayInjects,
|
@@ -415,7 +409,7 @@ class ListOfListsArray(ArrayRangeGenerator):
|
|
415
409
|
|
416
410
|
if array.minimum_number_dimensions:
|
417
411
|
# some minimum anonymous dimensions but unlimited max dimensions
|
418
|
-
# e.g., if min = 3, len(dim) = 2, then res.range =
|
412
|
+
# e.g., if min = 3, len(dim) = 2, then res.range = list[Union[AnyShapeArray[dtype], dtype]]
|
419
413
|
# res.range will be wrapped with the 2 labeled dimensions later
|
420
414
|
res.range = self._list_of_lists(array.minimum_number_dimensions - len(array.dimensions), res.range)
|
421
415
|
|
@@ -433,13 +427,13 @@ class ListOfListsArray(ArrayRangeGenerator):
|
|
433
427
|
|
434
428
|
# Wrap inner dimension with labeled dimension
|
435
429
|
# e.g., if dimensions = [{min_card: 3}, {min_card: 2}]
|
436
|
-
# and res.range =
|
430
|
+
# and res.range = list[Union[AnyShapeArray[dtype], dtype]]
|
437
431
|
# (min 3 dims, no max dims)
|
438
432
|
# then the final range = conlist(
|
439
433
|
# min_length=3,
|
440
434
|
# item_type=conlist(
|
441
435
|
# min_length=2,
|
442
|
-
# item_type=
|
436
|
+
# item_type=list[Union[AnyShapeArray[dtype], dtype]]
|
443
437
|
# )
|
444
438
|
# )
|
445
439
|
for dim in reversed(array.dimensions):
|
@@ -473,7 +467,7 @@ class NumpydanticArray(ArrayRangeGenerator):
|
|
473
467
|
return result
|
474
468
|
|
475
469
|
@staticmethod
|
476
|
-
def ndarray_annotation(shape: Optional[
|
470
|
+
def ndarray_annotation(shape: Optional[list[Union[int, str]]] = None, dtype: Optional[str] = None) -> str:
|
477
471
|
"""
|
478
472
|
Make a stringified :class:`numpydantic.NDArray` annotation for a given shape
|
479
473
|
and dtype.
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from pathlib import Path
|
2
|
-
from typing import
|
2
|
+
from typing import Optional, TypeVar, Union
|
3
3
|
|
4
4
|
from linkml.generators.common.build import (
|
5
5
|
BuildResult,
|
@@ -24,8 +24,8 @@ class PydanticBuildResult(BuildResult):
|
|
24
24
|
BuildResult parent class for pydantic generator
|
25
25
|
"""
|
26
26
|
|
27
|
-
imports: Optional[Union[
|
28
|
-
injected_classes: Optional[
|
27
|
+
imports: Optional[Union[list[Import], Imports]] = None
|
28
|
+
injected_classes: Optional[list[Union[str, type]]] = None
|
29
29
|
|
30
30
|
def merge(self, other: T) -> T:
|
31
31
|
"""
|
@@ -92,7 +92,7 @@ class RangeResult(PydanticBuildResult, RangeResult_):
|
|
92
92
|
Returns:
|
93
93
|
:class:`.SlotResult`
|
94
94
|
"""
|
95
|
-
res = super(
|
95
|
+
res = super().merge(other)
|
96
96
|
# Replace with other's annotation
|
97
97
|
res.range = other.range
|
98
98
|
if other.field_extras is not None:
|