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
@@ -3,18 +3,15 @@ Generate JSON-LD contexts
|
|
3
3
|
|
4
4
|
"""
|
5
5
|
import csv
|
6
|
-
import logging
|
7
6
|
import os
|
8
7
|
from dataclasses import dataclass, field
|
9
|
-
from typing import Dict,
|
8
|
+
from typing import Dict, Optional, Set
|
10
9
|
|
11
10
|
import click
|
12
11
|
from jsonasobj2 import JsonObj, as_json
|
13
|
-
from linkml_runtime.linkml_model.meta import
|
14
|
-
Element, SchemaDefinition,
|
15
|
-
SlotDefinition)
|
12
|
+
from linkml_runtime.linkml_model.meta import ClassDefinition, SlotDefinition
|
16
13
|
from linkml_runtime.linkml_model.types import SHEX
|
17
|
-
from linkml_runtime.utils.formatutils import
|
14
|
+
from linkml_runtime.utils.formatutils import camelcase
|
18
15
|
from rdflib import XSD
|
19
16
|
|
20
17
|
from linkml._version import __version__
|
@@ -25,7 +22,6 @@ URI_RANGES = (XSD.anyURI, SHEX.nonliteral, SHEX.bnode, SHEX.iri)
|
|
25
22
|
|
26
23
|
@dataclass
|
27
24
|
class PrefixGenerator(Generator):
|
28
|
-
|
29
25
|
# ClassVars
|
30
26
|
generatorname = os.path.basename(__file__)
|
31
27
|
generatorversion = "0.1.1"
|
@@ -44,12 +40,10 @@ class PrefixGenerator(Generator):
|
|
44
40
|
super().__post_init__()
|
45
41
|
if self.namespaces is None:
|
46
42
|
raise TypeError(
|
47
|
-
"Schema text must be supplied to context
|
43
|
+
"Schema text must be supplied to context generator. Preparsed schema will not work"
|
48
44
|
)
|
49
45
|
|
50
|
-
def visit_schema(
|
51
|
-
self, base: Optional[str] = None, output: Optional[str] = None, **_
|
52
|
-
):
|
46
|
+
def visit_schema(self, base: Optional[str] = None, output: Optional[str] = None, **_):
|
53
47
|
# Add any explicitly declared prefixes
|
54
48
|
for prefix in self.schema.prefixes.values():
|
55
49
|
self.emit_prefixes.add(prefix.prefix_prefix)
|
@@ -66,9 +60,7 @@ class PrefixGenerator(Generator):
|
|
66
60
|
if self.default_ns:
|
67
61
|
self.emit_prefixes.add(self.default_ns)
|
68
62
|
|
69
|
-
def end_schema(
|
70
|
-
self, base: Optional[str] = None, output: Optional[str] = None, **_
|
71
|
-
) -> None:
|
63
|
+
def end_schema(self, base: Optional[str] = None, output: Optional[str] = None, **_) -> None:
|
72
64
|
context = JsonObj()
|
73
65
|
if base:
|
74
66
|
if "://" not in base:
|
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, Dict, List, Tuple, Type
|
7
|
+
from typing import Any, Dict, List, Tuple, Type
|
8
8
|
|
9
9
|
import click
|
10
10
|
import yaml
|
@@ -12,7 +12,6 @@ import yaml
|
|
12
12
|
from linkml._version import __version__
|
13
13
|
from linkml.generators.excelgen import ExcelGenerator
|
14
14
|
from linkml.generators.graphqlgen import GraphqlGenerator
|
15
|
-
from linkml.generators.javagen import JavaGenerator
|
16
15
|
from linkml.generators.jsonldcontextgen import ContextGenerator
|
17
16
|
from linkml.generators.jsonldgen import JSONLDGenerator
|
18
17
|
from linkml.generators.jsonschemagen import JsonSchemaGenerator
|
@@ -24,7 +23,7 @@ from linkml.generators.pythongen import PythonGenerator
|
|
24
23
|
from linkml.generators.shaclgen import ShaclGenerator
|
25
24
|
from linkml.generators.shexgen import ShExGenerator
|
26
25
|
from linkml.generators.sqlddlgen import SQLDDLGenerator
|
27
|
-
from linkml.utils.generator import Generator
|
26
|
+
from linkml.utils.generator import Generator
|
28
27
|
|
29
28
|
PATH_FSTRING = str
|
30
29
|
GENERATOR_NAME = str
|
@@ -98,11 +97,9 @@ class ProjectGenerator:
|
|
98
97
|
Note this doesn't conform to overall generator framework, as it is a meta-generator
|
99
98
|
"""
|
100
99
|
|
101
|
-
def generate(
|
102
|
-
self, schema_path: str, config: ProjectConfiguration = ProjectConfiguration()
|
103
|
-
):
|
100
|
+
def generate(self, schema_path: str, config: ProjectConfiguration = ProjectConfiguration()):
|
104
101
|
if config.directory is None:
|
105
|
-
raise Exception(
|
102
|
+
raise Exception("Must pass directory")
|
106
103
|
Path(config.directory).mkdir(parents=True, exist_ok=True)
|
107
104
|
if config.mergeimports:
|
108
105
|
all_schemas = [schema_path]
|
@@ -115,9 +112,7 @@ class ProjectGenerator:
|
|
115
112
|
and config.includes != []
|
116
113
|
and gen_name not in config.includes
|
117
114
|
):
|
118
|
-
logging.info(
|
119
|
-
f"Skipping {gen_name} as not in inclusion list: {config.includes}"
|
120
|
-
)
|
115
|
+
logging.info(f"Skipping {gen_name} as not in inclusion list: {config.includes}")
|
121
116
|
continue
|
122
117
|
if config.excludes is not None and gen_name in config.excludes:
|
123
118
|
logging.info(f"Skipping {gen_name} as it is in exclusion list")
|
@@ -177,9 +172,7 @@ class ProjectGenerator:
|
|
177
172
|
help="directory in which to place generated files. E.g. linkml_model, biolink_model",
|
178
173
|
)
|
179
174
|
@click.option("--generator-arguments", "-A", help="yaml configuration for generators")
|
180
|
-
@click.option(
|
181
|
-
"--config-file", "-C", type=click.File("rb"), help="path to yaml configuration"
|
182
|
-
)
|
175
|
+
@click.option("--config-file", "-C", type=click.File("rb"), help="path to yaml configuration")
|
183
176
|
@click.option(
|
184
177
|
"--exclude", "-X", multiple=True, help="list of artefacts to be excluded"
|
185
178
|
) # TODO: make this an enum
|
@@ -251,7 +244,7 @@ def cli(
|
|
251
244
|
try:
|
252
245
|
project_config.generator_args = yaml.safe_load(generator_arguments)
|
253
246
|
except Exception:
|
254
|
-
raise Exception(
|
247
|
+
raise Exception("Argument must be a valid YAML blob")
|
255
248
|
logging.info(f"generator args: {project_config.generator_args}")
|
256
249
|
if dir is not None:
|
257
250
|
project_config.directory = dir
|
linkml/generators/protogen.py
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
import os
|
2
2
|
from dataclasses import dataclass, field
|
3
|
-
from typing import TextIO, Union
|
4
3
|
|
5
4
|
import click
|
6
|
-
from linkml_runtime.linkml_model.meta import
|
7
|
-
SchemaDefinition, SlotDefinition)
|
5
|
+
from linkml_runtime.linkml_model.meta import ClassDefinition, SlotDefinition
|
8
6
|
from linkml_runtime.utils.formatutils import camelcase, lcamelcase
|
9
7
|
|
10
8
|
from linkml._version import __version__
|
@@ -33,8 +31,8 @@ class ProtoGenerator(Generator):
|
|
33
31
|
self.generate_header()
|
34
32
|
|
35
33
|
def generate_header(self):
|
36
|
-
print(
|
37
|
-
print(
|
34
|
+
print(' syntax="proto3";')
|
35
|
+
print(" package")
|
38
36
|
print(f"// metamodel_version: {self.schema.metamodel_version}")
|
39
37
|
if self.schema.version:
|
40
38
|
print(f"// version: {self.schema.version}")
|
linkml/generators/pydanticgen.py
CHANGED
@@ -1,36 +1,40 @@
|
|
1
1
|
import logging
|
2
2
|
import os
|
3
|
-
import logging
|
4
3
|
from collections import defaultdict
|
5
4
|
from copy import deepcopy
|
6
|
-
from dataclasses import
|
5
|
+
from dataclasses import dataclass, field
|
7
6
|
from types import ModuleType
|
8
|
-
from typing import Dict, List,
|
9
|
-
|
10
|
-
from linkml_runtime.utils.compile_python import compile_python
|
11
|
-
|
12
|
-
from linkml.utils.ifabsent_functions import ifabsent_value_declaration
|
7
|
+
from typing import Dict, List, Optional, Set
|
13
8
|
|
14
9
|
import click
|
15
10
|
from jinja2 import Template
|
11
|
+
|
16
12
|
# from linkml.generators import pydantic_GEN_VERSION
|
17
|
-
from linkml_runtime.linkml_model.meta import (
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
13
|
+
from linkml_runtime.linkml_model.meta import (
|
14
|
+
Annotation,
|
15
|
+
ClassDefinition,
|
16
|
+
SchemaDefinition,
|
17
|
+
SlotDefinition,
|
18
|
+
TypeDefinition,
|
19
|
+
)
|
20
|
+
from linkml_runtime.utils.compile_python import compile_python
|
23
21
|
from linkml_runtime.utils.formatutils import camelcase, underscore
|
24
22
|
from linkml_runtime.utils.schemaview import SchemaView
|
25
23
|
|
26
24
|
from linkml._version import __version__
|
27
25
|
from linkml.generators.common.type_designators import (
|
28
|
-
get_accepted_type_designator_values,
|
26
|
+
get_accepted_type_designator_values,
|
27
|
+
get_type_designator_value,
|
28
|
+
)
|
29
29
|
from linkml.generators.oocodegen import OOCodeGenerator
|
30
30
|
from linkml.utils.generator import shared_arguments
|
31
31
|
from linkml.utils.ifabsent_functions import ifabsent_value_declaration
|
32
32
|
|
33
|
-
|
33
|
+
|
34
|
+
def default_template(pydantic_ver: str = "1") -> str:
|
35
|
+
"""Constructs a default template for pydantic classes based on the version of pydantic"""
|
36
|
+
### HEADER ###
|
37
|
+
template = """
|
34
38
|
{#-
|
35
39
|
|
36
40
|
Jinja2 Template for a pydantic classes
|
@@ -40,7 +44,6 @@ from datetime import datetime, date
|
|
40
44
|
from enum import Enum
|
41
45
|
from typing import List, Dict, Optional, Any, Union
|
42
46
|
from pydantic import BaseModel as BaseModel, Field
|
43
|
-
from linkml_runtime.linkml_model import Decimal
|
44
47
|
import sys
|
45
48
|
if sys.version_info >= (3, 8):
|
46
49
|
from typing import Literal
|
@@ -50,7 +53,10 @@ else:
|
|
50
53
|
|
51
54
|
metamodel_version = "{{metamodel_version}}"
|
52
55
|
version = "{{version if version else None}}"
|
53
|
-
|
56
|
+
"""
|
57
|
+
### BASE MODEL ###
|
58
|
+
if pydantic_ver == "1":
|
59
|
+
template += """
|
54
60
|
class WeakRefShimBaseModel(BaseModel):
|
55
61
|
__slots__ = '__weakref__'
|
56
62
|
|
@@ -62,7 +68,19 @@ class ConfiguredBaseModel(WeakRefShimBaseModel,
|
|
62
68
|
arbitrary_types_allowed = True,
|
63
69
|
use_enum_values = True):
|
64
70
|
pass
|
65
|
-
|
71
|
+
"""
|
72
|
+
else:
|
73
|
+
template += """
|
74
|
+
class ConfiguredBaseModel(BaseModel,
|
75
|
+
validate_assignment = True,
|
76
|
+
validate_default = True,
|
77
|
+
extra = {% if allow_extra %}'allow'{% else %}'forbid'{% endif %},
|
78
|
+
arbitrary_types_allowed = True,
|
79
|
+
use_enum_values = True):
|
80
|
+
pass
|
81
|
+
"""
|
82
|
+
### ENUMS ###
|
83
|
+
template += """
|
66
84
|
{% for e in enums.values() %}
|
67
85
|
class {{ e.name }}(str, Enum):
|
68
86
|
{% if e.description -%}
|
@@ -80,7 +98,9 @@ class {{ e.name }}(str, Enum):
|
|
80
98
|
dummy = "dummy"
|
81
99
|
{% endif %}
|
82
100
|
{% endfor %}
|
83
|
-
|
101
|
+
"""
|
102
|
+
### CLASSES ###
|
103
|
+
template += """
|
84
104
|
{%- for c in schema.classes.values() %}
|
85
105
|
class {{ c.name }}
|
86
106
|
{%- if class_isa_plus_mixins[c.name] -%}
|
@@ -111,15 +131,26 @@ class {{ c.name }}
|
|
111
131
|
{% else -%}
|
112
132
|
None
|
113
133
|
{% endfor %}
|
114
|
-
|
115
134
|
{% endfor %}
|
116
|
-
|
135
|
+
"""
|
136
|
+
### FWD REFS / REBUILD MODEL ###
|
137
|
+
if pydantic_ver == "1":
|
138
|
+
template += """
|
117
139
|
# Update forward refs
|
118
140
|
# see https://pydantic-docs.helpmanual.io/usage/postponed_annotations/
|
119
141
|
{% for c in schema.classes.values() -%}
|
120
142
|
{{ c.name }}.update_forward_refs()
|
121
143
|
{% endfor %}
|
122
144
|
"""
|
145
|
+
else:
|
146
|
+
template += """
|
147
|
+
# Model rebuild
|
148
|
+
# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model
|
149
|
+
{% for c in schema.classes.values() -%}
|
150
|
+
{{ c.name }}.model_rebuild()
|
151
|
+
{% endfor %}
|
152
|
+
"""
|
153
|
+
return template
|
123
154
|
|
124
155
|
|
125
156
|
def _get_pyrange(t: TypeDefinition, sv: SchemaView) -> str:
|
@@ -147,10 +178,12 @@ class PydanticGenerator(OOCodeGenerator):
|
|
147
178
|
|
148
179
|
# ClassVar overrides
|
149
180
|
generatorname = os.path.basename(__file__)
|
150
|
-
generatorversion = "0.0.
|
181
|
+
generatorversion = "0.0.2"
|
151
182
|
valid_formats = ["pydantic"]
|
183
|
+
file_extension = "py"
|
152
184
|
|
153
185
|
# ObjectVars
|
186
|
+
pydantic_version: str = field(default_factory=lambda: "1")
|
154
187
|
template_file: str = None
|
155
188
|
allow_extra: bool = field(default_factory=lambda: False)
|
156
189
|
gen_mixin_inheritance: bool = field(default_factory=lambda: True)
|
@@ -219,22 +252,16 @@ class PydanticGenerator(OOCodeGenerator):
|
|
219
252
|
slot = sv.induced_slot(slot_name, class_def.name)
|
220
253
|
if slot.designates_type:
|
221
254
|
target_value = get_type_designator_value(sv, slot, class_def)
|
222
|
-
slot_values[camelcase(class_def.name)][
|
223
|
-
slot.name
|
224
|
-
] = f'"{target_value}"'
|
255
|
+
slot_values[camelcase(class_def.name)][slot.name] = f'"{target_value}"'
|
225
256
|
if slot.multivalued:
|
226
257
|
slot_values[camelcase(class_def.name)][slot.name] = (
|
227
|
-
"["
|
228
|
-
+ slot_values[camelcase(class_def.name)][slot.name]
|
229
|
-
+ "]"
|
258
|
+
"[" + slot_values[camelcase(class_def.name)][slot.name] + "]"
|
230
259
|
)
|
231
260
|
slot_values[camelcase(class_def.name)][slot.name] = slot_values[
|
232
261
|
camelcase(class_def.name)
|
233
262
|
][slot.name]
|
234
263
|
elif slot.ifabsent is not None:
|
235
|
-
value = ifabsent_value_declaration(
|
236
|
-
slot.ifabsent, sv, class_def, slot
|
237
|
-
)
|
264
|
+
value = ifabsent_value_declaration(slot.ifabsent, sv, class_def, slot)
|
238
265
|
slot_values[camelcase(class_def.name)][slot.name] = value
|
239
266
|
# Multivalued slots that are either not inlined (just an identifier) or are
|
240
267
|
# inlined as lists should get default_factory list, if they're inlined but
|
@@ -242,18 +269,10 @@ class PydanticGenerator(OOCodeGenerator):
|
|
242
269
|
elif slot.multivalued:
|
243
270
|
has_identifier_slot = self.range_class_has_identifier_slot(slot)
|
244
271
|
|
245
|
-
if
|
246
|
-
slot.
|
247
|
-
and not slot.inlined_as_list
|
248
|
-
and has_identifier_slot
|
249
|
-
):
|
250
|
-
slot_values[camelcase(class_def.name)][
|
251
|
-
slot.name
|
252
|
-
] = "default_factory=dict"
|
272
|
+
if slot.inlined and not slot.inlined_as_list and has_identifier_slot:
|
273
|
+
slot_values[camelcase(class_def.name)][slot.name] = "default_factory=dict"
|
253
274
|
else:
|
254
|
-
slot_values[camelcase(class_def.name)][
|
255
|
-
slot.name
|
256
|
-
] = "default_factory=list"
|
275
|
+
slot_values[camelcase(class_def.name)][slot.name] = "default_factory=list"
|
257
276
|
|
258
277
|
return slot_values
|
259
278
|
|
@@ -319,9 +338,7 @@ class PydanticGenerator(OOCodeGenerator):
|
|
319
338
|
else:
|
320
339
|
return f"Union[{'.'.join(id_ranges)}]"
|
321
340
|
|
322
|
-
def get_class_slot_range(
|
323
|
-
self, slot_range: str, inlined: bool, inlined_as_list: bool
|
324
|
-
) -> str:
|
341
|
+
def get_class_slot_range(self, slot_range: str, inlined: bool, inlined_as_list: bool) -> str:
|
325
342
|
sv = self.schemaview
|
326
343
|
range_cls = sv.get_class(slot_range)
|
327
344
|
|
@@ -340,14 +357,11 @@ class PydanticGenerator(OOCodeGenerator):
|
|
340
357
|
)
|
341
358
|
):
|
342
359
|
if (
|
343
|
-
len(
|
344
|
-
[x for x in sv.class_induced_slots(slot_range) if x.designates_type]
|
345
|
-
)
|
346
|
-
> 0
|
360
|
+
len([x for x in sv.class_induced_slots(slot_range) if x.designates_type]) > 0
|
347
361
|
and len(sv.class_descendants(slot_range)) > 1
|
348
362
|
):
|
349
363
|
return (
|
350
|
-
|
364
|
+
"Union["
|
351
365
|
+ ",".join([camelcase(c) for c in sv.class_descendants(slot_range)])
|
352
366
|
+ "]"
|
353
367
|
)
|
@@ -390,9 +404,7 @@ class PydanticGenerator(OOCodeGenerator):
|
|
390
404
|
+ ",".join(
|
391
405
|
[
|
392
406
|
'"' + x + '"'
|
393
|
-
for x in get_accepted_type_designator_values(
|
394
|
-
sv, slot_def, class_def
|
395
|
-
)
|
407
|
+
for x in get_accepted_type_designator_values(sv, slot_def, class_def)
|
396
408
|
]
|
397
409
|
)
|
398
410
|
+ "]"
|
@@ -446,9 +458,7 @@ class PydanticGenerator(OOCodeGenerator):
|
|
446
458
|
identifier_slot = self.schemaview.get_identifier_slot(slot_range, use_key=True)
|
447
459
|
if identifier_slot is not None:
|
448
460
|
collection_keys.add(
|
449
|
-
self.generate_python_range(
|
450
|
-
identifier_slot.range, slot_def, class_def
|
451
|
-
)
|
461
|
+
self.generate_python_range(identifier_slot.range, slot_def, class_def)
|
452
462
|
)
|
453
463
|
if len(collection_keys) > 1:
|
454
464
|
raise Exception(
|
@@ -463,7 +473,7 @@ class PydanticGenerator(OOCodeGenerator):
|
|
463
473
|
with open(self.template_file) as template_file:
|
464
474
|
template_obj = Template(template_file.read())
|
465
475
|
else:
|
466
|
-
template_obj = Template(default_template)
|
476
|
+
template_obj = Template(default_template(self.pydantic_version))
|
467
477
|
|
468
478
|
sv: SchemaView
|
469
479
|
sv = self.schemaview
|
@@ -471,9 +481,7 @@ class PydanticGenerator(OOCodeGenerator):
|
|
471
481
|
pyschema = SchemaDefinition(
|
472
482
|
id=schema.id,
|
473
483
|
name=schema.name,
|
474
|
-
description=schema.description.replace('"', '\\"')
|
475
|
-
if schema.description
|
476
|
-
else None,
|
484
|
+
description=schema.description.replace('"', '\\"') if schema.description else None,
|
477
485
|
)
|
478
486
|
enums = self.generate_enums(sv.all_enums())
|
479
487
|
|
@@ -511,7 +519,13 @@ class PydanticGenerator(OOCodeGenerator):
|
|
511
519
|
# Confirm that the original slot range (ignoring the default that comes in from
|
512
520
|
# induced_slot) isn't in addition to setting any_of
|
513
521
|
if len(s.any_of) > 0 and sv.get_slot(sn).range is not None:
|
514
|
-
|
522
|
+
base_range_subsumes_any_of = False
|
523
|
+
base_range = sv.get_slot(sn).range
|
524
|
+
base_range_cls = sv.get_class(base_range, strict=False)
|
525
|
+
if base_range_cls is not None and base_range_cls.class_uri == "linkml:Any":
|
526
|
+
base_range_subsumes_any_of = True
|
527
|
+
if not base_range_subsumes_any_of:
|
528
|
+
raise ValueError("Slot cannot have both range and any_of defined")
|
515
529
|
|
516
530
|
if s.any_of is not None and len(s.any_of) > 0:
|
517
531
|
# list comprehension here is pulling ranges from within AnonymousSlotExpression
|
@@ -532,22 +546,14 @@ class PydanticGenerator(OOCodeGenerator):
|
|
532
546
|
elif len(pyranges) > 1:
|
533
547
|
pyrange = f"Union[{', '.join(pyranges)}]"
|
534
548
|
else:
|
535
|
-
raise Exception(
|
536
|
-
f"Could not generate python range for {class_name}.{s.name}"
|
537
|
-
)
|
549
|
+
raise Exception(f"Could not generate python range for {class_name}.{s.name}")
|
538
550
|
|
539
551
|
if s.multivalued:
|
540
552
|
if s.inlined or s.inlined_as_list:
|
541
|
-
collection_key = self.generate_collection_key(
|
542
|
-
slot_ranges, s, class_def
|
543
|
-
)
|
553
|
+
collection_key = self.generate_collection_key(slot_ranges, s, class_def)
|
544
554
|
else:
|
545
555
|
collection_key = None
|
546
|
-
if
|
547
|
-
s.inlined == False
|
548
|
-
or collection_key is None
|
549
|
-
or s.inlined_as_list == True
|
550
|
-
):
|
556
|
+
if s.inlined is False or collection_key is None or s.inlined_as_list is True:
|
551
557
|
pyrange = f"List[{pyrange}]"
|
552
558
|
else:
|
553
559
|
pyrange = f"Dict[{collection_key}, {pyrange}]"
|
@@ -572,8 +578,12 @@ class PydanticGenerator(OOCodeGenerator):
|
|
572
578
|
|
573
579
|
|
574
580
|
@shared_arguments(PydanticGenerator)
|
581
|
+
@click.option("--template_file", help="Optional jinja2 template to use for class generation")
|
575
582
|
@click.option(
|
576
|
-
"--
|
583
|
+
"--pydantic_version",
|
584
|
+
type=click.Choice(["1", "2"]),
|
585
|
+
default="1",
|
586
|
+
help="Pydantic version to use (1 or 2)",
|
577
587
|
)
|
578
588
|
@click.version_option(__version__, "-V", "--version")
|
579
589
|
@click.command()
|
@@ -585,12 +595,14 @@ def cli(
|
|
585
595
|
genmeta=False,
|
586
596
|
classvars=True,
|
587
597
|
slots=True,
|
598
|
+
pydantic_version="1",
|
588
599
|
**args,
|
589
600
|
):
|
590
601
|
"""Generate pydantic classes to represent a LinkML model"""
|
591
602
|
gen = PydanticGenerator(
|
592
603
|
yamlfile,
|
593
604
|
template_file=template_file,
|
605
|
+
pydantic_version=pydantic_version,
|
594
606
|
emit_metadata=head,
|
595
607
|
genmeta=genmeta,
|
596
608
|
gen_classvars=classvars,
|