linkml 1.8.7__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/class.md.jinja2 +1 -1
- linkml/generators/docgen/enum.md.jinja2 +1 -1
- linkml/generators/docgen/schema.md.jinja2 +1 -1
- linkml/generators/docgen/slot.md.jinja2 +4 -1
- linkml/generators/docgen/subset.md.jinja2 +1 -1
- linkml/generators/docgen/type.md.jinja2 +1 -1
- 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/linkmlgen.py +10 -2
- 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 +19 -10
- 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.8.7.dist-info → linkml-1.9.1.dist-info}/METADATA +6 -9
- linkml-1.9.1.dist-info/RECORD +162 -0
- {linkml-1.8.7.dist-info → linkml-1.9.1.dist-info}/WHEEL +1 -1
- linkml-1.8.7.dist-info/RECORD +0 -162
- {linkml-1.8.7.dist-info → linkml-1.9.1.dist-info}/LICENSE +0 -0
- {linkml-1.8.7.dist-info → linkml-1.9.1.dist-info}/entry_points.txt +0 -0
@@ -8,7 +8,7 @@ from dataclasses import dataclass, field
|
|
8
8
|
from enum import Enum
|
9
9
|
from pathlib import Path
|
10
10
|
from types import ModuleType
|
11
|
-
from typing import ClassVar,
|
11
|
+
from typing import ClassVar, Literal, Optional, TypeVar, Union, overload
|
12
12
|
|
13
13
|
import click
|
14
14
|
from jinja2 import ChoiceLoader, Environment, FileSystemLoader, Template
|
@@ -82,9 +82,7 @@ DEFAULT_IMPORTS = (
|
|
82
82
|
objects=[
|
83
83
|
ObjectImport(name="Any"),
|
84
84
|
ObjectImport(name="ClassVar"),
|
85
|
-
ObjectImport(name="List"),
|
86
85
|
ObjectImport(name="Literal"),
|
87
|
-
ObjectImport(name="Dict"),
|
88
86
|
ObjectImport(name="Optional"),
|
89
87
|
ObjectImport(name="Union"),
|
90
88
|
],
|
@@ -183,7 +181,7 @@ class PydanticGenerator(OOCodeGenerator, LifecycleMixin):
|
|
183
181
|
file_extension = "py"
|
184
182
|
|
185
183
|
# ObjectVars
|
186
|
-
array_representations:
|
184
|
+
array_representations: list[ArrayRepresentation] = field(default_factory=lambda: [ArrayRepresentation.LIST])
|
187
185
|
black: bool = False
|
188
186
|
"""
|
189
187
|
If black is present in the environment, format the serialized code with it
|
@@ -199,7 +197,7 @@ class PydanticGenerator(OOCodeGenerator, LifecycleMixin):
|
|
199
197
|
"""
|
200
198
|
extra_fields: Literal["allow", "forbid", "ignore"] = "forbid"
|
201
199
|
gen_mixin_inheritance: bool = True
|
202
|
-
injected_classes: Optional[
|
200
|
+
injected_classes: Optional[list[Union[type, str]]] = None
|
203
201
|
"""
|
204
202
|
A list/tuple of classes to inject into the generated module.
|
205
203
|
|
@@ -208,7 +206,7 @@ class PydanticGenerator(OOCodeGenerator, LifecycleMixin):
|
|
208
206
|
source file (ie. the module they are contained in needs a ``__file__`` attr,
|
209
207
|
see: :func:`inspect.getsource` )
|
210
208
|
"""
|
211
|
-
injected_fields: Optional[
|
209
|
+
injected_fields: Optional[list[str]] = None
|
212
210
|
"""
|
213
211
|
A list/tuple of field strings to inject into the base class.
|
214
212
|
|
@@ -221,7 +219,7 @@ class PydanticGenerator(OOCodeGenerator, LifecycleMixin):
|
|
221
219
|
)
|
222
220
|
|
223
221
|
"""
|
224
|
-
imports: Optional[Union[
|
222
|
+
imports: Optional[Union[list[Import], Imports]] = None
|
225
223
|
"""
|
226
224
|
Additional imports to inject into generated module.
|
227
225
|
|
@@ -353,8 +351,8 @@ class PydanticGenerator(OOCodeGenerator, LifecycleMixin):
|
|
353
351
|
"""Substitute CamelCase and non-word characters with _"""
|
354
352
|
|
355
353
|
# Private attributes
|
356
|
-
_predefined_slot_values: Optional[
|
357
|
-
_class_bases: Optional[
|
354
|
+
_predefined_slot_values: Optional[dict[str, dict[str, str]]] = None
|
355
|
+
_class_bases: Optional[dict[str, list[str]]] = None
|
358
356
|
|
359
357
|
def __post_init__(self):
|
360
358
|
super().__post_init__()
|
@@ -372,7 +370,7 @@ class PydanticGenerator(OOCodeGenerator, LifecycleMixin):
|
|
372
370
|
logger.error(f"Error compiling generated python code: {e}")
|
373
371
|
raise e
|
374
372
|
|
375
|
-
def _get_classes(self, sv: SchemaView) ->
|
373
|
+
def _get_classes(self, sv: SchemaView) -> tuple[list[ClassDefinition], Optional[list[ClassDefinition]]]:
|
376
374
|
all_classes = sv.all_classes(imports=True).values()
|
377
375
|
|
378
376
|
if self.split:
|
@@ -384,8 +382,8 @@ class PydanticGenerator(OOCodeGenerator, LifecycleMixin):
|
|
384
382
|
|
385
383
|
@staticmethod
|
386
384
|
def sort_classes(
|
387
|
-
clist:
|
388
|
-
) ->
|
385
|
+
clist: list[ClassDefinition], imported: Optional[list[ClassDefinition]] = None
|
386
|
+
) -> list[ClassDefinition]:
|
389
387
|
"""
|
390
388
|
sort classes such that if C is a child of P then C appears after P in the list
|
391
389
|
|
@@ -515,7 +513,7 @@ class PydanticGenerator(OOCodeGenerator, LifecycleMixin):
|
|
515
513
|
else:
|
516
514
|
collection_key = None
|
517
515
|
if slot.inlined is False or collection_key is None or slot.inlined_as_list is True:
|
518
|
-
result.attribute.range = f"
|
516
|
+
result.attribute.range = f"list[{result.attribute.range}]"
|
519
517
|
else:
|
520
518
|
simple_dict_value = None
|
521
519
|
if len(slot_ranges) == 1:
|
@@ -525,15 +523,15 @@ class PydanticGenerator(OOCodeGenerator, LifecycleMixin):
|
|
525
523
|
# so we specify either that identifier or the range itself
|
526
524
|
if simple_dict_value != result.attribute.range:
|
527
525
|
simple_dict_value = f"Union[{simple_dict_value}, {result.attribute.range}]"
|
528
|
-
result.attribute.range = f"
|
526
|
+
result.attribute.range = f"dict[str, {simple_dict_value}]"
|
529
527
|
else:
|
530
|
-
result.attribute.range = f"
|
528
|
+
result.attribute.range = f"dict[{collection_key}, {result.attribute.range}]"
|
531
529
|
if not (slot.required or slot.identifier or slot.key) and not slot.designates_type:
|
532
530
|
result.attribute.range = f"Optional[{result.attribute.range}]"
|
533
531
|
return result
|
534
532
|
|
535
533
|
@property
|
536
|
-
def predefined_slot_values(self) ->
|
534
|
+
def predefined_slot_values(self) -> dict[str, dict[str, str]]:
|
537
535
|
"""
|
538
536
|
:return: Dictionary of dictionaries with predefined slot values for each class
|
539
537
|
"""
|
@@ -563,7 +561,7 @@ class PydanticGenerator(OOCodeGenerator, LifecycleMixin):
|
|
563
561
|
return self._predefined_slot_values
|
564
562
|
|
565
563
|
@property
|
566
|
-
def class_bases(self) ->
|
564
|
+
def class_bases(self) -> dict[str, list[str]]:
|
567
565
|
"""
|
568
566
|
Generate the inheritance list for each class from is_a plus mixins
|
569
567
|
:return:
|
@@ -682,7 +680,7 @@ class PydanticGenerator(OOCodeGenerator, LifecycleMixin):
|
|
682
680
|
|
683
681
|
def generate_collection_key(
|
684
682
|
self,
|
685
|
-
slot_ranges:
|
683
|
+
slot_ranges: list[str],
|
686
684
|
slot_def: SlotDefinition,
|
687
685
|
class_def: ClassDefinition,
|
688
686
|
) -> Optional[str]:
|
@@ -697,7 +695,7 @@ class PydanticGenerator(OOCodeGenerator, LifecycleMixin):
|
|
697
695
|
:param slot_ranges: list of python range values
|
698
696
|
"""
|
699
697
|
|
700
|
-
collection_keys:
|
698
|
+
collection_keys: set[str] = set()
|
701
699
|
|
702
700
|
if slot_ranges is None:
|
703
701
|
return None
|
@@ -715,7 +713,7 @@ class PydanticGenerator(OOCodeGenerator, LifecycleMixin):
|
|
715
713
|
return list(collection_keys)[0]
|
716
714
|
return None
|
717
715
|
|
718
|
-
def _clean_injected_classes(self, injected_classes:
|
716
|
+
def _clean_injected_classes(self, injected_classes: list[Union[str, type]]) -> Optional[list[str]]:
|
719
717
|
"""Get source, deduplicate, and dedent injected classes"""
|
720
718
|
if len(injected_classes) == 0:
|
721
719
|
return None
|
@@ -769,7 +767,7 @@ class PydanticGenerator(OOCodeGenerator, LifecycleMixin):
|
|
769
767
|
env.loader = loader
|
770
768
|
return env
|
771
769
|
|
772
|
-
def get_array_representations_range(self, slot: SlotDefinition, range: str) ->
|
770
|
+
def get_array_representations_range(self, slot: SlotDefinition, range: str) -> list[SlotResult]:
|
773
771
|
"""
|
774
772
|
Generate the python range for array representations
|
775
773
|
"""
|
@@ -1028,7 +1026,7 @@ class PydanticGenerator(OOCodeGenerator, LifecycleMixin):
|
|
1028
1026
|
split_context: Optional[dict] = None,
|
1029
1027
|
split_mode: SplitMode = SplitMode.AUTO,
|
1030
1028
|
**kwargs,
|
1031
|
-
) ->
|
1029
|
+
) -> list[SplitResult]:
|
1032
1030
|
"""
|
1033
1031
|
Generate a schema that imports from other schema as a set of python modules that
|
1034
1032
|
import from one another, rather than generating all imported classes in a single schema.
|
@@ -1119,7 +1117,7 @@ class PydanticGenerator(OOCodeGenerator, LifecycleMixin):
|
|
1119
1117
|
return results
|
1120
1118
|
|
1121
1119
|
|
1122
|
-
def _subclasses(cls:
|
1120
|
+
def _subclasses(cls: type):
|
1123
1121
|
return set(cls.__subclasses__()).union([s for c in cls.__subclasses__() for s in _subclasses(c)])
|
1124
1122
|
|
1125
1123
|
|
@@ -1137,7 +1135,7 @@ def _import_to_path(module: str) -> Path:
|
|
1137
1135
|
return Path(*dir_pieces)
|
1138
1136
|
|
1139
1137
|
|
1140
|
-
def _ensure_inits(paths:
|
1138
|
+
def _ensure_inits(paths: list[Path]):
|
1141
1139
|
"""For a set of paths, find the common root and it and all the subdirectories have an __init__.py"""
|
1142
1140
|
# if there is only one file, there is no relative importing to be done
|
1143
1141
|
if len(paths) <= 1:
|
@@ -1223,10 +1221,8 @@ def cli(
|
|
1223
1221
|
"""Generate pydantic classes to represent a LinkML model"""
|
1224
1222
|
if template_file is not None:
|
1225
1223
|
raise DeprecationWarning(
|
1226
|
-
|
1227
|
-
|
1228
|
-
"See help string for --template-dir"
|
1229
|
-
)
|
1224
|
+
"Passing a single template_file is deprecated. Pass a directory of template files instead. "
|
1225
|
+
"See help string for --template-dir"
|
1230
1226
|
)
|
1231
1227
|
|
1232
1228
|
if template_dir is not None:
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import sys
|
2
|
+
from collections.abc import Generator
|
2
3
|
from importlib.util import find_spec
|
3
|
-
from typing import Any, ClassVar,
|
4
|
+
from typing import Any, ClassVar, Literal, Optional, Union, get_args
|
4
5
|
|
5
6
|
from jinja2 import Environment, PackageLoader
|
6
7
|
from pydantic import BaseModel, Field, field_validator
|
@@ -74,7 +75,7 @@ class PydanticTemplateModel(TemplateModel):
|
|
74
75
|
loader=PackageLoader("linkml.generators.pydanticgen", "templates"), trim_blocks=True, lstrip_blocks=True
|
75
76
|
)
|
76
77
|
|
77
|
-
meta_exclude: ClassVar[
|
78
|
+
meta_exclude: ClassVar[list[str]] = None
|
78
79
|
|
79
80
|
def render(self, environment: Optional[Environment] = None, black: bool = False) -> str:
|
80
81
|
"""
|
@@ -125,7 +126,7 @@ class PydanticEnum(PydanticTemplateModel):
|
|
125
126
|
|
126
127
|
name: str
|
127
128
|
description: Optional[str] = None
|
128
|
-
values:
|
129
|
+
values: dict[str, EnumValue] = Field(default_factory=dict)
|
129
130
|
|
130
131
|
|
131
132
|
class PydanticBaseModel(PydanticTemplateModel):
|
@@ -141,7 +142,7 @@ class PydanticBaseModel(PydanticTemplateModel):
|
|
141
142
|
"""
|
142
143
|
Sets the ``extra`` model for pydantic models
|
143
144
|
"""
|
144
|
-
fields: Optional[
|
145
|
+
fields: Optional[list[str]] = None
|
145
146
|
"""
|
146
147
|
Extra fields that are typically injected into the base model via
|
147
148
|
:attr:`~linkml.generators.pydanticgen.PydanticGenerator.injected_fields`
|
@@ -166,7 +167,7 @@ class PydanticAttribute(PydanticTemplateModel):
|
|
166
167
|
"""
|
167
168
|
|
168
169
|
template: ClassVar[str] = "attribute.py.jinja"
|
169
|
-
meta_exclude: ClassVar[
|
170
|
+
meta_exclude: ClassVar[list[str]] = ["from_schema", "owner", "range", "inlined", "inlined_as_list"]
|
170
171
|
|
171
172
|
name: str
|
172
173
|
required: bool = False
|
@@ -186,7 +187,7 @@ class PydanticAttribute(PydanticTemplateModel):
|
|
186
187
|
maximum_cardinality: Optional[int] = None
|
187
188
|
multivalued: Optional[bool] = None
|
188
189
|
pattern: Optional[str] = None
|
189
|
-
meta: Optional[
|
190
|
+
meta: Optional[dict[str, Any]] = None
|
190
191
|
"""
|
191
192
|
Metadata for the slot to be included in a Field annotation
|
192
193
|
"""
|
@@ -222,29 +223,29 @@ class PydanticClass(PydanticTemplateModel):
|
|
222
223
|
"""
|
223
224
|
|
224
225
|
template: ClassVar[str] = "class.py.jinja"
|
225
|
-
meta_exclude: ClassVar[
|
226
|
+
meta_exclude: ClassVar[list[str]] = ["slots", "is_a"]
|
226
227
|
|
227
228
|
name: str
|
228
|
-
bases: Union[
|
229
|
+
bases: Union[list[str], str] = PydanticBaseModel.default_name
|
229
230
|
description: Optional[str] = None
|
230
|
-
attributes: Optional[
|
231
|
-
meta: Optional[
|
231
|
+
attributes: Optional[dict[str, PydanticAttribute]] = None
|
232
|
+
meta: Optional[dict[str, Any]] = None
|
232
233
|
"""
|
233
234
|
Metadata for the class to be included in a linkml_meta class attribute
|
234
235
|
"""
|
235
236
|
|
236
|
-
def _validators(self) -> Optional[
|
237
|
+
def _validators(self) -> Optional[dict[str, PydanticValidator]]:
|
237
238
|
if self.attributes is None:
|
238
239
|
return None
|
239
240
|
|
240
241
|
return {k: PydanticValidator(**v.model_dump()) for k, v in self.attributes.items() if v.pattern is not None}
|
241
242
|
|
242
243
|
@computed_field
|
243
|
-
def validators(self) -> Optional[
|
244
|
+
def validators(self) -> Optional[dict[str, PydanticValidator]]:
|
244
245
|
return self._validators()
|
245
246
|
|
246
247
|
@computed_field
|
247
|
-
def slots(self) -> Optional[
|
248
|
+
def slots(self) -> Optional[dict[str, PydanticAttribute]]:
|
248
249
|
"""alias of attributes"""
|
249
250
|
return self.attributes
|
250
251
|
|
@@ -293,7 +294,7 @@ class Import(PydanticTemplateModel):
|
|
293
294
|
template: ClassVar[str] = "imports.py.jinja"
|
294
295
|
module: str
|
295
296
|
alias: Optional[str] = None
|
296
|
-
objects: Optional[
|
297
|
+
objects: Optional[list[ObjectImport]] = None
|
297
298
|
is_schema: bool = False
|
298
299
|
"""
|
299
300
|
Whether or not this ``Import`` is importing another schema imported by the main schema --
|
@@ -323,7 +324,7 @@ class Import(PydanticTemplateModel):
|
|
323
324
|
else:
|
324
325
|
return "thirdparty"
|
325
326
|
|
326
|
-
def merge(self, other: "Import") ->
|
327
|
+
def merge(self, other: "Import") -> list["Import"]:
|
327
328
|
"""
|
328
329
|
Merge one import with another, see :meth:`.Imports` for an example.
|
329
330
|
|
@@ -440,7 +441,7 @@ class ConditionalImport(Import):
|
|
440
441
|
"""
|
441
442
|
:meth:`.Import.sort` called for self and :attr:`.alternative`
|
442
443
|
"""
|
443
|
-
super(
|
444
|
+
super().sort()
|
444
445
|
self.alternative.sort()
|
445
446
|
|
446
447
|
|
@@ -478,16 +479,16 @@ class Imports(PydanticTemplateModel):
|
|
478
479
|
|
479
480
|
template: ClassVar[str] = "imports.py.jinja"
|
480
481
|
|
481
|
-
imports:
|
482
|
-
group_order:
|
482
|
+
imports: list[Union[Import, ConditionalImport]] = Field(default_factory=list)
|
483
|
+
group_order: tuple[str, ...] = get_args(IMPORT_GROUPS)
|
483
484
|
"""Order in which to sort imports by their :attr:`.Import.group`"""
|
484
485
|
render_sorted: bool = True
|
485
486
|
"""When rendering, render in sorted groups"""
|
486
487
|
|
487
488
|
@classmethod
|
488
489
|
def _merge(
|
489
|
-
cls, imports:
|
490
|
-
) ->
|
490
|
+
cls, imports: list[Union[Import, ConditionalImport]], other: Union[Import, "Imports", list[Import]]
|
491
|
+
) -> list[Union[Import, ConditionalImport]]:
|
491
492
|
"""
|
492
493
|
Add a new import to an existing imports list, handling deduplication and flattening.
|
493
494
|
|
@@ -545,7 +546,7 @@ class Imports(PydanticTemplateModel):
|
|
545
546
|
imports = sorted(imports, key=lambda i: i.module == "__future__", reverse=True)
|
546
547
|
return imports
|
547
548
|
|
548
|
-
def __add__(self, other: Union[Import, "Imports",
|
549
|
+
def __add__(self, other: Union[Import, "Imports", list[Import]]) -> "Imports":
|
549
550
|
imports = self.imports.copy()
|
550
551
|
imports = self._merge(imports, other)
|
551
552
|
return Imports.model_construct(
|
@@ -556,8 +557,7 @@ class Imports(PydanticTemplateModel):
|
|
556
557
|
return len(self.imports)
|
557
558
|
|
558
559
|
def __iter__(self) -> Generator[Import, None, None]:
|
559
|
-
|
560
|
-
yield i
|
560
|
+
yield from self.imports
|
561
561
|
|
562
562
|
def __getitem__(self, item: Union[int, str]) -> Import:
|
563
563
|
if isinstance(item, int):
|
@@ -571,7 +571,7 @@ class Imports(PydanticTemplateModel):
|
|
571
571
|
else:
|
572
572
|
raise TypeError(f"Can only index with an int or a string as the name of the module,\nGot: {type(item)}")
|
573
573
|
|
574
|
-
def __contains__(self, item: Union[Import, "Imports",
|
574
|
+
def __contains__(self, item: Union[Import, "Imports", list[Import]]) -> bool:
|
575
575
|
"""
|
576
576
|
Check if all the objects are imported from the given module(s)
|
577
577
|
|
@@ -601,8 +601,8 @@ class Imports(PydanticTemplateModel):
|
|
601
601
|
@field_validator("imports", mode="after")
|
602
602
|
@classmethod
|
603
603
|
def imports_are_merged(
|
604
|
-
cls, imports:
|
605
|
-
) ->
|
604
|
+
cls, imports: list[Union[Import, ConditionalImport]]
|
605
|
+
) -> list[Union[Import, ConditionalImport]]:
|
606
606
|
"""
|
607
607
|
When creating from a list of imports, construct model as if we have done so by iteratively
|
608
608
|
constructing with __add__ calls
|
@@ -613,7 +613,7 @@ class Imports(PydanticTemplateModel):
|
|
613
613
|
return merged_imports
|
614
614
|
|
615
615
|
@computed_field
|
616
|
-
def import_groups(self) ->
|
616
|
+
def import_groups(self) -> list[IMPORT_GROUPS]:
|
617
617
|
"""
|
618
618
|
List of what group each import belongs to
|
619
619
|
"""
|
@@ -629,7 +629,7 @@ class Imports(PydanticTemplateModel):
|
|
629
629
|
* Then alphabetically by module name
|
630
630
|
"""
|
631
631
|
|
632
|
-
def _sort_key(i: Import) ->
|
632
|
+
def _sort_key(i: Import) -> tuple[int, int, str]:
|
633
633
|
return (self.group_order.index(i.group), int(i.objects is not None), i.module)
|
634
634
|
|
635
635
|
imports = sorted(self.imports, key=_sort_key)
|
@@ -640,7 +640,7 @@ class Imports(PydanticTemplateModel):
|
|
640
640
|
def render(self, environment: Optional[Environment] = None, black: bool = False) -> str:
|
641
641
|
if self.render_sorted:
|
642
642
|
self.sort()
|
643
|
-
return super(
|
643
|
+
return super().render(environment=environment, black=black)
|
644
644
|
|
645
645
|
|
646
646
|
class PydanticModule(PydanticTemplateModel):
|
@@ -654,24 +654,24 @@ class PydanticModule(PydanticTemplateModel):
|
|
654
654
|
metamodel_version: Optional[str] = None
|
655
655
|
version: Optional[str] = None
|
656
656
|
base_model: PydanticBaseModel = PydanticBaseModel()
|
657
|
-
injected_classes: Optional[
|
658
|
-
python_imports: Union[Imports,
|
659
|
-
enums:
|
660
|
-
classes:
|
661
|
-
meta: Optional[
|
657
|
+
injected_classes: Optional[list[str]] = None
|
658
|
+
python_imports: Union[Imports, list[Union[Import, ConditionalImport]]] = Imports()
|
659
|
+
enums: dict[str, PydanticEnum] = Field(default_factory=dict)
|
660
|
+
classes: dict[str, PydanticClass] = Field(default_factory=dict)
|
661
|
+
meta: Optional[dict[str, Any]] = None
|
662
662
|
"""
|
663
663
|
Metadata for the schema to be included in a linkml_meta module-level instance of LinkMLMeta
|
664
664
|
"""
|
665
665
|
|
666
666
|
@field_validator("python_imports", mode="after")
|
667
667
|
@classmethod
|
668
|
-
def cast_imports(cls, imports: Union[Imports,
|
668
|
+
def cast_imports(cls, imports: Union[Imports, list[Union[Import, ConditionalImport]]]) -> Imports:
|
669
669
|
if isinstance(imports, list):
|
670
670
|
imports = Imports(imports=imports)
|
671
671
|
return imports
|
672
672
|
|
673
673
|
@computed_field
|
674
|
-
def class_names(self) ->
|
674
|
+
def class_names(self) -> list[str]:
|
675
675
|
return [c.name for c in self.classes.values()]
|
676
676
|
|
677
677
|
|
linkml/generators/pythongen.py
CHANGED
@@ -2,11 +2,12 @@ import keyword
|
|
2
2
|
import logging
|
3
3
|
import os
|
4
4
|
import re
|
5
|
+
from collections.abc import Iterator
|
5
6
|
from copy import copy
|
6
7
|
from dataclasses import dataclass
|
7
8
|
from pathlib import Path
|
8
9
|
from types import ModuleType
|
9
|
-
from typing import Callable,
|
10
|
+
from typing import Callable, Optional, Union
|
10
11
|
|
11
12
|
import click
|
12
13
|
from linkml_runtime import SchemaView
|
@@ -204,12 +205,6 @@ class PythonGenerator(Generator):
|
|
204
205
|
ObjectImport(name="extended_int"),
|
205
206
|
],
|
206
207
|
)
|
207
|
-
+ Import(
|
208
|
-
module="linkml_runtime.utils.dataclass_extensions_376",
|
209
|
-
objects=[
|
210
|
-
ObjectImport(name="dataclasses_init_fn_with_kwargs"),
|
211
|
-
],
|
212
|
-
)
|
213
208
|
+ Import(
|
214
209
|
module="linkml_runtime.utils.formatutils",
|
215
210
|
objects=[
|
@@ -265,9 +260,6 @@ class PythonGenerator(Generator):
|
|
265
260
|
metamodel_version = "{self.schema.metamodel_version}"
|
266
261
|
version = {'"' + self.schema.version + '"' if self.schema.version else None}
|
267
262
|
|
268
|
-
# Overwrite dataclasses _init_fn to add **kwargs in __init__
|
269
|
-
dataclasses._init_fn = dataclasses_init_fn_with_kwargs
|
270
|
-
|
271
263
|
# Namespaces
|
272
264
|
{self.gen_namespaces()}
|
273
265
|
|
@@ -292,7 +284,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
|
|
292
284
|
list_ents = [f"from {k} import {', '.join(v)}" for k, v in self.gen_import_list().items()]
|
293
285
|
return "\n".join(list_ents)
|
294
286
|
|
295
|
-
def gen_import_list(self) ->
|
287
|
+
def gen_import_list(self) -> dict[str, list[str]]:
|
296
288
|
"""
|
297
289
|
Generate a list of types to import
|
298
290
|
|
@@ -302,7 +294,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
|
|
302
294
|
class ImportList:
|
303
295
|
def __init__(self, schema_location: str):
|
304
296
|
self.schema_location = schema_location
|
305
|
-
self.v:
|
297
|
+
self.v: dict[str, set[str]] = {}
|
306
298
|
|
307
299
|
def add_element(self, e: Element) -> None:
|
308
300
|
if e.imported_from:
|
@@ -324,7 +316,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
|
|
324
316
|
else:
|
325
317
|
innerself.v.setdefault(". " + path, set()).add(name)
|
326
318
|
|
327
|
-
def values(self) ->
|
319
|
+
def values(self) -> dict[str, list[str]]:
|
328
320
|
return {k: sorted(self.v[k]) for k in sorted(self.v.keys())}
|
329
321
|
|
330
322
|
def add_type_ref(typ: TypeDefinition) -> None:
|
@@ -515,7 +507,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
|
|
515
507
|
if slot.inherited:
|
516
508
|
inherited_slots.append(slot.alias if slot.alias else slotname)
|
517
509
|
inherited_slots_str = ", ".join([f'"{underscore(s)}"' for s in inherited_slots])
|
518
|
-
return f"\n\t_inherited_slots: ClassVar[
|
510
|
+
return f"\n\t_inherited_slots: ClassVar[list[str]] = [{inherited_slots_str}]\n"
|
519
511
|
|
520
512
|
def gen_class_meta(self, cls: ClassDefinition) -> str:
|
521
513
|
if not self.gen_classvars:
|
@@ -638,7 +630,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
|
|
638
630
|
slot: SlotDefinition,
|
639
631
|
cls: Optional[ClassDefinition],
|
640
632
|
positional_allowed: bool,
|
641
|
-
) ->
|
633
|
+
) -> tuple[str, Optional[str]]:
|
642
634
|
"""
|
643
635
|
Return the range type including initializers, etc.
|
644
636
|
Generate a class variable declaration for the supplied slot. Note: the positional_allowed attribute works,
|
@@ -663,33 +655,33 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
|
|
663
655
|
if num_elements == 1:
|
664
656
|
if slot.required:
|
665
657
|
return (
|
666
|
-
f"Union[
|
658
|
+
f"Union[list[{base_key}], dict[{base_key}, {range_type}]]",
|
667
659
|
dflt,
|
668
660
|
)
|
669
661
|
else:
|
670
662
|
return (
|
671
|
-
f"Optional[Union[
|
663
|
+
f"Optional[Union[list[{base_key}], dict[{base_key}, {range_type}]]]",
|
672
664
|
dflt,
|
673
665
|
)
|
674
666
|
else:
|
675
667
|
if slot.required:
|
676
668
|
return (
|
677
|
-
f"Union[
|
669
|
+
f"Union[dict[{base_key}, {range_type}], list[{range_type}]]",
|
678
670
|
dflt,
|
679
671
|
)
|
680
672
|
else:
|
681
673
|
return (
|
682
|
-
f"Optional[Union[
|
674
|
+
f"Optional[Union[dict[{base_key}, {range_type}], list[{range_type}]]]",
|
683
675
|
dflt,
|
684
676
|
)
|
685
677
|
|
686
678
|
# All other cases
|
687
679
|
if slot.multivalued:
|
688
680
|
if slot.required:
|
689
|
-
return f"Union[{range_type},
|
681
|
+
return f"Union[{range_type}, list[{range_type}]]", (None if positional_allowed else "None")
|
690
682
|
else:
|
691
683
|
return (
|
692
|
-
f"Optional[Union[{range_type},
|
684
|
+
f"Optional[Union[{range_type}, list[{range_type}]]]",
|
693
685
|
"empty_list()",
|
694
686
|
)
|
695
687
|
elif slot.required:
|
@@ -697,7 +689,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
|
|
697
689
|
else:
|
698
690
|
return f"Optional[{range_type}]", "None"
|
699
691
|
|
700
|
-
def class_reference_type(self, slot: SlotDefinition, cls: Optional[ClassDefinition]) ->
|
692
|
+
def class_reference_type(self, slot: SlotDefinition, cls: Optional[ClassDefinition]) -> tuple[str, str, str]:
|
701
693
|
"""
|
702
694
|
Return the type of slot referencing a class
|
703
695
|
|
@@ -719,7 +711,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
|
|
719
711
|
return str(self.gen_class_reference(rangelist)), prox_type, prox_type_name
|
720
712
|
|
721
713
|
@staticmethod
|
722
|
-
def gen_class_reference(rangelist:
|
714
|
+
def gen_class_reference(rangelist: list[str]) -> str:
|
723
715
|
"""
|
724
716
|
Return a basic or a union type depending on the number of elements in range list
|
725
717
|
|
@@ -766,7 +758,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
|
|
766
758
|
return (
|
767
759
|
(
|
768
760
|
f"""
|
769
|
-
def __post_init__(self, *_:
|
761
|
+
def __post_init__(self, *_: str, **kwargs: Any):
|
770
762
|
{post_inits_line}
|
771
763
|
super().__post_init__(**kwargs)
|
772
764
|
{post_inits_post_super_line}"""
|
@@ -777,7 +769,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
|
|
777
769
|
|
778
770
|
# sort classes such that if C is a child of P then C appears after P in the list
|
779
771
|
@staticmethod
|
780
|
-
def _sort_classes(clist:
|
772
|
+
def _sort_classes(clist: list[ClassDefinition]) -> list[ClassDefinition]:
|
781
773
|
clist = list(clist)
|
782
774
|
slist = [] # sorted
|
783
775
|
while len(clist) > 0:
|
@@ -825,7 +817,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
|
|
825
817
|
:param cls: class to generate constructor for
|
826
818
|
:return: python constructor
|
827
819
|
"""
|
828
|
-
rlines:
|
820
|
+
rlines: list[str] = []
|
829
821
|
designators = [x for x in self.domain_slots(cls) if x.designates_type]
|
830
822
|
if len(designators) > 0:
|
831
823
|
descendants = self.schemaview.class_descendants(cls.name)
|
@@ -880,7 +872,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
|
|
880
872
|
|
881
873
|
def gen_postinit(self, cls: ClassDefinition, slot: SlotDefinition) -> Optional[str]:
|
882
874
|
"""Generate python post init rules for slot in class"""
|
883
|
-
rlines:
|
875
|
+
rlines: list[str] = []
|
884
876
|
|
885
877
|
if slot.range in self.schema.classes:
|
886
878
|
if self.is_class_unconstrained(self.schema.classes[slot.range]):
|
@@ -1018,7 +1010,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
|
|
1018
1010
|
if first_hit_only:
|
1019
1011
|
break
|
1020
1012
|
|
1021
|
-
def primary_keys_for(self, cls: ClassDefinition) ->
|
1013
|
+
def primary_keys_for(self, cls: ClassDefinition) -> list[SlotDefinitionName]:
|
1022
1014
|
"""Return the primary key for cls.
|
1023
1015
|
|
1024
1016
|
Note: At the moment we return at most one entry. At some point, keys will be expanded to support
|
@@ -1068,7 +1060,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
|
|
1068
1060
|
return False # Occurs before
|
1069
1061
|
return True
|
1070
1062
|
|
1071
|
-
def python_uri_for(self, uriorcurie: Union[str, URIRef]) ->
|
1063
|
+
def python_uri_for(self, uriorcurie: Union[str, URIRef]) -> tuple[str, Optional[str]]:
|
1072
1064
|
"""Return the python form of uriorcurie
|
1073
1065
|
:param uriorcurie:
|
1074
1066
|
:return: URI and CURIE form
|
linkml/generators/rdfgen.py
CHANGED
@@ -9,7 +9,7 @@ import os
|
|
9
9
|
import urllib.parse as urlparse
|
10
10
|
from copy import deepcopy
|
11
11
|
from dataclasses import dataclass
|
12
|
-
from typing import
|
12
|
+
from typing import Optional
|
13
13
|
|
14
14
|
import click
|
15
15
|
from linkml_runtime.linkml_model import SchemaDefinition
|
@@ -36,7 +36,7 @@ class RDFGenerator(Generator):
|
|
36
36
|
|
37
37
|
# ObjectVars
|
38
38
|
emit_metadata: bool = False
|
39
|
-
context:
|
39
|
+
context: list[str] = None
|
40
40
|
original_schema: SchemaDefinition = None
|
41
41
|
"""See https://github.com/linkml/linkml/issues/871"""
|
42
42
|
|