linkml 1.7.2__py3-none-any.whl → 1.7.4__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 +7 -0
- linkml/generators/docgen.py +9 -9
- linkml/generators/erdiagramgen.py +6 -6
- linkml/generators/excelgen.py +3 -3
- linkml/generators/golanggen.py +1 -1
- linkml/generators/javagen.py +5 -5
- linkml/generators/jsonldcontextgen.py +6 -5
- linkml/generators/jsonschemagen.py +3 -3
- linkml/generators/linkmlgen.py +3 -3
- linkml/generators/markdowngen.py +2 -2
- linkml/generators/oocodegen.py +4 -1
- linkml/generators/owlgen.py +45 -26
- linkml/generators/plantumlgen.py +4 -3
- linkml/generators/protogen.py +2 -2
- linkml/generators/pydanticgen.py +159 -13
- linkml/generators/pythongen.py +5 -5
- linkml/generators/rdfgen.py +2 -2
- linkml/generators/shaclgen.py +2 -2
- linkml/generators/sqltablegen.py +13 -9
- linkml/generators/summarygen.py +2 -2
- linkml/generators/yamlgen.py +2 -2
- linkml/generators/yumlgen.py +5 -4
- linkml/transformers/relmodel_transformer.py +5 -5
- linkml/utils/converter.py +3 -2
- linkml/utils/generator.py +4 -4
- linkml/utils/sqlutils.py +2 -1
- {linkml-1.7.2.dist-info → linkml-1.7.4.dist-info}/METADATA +1 -2
- {linkml-1.7.2.dist-info → linkml-1.7.4.dist-info}/RECORD +31 -31
- {linkml-1.7.2.dist-info → linkml-1.7.4.dist-info}/LICENSE +0 -0
- {linkml-1.7.2.dist-info → linkml-1.7.4.dist-info}/WHEEL +0 -0
- {linkml-1.7.2.dist-info → linkml-1.7.4.dist-info}/entry_points.txt +0 -0
linkml/__init__.py
CHANGED
@@ -71,3 +71,10 @@ BIOLINK_MODEL_URI = "https://w3id.org/biolink/biolink-model"
|
|
71
71
|
BIOLINK_MODEL_PYTHON_LOC = "biolink.model"
|
72
72
|
|
73
73
|
TurtleSerializer.roundtrip_prefixes = [""]
|
74
|
+
|
75
|
+
REQUESTS_TIMEOUT = 10
|
76
|
+
"""
|
77
|
+
Time (in seconds) to wait before failing a network request
|
78
|
+
|
79
|
+
See: https://docs.python-requests.org/en/latest/user/advanced/#timeouts
|
80
|
+
"""
|
linkml/generators/docgen.py
CHANGED
@@ -126,7 +126,7 @@ class DocGenerator(Generator):
|
|
126
126
|
# ObjectVars
|
127
127
|
dialect: Optional[Union[DIALECT, str]] = None
|
128
128
|
"""markdown dialect (e.g MyST, Python)"""
|
129
|
-
sort_by: str =
|
129
|
+
sort_by: str = "name"
|
130
130
|
visit_all_class_slots = False
|
131
131
|
template_mappings: Dict[str, str] = None
|
132
132
|
directory: str = None
|
@@ -141,19 +141,19 @@ class DocGenerator(Generator):
|
|
141
141
|
diagram_type: Optional[Union[DiagramType, str]] = None
|
142
142
|
"""style of diagram (ER, UML)"""
|
143
143
|
|
144
|
-
include_top_level_diagram: bool =
|
144
|
+
include_top_level_diagram: bool = False
|
145
145
|
"""Whether the index page should include a schema diagram"""
|
146
146
|
|
147
147
|
example_directory: Optional[str] = None
|
148
148
|
example_runner: ExampleRunner = field(default_factory=lambda: ExampleRunner())
|
149
149
|
|
150
|
-
genmeta: bool =
|
151
|
-
gen_classvars: bool =
|
152
|
-
gen_slots: bool =
|
153
|
-
no_types_dir: bool =
|
154
|
-
use_slot_uris: bool =
|
155
|
-
use_class_uris: bool =
|
156
|
-
hierarchical_class_view: bool =
|
150
|
+
genmeta: bool = False
|
151
|
+
gen_classvars: bool = True
|
152
|
+
gen_slots: bool = True
|
153
|
+
no_types_dir: bool = False
|
154
|
+
use_slot_uris: bool = False
|
155
|
+
use_class_uris: bool = False
|
156
|
+
hierarchical_class_view: bool = False
|
157
157
|
|
158
158
|
def __post_init__(self):
|
159
159
|
dialect = self.dialect
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import os
|
2
|
-
from dataclasses import dataclass
|
2
|
+
from dataclasses import dataclass
|
3
3
|
from enum import Enum
|
4
4
|
from typing import List, Optional, Union
|
5
5
|
|
@@ -129,11 +129,11 @@ class ERDiagramGenerator(Generator):
|
|
129
129
|
exclude_attributes: bool = False
|
130
130
|
"""If True, do not include attributes in entities"""
|
131
131
|
|
132
|
-
genmeta: bool =
|
133
|
-
gen_classvars: bool =
|
134
|
-
gen_slots: bool =
|
135
|
-
no_types_dir: bool =
|
136
|
-
use_slot_uris: bool =
|
132
|
+
genmeta: bool = False
|
133
|
+
gen_classvars: bool = True
|
134
|
+
gen_slots: bool = True
|
135
|
+
no_types_dir: bool = False
|
136
|
+
use_slot_uris: bool = False
|
137
137
|
|
138
138
|
def __post_init__(self):
|
139
139
|
self.schemaview = SchemaView(self.schema)
|
linkml/generators/excelgen.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import logging
|
2
|
-
from dataclasses import dataclass
|
2
|
+
from dataclasses import dataclass
|
3
3
|
from pathlib import Path
|
4
4
|
from typing import List
|
5
5
|
|
@@ -22,8 +22,8 @@ class ExcelGenerator(Generator):
|
|
22
22
|
uses_schemaloader = False
|
23
23
|
requires_metamodel = False
|
24
24
|
|
25
|
-
split_workbook_by_class: bool =
|
26
|
-
include_mixins: bool =
|
25
|
+
split_workbook_by_class: bool = False
|
26
|
+
include_mixins: bool = False
|
27
27
|
|
28
28
|
def __post_init__(self) -> None:
|
29
29
|
super().__post_init__()
|
linkml/generators/golanggen.py
CHANGED
@@ -160,7 +160,7 @@ class GolangGenerator(Generator):
|
|
160
160
|
rc_name = self.name(rc)
|
161
161
|
id_slot = self.get_identifier_or_key_slot(r)
|
162
162
|
if slot.multivalued:
|
163
|
-
if not id_slot or slot.inlined:
|
163
|
+
if not id_slot or slot.inlined or slot.inlined_as_list:
|
164
164
|
if slot.inlined_as_list or not id_slot:
|
165
165
|
return f"[]{rc_name}"
|
166
166
|
else:
|
linkml/generators/javagen.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import importlib.util
|
2
2
|
import os
|
3
|
-
from dataclasses import dataclass
|
3
|
+
from dataclasses import dataclass
|
4
4
|
from typing import Optional
|
5
5
|
|
6
6
|
import click
|
@@ -78,10 +78,10 @@ class JavaGenerator(OOCodeGenerator):
|
|
78
78
|
|
79
79
|
template_file: Optional[str] = None
|
80
80
|
|
81
|
-
gen_classvars: bool =
|
82
|
-
gen_slots: bool =
|
83
|
-
genmeta: bool =
|
84
|
-
emit_metadata: bool =
|
81
|
+
gen_classvars: bool = True
|
82
|
+
gen_slots: bool = True
|
83
|
+
genmeta: bool = False
|
84
|
+
emit_metadata: bool = True
|
85
85
|
|
86
86
|
def default_value_for_type(self, typ: str) -> str:
|
87
87
|
return TYPE_DEFAULTS.get(typ, "null")
|
@@ -45,12 +45,12 @@ class ContextGenerator(Generator):
|
|
45
45
|
default_ns: str = None
|
46
46
|
context_body: Dict = field(default_factory=lambda: dict())
|
47
47
|
slot_class_maps: Dict = field(default_factory=lambda: dict())
|
48
|
-
emit_metadata: bool =
|
49
|
-
model: Optional[bool] =
|
48
|
+
emit_metadata: bool = False
|
49
|
+
model: Optional[bool] = True
|
50
50
|
base: Optional[str] = None
|
51
51
|
output: Optional[str] = None
|
52
|
-
prefixes: Optional[bool] =
|
53
|
-
flatprefixes: Optional[bool] =
|
52
|
+
prefixes: Optional[bool] = True
|
53
|
+
flatprefixes: Optional[bool] = False
|
54
54
|
|
55
55
|
def __post_init__(self) -> None:
|
56
56
|
super().__post_init__()
|
@@ -150,7 +150,8 @@ class ContextGenerator(Generator):
|
|
150
150
|
else:
|
151
151
|
slot_def = {}
|
152
152
|
if not slot.usage_slot_name:
|
153
|
-
|
153
|
+
any_of_ranges = [any_of_el.range for any_of_el in slot.any_of]
|
154
|
+
if slot.range in self.schema.classes or any(rng in self.schema.classes for rng in any_of_ranges):
|
154
155
|
slot_def["@type"] = "@id"
|
155
156
|
elif slot.range in self.schema.enums:
|
156
157
|
slot_def["@context"] = ENUM_CONTEXT
|
@@ -2,7 +2,7 @@ import json
|
|
2
2
|
import logging
|
3
3
|
import os
|
4
4
|
from copy import deepcopy
|
5
|
-
from dataclasses import dataclass
|
5
|
+
from dataclasses import dataclass
|
6
6
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
7
7
|
|
8
8
|
import click
|
@@ -179,7 +179,7 @@ class JsonSchemaGenerator(Generator):
|
|
179
179
|
# @deprecated("Use top_class")
|
180
180
|
topClass: Optional[str] = None
|
181
181
|
|
182
|
-
not_closed: Optional[bool] =
|
182
|
+
not_closed: Optional[bool] = True
|
183
183
|
"""If not closed, then an open-ended set of attributes can be instantiated for any object"""
|
184
184
|
|
185
185
|
indent: int = 4
|
@@ -188,7 +188,7 @@ class JsonSchemaGenerator(Generator):
|
|
188
188
|
top_class: Optional[Union[ClassDefinitionName, str]] = None # JSON object is one instance of this
|
189
189
|
"""Class instantiated by the root node of the document tree"""
|
190
190
|
|
191
|
-
include_range_class_descendants: bool =
|
191
|
+
include_range_class_descendants: bool = False
|
192
192
|
"""If set, use an open world assumption and allow the range of a slot to be any descendant of the declared range.
|
193
193
|
Note that if the range of a slot has a type designator, descendants will always be included.
|
194
194
|
"""
|
linkml/generators/linkmlgen.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import logging
|
2
2
|
import os
|
3
|
-
from dataclasses import dataclass
|
3
|
+
from dataclasses import dataclass
|
4
4
|
from typing import Union
|
5
5
|
|
6
6
|
import click
|
@@ -30,8 +30,8 @@ class LinkmlGenerator(Generator):
|
|
30
30
|
uses_schemaloader = False
|
31
31
|
requires_metamodel = False
|
32
32
|
|
33
|
-
materialize_attributes: bool =
|
34
|
-
materialize_patterns: bool =
|
33
|
+
materialize_attributes: bool = False
|
34
|
+
materialize_patterns: bool = False
|
35
35
|
|
36
36
|
def __post_init__(self):
|
37
37
|
# TODO: consider moving up a level
|
linkml/generators/markdowngen.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import os
|
2
2
|
from contextlib import redirect_stdout
|
3
|
-
from dataclasses import dataclass
|
3
|
+
from dataclasses import dataclass
|
4
4
|
from io import StringIO
|
5
5
|
from typing import Any, Callable, Dict, List, Optional, Set, Union
|
6
6
|
|
@@ -47,7 +47,7 @@ class MarkdownGenerator(Generator):
|
|
47
47
|
image_directory: Optional[str] = None
|
48
48
|
classes: Set[ClassDefinitionName] = None
|
49
49
|
image_dir: bool = False
|
50
|
-
index_file: str =
|
50
|
+
index_file: str = "index.md"
|
51
51
|
noimages: bool = False
|
52
52
|
noyuml: bool = False
|
53
53
|
no_types_dir: bool = False
|
linkml/generators/oocodegen.py
CHANGED
@@ -141,7 +141,10 @@ class OOCodeGenerator(Generator):
|
|
141
141
|
enum["description"] = enum_original.description
|
142
142
|
|
143
143
|
for pv in enum_original.permissible_values.values():
|
144
|
-
|
144
|
+
if pv.title:
|
145
|
+
label = self.generate_enum_label(pv.title)
|
146
|
+
else:
|
147
|
+
label = self.generate_enum_label(pv.text)
|
145
148
|
val = {"label": label, "value": pv.text.replace('"', '\\"')}
|
146
149
|
if hasattr(pv, "description"):
|
147
150
|
val["description"] = pv.description
|
linkml/generators/owlgen.py
CHANGED
@@ -118,29 +118,29 @@ class OwlSchemaGenerator(Generator):
|
|
118
118
|
"""By default, use the linkml metadata profile,
|
119
119
|
this allows for overrides."""
|
120
120
|
|
121
|
-
metaclasses: bool =
|
121
|
+
metaclasses: bool = True
|
122
122
|
"""if True, include OWL representations of ClassDefinition, SlotDefinition, etc. Introduces punning"""
|
123
123
|
|
124
|
-
add_root_classes: bool =
|
124
|
+
add_root_classes: bool = False
|
125
125
|
|
126
|
-
add_ols_annotations: bool =
|
126
|
+
add_ols_annotations: bool = True
|
127
127
|
graph: Optional[Graph] = None
|
128
128
|
"""Mutable graph that is being built up during OWL generation."""
|
129
129
|
|
130
130
|
top_value_uri: Optional[URIRef] = None
|
131
131
|
"""If metaclasses=True, then this property is used to connect object shadows to literals"""
|
132
132
|
|
133
|
-
type_objects: bool =
|
133
|
+
type_objects: bool = True
|
134
134
|
"""if True, represents types as classes (and thus all slots are object properties);
|
135
135
|
typed object classes effectively shadow the main xsd literal types.
|
136
136
|
The purpose of this is to allow a uniform ObjectProperty representation for all slots,
|
137
137
|
without having to commit to being either Data or Object property (OWL-DL does not
|
138
138
|
allow a property to be both."""
|
139
139
|
|
140
|
-
assert_equivalent_classes: bool =
|
140
|
+
assert_equivalent_classes: bool = False
|
141
141
|
"""If True, assert equivalence between definition_uris and class_uris"""
|
142
142
|
|
143
|
-
use_native_uris: bool =
|
143
|
+
use_native_uris: bool = True
|
144
144
|
"""If True, use the definition_uris, otherwise use class_uris."""
|
145
145
|
|
146
146
|
mixins_as_expressions: bool = None
|
@@ -152,10 +152,10 @@ class OwlSchemaGenerator(Generator):
|
|
152
152
|
node_owltypes: Mapping[Union[BNode, URIRef], Set[OWL_TYPE]] = field(default_factory=lambda: defaultdict(set))
|
153
153
|
"""rdfs:Datatype, owl:Thing"""
|
154
154
|
|
155
|
-
simplify: bool =
|
155
|
+
simplify: bool = True
|
156
156
|
"""Reduce complex expressions to simpler forms"""
|
157
157
|
|
158
|
-
use_swrl: bool =
|
158
|
+
use_swrl: bool = False
|
159
159
|
"""Use of SWRL is experimental"""
|
160
160
|
|
161
161
|
target_profile: OWLProfile = field(default_factory=lambda: OWLProfile.dl)
|
@@ -378,8 +378,12 @@ class OwlSchemaGenerator(Generator):
|
|
378
378
|
# rules yield OWL GCI subClassOf axioms
|
379
379
|
for rule in cls.rules:
|
380
380
|
pre_node = condition_to_bnode(rule.preconditions)
|
381
|
+
if not pre_node:
|
382
|
+
continue
|
381
383
|
pre_node = self._intersection_of([pre_node, subject_expr])
|
382
384
|
post_node = condition_to_bnode(rule.postconditions)
|
385
|
+
if not post_node:
|
386
|
+
continue
|
383
387
|
self.graph.add((pre_node, RDFS.subClassOf, post_node))
|
384
388
|
# classification rules yield OWL GCI subClassOf axioms
|
385
389
|
for expr in cls.classification_rules:
|
@@ -433,15 +437,10 @@ class OwlSchemaGenerator(Generator):
|
|
433
437
|
if slot.name in slot_map:
|
434
438
|
for k, v in slot.__dict__.items():
|
435
439
|
curr = slot_map[slot.name].get(k, None)
|
436
|
-
# print(f"MERGE={slot.name}.{k} = {v} // CURR={curr}")
|
437
440
|
if v and not curr:
|
438
441
|
slot_map[slot.name][k] = v
|
439
|
-
# print(f"OVERRIDE={slot.name}, k={k}, v={v}")
|
440
442
|
else:
|
441
443
|
slot_map[slot.name] = copy(slot.__dict__)
|
442
|
-
# print(f"INIT={slot.name}, vals={slot_map[slot.name]}")
|
443
|
-
|
444
|
-
# print(f"SN={slot.name}, vals={slot_map[slot.name]}")
|
445
444
|
own_slots = [SlotDefinition(**v) for v in slot_map.values()]
|
446
445
|
# sort by name
|
447
446
|
own_slots.sort(key=lambda x: x.name)
|
@@ -462,6 +461,8 @@ class OwlSchemaGenerator(Generator):
|
|
462
461
|
:param cls: LinkML class expression (anonymous if called recursively)
|
463
462
|
:return: blank node representing the OWL expression
|
464
463
|
"""
|
464
|
+
if cls is None:
|
465
|
+
cls = AnonymousClassExpression()
|
465
466
|
graph = self.graph
|
466
467
|
sv = self.schemaview
|
467
468
|
own_slots = self.get_own_slots(cls)
|
@@ -477,11 +478,13 @@ class OwlSchemaGenerator(Generator):
|
|
477
478
|
graph.add((cls_uri, OWL.disjointUnionOf, listnode))
|
478
479
|
else:
|
479
480
|
sub_sub_exprs = []
|
480
|
-
for i, x in enumerate(
|
481
|
-
rest =
|
482
|
-
neg_expr = self._complement_of_union_of(rest)
|
483
|
-
|
484
|
-
|
481
|
+
for i, x in enumerate(cls.exactly_one_of):
|
482
|
+
rest = cls.exactly_one_of[0:i] + cls.exactly_one_of[i + 1 :]
|
483
|
+
neg_expr = self._complement_of_union_of([self.transform_class_expression(nx) for nx in rest])
|
484
|
+
pos_expr = self._intersection_of([self.transform_class_expression(x), neg_expr])
|
485
|
+
sub_sub_exprs.append(pos_expr)
|
486
|
+
owl_exprs.append(self._union_of(sub_sub_exprs))
|
487
|
+
# owl_exprs.extend(sub_exprs)
|
485
488
|
if cls.all_of:
|
486
489
|
owl_exprs.append(self._intersection_of([self.transform_class_expression(x) for x in cls.all_of]))
|
487
490
|
if cls.none_of:
|
@@ -533,7 +536,8 @@ class OwlSchemaGenerator(Generator):
|
|
533
536
|
graph.add((max_card_expr, OWL.onProperty, slot_uri))
|
534
537
|
if slot.has_member:
|
535
538
|
has_member_expr = self.transform_class_slot_expression(cls, slot.has_member, slot)
|
536
|
-
|
539
|
+
if has_member_expr:
|
540
|
+
owl_exprs.append(self._some_values_from(slot_uri, has_member_expr))
|
537
541
|
return self._intersection_of(owl_exprs)
|
538
542
|
|
539
543
|
def slot_node_owltypes(self, slot: Union[SlotDefinition, AnonymousSlotExpression]) -> Set[URIRef]:
|
@@ -558,7 +562,7 @@ class OwlSchemaGenerator(Generator):
|
|
558
562
|
slot: Union[SlotDefinition, AnonymousSlotExpression],
|
559
563
|
main_slot: SlotDefinition = None,
|
560
564
|
owl_types: Set[OWL_TYPE] = None,
|
561
|
-
) -> Union[BNode, URIRef]:
|
565
|
+
) -> Optional[Union[BNode, URIRef]]:
|
562
566
|
"""
|
563
567
|
Take a ClassExpression and SlotExpression combination and transform to a node.
|
564
568
|
|
@@ -597,11 +601,19 @@ class OwlSchemaGenerator(Generator):
|
|
597
601
|
)
|
598
602
|
)
|
599
603
|
if slot.exactly_one_of:
|
600
|
-
|
601
|
-
|
602
|
-
|
604
|
+
disj_exprs = []
|
605
|
+
for i, operand in enumerate(slot.exactly_one_of):
|
606
|
+
rest = slot.exactly_one_of[0:i] + slot.exactly_one_of[i + 1 :]
|
607
|
+
neg_expr = self._complement_of_union_of(
|
608
|
+
[self.transform_class_slot_expression(cls, x, main_slot, owl_types) for x in rest],
|
609
|
+
owl_types=owl_types,
|
603
610
|
)
|
604
|
-
|
611
|
+
pos_expr = self._intersection_of(
|
612
|
+
[self.transform_class_slot_expression(cls, operand, main_slot, owl_types), neg_expr],
|
613
|
+
owl_types=owl_types,
|
614
|
+
)
|
615
|
+
disj_exprs.append(pos_expr)
|
616
|
+
owl_exprs.append(self._union_of(disj_exprs, owl_types=owl_types))
|
605
617
|
range = slot.range
|
606
618
|
# if not range and not owl_exprs:
|
607
619
|
# range = sv.schema.default_range
|
@@ -929,6 +941,10 @@ class OwlSchemaGenerator(Generator):
|
|
929
941
|
triples.extend(graph.triples((obj, None, None)))
|
930
942
|
|
931
943
|
def _some_values_from(self, property: URIRef, filler: Union[URIRef, BNode]) -> BNode:
|
944
|
+
if not property:
|
945
|
+
raise ValueError(f"Property is required, filler: {filler}")
|
946
|
+
if not filler:
|
947
|
+
raise ValueError(f"Filler is required, property: {property}")
|
932
948
|
node = BNode()
|
933
949
|
self.graph.add((node, RDF.type, OWL.Restriction))
|
934
950
|
self.graph.add((node, OWL.onProperty, property))
|
@@ -970,11 +986,14 @@ class OwlSchemaGenerator(Generator):
|
|
970
986
|
def _metaslot_uri(self, name: str) -> URIRef:
|
971
987
|
return URIRef("https://w3id.org/linkml/" + name)
|
972
988
|
|
973
|
-
def _complement_of_union_of(
|
989
|
+
def _complement_of_union_of(
|
990
|
+
self, exprs: List[Union[BNode, URIRef]], owl_types: Set[OWL_TYPE] = None, **kwargs
|
991
|
+
) -> Optional[Union[BNode, URIRef]]:
|
974
992
|
if not exprs:
|
975
993
|
raise ValueError("Must pass at least one")
|
976
994
|
neg_expr = BNode()
|
977
|
-
|
995
|
+
if not owl_types:
|
996
|
+
owl_types = self._get_owltypes(set(), exprs)
|
978
997
|
complement_predicate = OWL.complementOf
|
979
998
|
if len(owl_types) == 1:
|
980
999
|
if RDFS.Literal in owl_types:
|
linkml/generators/plantumlgen.py
CHANGED
@@ -7,7 +7,7 @@ https://plantuml.com/
|
|
7
7
|
import base64
|
8
8
|
import os
|
9
9
|
import zlib
|
10
|
-
from dataclasses import dataclass
|
10
|
+
from dataclasses import dataclass
|
11
11
|
from typing import Callable, List, Optional, Set, cast
|
12
12
|
|
13
13
|
import click
|
@@ -19,6 +19,7 @@ from linkml_runtime.linkml_model.meta import (
|
|
19
19
|
)
|
20
20
|
from linkml_runtime.utils.formatutils import camelcase, underscore
|
21
21
|
|
22
|
+
from linkml import REQUESTS_TIMEOUT
|
22
23
|
from linkml._version import __version__
|
23
24
|
from linkml.utils.generator import Generator, shared_arguments
|
24
25
|
|
@@ -48,7 +49,7 @@ class PlantumlGenerator(Generator):
|
|
48
49
|
classes: Set[ClassDefinitionName] = None
|
49
50
|
directory: Optional[str] = None
|
50
51
|
kroki_server: Optional[str] = "https://kroki.io"
|
51
|
-
load_image: bool =
|
52
|
+
load_image: bool = True
|
52
53
|
|
53
54
|
def visit_schema(
|
54
55
|
self,
|
@@ -101,7 +102,7 @@ class PlantumlGenerator(Generator):
|
|
101
102
|
camelcase(sorted(classes)[0] if classes else self.schema.name) + file_suffix,
|
102
103
|
)
|
103
104
|
if load_image:
|
104
|
-
resp = requests.get(plantuml_url, stream=True)
|
105
|
+
resp = requests.get(plantuml_url, stream=True, timeout=REQUESTS_TIMEOUT)
|
105
106
|
if resp.ok:
|
106
107
|
with open(self.output_file_name, "wb") as f:
|
107
108
|
for chunk in resp.iter_content(chunk_size=2048):
|
linkml/generators/protogen.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import os
|
2
|
-
from dataclasses import dataclass
|
2
|
+
from dataclasses import dataclass
|
3
3
|
|
4
4
|
import click
|
5
5
|
from linkml_runtime.linkml_model.meta import ClassDefinition, SlotDefinition
|
@@ -24,7 +24,7 @@ class ProtoGenerator(Generator):
|
|
24
24
|
uses_schemaloader = True
|
25
25
|
|
26
26
|
# ObjectVars
|
27
|
-
relative_slot_num: int =
|
27
|
+
relative_slot_num: int = 0
|
28
28
|
|
29
29
|
def __post_init__(self):
|
30
30
|
super().__post_init__()
|
linkml/generators/pydanticgen.py
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
+
import inspect
|
1
2
|
import logging
|
2
3
|
import os
|
3
4
|
from collections import defaultdict
|
4
5
|
from copy import deepcopy
|
5
6
|
from dataclasses import dataclass, field
|
6
7
|
from types import ModuleType
|
7
|
-
from typing import Dict, List, Optional, Set
|
8
|
+
from typing import Dict, List, Optional, Set, Type, Union
|
8
9
|
|
9
10
|
import click
|
10
11
|
from jinja2 import Template
|
@@ -32,7 +33,9 @@ from linkml.utils.generator import shared_arguments
|
|
32
33
|
from linkml.utils.ifabsent_functions import ifabsent_value_declaration
|
33
34
|
|
34
35
|
|
35
|
-
def default_template(
|
36
|
+
def default_template(
|
37
|
+
pydantic_ver: str = "1", extra_fields: str = "forbid", injected_classes: Optional[List[Union[Type, str]]] = None
|
38
|
+
) -> str:
|
36
39
|
"""Constructs a default template for pydantic classes based on the version of pydantic"""
|
37
40
|
### HEADER ###
|
38
41
|
template = """
|
@@ -46,6 +49,7 @@ from enum import Enum
|
|
46
49
|
{% if uses_numpy -%}
|
47
50
|
import numpy as np
|
48
51
|
{%- endif %}
|
52
|
+
from decimal import Decimal
|
49
53
|
from typing import List, Dict, Optional, Any, Union"""
|
50
54
|
if pydantic_ver == "1":
|
51
55
|
template += """
|
@@ -61,7 +65,26 @@ if sys.version_info >= (3, 8):
|
|
61
65
|
else:
|
62
66
|
from typing_extensions import Literal
|
63
67
|
|
64
|
-
|
68
|
+
{% if imports is not none %}
|
69
|
+
{%- for import_module, import_classes in imports.items() -%}
|
70
|
+
{% if import_classes is none -%}
|
71
|
+
import {{ import_module }}
|
72
|
+
{% elif import_classes is mapping -%}
|
73
|
+
import {{ import_module }} as {{ import_classes['as'] }}
|
74
|
+
{% else -%}
|
75
|
+
from {{ import_module }} import (
|
76
|
+
{% for imported_class in import_classes %}
|
77
|
+
{%- if imported_class is string -%}
|
78
|
+
{{ imported_class }}
|
79
|
+
{%- else -%}
|
80
|
+
{{ imported_class['name'] }} as {{ imported_class['as'] }}
|
81
|
+
{%- endif -%}
|
82
|
+
{%- if not loop.last %},{{ '\n ' }}{% else %}{{ '\n' }}{%- endif -%}
|
83
|
+
{% endfor -%}
|
84
|
+
)
|
85
|
+
{% endif -%}
|
86
|
+
{% endfor -%}
|
87
|
+
{% endif %}
|
65
88
|
metamodel_version = "{{metamodel_version}}"
|
66
89
|
version = "{{version if version else None}}"
|
67
90
|
"""
|
@@ -78,7 +101,6 @@ class ConfiguredBaseModel(WeakRefShimBaseModel,
|
|
78
101
|
extra = '{extra_fields}',
|
79
102
|
arbitrary_types_allowed = True,
|
80
103
|
use_enum_values = True):
|
81
|
-
pass
|
82
104
|
"""
|
83
105
|
else:
|
84
106
|
template += f"""
|
@@ -89,9 +111,27 @@ class ConfiguredBaseModel(BaseModel):
|
|
89
111
|
extra = '{extra_fields}',
|
90
112
|
arbitrary_types_allowed=True,
|
91
113
|
use_enum_values = True)
|
92
|
-
pass
|
93
114
|
"""
|
94
115
|
|
116
|
+
### Fields injected into base model
|
117
|
+
template += """{% if injected_fields is not none %}
|
118
|
+
{% for field in injected_fields -%}
|
119
|
+
{{ field }}
|
120
|
+
{% endfor %}
|
121
|
+
{% else %}
|
122
|
+
pass
|
123
|
+
{% endif %}
|
124
|
+
"""
|
125
|
+
|
126
|
+
### Extra classes
|
127
|
+
if injected_classes is not None and len(injected_classes) > 0:
|
128
|
+
template += """{{ '\n\n' }}"""
|
129
|
+
for cls in injected_classes:
|
130
|
+
if isinstance(cls, str):
|
131
|
+
template += cls + "\n\n"
|
132
|
+
else:
|
133
|
+
template += inspect.getsource(cls) + "\n\n"
|
134
|
+
|
95
135
|
### ENUMS ###
|
96
136
|
template += """
|
97
137
|
{% for e in enums.values() %}
|
@@ -270,14 +310,75 @@ class PydanticGenerator(OOCodeGenerator):
|
|
270
310
|
# ObjectVars
|
271
311
|
pydantic_version: str = field(default_factory=lambda: PYDANTIC_VERSION[0])
|
272
312
|
template_file: str = None
|
273
|
-
extra_fields: str =
|
274
|
-
gen_mixin_inheritance: bool =
|
313
|
+
extra_fields: str = "forbid"
|
314
|
+
gen_mixin_inheritance: bool = True
|
315
|
+
injected_classes: Optional[List[Union[Type, str]]] = None
|
316
|
+
"""
|
317
|
+
A list/tuple of classes to inject into the generated module.
|
318
|
+
|
319
|
+
Accepts either live classes or strings. Live classes will have their source code
|
320
|
+
extracted with inspect.get - so they need to be standard python classes declared in a
|
321
|
+
source file (ie. the module they are contained in needs a ``__file__`` attr,
|
322
|
+
see: :func:`inspect.getsource` )
|
323
|
+
"""
|
324
|
+
injected_fields: Optional[List[str]] = None
|
325
|
+
"""
|
326
|
+
A list/tuple of field strings to inject into the base class.
|
327
|
+
|
328
|
+
Examples:
|
329
|
+
|
330
|
+
.. code-block:: python
|
331
|
+
|
332
|
+
injected_fields = (
|
333
|
+
'object_id: Optional[str] = Field(None, description="Unique UUID for each object")',
|
334
|
+
)
|
335
|
+
|
336
|
+
"""
|
337
|
+
imports: Optional[Dict[str, Union[None, Dict[str, str], List[Union[str, Dict[str, str]]]]]] = None
|
338
|
+
"""
|
339
|
+
Additional imports to inject into generated module.
|
340
|
+
|
341
|
+
A dictionary mapping a module to a list of objects to import.
|
342
|
+
If the value of an entry is ``None`` , import the whole module.
|
343
|
+
|
344
|
+
Import Aliases:
|
345
|
+
If the value of a module import is a dictionary with a key "as",
|
346
|
+
or a class is specified as a dictionary with a "name" and "as",
|
347
|
+
then the import is renamed like "from module import class as renamedClass".
|
348
|
+
|
349
|
+
Examples:
|
350
|
+
|
351
|
+
.. code-block:: python
|
352
|
+
|
353
|
+
imports = {
|
354
|
+
'typing': ['Dict', 'List', 'Union'],
|
355
|
+
'types': None,
|
356
|
+
'numpy': {'as': 'np'},
|
357
|
+
'collections': [{'name': 'OrderedDict', 'as': 'odict'}]
|
358
|
+
}
|
359
|
+
|
360
|
+
becomes:
|
361
|
+
|
362
|
+
.. code-block:: python
|
363
|
+
|
364
|
+
from typing import (
|
365
|
+
Dict,
|
366
|
+
List,
|
367
|
+
Union
|
368
|
+
)
|
369
|
+
import types
|
370
|
+
import numpy as np
|
371
|
+
from collections import (
|
372
|
+
OrderedDict as odict
|
373
|
+
)
|
374
|
+
|
375
|
+
"""
|
275
376
|
|
276
377
|
# ObjectVars (identical to pythongen)
|
277
|
-
gen_classvars: bool =
|
278
|
-
gen_slots: bool =
|
279
|
-
genmeta: bool =
|
280
|
-
emit_metadata: bool =
|
378
|
+
gen_classvars: bool = True
|
379
|
+
gen_slots: bool = True
|
380
|
+
genmeta: bool = False
|
381
|
+
emit_metadata: bool = True
|
281
382
|
|
282
383
|
def compile_module(self, **kwargs) -> ModuleType:
|
283
384
|
"""
|
@@ -525,12 +626,48 @@ class PydanticGenerator(OOCodeGenerator):
|
|
525
626
|
return list(collection_keys)[0]
|
526
627
|
return None
|
527
628
|
|
629
|
+
def _inline_as_simple_dict_with_value(self, slot_def: SlotDefinition, sv: SchemaView) -> Optional[str]:
|
630
|
+
"""
|
631
|
+
Determine if a slot should be inlined as a simple dict with a value.
|
632
|
+
|
633
|
+
For example, if we have a class such as Prefix, with two slots prefix and expansion,
|
634
|
+
then an inlined list of prefixes can be serialized as:
|
635
|
+
|
636
|
+
.. code-block:: yaml
|
637
|
+
|
638
|
+
prefixes:
|
639
|
+
prefix1: expansion1
|
640
|
+
prefix2: expansion2
|
641
|
+
...
|
642
|
+
|
643
|
+
Provided that the prefix slot is the identifier slot for the Prefix class.
|
644
|
+
|
645
|
+
TODO: move this to SchemaView
|
646
|
+
|
647
|
+
:param slot_def: SlotDefinition
|
648
|
+
:param sv: SchemaView
|
649
|
+
:return: str
|
650
|
+
"""
|
651
|
+
if slot_def.inlined and not slot_def.inlined_as_list:
|
652
|
+
if slot_def.range in sv.all_classes():
|
653
|
+
id_slot = sv.get_identifier_slot(slot_def.range, use_key=True)
|
654
|
+
if id_slot is not None:
|
655
|
+
range_cls_slots = sv.class_induced_slots(slot_def.range)
|
656
|
+
if len(range_cls_slots) == 2:
|
657
|
+
non_id_slots = [slot for slot in range_cls_slots if slot.name != id_slot.name]
|
658
|
+
if len(non_id_slots) == 1:
|
659
|
+
value_slot = non_id_slots[0]
|
660
|
+
value_slot_range_type = sv.get_type(value_slot.range)
|
661
|
+
if value_slot_range_type is not None:
|
662
|
+
return _get_pyrange(value_slot_range_type, sv)
|
663
|
+
return None
|
664
|
+
|
528
665
|
def serialize(self) -> str:
|
529
666
|
if self.template_file is not None:
|
530
667
|
with open(self.template_file) as template_file:
|
531
668
|
template_obj = Template(template_file.read())
|
532
669
|
else:
|
533
|
-
template_obj = Template(default_template(self.pydantic_version, self.extra_fields))
|
670
|
+
template_obj = Template(default_template(self.pydantic_version, self.extra_fields, self.injected_classes))
|
534
671
|
|
535
672
|
sv: SchemaView
|
536
673
|
sv = self.schemaview
|
@@ -610,7 +747,14 @@ class PydanticGenerator(OOCodeGenerator):
|
|
610
747
|
if s.inlined is False or collection_key is None or s.inlined_as_list is True:
|
611
748
|
pyrange = f"List[{pyrange}]"
|
612
749
|
else:
|
613
|
-
|
750
|
+
simple_dict_value = None
|
751
|
+
if len(slot_ranges) == 1:
|
752
|
+
simple_dict_value = self._inline_as_simple_dict_with_value(s, sv)
|
753
|
+
if simple_dict_value:
|
754
|
+
# inlining as simple dict
|
755
|
+
pyrange = f"Dict[str, {simple_dict_value}]"
|
756
|
+
else:
|
757
|
+
pyrange = f"Dict[{collection_key}, {pyrange}]"
|
614
758
|
if not (s.required or s.identifier or s.key) and not s.designates_type:
|
615
759
|
pyrange = f"Optional[{pyrange}]"
|
616
760
|
ann = Annotation("python_range", pyrange)
|
@@ -624,6 +768,8 @@ class PydanticGenerator(OOCodeGenerator):
|
|
624
768
|
metamodel_version=self.schema.metamodel_version,
|
625
769
|
version=self.schema.version,
|
626
770
|
class_isa_plus_mixins=self.get_class_isa_plus_mixins(),
|
771
|
+
imports=self.imports,
|
772
|
+
injected_fields=self.injected_fields,
|
627
773
|
uses_numpy=uses_numpy,
|
628
774
|
)
|
629
775
|
return code
|
linkml/generators/pythongen.py
CHANGED
@@ -3,7 +3,7 @@ import logging
|
|
3
3
|
import os
|
4
4
|
import re
|
5
5
|
from copy import copy
|
6
|
-
from dataclasses import dataclass
|
6
|
+
from dataclasses import dataclass
|
7
7
|
from types import ModuleType
|
8
8
|
from typing import Callable, Dict, Iterator, List, Optional, Set, Tuple, Union
|
9
9
|
|
@@ -60,10 +60,10 @@ class PythonGenerator(Generator):
|
|
60
60
|
uses_schemaloader = True
|
61
61
|
|
62
62
|
# ObjectVars
|
63
|
-
gen_classvars: bool =
|
64
|
-
gen_slots: bool =
|
65
|
-
genmeta: bool =
|
66
|
-
emit_metadata: bool =
|
63
|
+
gen_classvars: bool = True
|
64
|
+
gen_slots: bool = True
|
65
|
+
genmeta: bool = False
|
66
|
+
emit_metadata: bool = True
|
67
67
|
|
68
68
|
def __post_init__(self) -> None:
|
69
69
|
self.sourcefile = self.schema
|
linkml/generators/rdfgen.py
CHANGED
@@ -7,7 +7,7 @@ Generate a JSON LD representation of the model
|
|
7
7
|
|
8
8
|
import os
|
9
9
|
import urllib.parse as urlparse
|
10
|
-
from dataclasses import dataclass
|
10
|
+
from dataclasses import dataclass
|
11
11
|
from typing import List, Optional
|
12
12
|
|
13
13
|
import click
|
@@ -33,7 +33,7 @@ class RDFGenerator(Generator):
|
|
33
33
|
uses_schemaloader = True
|
34
34
|
|
35
35
|
# ObjectVars
|
36
|
-
emit_metadata: bool =
|
36
|
+
emit_metadata: bool = False
|
37
37
|
context: List[str] = None
|
38
38
|
|
39
39
|
def _data(self, g: Graph) -> str:
|
linkml/generators/shaclgen.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import logging
|
2
2
|
import os
|
3
|
-
from dataclasses import dataclass
|
3
|
+
from dataclasses import dataclass
|
4
4
|
|
5
5
|
import click
|
6
6
|
from linkml_runtime.utils.formatutils import underscore
|
@@ -31,7 +31,7 @@ LINK_ML_TYPES_NODE_ID = URIRef("http://www.w3.org/ns/shex#nonLiteral")
|
|
31
31
|
@dataclass
|
32
32
|
class ShaclGenerator(Generator):
|
33
33
|
# ClassVars
|
34
|
-
closed: bool =
|
34
|
+
closed: bool = True
|
35
35
|
"""True means add 'sh:closed=true' to all shapes, except of mixin shapes and shapes, that have parents"""
|
36
36
|
|
37
37
|
generatorname = os.path.basename(__file__)
|
linkml/generators/sqltablegen.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import logging
|
2
2
|
import os
|
3
|
-
from dataclasses import dataclass
|
3
|
+
from dataclasses import dataclass
|
4
4
|
from typing import Optional
|
5
5
|
|
6
6
|
import click
|
@@ -123,12 +123,12 @@ class SQLTableGenerator(Generator):
|
|
123
123
|
|
124
124
|
# ObjectVars
|
125
125
|
use_inherits: bool = False # postgresql supports inheritance
|
126
|
-
dialect: str =
|
127
|
-
inject_primary_keys: bool =
|
128
|
-
use_foreign_keys: bool =
|
129
|
-
rename_foreign_keys: bool =
|
130
|
-
direct_mapping: bool =
|
131
|
-
relative_slot_num: bool =
|
126
|
+
dialect: str = "sqlite"
|
127
|
+
inject_primary_keys: bool = True
|
128
|
+
use_foreign_keys: bool = True
|
129
|
+
rename_foreign_keys: bool = False
|
130
|
+
direct_mapping: bool = False
|
131
|
+
relative_slot_num: bool = False
|
132
132
|
|
133
133
|
def serialize(self, **kwargs) -> str:
|
134
134
|
return self.generate_ddl(**kwargs)
|
@@ -220,8 +220,12 @@ class SQLTableGenerator(Generator):
|
|
220
220
|
schema = SchemaLoader(data=self.schema).resolve()
|
221
221
|
|
222
222
|
if range in schema.classes:
|
223
|
-
#
|
224
|
-
|
223
|
+
# FK type should be the same as the identifier of the foreign key
|
224
|
+
fk = SchemaView(schema).get_identifier_slot(range)
|
225
|
+
if fk:
|
226
|
+
return self.get_sql_range(fk, schema)
|
227
|
+
else:
|
228
|
+
return Text()
|
225
229
|
if range in schema.enums:
|
226
230
|
e = schema.enums[range]
|
227
231
|
if e.permissible_values is not None:
|
linkml/generators/summarygen.py
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
import os
|
6
6
|
import sys
|
7
7
|
from csv import DictWriter
|
8
|
-
from dataclasses import dataclass
|
8
|
+
from dataclasses import dataclass
|
9
9
|
from typing import Optional
|
10
10
|
|
11
11
|
import click
|
@@ -26,7 +26,7 @@ class SummaryGenerator(Generator):
|
|
26
26
|
dirname: str = None
|
27
27
|
classtab: Optional[DictWriter] = None
|
28
28
|
slottab: Optional[DictWriter] = None
|
29
|
-
dialect: str =
|
29
|
+
dialect: str = "excel-tab"
|
30
30
|
|
31
31
|
def visit_schema(self, **_) -> None:
|
32
32
|
self.classtab = DictWriter(
|
linkml/generators/yamlgen.py
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
"""
|
4
4
|
|
5
5
|
import os
|
6
|
-
from dataclasses import dataclass
|
6
|
+
from dataclasses import dataclass
|
7
7
|
|
8
8
|
import click
|
9
9
|
from linkml_runtime.utils.yamlutils import as_yaml
|
@@ -26,7 +26,7 @@ class YAMLGenerator(Generator):
|
|
26
26
|
uses_schemaloader = True
|
27
27
|
|
28
28
|
# ObjectVars
|
29
|
-
validateonly: bool =
|
29
|
+
validateonly: bool = False
|
30
30
|
|
31
31
|
def serialize(self, validateonly: bool = False, **kwargs) -> str:
|
32
32
|
if validateonly:
|
linkml/generators/yumlgen.py
CHANGED
@@ -5,7 +5,7 @@ https://yuml.me/diagram/scruffy/class/samples
|
|
5
5
|
"""
|
6
6
|
|
7
7
|
import os
|
8
|
-
from dataclasses import dataclass
|
8
|
+
from dataclasses import dataclass
|
9
9
|
from typing import Callable, List, Optional, Set, cast
|
10
10
|
|
11
11
|
import click
|
@@ -14,6 +14,7 @@ from linkml_runtime.linkml_model.meta import ClassDefinition, ClassDefinitionNam
|
|
14
14
|
from linkml_runtime.utils.formatutils import camelcase, underscore
|
15
15
|
from rdflib import Namespace
|
16
16
|
|
17
|
+
from linkml import REQUESTS_TIMEOUT
|
17
18
|
from linkml.utils.generator import Generator, shared_arguments
|
18
19
|
|
19
20
|
yuml_is_a = "^-"
|
@@ -49,7 +50,7 @@ class YumlGenerator(Generator):
|
|
49
50
|
classes: Set[ClassDefinitionName] = None
|
50
51
|
directory: Optional[str] = None
|
51
52
|
diagram_name: Optional[str] = None
|
52
|
-
load_image: bool =
|
53
|
+
load_image: bool = True
|
53
54
|
|
54
55
|
def visit_schema(
|
55
56
|
self,
|
@@ -95,10 +96,10 @@ class YumlGenerator(Generator):
|
|
95
96
|
payload = "dsl_text=" + (",".join(yumlclassdef))
|
96
97
|
payload = payload.replace("%3F", "?").replace("%2B", "+")
|
97
98
|
url = "https://yuml.me/diagram/plain/class/"
|
98
|
-
resp = requests.post(url, data=payload)
|
99
|
+
resp = requests.post(url, data=payload, timeout=REQUESTS_TIMEOUT)
|
99
100
|
if resp.ok:
|
100
101
|
filename = resp.text.strip().replace(".svg", file_suffix)
|
101
|
-
resp = requests.get(f"https://yuml.me/{filename}", stream=True)
|
102
|
+
resp = requests.get(f"https://yuml.me/{filename}", stream=True, timeout=REQUESTS_TIMEOUT)
|
102
103
|
with open(self.output_file_name, "wb") as f:
|
103
104
|
for chunk in resp.iter_content(chunk_size=2048):
|
104
105
|
f.write(chunk)
|
@@ -140,11 +140,11 @@ class RelationalModelTransformer:
|
|
140
140
|
"""
|
141
141
|
|
142
142
|
schemaview: SchemaView = None
|
143
|
-
# dialect: str =
|
144
|
-
skip_tree_root: bool =
|
145
|
-
skip_abstract: bool =
|
146
|
-
skip_mixins: bool =
|
147
|
-
join_table_separator: str =
|
143
|
+
# dialect: str = 'sqlite'
|
144
|
+
skip_tree_root: bool = False
|
145
|
+
skip_abstract: bool = True
|
146
|
+
skip_mixins: bool = True
|
147
|
+
join_table_separator: str = "_"
|
148
148
|
foreign_key_policy: ForeignKeyPolicy = field(default_factory=lambda: ForeignKeyPolicy.INJECT_FK_FOR_NESTED)
|
149
149
|
|
150
150
|
def transform(self, tgt_schema_name: str = None, top_class: ClassDefinitionName = None) -> TransformationResult:
|
linkml/utils/converter.py
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
import logging
|
2
2
|
import os
|
3
3
|
import sys
|
4
|
-
from typing import List
|
5
4
|
|
6
5
|
import click
|
7
6
|
from linkml_runtime.linkml_model import Prefix
|
@@ -77,7 +76,7 @@ def cli(
|
|
77
76
|
output=None,
|
78
77
|
input_format=None,
|
79
78
|
output_format=None,
|
80
|
-
prefix
|
79
|
+
prefix=None,
|
81
80
|
target_class_from_path=None,
|
82
81
|
schema=None,
|
83
82
|
validate=None,
|
@@ -98,6 +97,8 @@ def cli(
|
|
98
97
|
|
99
98
|
For more information, see https://linkml.io/linkml/data/index.html
|
100
99
|
"""
|
100
|
+
if prefix is None:
|
101
|
+
prefix = []
|
101
102
|
if module is None:
|
102
103
|
if schema is None:
|
103
104
|
raise Exception("must pass one of module OR schema")
|
linkml/utils/generator.py
CHANGED
@@ -124,7 +124,7 @@ class Generator(metaclass=abc.ABCMeta):
|
|
124
124
|
|
125
125
|
file_extension: ClassVar[str] = None
|
126
126
|
|
127
|
-
metadata: bool =
|
127
|
+
metadata: bool = True
|
128
128
|
"""True means include date, generator, etc. information in source header if appropriate"""
|
129
129
|
|
130
130
|
useuris: Optional[bool] = None
|
@@ -133,7 +133,7 @@ class Generator(metaclass=abc.ABCMeta):
|
|
133
133
|
log_level: int = DEFAULT_LOG_LEVEL_INT
|
134
134
|
"""Logging level, 0 is minimum"""
|
135
135
|
|
136
|
-
mergeimports: Optional[bool] =
|
136
|
+
mergeimports: Optional[bool] = True
|
137
137
|
"""True means merge non-linkml sources into importing package. False means separate packages"""
|
138
138
|
|
139
139
|
source_file_date: Optional[str] = None
|
@@ -465,7 +465,7 @@ class Generator(metaclass=abc.ABCMeta):
|
|
465
465
|
"""Return an ordered list of ancestor names for the supplied slot or class
|
466
466
|
|
467
467
|
@param element: Slot or class name or definition
|
468
|
-
@return: Ordered list of
|
468
|
+
@return: Ordered list of ancestor names
|
469
469
|
"""
|
470
470
|
return [element.name] + ([] if element.is_a is None else self.ancestors(self.parent(element)))
|
471
471
|
|
@@ -620,7 +620,7 @@ class Generator(metaclass=abc.ABCMeta):
|
|
620
620
|
|
621
621
|
def slot_range_path(self, slot_or_name: Union[str, SlotDefinition]) -> List[str]:
|
622
622
|
"""
|
623
|
-
Return
|
623
|
+
Return an ordered list of slot ranges from distal to proximal
|
624
624
|
|
625
625
|
:param slot_or_name: slot whose range is being typed
|
626
626
|
:return: ordered list of types from base type forward
|
linkml/utils/sqlutils.py
CHANGED
@@ -18,10 +18,11 @@ from linkml_runtime.utils.formatutils import underscore
|
|
18
18
|
from linkml_runtime.utils.introspection import package_schemaview
|
19
19
|
from linkml_runtime.utils.yamlutils import YAMLRoot
|
20
20
|
from pydantic import BaseModel
|
21
|
-
from sqlalchemy import
|
21
|
+
from sqlalchemy import create_engine
|
22
22
|
from sqlalchemy.engine import Engine
|
23
23
|
from sqlalchemy.ext.associationproxy import _AssociationCollection
|
24
24
|
from sqlalchemy.orm import sessionmaker
|
25
|
+
from sqlalchemy.pool import StaticPool
|
25
26
|
|
26
27
|
from linkml._version import __version__
|
27
28
|
from linkml.generators.pythongen import PythonGenerator
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: linkml
|
3
|
-
Version: 1.7.
|
3
|
+
Version: 1.7.4
|
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
|
@@ -22,7 +22,6 @@ Classifier: Programming Language :: Python :: 3.11
|
|
22
22
|
Classifier: Programming Language :: Python :: 3.8
|
23
23
|
Classifier: Programming Language :: Python :: 3.9
|
24
24
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
25
|
-
Provides-Extra: docs
|
26
25
|
Requires-Dist: antlr4-python3-runtime (>=4.9.0,<4.10)
|
27
26
|
Requires-Dist: click (>=7.0)
|
28
27
|
Requires-Dist: graphviz (>=0.10.1)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
linkml/__init__.py,sha256=
|
1
|
+
linkml/__init__.py,sha256=jwtUdhwmquj-0fCR6VwPH5_RfSpuG94hLWdre_W_FD4,3327
|
2
2
|
linkml/_version.py,sha256=udxv6OEqcE89DTVMYPtetXYg8IA8Sgc6yW26-6f8C3M,310
|
3
3
|
linkml/generators/PythonGenNotes.md,sha256=wb9BPLLhF6378OKLbwSBAwmniLpwrcT5_bbfCHfLme8,51006
|
4
4
|
linkml/generators/README.md,sha256=RMzT8EblC_GEdPy5WyfXHDBXlFI6k6mz3Cx2sdpcyWI,4438
|
@@ -16,33 +16,33 @@ linkml/generators/docgen/schema.md.jinja2,sha256=xlENfnzNRYgPT_0tdqNFxgklVM4Qf5B
|
|
16
16
|
linkml/generators/docgen/slot.md.jinja2,sha256=XVd0M4gKx9Q2fONcsUGBRj_bJivyN4P9jhj9IO496jQ,2817
|
17
17
|
linkml/generators/docgen/subset.md.jinja2,sha256=fTNIpAkml5RKFbbtLore3IAzFN1cISVsyL1ru2-Z4oA,2665
|
18
18
|
linkml/generators/docgen/type.md.jinja2,sha256=QmCMJZrFwP33eHkggBVtypbyrxTb-XZn9vHOYojVaYk,635
|
19
|
-
linkml/generators/docgen.py,sha256=
|
19
|
+
linkml/generators/docgen.py,sha256=265bWbIkH4z5Cc7RLmyMTi2YR6hXAPBEdo5t1dqALYk,33476
|
20
20
|
linkml/generators/dotgen.py,sha256=kzp1EYtF8bBqOuycJokDmsvHVDVkkW-J-6-MiUhwJH0,5013
|
21
|
-
linkml/generators/erdiagramgen.py,sha256=
|
22
|
-
linkml/generators/excelgen.py,sha256=
|
23
|
-
linkml/generators/golanggen.py,sha256=
|
21
|
+
linkml/generators/erdiagramgen.py,sha256=xCnxIldDYZ7xnQ-vLaXlH3FH8MH3cxu-E0j8it-lyJE,10153
|
22
|
+
linkml/generators/excelgen.py,sha256=VsvdaGAOcNZ3qPLq-3lycGe_atG6B3hid3WMCV99XZI,7668
|
23
|
+
linkml/generators/golanggen.py,sha256=lGP0wDU6QwLjfiUDpTLFvJsnaDGKxOyQLizlz2lGfPc,5775
|
24
24
|
linkml/generators/golrgen.py,sha256=OUBYRWicP7G7lrPu1VrxumbVpx3CBJal-o2imyeP05M,3437
|
25
25
|
linkml/generators/graphqlgen.py,sha256=6qZpI0rwg3ypsv_KrLVzXgdsJfR8LNPqgMwaRwzwnDs,2151
|
26
26
|
linkml/generators/javagen/example_template.java.jinja2,sha256=ec4CVTv_0zS7V5Y-1E6H4lRraya10gfX7BEMBlu38X4,444
|
27
27
|
linkml/generators/javagen/java_record_template.jinja2,sha256=OQZffLSy_xR3FIhQMltvrYyVeut7l2Q-tzK7AOiVmWs,1729
|
28
|
-
linkml/generators/javagen.py,sha256=
|
29
|
-
linkml/generators/jsonldcontextgen.py,sha256=
|
28
|
+
linkml/generators/javagen.py,sha256=KxwupMztyCRHcSsbtTnOovuj1WamsAny0mxbYWvTiDs,5324
|
29
|
+
linkml/generators/jsonldcontextgen.py,sha256=RQFBgNjWMdnjf0j8S_k1lyMauI9Ia5Ao0ruuFjLwhdA,7886
|
30
30
|
linkml/generators/jsonldgen.py,sha256=BtPXLYbnXTXorUcoe6y4DL0xa-TPjHOvtXdhv-ppW3I,7656
|
31
|
-
linkml/generators/jsonschemagen.py,sha256=
|
31
|
+
linkml/generators/jsonschemagen.py,sha256=XOsYIrpA6BwtAcfh8GNGduIjpuPAsF2f_PQ1mghj_WU,27455
|
32
32
|
linkml/generators/legacy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
33
|
-
linkml/generators/linkmlgen.py,sha256=
|
34
|
-
linkml/generators/markdowngen.py,sha256=
|
33
|
+
linkml/generators/linkmlgen.py,sha256=1_Kt_7rD42WvCTjq0yaq1Of7jEDZR_uusyzAT-qWMHk,3471
|
34
|
+
linkml/generators/markdowngen.py,sha256=dDMyusNXLvM2raIW-_vd2AQBhDHn0P_rMeDq3QFuacs,32926
|
35
35
|
linkml/generators/namespacegen.py,sha256=vVcIyM0zlKd7XRvtdzwTwHjG4Pg49801gy4FUmjJlqQ,6450
|
36
|
-
linkml/generators/oocodegen.py,sha256=
|
37
|
-
linkml/generators/owlgen.py,sha256=
|
38
|
-
linkml/generators/plantumlgen.py,sha256=
|
36
|
+
linkml/generators/oocodegen.py,sha256=cWjQSFJmhrnRRsHqOa9dOks0k-QogrP97m5lEkLRxdY,7738
|
37
|
+
linkml/generators/owlgen.py,sha256=Oo3ACE5BXRbQU1p4XhyYhP4JQgAW4w2nmNZ591FWkVM,53142
|
38
|
+
linkml/generators/plantumlgen.py,sha256=tk-_XJtBA5EqYJSUTc3bdMdCwqlC-rc9VYO9A2V_HpM,14895
|
39
39
|
linkml/generators/prefixmapgen.py,sha256=-IGKre3rRxCdNs0BP3Oo7Wzv8edeUQXkjjwN7od3qjU,4627
|
40
40
|
linkml/generators/projectgen.py,sha256=g3JR2oXPM_QXhWUGukP9ts1P7tqxIeABaRdv130gbo4,9578
|
41
|
-
linkml/generators/protogen.py,sha256=
|
42
|
-
linkml/generators/pydanticgen.py,sha256=
|
43
|
-
linkml/generators/pythongen.py,sha256=
|
44
|
-
linkml/generators/rdfgen.py,sha256=
|
45
|
-
linkml/generators/shaclgen.py,sha256=
|
41
|
+
linkml/generators/protogen.py,sha256=5UxThsFDVyDTzzTDh77Z0anx4-tLgz8kQQ-e7ywIqwI,2290
|
42
|
+
linkml/generators/pydanticgen.py,sha256=syiqMBBiGWwT_ZD5KLstGGMdh4d2fNQ3o2qZRfQLtqs,30539
|
43
|
+
linkml/generators/pythongen.py,sha256=JCFuGishyoBx75sGFL2zWlHj-qsyBizyZIc_jqpzdTU,52429
|
44
|
+
linkml/generators/rdfgen.py,sha256=L6F08iDUqVemXXrRbJmcOxvJTt14hR0oo8WLoqf4npw,2656
|
45
|
+
linkml/generators/shaclgen.py,sha256=jxQjQ-iT2SYpLso3aIyBtqaVVPvZflrVzWaNA7nxQa0,8686
|
46
46
|
linkml/generators/shexgen.py,sha256=VVlSrGCighLf0YSStK_VeMnCK0imAL4BJNDRVTpUFkw,9859
|
47
47
|
linkml/generators/sparqlgen.py,sha256=xIT4abjYTjPvAjczZ2mkqfap5z8-AImK_jaCvgZyRGs,6120
|
48
48
|
linkml/generators/sqlalchemy/__init__.py,sha256=mb9AC1rIFkSiNZhhG0TAk45ol9PjS1XvsrvCjgfVUpQ,249
|
@@ -50,14 +50,14 @@ linkml/generators/sqlalchemy/sqlalchemy_declarative_template.py,sha256=X_Ws1NUBi
|
|
50
50
|
linkml/generators/sqlalchemy/sqlalchemy_imperative_template.py,sha256=u4ZpponG1h6XROrOHGOf_0H2e6xL1_s8twAOA-gx94A,1622
|
51
51
|
linkml/generators/sqlalchemygen.py,sha256=XjvyCYr9h0EsFaKy4y-lMXunYw4dnk9vSpwlPjZK8gU,9186
|
52
52
|
linkml/generators/sqlddlgen.py,sha256=ickgQovcWunsBRMpHBDTGxGlZKU9kfExb-XQPBS6qPs,18437
|
53
|
-
linkml/generators/sqltablegen.py,sha256=
|
53
|
+
linkml/generators/sqltablegen.py,sha256=yaGdnl1kbovf9b4Qi6dx5VpRlBfy4PX1dtHsXtvg6qQ,11494
|
54
54
|
linkml/generators/sssomgen.py,sha256=yur3q7so9uwIELWZaZRzkJwNbz_ppBL7IQki9XLIM3k,6879
|
55
55
|
linkml/generators/string_template.md,sha256=kRcfic6entgIaJdpSg6GF3jcjC9wbKsCVM6wVT2qipc,1788
|
56
|
-
linkml/generators/summarygen.py,sha256=
|
56
|
+
linkml/generators/summarygen.py,sha256=crH_Hegd4Z7G7byUtgyXoP2XtG_4iblzZsu_pE4aQrk,2887
|
57
57
|
linkml/generators/terminusdbgen.py,sha256=_po9KnOz6Uoctb9Y4fJuU_voW4uA5pepvZTmHLQ2_DE,4470
|
58
58
|
linkml/generators/typescriptgen.py,sha256=VDJ-97jk7zqYx6XlnIcxZmY8mCv885T7D2brKJHaihI,8565
|
59
|
-
linkml/generators/yamlgen.py,sha256=
|
60
|
-
linkml/generators/yumlgen.py,sha256=
|
59
|
+
linkml/generators/yamlgen.py,sha256=PKssrY5VHpdbOU54PVwLVLbsRqHwoOgOImlMb6DVcXM,1614
|
60
|
+
linkml/generators/yumlgen.py,sha256=lnHcLY0VwlU_i8QporSmwR62CXSV4aOCXKxRDw5AxUk,12196
|
61
61
|
linkml/linter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
62
62
|
linkml/linter/cli.py,sha256=BpQiETcRfQlZsI1-JALyU9wXPgPOm_KE_n11p0iJaC8,4494
|
63
63
|
linkml/linter/config/datamodel/.linkmllint.yaml,sha256=40rNhcL35FXQSjlVBMWnyPgplwEUqFT_sJmeZqUzxMw,292
|
@@ -79,15 +79,15 @@ linkml/reporting/model.py,sha256=-10yNfk8wuRC48ZI-akrWvtlJ9a6RYWET2TzlZV3XXo,862
|
|
79
79
|
linkml/transformers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
80
80
|
linkml/transformers/logical_model_transformer.py,sha256=1ZSB-HBoI0E2X2NV9TSgbTghp2GO4HQA27JaK0XiPBE,26510
|
81
81
|
linkml/transformers/model_transformer.py,sha256=tK_MpRDI-q2qfe8KHT6qJHP8ZruKjYx1FcM-Fnjse-E,678
|
82
|
-
linkml/transformers/relmodel_transformer.py,sha256=
|
82
|
+
linkml/transformers/relmodel_transformer.py,sha256=STQ4ALoJpPKW5-soTVcErHof_1kEDJEEZ1EJrzneql4,17680
|
83
83
|
linkml/transformers/schema_renamer.py,sha256=Cr18TyktX64b5iFh5V6R_ILPVzXjbDYVDDZQyqFiAv8,5271
|
84
84
|
linkml/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
85
85
|
linkml/utils/cli_utils.py,sha256=MdVbox1qr6kDUHUCWAmFhV-D6ebs_nn8vVwB8KDQfew,742
|
86
|
-
linkml/utils/converter.py,sha256=
|
86
|
+
linkml/utils/converter.py,sha256=snAF-pgZBSi4ANiaKXxqtZk5w3qonJjTfI4X5OVLmUE,6283
|
87
87
|
linkml/utils/datautils.py,sha256=QlbzwXykh5Fphfe5KxPo6_ekXfniLbHiEGJtLWjUrvY,3742
|
88
88
|
linkml/utils/datavalidator.py,sha256=kBdWaVi8IZT1bOwEJgJYx-wZAb_PTBObB9nHpYORfKA,472
|
89
89
|
linkml/utils/execute_tutorial.py,sha256=T4kHTSyz3ItJGEUZxVjR-3yLVKnOr5Ix4NMGE47-IuE,6912
|
90
|
-
linkml/utils/generator.py,sha256=
|
90
|
+
linkml/utils/generator.py,sha256=YB2HXb9_aJx284xHnVq-glVgTtvWnBaE-yGCJXZ6nFM,38207
|
91
91
|
linkml/utils/helpers.py,sha256=yR8n4zFA5wPcYC7xzRuNF3wO16vG80v6j7DM3qTNmIc,447
|
92
92
|
linkml/utils/ifabsent_functions.py,sha256=C2mllfh7aIZgVqqJjN5nydurVUk0hV62Wy7uVMYfPDg,5801
|
93
93
|
linkml/utils/logictools.py,sha256=GSmBiobC49TcQjE08RtXEE3JwJEOV7eEREio25uJiFs,21184
|
@@ -97,7 +97,7 @@ linkml/utils/schema_builder.py,sha256=WLSit3J4lTifaFLLWTwjqIRiTru1pqvTIUuC1TrxS6
|
|
97
97
|
linkml/utils/schema_fixer.py,sha256=ajsxpwD4yMjDk1iDtoKJTsa34SIqGshWxlnSNXVZ52w,16745
|
98
98
|
linkml/utils/schemaloader.py,sha256=bBSTqimMDTFH2FcKtRz99dKNJzV_myPsZSkIFp_6-A0,46421
|
99
99
|
linkml/utils/schemasynopsis.py,sha256=jKf5ZJxpNgSZ5x9oAcdGO3m1IwzcIU2Ni1ugm0SQq1g,18447
|
100
|
-
linkml/utils/sqlutils.py,sha256=
|
100
|
+
linkml/utils/sqlutils.py,sha256=FPUA6Smn3FogE97TZZZtGNhaMK9kpC-qJ5Ylo1mg9l8,16618
|
101
101
|
linkml/utils/typereferences.py,sha256=8Yfuz9-HAwOPoJLbIcO_sY9zf32hvPRzGeSOzECfMWA,2232
|
102
102
|
linkml/utils/validation.py,sha256=eLw1-d8x3N1V14bSc6H_mJJXo59ryKvvUIBcOJ1dVMw,1438
|
103
103
|
linkml/validator/__init__.py,sha256=djKc1IyxGKvVYaX5jSxsqjfcEYaw3rFgKbFJxI2wDNk,5002
|
@@ -125,8 +125,8 @@ linkml/workspaces/datamodel/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMp
|
|
125
125
|
linkml/workspaces/datamodel/workspaces.py,sha256=4HdkqweGNfMPqnB1_Onc9DcTfkhoagTRcqruh08nRoI,14905
|
126
126
|
linkml/workspaces/datamodel/workspaces.yaml,sha256=EjVrwPpeRZqJRjuGyyDRxxFzuv55SiLIXPBRUG6HStU,4233
|
127
127
|
linkml/workspaces/example_runner.py,sha256=OmC_yZLIb4KXGQrstBVZL0UAQ9ZAaraguQF0RSf-snk,11611
|
128
|
-
linkml-1.7.
|
129
|
-
linkml-1.7.
|
130
|
-
linkml-1.7.
|
131
|
-
linkml-1.7.
|
132
|
-
linkml-1.7.
|
128
|
+
linkml-1.7.4.dist-info/METADATA,sha256=-EvbTFKzOQZ3HPP2-hd7LmWlELMPIF6ZUWqvGnPGoGs,3463
|
129
|
+
linkml-1.7.4.dist-info/WHEEL,sha256=vVCvjcmxuUltf8cYhJ0sJMRDLr1XsPuxEId8YDzbyCY,88
|
130
|
+
linkml-1.7.4.dist-info/LICENSE,sha256=kORMoywK6j9_iy0UvLR-a80P1Rvc9AOM4gsKlUNZABg,535
|
131
|
+
linkml-1.7.4.dist-info/entry_points.txt,sha256=za8r49Z5gcz3rAYTZLbxw5EPZr1rGuxSe1uiRUpf8R0,2143
|
132
|
+
linkml-1.7.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|