linkml 1.7.8__py3-none-any.whl → 1.7.9__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.
@@ -30,7 +30,7 @@ from linkml_runtime.linkml_model.meta import (
30
30
  )
31
31
  from linkml_runtime.utils.formatutils import camelcase, underscore
32
32
  from linkml_runtime.utils.introspection import package_schemaview
33
- from rdflib import OWL, RDF, XSD, BNode, Graph, Literal, URIRef
33
+ from rdflib import DCTERMS, OWL, RDF, XSD, BNode, Graph, Literal, URIRef
34
34
  from rdflib.collection import Collection
35
35
  from rdflib.namespace import RDFS, SKOS
36
36
  from rdflib.plugin import Parser as rdflib_Parser
@@ -147,6 +147,8 @@ class OwlSchemaGenerator(Generator):
147
147
  mixins_as_expressions: bool = None
148
148
  """EXPERIMENTAL: If True, use OWL existential restrictions to represent mixins"""
149
149
 
150
+ default_permissible_value_type: Union[str, URIRef] = field(default_factory=lambda: OWL.Class)
151
+
150
152
  slot_is_literal_map: Mapping[str, Set[bool]] = field(default_factory=lambda: defaultdict(set))
151
153
  """DEPRECATED: use node_owltypes"""
152
154
 
@@ -830,6 +832,28 @@ class OwlSchemaGenerator(Generator):
830
832
  if ixn:
831
833
  self.graph.add((type_uri, OWL.equivalentClass, ixn))
832
834
 
835
+ def _get_metatype(
836
+ self, element: Union[Definition, PermissibleValue], default_value: Optional[Union[str, URIRef]] = None
837
+ ) -> Optional[URIRef]:
838
+ impls = []
839
+ if isinstance(element, Definition):
840
+ impls.extend(element.implements)
841
+ if isinstance(element, PermissibleValue):
842
+ if "implements" in element.annotations:
843
+ ann = element.annotations["implements"]
844
+ v = ann.value
845
+ if not isinstance(v, list):
846
+ v = [v]
847
+ impls.extend(v)
848
+ for impl in impls:
849
+ if impl.startswith("owl:"):
850
+ return OWL[impl.split(":")[1]]
851
+ if impl.startswith("rdfs:"):
852
+ return RDFS[impl.split(":")[1]]
853
+ if isinstance(default_value, str):
854
+ return URIRef(default_value)
855
+ return default_value
856
+
833
857
  def add_enum(self, e: EnumDefinition) -> None:
834
858
  g = self.graph
835
859
  enum_uri = self._enum_uri(e.name)
@@ -856,26 +880,36 @@ class OwlSchemaGenerator(Generator):
856
880
  )
857
881
  )
858
882
  pv_uris = []
883
+ owl_types = []
884
+ enum_owl_type = self._get_metatype(e, self.default_permissible_value_type)
885
+
859
886
  for pv in e.permissible_values.values():
860
- pv_uri = self._permissible_value_uri(pv, enum_uri, e)
861
- pv_uris.append(pv_uri)
862
- if pv_uri:
863
- g.add((pv_uri, RDF.type, OWL.Class))
864
- g.add((pv_uri, RDFS.label, Literal(pv.text)))
865
- # TODO: make this configurable
866
- g.add(
867
- (
868
- enum_uri,
869
- self.metamodel.namespaces[METAMODEL_NAMESPACE_NAME]["permissible_values"],
870
- pv_uri,
871
- )
887
+ pv_owl_type = self._get_metatype(pv, enum_owl_type)
888
+ owl_types.append(pv_owl_type)
889
+ if pv_owl_type == RDFS.Literal:
890
+ pv_node = Literal(pv.text)
891
+ if pv.meaning:
892
+ logging.warning(f"Meaning on literal {pv.text} in {e.name} is ignored")
893
+ else:
894
+ pv_node = self._permissible_value_uri(pv, enum_uri, e)
895
+ pv_uris.append(pv_node)
896
+ g.add(
897
+ (
898
+ enum_uri,
899
+ self.metamodel.namespaces[METAMODEL_NAMESPACE_NAME]["permissible_values"],
900
+ pv_node,
872
901
  )
902
+ )
903
+ if not isinstance(pv_node, Literal):
904
+ g.add((pv_node, RDF.type, pv_owl_type))
905
+ g.add((pv_node, RDFS.label, Literal(pv.text)))
906
+ # TODO: make this configurable
873
907
  # self._add_element_properties(pv_uri, pv)
874
908
  if self.metaclasses:
875
- g.add((pv_uri, RDF.type, enum_uri))
909
+ g.add((pv_node, RDF.type, enum_uri))
876
910
  has_parent = False
877
911
  if pv.is_a:
878
- self.graph.add((pv_uri, RDFS.subClassOf, self._permissible_value_uri(pv.is_a, enum_uri, e)))
912
+ self.graph.add((pv_node, RDFS.subClassOf, self._permissible_value_uri(pv.is_a, enum_uri, e)))
879
913
  has_parent = True
880
914
  for mixin in sorted(pv.mixins):
881
915
  parent = self._permissible_value_uri(mixin, enum_uri, e)
@@ -885,13 +919,33 @@ class OwlSchemaGenerator(Generator):
885
919
  has_parent = True
886
920
  self.graph.add((enum_uri, RDFS.subClassOf, parent))
887
921
  if not has_parent and self.add_root_classes:
888
- self.graph.add((pv_uri, RDFS.subClassOf, URIRef(PermissibleValue.class_class_uri)))
922
+ self.graph.add((pv_node, RDFS.subClassOf, URIRef(PermissibleValue.class_class_uri)))
889
923
  if all([pv is not None for pv in pv_uris]):
890
- self._union_of(pv_uris, node=enum_uri)
891
- for pv_uri in pv_uris:
924
+ all_is_class = all([owl_type == OWL.Class for owl_type in owl_types])
925
+ all_is_individual = all([owl_type == OWL.NamedIndividual for owl_type in owl_types])
926
+ all_is_literal = all([owl_type == RDFS.Literal for owl_type in owl_types])
927
+ sub_pred = DCTERMS.isPartOf
928
+ combo_pred = None
929
+ if all_is_class or all_is_individual or all_is_literal:
930
+ if all_is_class:
931
+ combo_pred = OWL.unionOf
932
+ # self._union_of(pv_uris, node=enum_uri)
933
+ sub_pred = RDFS.subClassOf
934
+ elif all_is_individual:
935
+ combo_pred = OWL.oneOf
936
+ # self._object_one_of(pv_uris, node=enum_uri)
937
+ sub_pred = RDF.type
938
+ elif all_is_literal:
939
+ combo_pred = OWL.oneOf
940
+ # self._object_one_of(pv_uris, node=enum_uri)
941
+ sub_pred = RDF.type
942
+ if combo_pred:
943
+ self._boolean_expression(pv_uris, combo_pred, enum_uri, owl_types=set(owl_types))
944
+ for pv_node in pv_uris:
892
945
  # this would normally be entailed, but we assert here so it is visible
893
946
  # without reasoning
894
- g.add((pv_uri, RDFS.subClassOf, enum_uri))
947
+ if not isinstance(pv_node, Literal):
948
+ g.add((pv_node, sub_pred, enum_uri))
895
949
 
896
950
  def _add_rule(self, subject: Union[URIRef, BNode], rule: ClassRule, cls: ClassDefinition):
897
951
  if not self.use_swrl:
@@ -1030,6 +1084,9 @@ class OwlSchemaGenerator(Generator):
1030
1084
  def _union_of(self, exprs: List[Union[BNode, URIRef]], **kwargs) -> Optional[Union[BNode, URIRef]]:
1031
1085
  return self._boolean_expression(exprs, OWL.unionOf, **kwargs)
1032
1086
 
1087
+ def _object_one_of(self, exprs: List[Union[BNode, URIRef]], **kwargs) -> Optional[Union[BNode, URIRef]]:
1088
+ return self._boolean_expression(exprs, OWL.oneOf, **kwargs)
1089
+
1033
1090
  def _exactly_one_of(self, exprs: List[Union[BNode, URIRef]]) -> Optional[Union[BNode, URIRef]]:
1034
1091
  if not exprs:
1035
1092
  raise ValueError("Must pass at least one")
@@ -1165,7 +1222,7 @@ class OwlSchemaGenerator(Generator):
1165
1222
  return URIRef(expanded)
1166
1223
 
1167
1224
  def _permissible_value_uri(
1168
- self, pv: Union[str, EnumDefinition], enum_uri: str, enum_def: EnumDefinition = None
1225
+ self, pv: Union[str, PermissibleValue], enum_uri: str, enum_def: EnumDefinition = None
1169
1226
  ) -> URIRef:
1170
1227
  if isinstance(pv, str):
1171
1228
  pv_name = pv
@@ -1182,6 +1239,10 @@ class OwlSchemaGenerator(Generator):
1182
1239
 
1183
1240
  def slot_owl_type(self, slot: SlotDefinition) -> URIRef:
1184
1241
  sv = self.schemaview
1242
+ if slot.implements:
1243
+ for t in ["owl:AnnotationProperty", "owl:ObjectProperty", "owl:DatatypeProperty"]:
1244
+ if t in slot.implements:
1245
+ return OWL[t.replace("owl:", "")]
1185
1246
  if slot.range is None:
1186
1247
  range = self.schemaview.schema.default_range
1187
1248
  else:
@@ -1267,6 +1328,12 @@ class OwlSchemaGenerator(Generator):
1267
1328
  show_default=True,
1268
1329
  help="Use model URIs rather than class/slot URIs",
1269
1330
  )
1331
+ @click.option(
1332
+ "--default-permissible-value-type",
1333
+ default=str(OWL.Class),
1334
+ show_default=True,
1335
+ help="Default OWL type for permissible values",
1336
+ )
1270
1337
  @click.version_option(__version__, "-V", "--version")
1271
1338
  def cli(yamlfile, metadata_profile: str, **kwargs):
1272
1339
  """Generate an OWL representation of a LinkML model
@@ -57,7 +57,7 @@ class PlantumlGenerator(Generator):
57
57
  directory: Optional[str] = None,
58
58
  load_image: bool = True,
59
59
  **_,
60
- ) -> None:
60
+ ) -> Optional[str]:
61
61
  if directory:
62
62
  os.makedirs(directory, exist_ok=True)
63
63
  if classes is not None:
@@ -110,10 +110,13 @@ class PlantumlGenerator(Generator):
110
110
  else:
111
111
  self.logger.error(f"{resp.reason} accessing {plantuml_url}")
112
112
  else:
113
- print(
114
- "@startuml\nskinparam nodesep 10\n" + "\n".join(dedup_plantumlclassdef),
115
- end="\n@enduml\n",
113
+ out = (
114
+ "@startuml\n"
115
+ "skinparam nodesep 10\n"
116
+ "hide circle\n"
117
+ "hide empty members\n" + "\n".join(dedup_plantumlclassdef) + "\n@enduml\n"
116
118
  )
119
+ return out
117
120
 
118
121
  def add_class(self, cn: ClassDefinitionName) -> str:
119
122
  """Define the class only if
@@ -130,13 +133,11 @@ class PlantumlGenerator(Generator):
130
133
  if True or cn in slot.domain_of:
131
134
  mod = self.prop_modifier(cls, slot)
132
135
  slot_defs.append(
133
- ' {field} "'
136
+ " {field} "
134
137
  + underscore(self.aliased_slot_name(slot))
135
- + '"'
136
138
  + mod
137
- + ': "'
139
+ + ": "
138
140
  + underscore(slot.range)
139
- + '"'
140
141
  + self.cardinality(slot)
141
142
  )
142
143
  self.class_generated.add(cn)
@@ -3,7 +3,6 @@ Generate JSON-LD contexts
3
3
 
4
4
  """
5
5
 
6
- import csv
7
6
  import os
8
7
  from dataclasses import dataclass, field
9
8
  from typing import Dict, Optional, Set, Union
@@ -59,7 +58,7 @@ class PrefixGenerator(Generator):
59
58
  if self.default_ns:
60
59
  self.emit_prefixes.add(self.default_ns)
61
60
 
62
- def end_schema(self, base: Optional[Union[str, Namespace]] = None, output: Optional[str] = None, **_) -> None:
61
+ def end_schema(self, base: Optional[Union[str, Namespace]] = None, output: Optional[str] = None, **_) -> str:
63
62
  context = JsonObj()
64
63
  if base:
65
64
  base = str(base)
@@ -74,31 +73,24 @@ class PrefixGenerator(Generator):
74
73
  for k, v in self.slot_class_maps.items():
75
74
  context[k] = v
76
75
 
77
- if output:
78
- output_ext = output.split(".")[-1]
76
+ if self.format == "tsv":
77
+ mapping: Dict = {} # prefix to IRI mapping
78
+ for prefix in sorted(self.emit_prefixes):
79
+ mapping[prefix] = self.namespaces[prefix]
79
80
 
80
- if output_ext == "tsv":
81
- mapping: Dict = {}
82
- for prefix in sorted(self.emit_prefixes):
83
- mapping[prefix] = self.namespaces[prefix]
81
+ items = []
82
+ for key, value in mapping.items():
83
+ items.append("\t".join([key, value]))
84
+ out = "\n".join(items)
84
85
 
85
- with open(output, "w", encoding="UTF-8") as outf:
86
- writer = csv.writer(outf, delimiter="\t")
87
- for key, value in mapping.items():
88
- writer.writerow([key, value])
89
- else:
90
- with open(output, "w", encoding="UTF-8") as outf:
91
- outf.write(as_json(context))
92
86
  else:
93
- if self.format == "tsv":
94
- mapping: Dict = {} # prefix to IRI mapping
95
- for prefix in sorted(self.emit_prefixes):
96
- mapping[prefix] = self.namespaces[prefix]
87
+ out = str(as_json(context))
97
88
 
98
- for key, value in mapping.items():
99
- print(key, value, sep="\t")
100
- else:
101
- print(as_json(context))
89
+ if output:
90
+ with open(output, "w", encoding="UTF-8") as outf:
91
+ outf.write(out)
92
+
93
+ return out
102
94
 
103
95
  def visit_class(self, cls: ClassDefinition) -> bool:
104
96
  class_def = {}
@@ -1,5 +1,6 @@
1
1
  import os
2
2
  from dataclasses import dataclass
3
+ from typing import Optional
3
4
 
4
5
  import click
5
6
  from linkml_runtime.linkml_model.meta import ClassDefinition, SlotDefinition
@@ -26,32 +27,36 @@ class ProtoGenerator(Generator):
26
27
  # ObjectVars
27
28
  relative_slot_num: int = 0
28
29
 
29
- def __post_init__(self):
30
- super().__post_init__()
31
- self.generate_header()
30
+ def visit_schema(self, **kwargs) -> Optional[str]:
31
+ return self.generate_header()
32
32
 
33
- def generate_header(self):
34
- print(' syntax="proto3";')
35
- print(" package")
36
- print(f"// metamodel_version: {self.schema.metamodel_version}")
33
+ def generate_header(self) -> str:
34
+ items = []
35
+ items.append(' syntax="proto3";')
36
+ items.append(" package")
37
+ items.append(f"// metamodel_version: {self.schema.metamodel_version}")
37
38
  if self.schema.version:
38
- print(f"// version: {self.schema.version}")
39
+ items.append(f"// version: {self.schema.version}")
40
+ out = "\n".join(items) + "\n"
41
+ return out
39
42
 
40
- def visit_class(self, cls: ClassDefinition) -> bool:
43
+ def visit_class(self, cls: ClassDefinition) -> Optional[str]:
41
44
  if cls.mixin or cls.abstract or not cls.slots:
42
- return False
45
+ return None
46
+ items = []
43
47
  if cls.description:
44
48
  for dline in cls.description.split("\n"):
45
- print(f"// {dline}")
46
- print(f"message {camelcase(cls.name)}")
47
- print(" {")
49
+ items.append(f"// {dline}")
50
+ items.append(f"message {camelcase(cls.name)}")
51
+ items.append(" {")
52
+ out = "\n".join(items)
48
53
  self.relative_slot_num = 0
49
- return True
54
+ return out
50
55
 
51
- def end_class(self, cls: ClassDefinition) -> None:
52
- print(" }")
56
+ def end_class(self, cls: ClassDefinition) -> str:
57
+ return "\n }\n"
53
58
 
54
- def visit_class_slot(self, cls: ClassDefinition, aliased_slot_name: str, slot: SlotDefinition) -> None:
59
+ def visit_class_slot(self, cls: ClassDefinition, aliased_slot_name: str, slot: SlotDefinition) -> str:
55
60
  qual = "repeated " if slot.multivalued else ""
56
61
  slotname = lcamelcase(aliased_slot_name)
57
62
  slot_range = camelcase(slot.range)
@@ -59,7 +64,7 @@ class ProtoGenerator(Generator):
59
64
  # numbering of slots is important in the proto implementation
60
65
  # and should be determined by the rank param.
61
66
  slot.rank = 0
62
- print(f" {qual} {lcamelcase(slot_range)} {(slotname)} = {slot.rank}")
67
+ return f"\n {qual} {lcamelcase(slot_range)} {(slotname)} = {slot.rank}"
63
68
 
64
69
 
65
70
  @shared_arguments(ProtoGenerator)
@@ -50,9 +50,13 @@ from linkml.generators.pydanticgen.template import (
50
50
  PydanticModule,
51
51
  TemplateModel,
52
52
  )
53
+ from linkml.utils import deprecation_warning
53
54
  from linkml.utils.generator import shared_arguments
54
55
  from linkml.utils.ifabsent_functions import ifabsent_value_declaration
55
56
 
57
+ if int(PYDANTIC_VERSION[0]) == 1:
58
+ deprecation_warning("pydantic-v1")
59
+
56
60
 
57
61
  def _get_pyrange(t: TypeDefinition, sv: SchemaView) -> str:
58
62
  pyrange = t.repr if t is not None else None
@@ -215,6 +219,11 @@ class PydanticGenerator(OOCodeGenerator):
215
219
  genmeta: bool = False
216
220
  emit_metadata: bool = True
217
221
 
222
+ def __post_init__(self):
223
+ super().__post_init__()
224
+ if int(self.pydantic_version) == 1:
225
+ deprecation_warning("pydanticgen-v1")
226
+
218
227
  def compile_module(self, **kwargs) -> ModuleType:
219
228
  """
220
229
  Compiles generated python code to a module
@@ -185,11 +185,8 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
185
185
  # Slots
186
186
  {self.gen_slotdefs()}"""
187
187
 
188
- def end_schema(self, **_):
189
- print(
190
- re.sub(r" +\n", "\n", self.gen_schema().replace("\t", " ")).strip(" "),
191
- end="",
192
- )
188
+ def end_schema(self, **_) -> str:
189
+ return re.sub(r" +\n", "\n", self.gen_schema().replace("\t", " ")).strip(" ")
193
190
 
194
191
  def gen_imports(self) -> str:
195
192
  list_ents = [f"from {k} import {', '.join(v)}" for k, v in self.gen_import_list().items()]
@@ -47,7 +47,7 @@ class RDFGenerator(Generator):
47
47
  def _data(self, g: Graph) -> str:
48
48
  return g.serialize(format="turtle" if self.format == "ttl" else self.format)
49
49
 
50
- def end_schema(self, output: Optional[str] = None, context: str = None, **_) -> None:
50
+ def end_schema(self, output: Optional[str] = None, context: str = None, **_) -> str:
51
51
  gen = JSONLDGenerator(
52
52
  self.original_schema,
53
53
  format=JSONLDGenerator.valid_formats[0],
@@ -67,11 +67,12 @@ class RDFGenerator(Generator):
67
67
  base=str(self.namespaces._base),
68
68
  prefix=True,
69
69
  )
70
+ out = self._data(graph)
70
71
  if output:
71
72
  with open(output, "w", encoding="UTF-8") as outf:
72
- outf.write(self._data(graph))
73
- else:
74
- print(self._data(graph))
73
+ outf.write(out)
74
+
75
+ return out
75
76
 
76
77
 
77
78
  @shared_arguments(RDFGenerator)
@@ -36,10 +36,11 @@ class ShaclGenerator(Generator):
36
36
  super().__post_init__()
37
37
  self.generate_header()
38
38
 
39
- def generate_header(self):
40
- print(f"# metamodel_version: {self.schema.metamodel_version}")
39
+ def generate_header(self) -> str:
40
+ out = f"\n# metamodel_version: {self.schema.metamodel_version}"
41
41
  if self.schema.version:
42
- print(f"# version: {self.schema.version}")
42
+ out += f"\n# version: {self.schema.version}"
43
+ return out
43
44
 
44
45
  def serialize(self, **args) -> str:
45
46
  g = self.as_graph()
@@ -55,12 +55,12 @@ class ShExGenerator(Generator):
55
55
  self.namespaces.join(self.namespaces[METAMODEL_NAMESPACE_NAME], "")
56
56
  ) # URI for the metamodel
57
57
  self.base = Namespace(self.namespaces.join(self.namespaces._base, "")) # Base URI for what is being modeled
58
- self.generate_header()
59
58
 
60
- def generate_header(self):
61
- print(f"# metamodel_version: {self.schema.metamodel_version}")
59
+ def generate_header(self) -> str:
60
+ out = f"# metamodel_version: {self.schema.metamodel_version}\n"
62
61
  if self.schema.version:
63
- print(f"# version: {self.schema.version}")
62
+ out += f"# version: {self.schema.version}\n"
63
+ return out
64
64
 
65
65
  def visit_schema(self, **_):
66
66
  # Adjust the schema context to include the base model URI
@@ -80,6 +80,8 @@ class ShExGenerator(Generator):
80
80
  else:
81
81
  typeof_uri = self._class_or_type_uri(typ.typeof)
82
82
  self.shapes.append(Shape(id=model_uri, expression=typeof_uri))
83
+ if self.format != "json":
84
+ return self.generate_header()
83
85
 
84
86
  def visit_class(self, cls: ClassDefinition) -> bool:
85
87
  self.shape = Shape()
@@ -160,7 +162,7 @@ class ShExGenerator(Generator):
160
162
  else:
161
163
  constraint.valueExpr = self._class_or_type_uri(slot.range)
162
164
 
163
- def end_schema(self, output: Optional[str] = None, **_) -> None:
165
+ def end_schema(self, output: Optional[str] = None, **_) -> str:
164
166
  self.shex.shapes = self.shapes if self.shapes else [Shape()]
165
167
  shex = as_json_1(self.shex)
166
168
  if self.format == "rdf":
@@ -172,11 +174,11 @@ class ShExGenerator(Generator):
172
174
  g = Graph()
173
175
  self.namespaces.load_graph(g)
174
176
  shex = str(ShExC(self.shex, base=sfx(self.namespaces._base), namespaces=g))
177
+
175
178
  if output:
176
179
  with open(output, "w", encoding="UTF-8") as outf:
177
180
  outf.write(shex)
178
- else:
179
- print(shex)
181
+ return shex
180
182
 
181
183
  def _class_or_type_uri(
182
184
  self,
@@ -3,9 +3,9 @@
3
3
  """
4
4
 
5
5
  import os
6
- import sys
7
6
  from csv import DictWriter
8
7
  from dataclasses import dataclass
8
+ from io import StringIO
9
9
  from typing import Optional
10
10
 
11
11
  import click
@@ -28,9 +28,12 @@ class SummaryGenerator(Generator):
28
28
  slottab: Optional[DictWriter] = None
29
29
  dialect: str = "excel-tab"
30
30
 
31
+ _str_io: Optional[StringIO] = None
32
+
31
33
  def visit_schema(self, **_) -> None:
34
+ self._str_io = StringIO()
32
35
  self.classtab = DictWriter(
33
- sys.stdout,
36
+ self._str_io,
34
37
  [
35
38
  "Class Name",
36
39
  "Parent Class",
@@ -79,6 +82,9 @@ class SummaryGenerator(Generator):
79
82
  }
80
83
  )
81
84
 
85
+ def end_schema(self, **kwargs) -> Optional[str]:
86
+ return self._str_io.getvalue()
87
+
82
88
 
83
89
  @shared_arguments(SummaryGenerator)
84
90
  @click.version_option(__version__, "-V", "--version")
@@ -71,8 +71,8 @@ class TerminusdbGenerator(Generator):
71
71
  self.classes = []
72
72
  self.raw_additions = []
73
73
 
74
- def end_schema(self, **_) -> None:
75
- print(json.dumps(WQ().woql_and(*self.classes, *self.raw_additions).to_dict(), indent=2))
74
+ def end_schema(self, **_) -> str:
75
+ return json.dumps(WQ().woql_and(*self.classes, *self.raw_additions).to_dict(), indent=2)
76
76
 
77
77
  def visit_class(self, cls: ClassDefinition) -> bool:
78
78
  self.clswq = WQ().add_class(camelcase(cls.name)).label(camelcase(cls.name)).description(be(cls.description))
@@ -59,7 +59,7 @@ class YumlGenerator(Generator):
59
59
  diagram_name: Optional[str] = None,
60
60
  load_image: bool = True,
61
61
  **_,
62
- ) -> None:
62
+ ) -> Optional[str]:
63
63
  if directory:
64
64
  os.makedirs(directory, exist_ok=True)
65
65
  if classes is not None:
@@ -106,7 +106,7 @@ class YumlGenerator(Generator):
106
106
  else:
107
107
  self.logger.error(f"{resp.reason} accessing {url}: {resp!r}")
108
108
  else:
109
- print(str(YUML) + ",".join(yumlclassdef), end="")
109
+ return str(YUML) + ",".join(yumlclassdef)
110
110
 
111
111
  def class_box(self, cn: ClassDefinitionName) -> str:
112
112
  """Generate a box for the class. Populate its interior only if (a) it hasn't previously been generated and
linkml/utils/__init__.py CHANGED
@@ -0,0 +1,3 @@
1
+ from linkml.utils.deprecation import deprecation_warning
2
+
3
+ __all__ = ["deprecation_warning"]