linkml 1.7.10__tar.gz → 1.8.0__tar.gz
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-1.7.10 → linkml-1.8.0}/PKG-INFO +2 -2
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/docgen/class.md.jinja2 +0 -14
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/docgen/common_metadata.md.jinja2 +14 -1
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/erdiagramgen.py +37 -3
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/golanggen.py +1 -6
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/plantumlgen.py +1 -5
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/pydanticgen/array.py +1 -12
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/pydanticgen/black.py +1 -3
- linkml-1.8.0/linkml/generators/pydanticgen/includes.py +46 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/pydanticgen/pydanticgen.py +109 -16
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/pydanticgen/template.py +26 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/pydanticgen/templates/attribute.py.jinja +7 -1
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/pydanticgen/templates/class.py.jinja +4 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/pydanticgen/templates/module.py.jinja +12 -5
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/pythongen.py +1 -8
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/shaclgen.py +95 -3
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/sqlalchemygen.py +2 -10
- {linkml-1.7.10 → linkml-1.8.0}/linkml/linter/rules.py +1 -6
- {linkml-1.7.10 → linkml-1.8.0}/linkml/transformers/logical_model_transformer.py +6 -2
- {linkml-1.7.10 → linkml-1.8.0}/linkml/utils/schemaloader.py +27 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/utils/schemasynopsis.py +1 -9
- {linkml-1.7.10 → linkml-1.8.0}/linkml/validator/__init__.py +1 -3
- {linkml-1.7.10 → linkml-1.8.0}/linkml/validator/plugins/__init__.py +1 -3
- {linkml-1.7.10 → linkml-1.8.0}/pyproject.toml +2 -2
- {linkml-1.7.10 → linkml-1.8.0}/setup.py +2 -2
- {linkml-1.7.10 → linkml-1.8.0}/LICENSE +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/README.md +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/__init__.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/_version.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/PythonGenNotes.md +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/README.md +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/__init__.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/common/__init__.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/common/type_designators.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/csvgen.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/docgen/class_diagram.md.jinja2 +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/docgen/enum.md.jinja2 +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/docgen/index.md.jinja2 +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/docgen/index.tex.jinja2 +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/docgen/schema.md.jinja2 +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/docgen/slot.md.jinja2 +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/docgen/subset.md.jinja2 +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/docgen/type.md.jinja2 +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/docgen.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/dotgen.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/excelgen.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/golrgen.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/graphqlgen.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/javagen/example_template.java.jinja2 +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/javagen/java_record_template.jinja2 +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/javagen.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/jsonldcontextgen.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/jsonldgen.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/jsonschemagen.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/legacy/__init__.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/linkmlgen.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/markdowngen.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/namespacegen.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/oocodegen.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/owlgen.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/prefixmapgen.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/projectgen.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/protogen.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/pydanticgen/__init__.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/pydanticgen/build.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/pydanticgen/templates/base_model.py.jinja +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/pydanticgen/templates/conditional_import.py.jinja +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/pydanticgen/templates/enum.py.jinja +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/pydanticgen/templates/footer.py.jinja +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/pydanticgen/templates/imports.py.jinja +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/pydanticgen/templates/validator.py.jinja +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/rdfgen.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/shacl/__init__.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/shacl/ifabsent_processor.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/shacl/shacl_data_type.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/shexgen.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/sparqlgen.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/sqlalchemy/__init__.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/sqlalchemy/sqlalchemy_declarative_template.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/sqlalchemy/sqlalchemy_imperative_template.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/sqltablegen.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/sssomgen.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/string_template.md +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/summarygen.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/terminusdbgen.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/typescriptgen.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/yamlgen.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/generators/yumlgen.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/linter/__init__.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/linter/cli.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/linter/config/datamodel/.linkmllint.yaml +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/linter/config/datamodel/__init__.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/linter/config/datamodel/config.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/linter/config/datamodel/config.yaml +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/linter/config/default.yaml +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/linter/config/recommended.yaml +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/linter/formatters/__init__.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/linter/formatters/formatter.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/linter/formatters/json_formatter.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/linter/formatters/markdown_formatter.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/linter/formatters/terminal_formatter.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/linter/formatters/tsv_formatter.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/linter/linter.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/reporting/__init__.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/reporting/model.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/transformers/__init__.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/transformers/model_transformer.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/transformers/relmodel_transformer.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/transformers/schema_renamer.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/utils/__init__.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/utils/cli_utils.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/utils/converter.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/utils/datautils.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/utils/datavalidator.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/utils/deprecation.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/utils/execute_tutorial.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/utils/generator.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/utils/helpers.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/utils/ifabsent_functions.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/utils/logictools.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/utils/mergeutils.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/utils/rawloader.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/utils/schema_builder.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/utils/schema_fixer.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/utils/sqlutils.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/utils/typereferences.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/utils/validation.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/validator/cli.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/validator/loaders/__init__.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/validator/loaders/delimited_file_loader.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/validator/loaders/json_loader.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/validator/loaders/loader.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/validator/loaders/passthrough_loader.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/validator/loaders/yaml_loader.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/validator/plugins/jsonschema_validation_plugin.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/validator/plugins/pydantic_validation_plugin.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/validator/plugins/recommended_slots_plugin.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/validator/plugins/shacl_validation_plugin.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/validator/plugins/validation_plugin.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/validator/report.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/validator/validation_context.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/validator/validator.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/validators/__init__.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/validators/jsonschemavalidator.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/validators/sparqlvalidator.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/workspaces/__init__.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/workspaces/datamodel/__init__.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/workspaces/datamodel/workspaces.py +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/workspaces/datamodel/workspaces.yaml +0 -0
- {linkml-1.7.10 → linkml-1.8.0}/linkml/workspaces/example_runner.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: linkml
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.8.0
|
4
4
|
Summary: Linked Open Data Modeling Language
|
5
5
|
Home-page: https://linkml.io/linkml/
|
6
6
|
Keywords: schema,linked data,data modeling,rdf,owl,biolink
|
@@ -36,7 +36,7 @@ Requires-Dist: jinja2 (>=3.1.0)
|
|
36
36
|
Requires-Dist: jsonasobj2 (>=1.0.3,<2.0.0)
|
37
37
|
Requires-Dist: jsonschema[format] (>=4.0.0)
|
38
38
|
Requires-Dist: linkml-dataops
|
39
|
-
Requires-Dist: linkml-runtime (
|
39
|
+
Requires-Dist: linkml-runtime (==1.8.0)
|
40
40
|
Requires-Dist: openpyxl
|
41
41
|
Requires-Dist: parse
|
42
42
|
Requires-Dist: prefixcommons (>=0.1.7)
|
@@ -99,20 +99,6 @@ URI: {{ gen.uri_link(element) }}
|
|
99
99
|
|
100
100
|
{% include "common_metadata.md.jinja2" %}
|
101
101
|
|
102
|
-
|
103
|
-
{% if schemaview.get_mappings(element.name).items() -%}
|
104
|
-
## Mappings
|
105
|
-
|
106
|
-
| Mapping Type | Mapped Value |
|
107
|
-
| --- | --- |
|
108
|
-
{% for m, mt in schemaview.get_mappings(element.name).items() -%}
|
109
|
-
{% if mt|length > 0 -%}
|
110
|
-
| {{ m }} | {{ mt|join(', ') }} |
|
111
|
-
{% endif -%}
|
112
|
-
{% endfor %}
|
113
|
-
|
114
|
-
{% endif -%}
|
115
|
-
|
116
102
|
{% if gen.example_object_blobs(element.name) -%}
|
117
103
|
## Examples
|
118
104
|
{% for name, blob in gen.example_object_blobs(element.name) -%}
|
@@ -75,4 +75,17 @@ Instances of this class *should* have identifiers with one of the following pref
|
|
75
75
|
{% if element.imported_from %}
|
76
76
|
* imported from: {{ element.imported_from }}
|
77
77
|
{% endif %}
|
78
|
-
{% endif %}
|
78
|
+
{% endif %}
|
79
|
+
|
80
|
+
{% if schemaview.get_mappings(element.name).items() -%}
|
81
|
+
## Mappings
|
82
|
+
|
83
|
+
| Mapping Type | Mapped Value |
|
84
|
+
| --- | --- |
|
85
|
+
{% for m, mt in schemaview.get_mappings(element.name).items() -%}
|
86
|
+
{% if mt|length > 0 -%}
|
87
|
+
| {{ m }} | {{ mt|join(', ') }} |
|
88
|
+
{% endif -%}
|
89
|
+
{% endfor %}
|
90
|
+
|
91
|
+
{% endif -%}
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import os
|
2
2
|
from dataclasses import dataclass
|
3
3
|
from enum import Enum
|
4
|
-
from typing import List, Optional, Union
|
4
|
+
from typing import List, Optional, Set, Union
|
5
5
|
|
6
6
|
import click
|
7
7
|
import pydantic
|
@@ -163,6 +163,7 @@ class ERDiagramGenerator(Generator):
|
|
163
163
|
class_names: List[Union[str, ClassDefinitionName]],
|
164
164
|
follow_references=False,
|
165
165
|
max_hops: int = None,
|
166
|
+
include_upstream: bool = False,
|
166
167
|
) -> MERMAID_SERIALIZATION:
|
167
168
|
"""
|
168
169
|
Serialize a list of classes as an ER Diagram.
|
@@ -195,6 +196,15 @@ class ERDiagramGenerator(Generator):
|
|
195
196
|
if follow_references or sv.is_inlined(slot):
|
196
197
|
if rng not in visited:
|
197
198
|
stack.append((rng, depth + 1))
|
199
|
+
|
200
|
+
# Now Add upstream classes if needed
|
201
|
+
if include_upstream:
|
202
|
+
for sn in sv.all_slots():
|
203
|
+
slot = sv.schema.slots.get(sn)
|
204
|
+
if slot and slot.range in set(class_names):
|
205
|
+
for cl in sv.all_classes():
|
206
|
+
if slot.name in sv.get_class(cl).slots and cl not in visited:
|
207
|
+
self.add_upstream_class(cl, set(class_names), diagram)
|
198
208
|
return self.serialize_diagram(diagram)
|
199
209
|
|
200
210
|
def serialize_diagram(self, diagram: ERDiagram) -> str:
|
@@ -210,6 +220,15 @@ class ERDiagramGenerator(Generator):
|
|
210
220
|
else:
|
211
221
|
return er
|
212
222
|
|
223
|
+
def add_upstream_class(self, class_name: ClassDefinitionName, targets: Set[str], diagram: ERDiagram) -> None:
|
224
|
+
sv = self.schemaview
|
225
|
+
cls = sv.get_class(class_name)
|
226
|
+
entity = Entity(name=camelcase(cls.name))
|
227
|
+
diagram.entities.append(entity)
|
228
|
+
for slot in sv.class_induced_slots(class_name):
|
229
|
+
if slot.range in targets:
|
230
|
+
self.add_relationship(entity, slot, diagram)
|
231
|
+
|
213
232
|
def add_class(self, class_name: ClassDefinitionName, diagram: ERDiagram) -> None:
|
214
233
|
"""
|
215
234
|
Add a class to the ER Diagram.
|
@@ -291,9 +310,17 @@ class ERDiagramGenerator(Generator):
|
|
291
310
|
)
|
292
311
|
@click.option("--max-hops", default=None, type=click.INT, help="Maximum number of hops")
|
293
312
|
@click.option("--classes", "-c", multiple=True, help="List of classes to serialize")
|
313
|
+
@click.option("--include-upstream", is_flag=True, help="Include upstream classes")
|
294
314
|
@click.version_option(__version__, "-V", "--version")
|
295
315
|
@click.command()
|
296
|
-
def cli(
|
316
|
+
def cli(
|
317
|
+
yamlfile,
|
318
|
+
classes: List[str],
|
319
|
+
max_hops: Optional[int],
|
320
|
+
follow_references: bool,
|
321
|
+
include_upstream: bool = False,
|
322
|
+
**args,
|
323
|
+
):
|
297
324
|
"""Generate a mermaid ER diagram from a schema.
|
298
325
|
|
299
326
|
By default, all entities traversable from the tree_root are included. If no tree_root is
|
@@ -306,7 +333,14 @@ def cli(yamlfile, classes: List[str], max_hops: Optional[int], follow_references
|
|
306
333
|
**args,
|
307
334
|
)
|
308
335
|
if classes:
|
309
|
-
print(
|
336
|
+
print(
|
337
|
+
gen.serialize_classes(
|
338
|
+
classes,
|
339
|
+
follow_references=follow_references,
|
340
|
+
max_hops=max_hops,
|
341
|
+
include_upstream=include_upstream,
|
342
|
+
)
|
343
|
+
)
|
310
344
|
else:
|
311
345
|
print(gen.serialize())
|
312
346
|
|
@@ -5,12 +5,7 @@ from typing import List, Optional
|
|
5
5
|
|
6
6
|
import click
|
7
7
|
from jinja2 import Template
|
8
|
-
from linkml_runtime.linkml_model.meta import
|
9
|
-
ClassDefinition,
|
10
|
-
ClassDefinitionName,
|
11
|
-
Element,
|
12
|
-
SlotDefinition,
|
13
|
-
)
|
8
|
+
from linkml_runtime.linkml_model.meta import ClassDefinition, ClassDefinitionName, Element, SlotDefinition
|
14
9
|
from linkml_runtime.utils.formatutils import camelcase, underscore
|
15
10
|
|
16
11
|
from linkml._version import __version__
|
@@ -12,11 +12,7 @@ from typing import Callable, List, Optional, Set, cast
|
|
12
12
|
|
13
13
|
import click
|
14
14
|
import requests
|
15
|
-
from linkml_runtime.linkml_model.meta import
|
16
|
-
ClassDefinition,
|
17
|
-
ClassDefinitionName,
|
18
|
-
SlotDefinition,
|
19
|
-
)
|
15
|
+
from linkml_runtime.linkml_model.meta import ClassDefinition, ClassDefinitionName, SlotDefinition
|
20
16
|
from linkml_runtime.utils.formatutils import camelcase, underscore
|
21
17
|
|
22
18
|
from linkml import REQUESTS_TIMEOUT
|
@@ -1,18 +1,7 @@
|
|
1
1
|
import sys
|
2
2
|
from abc import ABC, abstractmethod
|
3
3
|
from enum import Enum
|
4
|
-
from typing import
|
5
|
-
Any,
|
6
|
-
ClassVar,
|
7
|
-
Generic,
|
8
|
-
Iterable,
|
9
|
-
List,
|
10
|
-
Optional,
|
11
|
-
Type,
|
12
|
-
TypeVar,
|
13
|
-
Union,
|
14
|
-
get_args,
|
15
|
-
)
|
4
|
+
from typing import Any, ClassVar, Generic, Iterable, List, Optional, Type, TypeVar, Union, get_args
|
16
5
|
|
17
6
|
from linkml_runtime.linkml_model import Element
|
18
7
|
from linkml_runtime.linkml_model.meta import ArrayExpression, DimensionExpression
|
@@ -16,9 +16,7 @@ except ImportError:
|
|
16
16
|
|
17
17
|
|
18
18
|
def _default_mode() -> "Mode":
|
19
|
-
return Mode(
|
20
|
-
target_versions={TargetVersion.PY311},
|
21
|
-
)
|
19
|
+
return Mode(target_versions={TargetVersion.PY311}, line_length=120)
|
22
20
|
|
23
21
|
|
24
22
|
def format_black(code: str, mode: Optional["Mode"] = None) -> str:
|
@@ -0,0 +1,46 @@
|
|
1
|
+
"""
|
2
|
+
Classes to inject in generated pydantic models
|
3
|
+
"""
|
4
|
+
|
5
|
+
from pydantic.version import VERSION
|
6
|
+
|
7
|
+
PYDANTIC_VERSION = int(VERSION[0])
|
8
|
+
|
9
|
+
|
10
|
+
LinkMLMeta_v1 = """
|
11
|
+
class LinkMLMeta(BaseModel):
|
12
|
+
__root__: Dict[str, Any] = {}
|
13
|
+
|
14
|
+
def __getattr__(self, key:str):
|
15
|
+
return getattr(self.__root__, key)
|
16
|
+
|
17
|
+
def __getitem__(self, key:str):
|
18
|
+
return self.__root__[key]
|
19
|
+
|
20
|
+
def __setitem__(self, key:str, value):
|
21
|
+
self.__root__[key] = value
|
22
|
+
|
23
|
+
class Config:
|
24
|
+
allow_mutation = False
|
25
|
+
"""
|
26
|
+
|
27
|
+
LinkMLMeta_v2 = """
|
28
|
+
class LinkMLMeta(RootModel):
|
29
|
+
root: Dict[str, Any] = {}
|
30
|
+
model_config = ConfigDict(frozen=True)
|
31
|
+
|
32
|
+
def __getattr__(self, key:str):
|
33
|
+
return getattr(self.root, key)
|
34
|
+
|
35
|
+
def __getitem__(self, key:str):
|
36
|
+
return self.root[key]
|
37
|
+
|
38
|
+
def __setitem__(self, key:str, value):
|
39
|
+
self.root[key] = value
|
40
|
+
|
41
|
+
"""
|
42
|
+
|
43
|
+
# if PYDANTIC_VERSION >= 2:
|
44
|
+
# LinkMLMeta = eval(LinkMLMeta_v2)
|
45
|
+
# else:
|
46
|
+
# LinkMLMeta = eval(LinkMLMeta_v1)
|
@@ -3,19 +3,12 @@ import logging
|
|
3
3
|
import os
|
4
4
|
import textwrap
|
5
5
|
from collections import defaultdict
|
6
|
-
from copy import deepcopy
|
6
|
+
from copy import copy, deepcopy
|
7
7
|
from dataclasses import dataclass, field
|
8
|
+
from enum import Enum
|
8
9
|
from pathlib import Path
|
9
10
|
from types import ModuleType
|
10
|
-
from typing import
|
11
|
-
Dict,
|
12
|
-
List,
|
13
|
-
Literal,
|
14
|
-
Optional,
|
15
|
-
Set,
|
16
|
-
Type,
|
17
|
-
Union,
|
18
|
-
)
|
11
|
+
from typing import Dict, List, Literal, Optional, Set, Type, TypeVar, Union, overload
|
19
12
|
|
20
13
|
import click
|
21
14
|
from jinja2 import ChoiceLoader, Environment, FileSystemLoader
|
@@ -27,16 +20,14 @@ from linkml_runtime.linkml_model.meta import (
|
|
27
20
|
TypeDefinition,
|
28
21
|
)
|
29
22
|
from linkml_runtime.utils.compile_python import compile_python
|
30
|
-
from linkml_runtime.utils.formatutils import camelcase, underscore
|
23
|
+
from linkml_runtime.utils.formatutils import camelcase, remove_empty_items, underscore
|
31
24
|
from linkml_runtime.utils.schemaview import SchemaView
|
32
25
|
from pydantic.version import VERSION as PYDANTIC_VERSION
|
33
26
|
|
34
27
|
from linkml._version import __version__
|
35
|
-
from linkml.generators.common.type_designators import
|
36
|
-
get_accepted_type_designator_values,
|
37
|
-
get_type_designator_value,
|
38
|
-
)
|
28
|
+
from linkml.generators.common.type_designators import get_accepted_type_designator_values, get_type_designator_value
|
39
29
|
from linkml.generators.oocodegen import OOCodeGenerator
|
30
|
+
from linkml.generators.pydanticgen import includes
|
40
31
|
from linkml.generators.pydanticgen.array import ArrayRangeGenerator, ArrayRepresentation
|
41
32
|
from linkml.generators.pydanticgen.build import SlotResult
|
42
33
|
from linkml.generators.pydanticgen.template import (
|
@@ -85,6 +76,7 @@ DEFAULT_IMPORTS = (
|
|
85
76
|
module="typing",
|
86
77
|
objects=[
|
87
78
|
ObjectImport(name="Any"),
|
79
|
+
ObjectImport(name="ClassVar"),
|
88
80
|
ObjectImport(name="List"),
|
89
81
|
ObjectImport(name="Literal"),
|
90
82
|
ObjectImport(name="Dict"),
|
@@ -100,6 +92,7 @@ DEFAULT_IMPORTS = (
|
|
100
92
|
ObjectImport(name="BaseModel"),
|
101
93
|
ObjectImport(name="ConfigDict"),
|
102
94
|
ObjectImport(name="Field"),
|
95
|
+
ObjectImport(name="RootModel"),
|
103
96
|
ObjectImport(name="field_validator"),
|
104
97
|
],
|
105
98
|
alternative=Import(
|
@@ -109,6 +102,33 @@ DEFAULT_IMPORTS = (
|
|
109
102
|
)
|
110
103
|
)
|
111
104
|
|
105
|
+
DEFAULT_INJECTS = {1: [includes.LinkMLMeta_v1], 2: [includes.LinkMLMeta_v2]}
|
106
|
+
|
107
|
+
|
108
|
+
class MetadataMode(str, Enum):
|
109
|
+
FULL = "full"
|
110
|
+
"""
|
111
|
+
all metadata from the source schema will be included, even if it is represented by the template classes,
|
112
|
+
and even if it is represented by some child class (eg. "classes" will be included with schema metadata
|
113
|
+
"""
|
114
|
+
EXCEPT_CHILDREN = "except_children"
|
115
|
+
"""
|
116
|
+
all metadata from the source schema will be included, even if it is represented by the template classes,
|
117
|
+
except if it is represented by some child template class (eg. "classes" will be excluded from schema metadata)
|
118
|
+
"""
|
119
|
+
AUTO = "auto"
|
120
|
+
"""
|
121
|
+
Only the metadata that isn't represented by the template classes or excluded with ``meta_exclude`` will be included
|
122
|
+
"""
|
123
|
+
NONE = None
|
124
|
+
"""
|
125
|
+
No metadata will be included.
|
126
|
+
"""
|
127
|
+
|
128
|
+
|
129
|
+
DefinitionType = TypeVar("DefinitionType", bound=Union[SchemaDefinition, ClassDefinition, SlotDefinition])
|
130
|
+
TemplateType = TypeVar("TemplateType", bound=Union[PydanticModule, PydanticClass, PydanticAttribute])
|
131
|
+
|
112
132
|
|
113
133
|
@dataclass
|
114
134
|
class PydanticGenerator(OOCodeGenerator):
|
@@ -212,6 +232,12 @@ class PydanticGenerator(OOCodeGenerator):
|
|
212
232
|
from typing_extensions import Literal
|
213
233
|
|
214
234
|
"""
|
235
|
+
metadata_mode: Union[MetadataMode, str, None] = MetadataMode.AUTO
|
236
|
+
"""
|
237
|
+
How to include schema metadata in generated pydantic models.
|
238
|
+
|
239
|
+
See :class:`.MetadataMode` for mode documentation
|
240
|
+
"""
|
215
241
|
|
216
242
|
# ObjectVars (identical to pythongen)
|
217
243
|
gen_classvars: bool = True
|
@@ -530,6 +556,56 @@ class PydanticGenerator(OOCodeGenerator):
|
|
530
556
|
|
531
557
|
return array_reps
|
532
558
|
|
559
|
+
@overload
|
560
|
+
def include_metadata(self, model: PydanticModule, source: SchemaDefinition) -> PydanticModule: ...
|
561
|
+
|
562
|
+
@overload
|
563
|
+
def include_metadata(self, model: PydanticClass, source: ClassDefinition) -> PydanticClass: ...
|
564
|
+
|
565
|
+
@overload
|
566
|
+
def include_metadata(self, model: PydanticAttribute, source: SlotDefinition) -> PydanticAttribute: ...
|
567
|
+
|
568
|
+
def include_metadata(self, model: TemplateType, source: DefinitionType) -> TemplateType:
|
569
|
+
"""
|
570
|
+
Include metadata from the source schema that is otherwise not represented in the pydantic template models.
|
571
|
+
|
572
|
+
Metadata inclusion mode is dependent on :attr:`.metadata_mode` - see:
|
573
|
+
|
574
|
+
- :class:`.MetadataMode`
|
575
|
+
- :meth:`.TemplateModel.exclude_from_meta`
|
576
|
+
|
577
|
+
"""
|
578
|
+
if self.metadata_mode is None or self.metadata_mode == MetadataMode.NONE:
|
579
|
+
return model
|
580
|
+
elif self.metadata_mode in (MetadataMode.AUTO, MetadataMode.AUTO.value):
|
581
|
+
meta = {k: v for k, v in remove_empty_items(source).items() if k not in model.exclude_from_meta()}
|
582
|
+
elif self.metadata_mode in (MetadataMode.EXCEPT_CHILDREN, MetadataMode.EXCEPT_CHILDREN.value):
|
583
|
+
meta = {}
|
584
|
+
for k, v in remove_empty_items(source).items():
|
585
|
+
if not hasattr(model, k):
|
586
|
+
meta[k] = v
|
587
|
+
elif isinstance(getattr(model, k), list) and not any(
|
588
|
+
[isinstance(item, TemplateModel) for item in getattr(model, k)]
|
589
|
+
):
|
590
|
+
meta[k] = v
|
591
|
+
elif isinstance(getattr(model, k), dict) and not any(
|
592
|
+
[isinstance(item, TemplateModel) for item in getattr(model, k).values()]
|
593
|
+
):
|
594
|
+
meta[k] = v
|
595
|
+
elif not isinstance(getattr(model, k), TemplateModel):
|
596
|
+
meta[k] = v
|
597
|
+
|
598
|
+
elif self.metadata_mode in (MetadataMode.FULL, MetadataMode.FULL.value):
|
599
|
+
meta = remove_empty_items(source)
|
600
|
+
else:
|
601
|
+
raise ValueError(
|
602
|
+
f"Unknown metadata mode '{self.metadata_mode}', needs to be one of "
|
603
|
+
f"{[mode.value for mode in MetadataMode]}"
|
604
|
+
)
|
605
|
+
|
606
|
+
model.meta = meta
|
607
|
+
return model
|
608
|
+
|
533
609
|
def render(self) -> PydanticModule:
|
534
610
|
sv: SchemaView
|
535
611
|
sv = self.schemaview
|
@@ -540,7 +616,7 @@ class PydanticGenerator(OOCodeGenerator):
|
|
540
616
|
description=schema.description.replace('"', '\\"') if schema.description else None,
|
541
617
|
)
|
542
618
|
enums = self.generate_enums(sv.all_enums())
|
543
|
-
injected_classes = []
|
619
|
+
injected_classes = copy(DEFAULT_INJECTS[self.pydantic_version])
|
544
620
|
if self.injected_classes is not None:
|
545
621
|
injected_classes += self.injected_classes
|
546
622
|
|
@@ -665,15 +741,22 @@ class PydanticGenerator(OOCodeGenerator):
|
|
665
741
|
predef_slot = str(predef_slot)
|
666
742
|
new_fields["predefined"] = predef_slot
|
667
743
|
new_fields["name"] = attr_name
|
744
|
+
|
668
745
|
attrs[attr_name] = PydanticAttribute(**new_fields, pydantic_ver=self.pydantic_version)
|
746
|
+
attrs[attr_name] = self.include_metadata(attrs[attr_name], src_attr)
|
669
747
|
|
670
748
|
new_class = PydanticClass(
|
671
749
|
name=k, attributes=attrs, description=c.description, pydantic_ver=self.pydantic_version
|
672
750
|
)
|
751
|
+
new_class = self.include_metadata(new_class, c)
|
673
752
|
if k in bases:
|
674
753
|
new_class.bases = bases[k]
|
675
754
|
classes[k] = new_class
|
676
755
|
|
756
|
+
schema_meta = {
|
757
|
+
k: v for k, v in remove_empty_items(schema).items() if k not in PydanticModule.exclude_from_meta()
|
758
|
+
}
|
759
|
+
|
677
760
|
module = PydanticModule(
|
678
761
|
pydantic_ver=self.pydantic_version,
|
679
762
|
metamodel_version=self.schema.metamodel_version,
|
@@ -683,7 +766,9 @@ class PydanticGenerator(OOCodeGenerator):
|
|
683
766
|
injected_classes=injected_classes,
|
684
767
|
enums=enums,
|
685
768
|
classes=classes,
|
769
|
+
meta=schema_meta,
|
686
770
|
)
|
771
|
+
module = self.include_metadata(module, pyschema)
|
687
772
|
return module
|
688
773
|
|
689
774
|
def serialize(self) -> str:
|
@@ -739,6 +824,12 @@ Available templates to override:
|
|
739
824
|
default="forbid",
|
740
825
|
help="How to handle extra fields in BaseModel.",
|
741
826
|
)
|
827
|
+
@click.option(
|
828
|
+
"--black",
|
829
|
+
is_flag=True,
|
830
|
+
default=False,
|
831
|
+
help="Format generated models with black (must be present in the environment)",
|
832
|
+
)
|
742
833
|
@click.version_option(__version__, "-V", "--version")
|
743
834
|
@click.command()
|
744
835
|
def cli(
|
@@ -752,6 +843,7 @@ def cli(
|
|
752
843
|
array_representations=list("list"),
|
753
844
|
pydantic_version=int(PYDANTIC_VERSION[0]),
|
754
845
|
extra_fields: Literal["allow", "forbid", "ignore"] = "forbid",
|
846
|
+
black: bool = False,
|
755
847
|
**args,
|
756
848
|
):
|
757
849
|
"""Generate pydantic classes to represent a LinkML model"""
|
@@ -777,6 +869,7 @@ def cli(
|
|
777
869
|
gen_classvars=classvars,
|
778
870
|
gen_slots=slots,
|
779
871
|
template_dir=template_dir,
|
872
|
+
black=black,
|
780
873
|
**args,
|
781
874
|
)
|
782
875
|
print(gen.serialize())
|
@@ -60,6 +60,7 @@ class TemplateModel(BaseModel):
|
|
60
60
|
)
|
61
61
|
|
62
62
|
pydantic_ver: int = int(PYDANTIC_VERSION[0])
|
63
|
+
meta_exclude: ClassVar[List[str]] = None
|
63
64
|
|
64
65
|
def render(self, environment: Optional[Environment] = None, black: bool = False) -> str:
|
65
66
|
"""
|
@@ -132,6 +133,16 @@ class TemplateModel(BaseModel):
|
|
132
133
|
return self.json(**kwargs)
|
133
134
|
return self.dict(**kwargs)
|
134
135
|
|
136
|
+
@classmethod
|
137
|
+
def exclude_from_meta(cls: "TemplateModel") -> List[str]:
|
138
|
+
"""
|
139
|
+
Attributes in the source definition to exclude from linkml_meta
|
140
|
+
"""
|
141
|
+
ret = [*cls.model_fields.keys()]
|
142
|
+
if cls.meta_exclude is not None:
|
143
|
+
ret = ret + cls.meta_exclude
|
144
|
+
return ret
|
145
|
+
|
135
146
|
|
136
147
|
def _render(
|
137
148
|
item: Union[TemplateModel, Any, List[Union[Any, TemplateModel]], Dict[str, Union[Any, TemplateModel]]],
|
@@ -213,6 +224,7 @@ class PydanticAttribute(TemplateModel):
|
|
213
224
|
"""
|
214
225
|
|
215
226
|
template: ClassVar[str] = "attribute.py.jinja"
|
227
|
+
meta_exclude: ClassVar[List[str]] = ["from_schema", "owner", "range", "multivalued", "inlined", "inlined_as_list"]
|
216
228
|
|
217
229
|
name: str
|
218
230
|
required: bool = False
|
@@ -237,6 +249,10 @@ class PydanticAttribute(TemplateModel):
|
|
237
249
|
minimum_value: Optional[Union[int, float]] = None
|
238
250
|
maximum_value: Optional[Union[int, float]] = None
|
239
251
|
pattern: Optional[str] = None
|
252
|
+
meta: Optional[Dict[str, Any]] = None
|
253
|
+
"""
|
254
|
+
Metadata for the slot to be included in a Field annotation
|
255
|
+
"""
|
240
256
|
|
241
257
|
if int(PYDANTIC_VERSION[0]) >= 2:
|
242
258
|
|
@@ -283,11 +299,16 @@ class PydanticClass(TemplateModel):
|
|
283
299
|
"""
|
284
300
|
|
285
301
|
template: ClassVar[str] = "class.py.jinja"
|
302
|
+
meta_exclude: ClassVar[List[str]] = ["slots", "is_a"]
|
286
303
|
|
287
304
|
name: str
|
288
305
|
bases: Union[List[str], str] = PydanticBaseModel.default_name
|
289
306
|
description: Optional[str] = None
|
290
307
|
attributes: Optional[Dict[str, PydanticAttribute]] = None
|
308
|
+
meta: Optional[Dict[str, Any]] = None
|
309
|
+
"""
|
310
|
+
Metadata for the class to be included in a linkml_meta class attribute
|
311
|
+
"""
|
291
312
|
|
292
313
|
def _validators(self) -> Optional[Dict[str, PydanticValidator]]:
|
293
314
|
if self.attributes is None:
|
@@ -548,6 +569,7 @@ class PydanticModule(TemplateModel):
|
|
548
569
|
"""
|
549
570
|
|
550
571
|
template: ClassVar[str] = "module.py.jinja"
|
572
|
+
meta_exclude: ClassVar[str] = ["slots"]
|
551
573
|
|
552
574
|
metamodel_version: Optional[str] = None
|
553
575
|
version: Optional[str] = None
|
@@ -556,6 +578,10 @@ class PydanticModule(TemplateModel):
|
|
556
578
|
imports: List[Union[Import, ConditionalImport]] = Field(default_factory=list)
|
557
579
|
enums: Dict[str, PydanticEnum] = Field(default_factory=dict)
|
558
580
|
classes: Dict[str, PydanticClass] = Field(default_factory=dict)
|
581
|
+
meta: Optional[Dict[str, Any]] = None
|
582
|
+
"""
|
583
|
+
Metadata for the schema to be included in a linkml_meta module-level instance of LinkMLMeta
|
584
|
+
"""
|
559
585
|
|
560
586
|
if int(PYDANTIC_VERSION[0]) >= 2:
|
561
587
|
|
@@ -7,4 +7,10 @@
|
|
7
7
|
{%- if minimum_value != None %}, ge={{minimum_value}}{% endif -%}
|
8
8
|
{%- if maximum_value != None %}, le={{maximum_value}}{% endif -%}
|
9
9
|
{%- endif -%}
|
10
|
-
|
10
|
+
{%- if meta != None -%}
|
11
|
+
{%- if pydantic_ver == 1 -%}
|
12
|
+
, linkml_meta = {{ meta | pprint | indent(width=8) }}
|
13
|
+
{% else -%}
|
14
|
+
, json_schema_extra = { "linkml_meta": {{ meta | pprint | indent(width=8) }} }
|
15
|
+
{%- endif -%}
|
16
|
+
{%- endif -%})
|
@@ -4,6 +4,10 @@ class {{ name }}({% if bases is string %}{{ bases }}{% else %}{{ bases | join(',
|
|
4
4
|
{{ description | indent(width=4) }}
|
5
5
|
"""
|
6
6
|
{% endif -%}
|
7
|
+
{% if meta != None %}
|
8
|
+
linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({% if pydantic_ver == 1 %}__root__={% endif %}{{ meta | pprint | indent(width=8) }})
|
9
|
+
|
10
|
+
{% endif %}
|
7
11
|
{% if attributes or validators %}
|
8
12
|
{% if attributes %}
|
9
13
|
{% for attr in attributes.values() %}
|
@@ -7,18 +7,25 @@ version = "{{version if version else None}}"
|
|
7
7
|
|
8
8
|
|
9
9
|
{{ base_model }}
|
10
|
-
{% if enums %}
|
11
|
-
{% for e in enums.values() %}
|
12
10
|
|
13
|
-
{{ e }}
|
14
|
-
{% endfor %}
|
15
|
-
{% endif %}
|
16
11
|
{% if injected_classes %}
|
17
12
|
{% for c in injected_classes%}
|
18
13
|
|
19
14
|
{{ c }}
|
20
15
|
{% endfor %}
|
21
16
|
{% endif %}
|
17
|
+
{% if meta != None %}
|
18
|
+
linkml_meta = LinkMLMeta({% if pydantic_ver == 1 %}__root__={% endif %}{{ meta | pprint | indent(width=4) }} )
|
19
|
+
{% else %}
|
20
|
+
linkml_meta = None
|
21
|
+
{% endif %}
|
22
|
+
{% if enums %}
|
23
|
+
{% for e in enums.values() %}
|
24
|
+
|
25
|
+
{{ e }}
|
26
|
+
{% endfor %}
|
27
|
+
{% endif %}
|
28
|
+
|
22
29
|
{% for c in classes.values() %}
|
23
30
|
|
24
31
|
{{ c }}
|
@@ -23,14 +23,7 @@ from linkml_runtime.linkml_model.meta import (
|
|
23
23
|
TypeDefinition,
|
24
24
|
)
|
25
25
|
from linkml_runtime.utils.compile_python import compile_python
|
26
|
-
from linkml_runtime.utils.formatutils import
|
27
|
-
be,
|
28
|
-
camelcase,
|
29
|
-
sfx,
|
30
|
-
split_col,
|
31
|
-
underscore,
|
32
|
-
wrapped_annotation,
|
33
|
-
)
|
26
|
+
from linkml_runtime.utils.formatutils import be, camelcase, sfx, split_col, underscore, wrapped_annotation
|
34
27
|
from linkml_runtime.utils.metamodelcore import builtinnames
|
35
28
|
from rdflib import URIRef
|
36
29
|
|