linkml 1.5.5__py3-none-any.whl → 1.5.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. linkml/__init__.py +2 -6
  2. linkml/_version.py +1 -1
  3. linkml/generators/PythonGenNotes.md +4 -4
  4. linkml/generators/__init__.py +26 -5
  5. linkml/generators/common/type_designators.py +27 -22
  6. linkml/generators/csvgen.py +4 -10
  7. linkml/generators/docgen/class.md.jinja2 +7 -0
  8. linkml/generators/docgen/class_diagram.md.jinja2 +0 -6
  9. linkml/generators/docgen/subset.md.jinja2 +54 -13
  10. linkml/generators/docgen.py +94 -92
  11. linkml/generators/dotgen.py +5 -9
  12. linkml/generators/erdiagramgen.py +58 -53
  13. linkml/generators/excelgen.py +10 -16
  14. linkml/generators/golanggen.py +11 -21
  15. linkml/generators/golrgen.py +4 -13
  16. linkml/generators/graphqlgen.py +3 -11
  17. linkml/generators/javagen.py +8 -15
  18. linkml/generators/jsonldcontextgen.py +7 -36
  19. linkml/generators/jsonldgen.py +14 -12
  20. linkml/generators/jsonschemagen.py +183 -136
  21. linkml/generators/linkmlgen.py +1 -1
  22. linkml/generators/markdowngen.py +40 -89
  23. linkml/generators/namespacegen.py +1 -2
  24. linkml/generators/oocodegen.py +22 -25
  25. linkml/generators/owlgen.py +48 -49
  26. linkml/generators/prefixmapgen.py +6 -14
  27. linkml/generators/projectgen.py +7 -14
  28. linkml/generators/protogen.py +3 -5
  29. linkml/generators/pydanticgen.py +85 -73
  30. linkml/generators/pythongen.py +89 -157
  31. linkml/generators/rdfgen.py +5 -11
  32. linkml/generators/shaclgen.py +32 -18
  33. linkml/generators/shexgen.py +19 -24
  34. linkml/generators/sparqlgen.py +5 -13
  35. linkml/generators/sqlalchemy/__init__.py +3 -2
  36. linkml/generators/sqlalchemy/sqlalchemy_declarative_template.py +7 -7
  37. linkml/generators/sqlalchemy/sqlalchemy_imperative_template.py +3 -3
  38. linkml/generators/sqlalchemygen.py +29 -27
  39. linkml/generators/sqlddlgen.py +34 -26
  40. linkml/generators/sqltablegen.py +21 -21
  41. linkml/generators/sssomgen.py +11 -13
  42. linkml/generators/summarygen.py +2 -4
  43. linkml/generators/terminusdbgen.py +7 -19
  44. linkml/generators/typescriptgen.py +10 -18
  45. linkml/generators/yamlgen.py +0 -2
  46. linkml/generators/yumlgen.py +23 -71
  47. linkml/linter/cli.py +4 -11
  48. linkml/linter/config/datamodel/config.py +17 -47
  49. linkml/linter/linter.py +2 -4
  50. linkml/linter/rules.py +34 -48
  51. linkml/reporting/__init__.py +2 -0
  52. linkml/reporting/model.py +9 -24
  53. linkml/transformers/relmodel_transformer.py +20 -33
  54. linkml/transformers/schema_renamer.py +14 -10
  55. linkml/utils/converter.py +15 -15
  56. linkml/utils/datautils.py +9 -24
  57. linkml/utils/datavalidator.py +2 -2
  58. linkml/utils/execute_tutorial.py +10 -12
  59. linkml/utils/generator.py +74 -92
  60. linkml/utils/helpers.py +4 -2
  61. linkml/utils/ifabsent_functions.py +23 -15
  62. linkml/utils/mergeutils.py +19 -35
  63. linkml/utils/rawloader.py +2 -6
  64. linkml/utils/schema_builder.py +31 -19
  65. linkml/utils/schema_fixer.py +28 -18
  66. linkml/utils/schemaloader.py +44 -89
  67. linkml/utils/schemasynopsis.py +50 -73
  68. linkml/utils/sqlutils.py +40 -30
  69. linkml/utils/typereferences.py +9 -6
  70. linkml/utils/validation.py +4 -5
  71. linkml/validators/__init__.py +2 -0
  72. linkml/validators/jsonschemavalidator.py +104 -53
  73. linkml/validators/sparqlvalidator.py +5 -15
  74. linkml/workspaces/datamodel/workspaces.py +13 -30
  75. linkml/workspaces/example_runner.py +75 -68
  76. {linkml-1.5.5.dist-info → linkml-1.5.7.dist-info}/METADATA +2 -2
  77. linkml-1.5.7.dist-info/RECORD +109 -0
  78. linkml-1.5.5.dist-info/RECORD +0 -109
  79. {linkml-1.5.5.dist-info → linkml-1.5.7.dist-info}/LICENSE +0 -0
  80. {linkml-1.5.5.dist-info → linkml-1.5.7.dist-info}/WHEEL +0 -0
  81. {linkml-1.5.5.dist-info → linkml-1.5.7.dist-info}/entry_points.txt +0 -0
@@ -3,23 +3,25 @@
3
3
  """
4
4
  import os
5
5
  from dataclasses import dataclass, field
6
- from typing import List, Optional, TextIO, Union
6
+ from typing import List, Optional, Union
7
7
 
8
8
  import click
9
9
  from jsonasobj import as_json as as_json_1
10
- from linkml_runtime.linkml_model.meta import (ClassDefinition, ElementName,
11
- EnumDefinition, SchemaDefinition,
12
- SlotDefinition,
13
- SlotDefinitionName,
14
- TypeDefinition)
10
+ from linkml_runtime.linkml_model.meta import (
11
+ ClassDefinition,
12
+ ElementName,
13
+ EnumDefinition,
14
+ SlotDefinition,
15
+ SlotDefinitionName,
16
+ TypeDefinition,
17
+ )
15
18
  from linkml_runtime.linkml_model.types import SHEX
16
19
  from linkml_runtime.utils.formatutils import camelcase, sfx
17
20
  from linkml_runtime.utils.metamodelcore import URIorCURIE
18
21
  from rdflib import OWL, RDF, XSD, Graph, Namespace
19
22
  from ShExJSG import ShExC
20
23
  from ShExJSG.SchemaWithContext import Schema
21
- from ShExJSG.ShExJ import (IRIREF, EachOf, NodeConstraint, Shape, ShapeOr,
22
- TripleConstraint)
24
+ from ShExJSG.ShExJ import IRIREF, EachOf, NodeConstraint, Shape, ShapeOr, TripleConstraint
23
25
 
24
26
  from linkml import METAMODEL_NAMESPACE, METAMODEL_NAMESPACE_NAME
25
27
  from linkml._version import __version__
@@ -32,6 +34,7 @@ class ShExGenerator(Generator):
32
34
  generatorname = os.path.basename(__file__)
33
35
  generatorversion = "0.0.2"
34
36
  valid_formats = ["shex", "json", "rdf"]
37
+ file_extension = "shex.rdf"
35
38
  visit_all_class_slots = False
36
39
  uses_schemaloader = True
37
40
 
@@ -39,7 +42,9 @@ class ShExGenerator(Generator):
39
42
  shex: Schema = field(default_factory=lambda: Schema()) # ShEx Schema being generated
40
43
  shapes: List = field(default_factory=lambda: [])
41
44
  shape: Optional[Shape] = None # Current shape being defined
42
- list_shapes: List[IRIREF] = field(default_factory=lambda: []) # Shapes that have been defined as lists
45
+ list_shapes: List[IRIREF] = field(
46
+ default_factory=lambda: []
47
+ ) # Shapes that have been defined as lists
43
48
 
44
49
  def __post_init__(self):
45
50
  super().__post_init__()
@@ -71,14 +76,10 @@ class ShExGenerator(Generator):
71
76
  if typ_type_uri in (XSD.anyURI, SHEX.iri):
72
77
  self.shapes.append(NodeConstraint(id=model_uri, nodeKind="iri"))
73
78
  elif typ_type_uri == SHEX.nonLiteral:
74
- self.shapes.append(
75
- NodeConstraint(id=model_uri, nodeKind="nonliteral")
76
- )
79
+ self.shapes.append(NodeConstraint(id=model_uri, nodeKind="nonliteral"))
77
80
  else:
78
81
  self.shapes.append(
79
- NodeConstraint(
80
- id=model_uri, datatype=self.namespaces.uri_for(typ.uri)
81
- )
82
+ NodeConstraint(id=model_uri, datatype=self.namespaces.uri_for(typ.uri))
82
83
  )
83
84
  else:
84
85
  typeof_uri = self._class_or_type_uri(typ.typeof)
@@ -95,9 +96,7 @@ class ShExGenerator(Generator):
95
96
  struct_ref_list.append(applier)
96
97
  for sr in struct_ref_list:
97
98
  self._add_constraint(self._class_or_type_uri(sr, "_tes"))
98
- self._add_constraint(
99
- self._type_arc(self.schema.classes[sr].class_uri, opt=True)
100
- )
99
+ self._add_constraint(self._type_arc(self.schema.classes[sr].class_uri, opt=True))
101
100
  return True
102
101
 
103
102
  def end_class(self, cls: ClassDefinition) -> None:
@@ -121,9 +120,7 @@ class ShExGenerator(Generator):
121
120
  # If this class has subtypes, define the class as the union of its subtypes and itself (if not abstract)
122
121
  if cls.name in self.synopsis.isarefs:
123
122
  childrenExprs = []
124
- for child_classname in sorted(
125
- list(self.synopsis.isarefs[cls.name].classrefs)
126
- ):
123
+ for child_classname in sorted(list(self.synopsis.isarefs[cls.name].classrefs)):
127
124
  childrenExprs.append(self._class_or_type_uri(child_classname))
128
125
  if not (cls.mixin or cls.abstract) or len(childrenExprs) == 1:
129
126
  childrenExprs.insert(0, self.shape)
@@ -203,9 +200,7 @@ class ShExGenerator(Generator):
203
200
  self.shape.expression = constraint
204
201
  # One constraint
205
202
  elif not isinstance(self.shape.expression, EachOf):
206
- self.shape.expression = EachOf(
207
- expressions=[self.shape.expression, constraint]
208
- )
203
+ self.shape.expression = EachOf(expressions=[self.shape.expression, constraint])
209
204
  # Two or more constraints
210
205
  else:
211
206
  self.shape.expression.expressions.append(constraint)
@@ -3,18 +3,12 @@ import os
3
3
  from collections import defaultdict
4
4
  from dataclasses import dataclass
5
5
  from pathlib import Path
6
- from typing import Dict, List, Optional, TextIO, Union, cast
6
+ from typing import Dict, List, Optional
7
7
 
8
8
  import click
9
9
  from jinja2 import Template
10
- from linkml_runtime.linkml_model.meta import (ClassDefinition,
11
- ClassDefinitionName, Element,
12
- EnumDefinitionName, Prefix,
13
- SchemaDefinition, SlotDefinition,
14
- SlotDefinitionName,
15
- TypeDefinition,
16
- TypeDefinitionName)
17
- from linkml_runtime.utils.formatutils import camelcase, underscore
10
+ from linkml_runtime.linkml_model.meta import Prefix
11
+ from linkml_runtime.utils.formatutils import underscore
18
12
  from linkml_runtime.utils.schemaview import SchemaView
19
13
 
20
14
  from linkml._version import __version__
@@ -109,9 +103,7 @@ x = """
109
103
  def materialize_schema(schemaview: SchemaView):
110
104
  schema = schemaview.schema
111
105
  if "rdf" not in schema.prefixes:
112
- schema.prefixes["rdf"] = Prefix(
113
- "rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
114
- )
106
+ schema.prefixes["rdf"] = Prefix("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
115
107
  for scn in schemaview.imports_closure():
116
108
  for pfxn, pfx in schemaview.schema_map[scn].prefixes.items():
117
109
  if pfxn not in schema:
@@ -203,7 +195,7 @@ class SparqlGenerator(Generator):
203
195
  def cli(yamlfile, dir, **kwargs):
204
196
  """Generate SPARQL queries for validation
205
197
 
206
- This will generate a directory of queries that cann be used for QC over a triplestore that
198
+ This will generate a directory of queries that can be used for QC over a triplestore that
207
199
  is conformant to the same LinkML schema.
208
200
 
209
201
  Each query in the directory will be of the form
@@ -1,3 +1,4 @@
1
- from .sqlalchemy_declarative_template import \
2
- sqlalchemy_declarative_template_str
1
+ from .sqlalchemy_declarative_template import sqlalchemy_declarative_template_str
3
2
  from .sqlalchemy_imperative_template import sqlalchemy_imperative_template_str
3
+
4
+ __all__ = ["sqlalchemy_declarative_template_str", "sqlalchemy_imperative_template_str"]
@@ -14,7 +14,7 @@ class {{classname(c.name)}}({% if c.is_a %}{{ classname(c.is_a) }}{% else %}Base
14
14
  {% if c.description %}{{c.description}}{% else %}{{c.alias}}{% endif %}
15
15
  \"\"\"
16
16
  __tablename__ = '{{c.name}}'
17
-
17
+
18
18
  {% for s in c.attributes.values() -%}
19
19
  {{s.alias}} = Column({{s.annotations['sql_type'].value}}
20
20
  {%- if 'foreign_key' in s.annotations -%}, ForeignKey('{{ s.annotations['foreign_key'].value }}') {%- endif -%}
@@ -26,7 +26,7 @@ class {{classname(c.name)}}({% if c.is_a %}{{ classname(c.is_a) }}{% else %}Base
26
26
  {{s.annotations['original_slot'].value}} = relationship("{{classname(s.range)}}", uselist=False, foreign_keys=[{{s.alias}}])
27
27
  {% endif -%}
28
28
  {% endfor %}
29
-
29
+
30
30
  {%- for mapping in backrefs[c.name] %}
31
31
  {% if mapping.mapping_type == "ManyToMany" %}
32
32
  # ManyToMany
@@ -40,15 +40,15 @@ class {{classname(c.name)}}({% if c.is_a %}{{ classname(c.is_a) }}{% else %}Base
40
40
  {{mapping.source_slot}} = relationship( "{{ classname(mapping.target_class) }}", foreign_keys="[{{ mapping.target_class }}.{{mapping.target_slot}}]")
41
41
  {% endif -%}
42
42
  {%- endfor %}
43
-
43
+
44
44
  def __repr__(self):
45
45
  return f"{{c.name}}(
46
46
  {%- for s in c.attributes.values() -%}
47
- {{s.alias}}={self.{{s.alias}}},
47
+ {{s.alias}}={self.{{s.alias}}},
48
48
  {%- endfor %})"
49
-
50
-
51
-
49
+
50
+
51
+
52
52
  {% if c.is_a or c.mixins %}
53
53
  # Using concrete inheritance: see https://docs.sqlalchemy.org/en/14/orm/inheritance.html
54
54
  __mapper_args__ = {
@@ -27,10 +27,10 @@ tbl_{{classname(c.name)}} = Table('{{c.name}}', metadata,
27
27
  Column('{{s.name}}',
28
28
  Text,
29
29
  {% if 'foreign_key' in s.annotations -%}
30
- ForeignKey('{{ s.annotations['foreign_key'].value }}'),
30
+ ForeignKey('{{ s.annotations['foreign_key'].value }}'),
31
31
  {% endif -%}
32
- {% if 'primary_key' in s.annotations -%}
33
- primary_key=True
32
+ {% if 'primary_key' in s.annotations -%}
33
+ primary_key=True
34
34
  {%- endif -%}
35
35
  ),
36
36
  {%- endfor %}
@@ -3,26 +3,30 @@ import os
3
3
  from collections import defaultdict
4
4
  from dataclasses import dataclass
5
5
  from types import ModuleType
6
- from typing import Dict, List, TextIO, Union
6
+ from typing import List, Union
7
7
 
8
8
  import click
9
9
  from jinja2 import Template
10
- from linkml_runtime.linkml_model import (Annotation, ClassDefinition,
11
- ClassDefinitionName, Prefix,
12
- SchemaDefinition)
10
+ from linkml_runtime.linkml_model import (
11
+ Annotation,
12
+ ClassDefinition,
13
+ ClassDefinitionName,
14
+ SchemaDefinition,
15
+ )
13
16
  from linkml_runtime.utils.compile_python import compile_python
14
17
  from linkml_runtime.utils.formatutils import camelcase, underscore
15
18
  from linkml_runtime.utils.schemaview import SchemaView
16
- from sqlalchemy import *
19
+ from sqlalchemy import Enum
17
20
 
18
21
  from linkml._version import __version__
19
22
  from linkml.generators.pydanticgen import PydanticGenerator
20
23
  from linkml.generators.pythongen import PythonGenerator
21
- from linkml.generators.sqlalchemy import (sqlalchemy_declarative_template_str,
22
- sqlalchemy_imperative_template_str)
24
+ from linkml.generators.sqlalchemy import (
25
+ sqlalchemy_declarative_template_str,
26
+ sqlalchemy_imperative_template_str,
27
+ )
23
28
  from linkml.generators.sqltablegen import SQLTableGenerator
24
- from linkml.transformers.relmodel_transformer import (
25
- ForeignKeyPolicy, RelationalModelTransformer)
29
+ from linkml.transformers.relmodel_transformer import ForeignKeyPolicy, RelationalModelTransformer
26
30
  from linkml.utils.generator import Generator, shared_arguments
27
31
 
28
32
 
@@ -43,7 +47,9 @@ class SQLAlchemyGenerator(Generator):
43
47
  generatorname = os.path.basename(__file__)
44
48
  generatorversion = "0.1.1"
45
49
  valid_formats = ["sqla"]
50
+ file_extension = "py"
46
51
  uses_schemaloader = False
52
+ template: TemplateEnum = None
47
53
 
48
54
  # ObjectVars
49
55
  original_schema: Union[SchemaDefinition, str] = None
@@ -53,17 +59,20 @@ class SQLAlchemyGenerator(Generator):
53
59
  self.schemaview = SchemaView(self.schema)
54
60
  super().__post_init__()
55
61
 
56
-
57
62
  def generate_sqla(
58
63
  self,
59
64
  model_path: str = None,
60
65
  no_model_import=False,
61
- template: TemplateEnum = TemplateEnum.IMPERATIVE,
66
+ template: TemplateEnum = None,
62
67
  foreign_key_policy: ForeignKeyPolicy = None,
63
68
  **kwargs,
64
69
  ) -> str:
65
70
  # src_sv = SchemaView(self.schema)
66
71
  # self.schema = src_sv.schema
72
+ if template is None:
73
+ template = self.template
74
+ if template is None:
75
+ template = TemplateEnum.IMPERATIVE
67
76
  sqltr = RelationalModelTransformer(self.schemaview)
68
77
  if foreign_key_policy:
69
78
  sqltr.foreign_key_policy = foreign_key_policy
@@ -90,7 +99,6 @@ class SQLAlchemyGenerator(Generator):
90
99
  backrefs = defaultdict(list)
91
100
  for m in tr_result.mappings:
92
101
  backrefs[m.source_class].append(m)
93
- skip = {}
94
102
  # for c in tr_schema.classes.values():
95
103
  # if len(c.attributes) == 0:
96
104
  # skip[c.name] = True
@@ -98,18 +106,13 @@ class SQLAlchemyGenerator(Generator):
98
106
  self.add_safe_aliases(tr_schema)
99
107
  tr_sv = SchemaView(tr_schema)
100
108
  rel_schema_classes_ordered = [
101
- tr_sv.get_class(cn, strict=True)
102
- for cn in self.order_classes_by_hierarchy(tr_sv)
103
- ]
104
- rel_schema_classes_ordered = [
105
- c for c in rel_schema_classes_ordered if not self.skip(c)
109
+ tr_sv.get_class(cn, strict=True) for cn in self.order_classes_by_hierarchy(tr_sv)
106
110
  ]
111
+ rel_schema_classes_ordered = [c for c in rel_schema_classes_ordered if not self.skip(c)]
107
112
  for c in rel_schema_classes_ordered:
108
113
  # For SQLA there needs to be a primary key for each class;
109
114
  # autogenerate this as a compound key if none declared
110
- has_pk = any(
111
- a for a in c.attributes.values() if "primary_key" in a.annotations
112
- )
115
+ has_pk = any(a for a in c.attributes.values() if "primary_key" in a.annotations)
113
116
  if not has_pk:
114
117
  for a in c.attributes.values():
115
118
  ann = Annotation("primary_key", "true")
@@ -127,6 +130,9 @@ class SQLAlchemyGenerator(Generator):
127
130
  )
128
131
  return code
129
132
 
133
+ def serialize(self, **kwargs) -> str:
134
+ return self.generate_sqla(**kwargs)
135
+
130
136
  def compile_sqla(
131
137
  self,
132
138
  compile_python_dataclasses=False,
@@ -167,9 +173,7 @@ class SQLAlchemyGenerator(Generator):
167
173
  else:
168
174
  pygen = PythonGenerator(self.original_schema)
169
175
  dc_code = pygen.serialize()
170
- sqla_code = self.generate_sqla(
171
- model_path=None, no_model_import=True, **kwargs
172
- )
176
+ sqla_code = self.generate_sqla(model_path=None, no_model_import=True, **kwargs)
173
177
  return compile_python(f"{dc_code}\n{sqla_code}", package_path=model_path)
174
178
  else:
175
179
  code = self.generate_sqla(model_path=model_path, **kwargs)
@@ -231,9 +235,7 @@ class SQLAlchemyGenerator(Generator):
231
235
  )
232
236
  @click.version_option(__version__, "-V", "--version")
233
237
  @click.command()
234
- def cli(
235
- yamlfile, declarative, generate_classes, pydantic, use_foreign_keys=True, **args
236
- ):
238
+ def cli(yamlfile, declarative, generate_classes, pydantic, use_foreign_keys=True, **args):
237
239
  """Generate SQL DDL representation"""
238
240
  if pydantic:
239
241
  pygen = PydanticGenerator(yamlfile)
@@ -249,7 +251,7 @@ def cli(
249
251
  foreign_key_policy = ForeignKeyPolicy.NO_FOREIGN_KEYS
250
252
  print(gen.generate_sqla(template=t, foreign_key_policy=foreign_key_policy))
251
253
  if generate_classes:
252
- raise NotImplementedError(f"generate classes not implemented")
254
+ raise NotImplementedError("generate classes not implemented")
253
255
 
254
256
 
255
257
  if __name__ == "__main__":
@@ -1,17 +1,34 @@
1
+ """DEPRECATED: Use SQLTableGenerator instead"""
1
2
  import logging
2
3
  import os
3
4
  from contextlib import redirect_stdout
4
5
  from dataclasses import dataclass, field
5
- from typing import Dict, List, Optional, TextIO, Tuple, Union
6
+ from typing import Dict, List, Optional, TextIO, Union
6
7
 
7
8
  import click
8
9
  from deprecated.classic import deprecated
9
- from linkml_runtime.linkml_model.meta import (ClassDefinition,
10
- ClassDefinitionName,
11
- SchemaDefinition, SlotDefinition,
12
- SlotDefinitionName)
10
+ from linkml_runtime.linkml_model.meta import (
11
+ ClassDefinition,
12
+ ClassDefinitionName,
13
+ SchemaDefinition,
14
+ SlotDefinition,
15
+ )
13
16
  from linkml_runtime.utils.formatutils import camelcase, underscore
14
- from sqlalchemy import *
17
+ from sqlalchemy import (
18
+ Boolean,
19
+ Column,
20
+ Date,
21
+ DateTime,
22
+ Enum,
23
+ Float,
24
+ ForeignKey,
25
+ Integer,
26
+ MetaData,
27
+ Table,
28
+ Text,
29
+ Time,
30
+ create_mock_engine,
31
+ )
15
32
 
16
33
  from linkml._version import __version__
17
34
  from linkml.utils.generator import Generator, shared_arguments
@@ -202,7 +219,7 @@ class SQLDDLGenerator(Generator):
202
219
  generatorversion = "0.1.1"
203
220
  valid_formats = ["sql"]
204
221
  visit_all_class_slots: bool = True
205
- use_inherits: bool = False ## postgresql supports inheritance
222
+ use_inherits: bool = False # postgresql supports inheritance
206
223
  dialect: str
207
224
  inject_primary_keys: bool = True
208
225
  sqlschema: SQLSchema = SQLSchema()
@@ -248,7 +265,7 @@ class SQLDDLGenerator(Generator):
248
265
  if self._is_hidden(cls):
249
266
  return False
250
267
  if cls.description:
251
- None ## TODO
268
+ None # TODO
252
269
  tname = self._class_name_to_table(cls.name)
253
270
  # add table
254
271
  self.sqlschema.tables[tname] = SQLTable(name=tname, mapped_to=cls)
@@ -261,7 +278,7 @@ class SQLDDLGenerator(Generator):
261
278
  # postgresql supports inheritance
262
279
  # if you want to use plain SQL DDL then use sqlutils to unfold hierarchy
263
280
  # TODO: raise error if the target is standard SQL
264
- raise Exception(f"PostgreSQL Inheritance not yet supported")
281
+ raise Exception("PostgreSQL Inheritance not yet supported")
265
282
 
266
283
  def visit_class_slot(
267
284
  self, cls: ClassDefinition, aliased_slot_name: str, slot: SlotDefinition
@@ -320,17 +337,11 @@ class SQLDDLGenerator(Generator):
320
337
  linktable_name = f"{table.name}_{sqlcol.name}"
321
338
  backref_col_name = "backref_id"
322
339
  linktable = SQLTable(name=linktable_name)
323
- linktable.add_column(
324
- SQLColumn(name=backref_col_name, foreign_key=table_pk)
325
- )
340
+ linktable.add_column(SQLColumn(name=backref_col_name, foreign_key=table_pk))
326
341
  linktable.add_column(sqlcol)
327
342
  sqlschema.add_table(linktable)
328
343
  table.remove_column(sqlcol)
329
- if (
330
- not is_primitive
331
- and table_pk is not None
332
- and len(ref.referenced_by) == 1
333
- ):
344
+ if not is_primitive and table_pk is not None and len(ref.referenced_by) == 1:
334
345
  # e.g. user->addresses
335
346
  backref_col_name = f"{table.name}_{table_pk.name}"
336
347
  backref_col = SQLColumn(name=backref_col_name, foreign_key=table_pk)
@@ -392,12 +403,9 @@ class SQLDDLGenerator(Generator):
392
403
  def dump(sql, *multiparams, **params):
393
404
  print(f"{str(sql.compile(dialect=engine.dialect)).rstrip()};")
394
405
 
395
- engine = create_mock_engine(
396
- f"{self.dialect}://./MyDb", strategy="mock", executor=dump
397
- )
406
+ engine = create_mock_engine(f"{self.dialect}://./MyDb", strategy="mock", executor=dump)
398
407
  schema_metadata = MetaData()
399
408
  for t in self.sqlschema.tables.values():
400
- cls = t.mapped_to
401
409
  sqlcols = t.columns.values()
402
410
  if len(sqlcols) > 0:
403
411
  cols = []
@@ -415,14 +423,14 @@ class SQLDDLGenerator(Generator):
415
423
  )
416
424
 
417
425
  cols.append(col)
418
- alchemy_tbl = Table(t.name, schema_metadata, *cols)
426
+ Table(t.name, schema_metadata, *cols)
419
427
  print()
420
428
  schema_metadata.create_all(engine)
421
429
 
422
430
  def write_sqla_python_imperative(self, model_path: str) -> str:
423
431
  """
424
432
  imperative mapping:
425
- https://docs.sqlalchemy.org/en/14/orm/mapping_styles.html#imperative-mapping-with-dataclasses-and-attrs
433
+ https://docs.sqlalchemy.org/en/14/orm/dataclasses.html#mapping-dataclasses-using-imperative-mapping
426
434
 
427
435
  maps to the python classes generated using PythonGenerator
428
436
 
@@ -491,8 +499,8 @@ from {model_path} import *
491
499
  backref_slot_range = camelcase(backref_slot.range)
492
500
  print(
493
501
  f"""
494
- '{underscore(original_col.mapped_to_alias)}':
495
- relationship({backref_slot_range},
502
+ '{underscore(original_col.mapped_to_alias)}':
503
+ relationship({backref_slot_range},
496
504
  foreign_keys={backref.backref_column.table.as_var()}.columns["{backref.backref_column.name}"],
497
505
  backref='{cn}'),
498
506
  """
@@ -527,7 +535,7 @@ Python import header for generated sql-alchemy code
527
535
  default=False,
528
536
  show_default=True,
529
537
  help="""
530
- Map classes directly to
538
+ Map classes directly to
531
539
  """,
532
540
  )
533
541
  @click.option(
@@ -1,22 +1,18 @@
1
1
  import logging
2
2
  import os
3
3
  from dataclasses import dataclass, field
4
- from typing import Dict, List, Optional, TextIO, Union
4
+ from typing import Optional
5
5
 
6
6
  import click
7
7
  from linkml_runtime.dumpers import yaml_dumper
8
- from linkml_runtime.linkml_model import (ClassDefinition, SchemaDefinition,
9
- SlotDefinition)
8
+ from linkml_runtime.linkml_model import SchemaDefinition, SlotDefinition
10
9
  from linkml_runtime.utils.formatutils import camelcase, underscore
11
10
  from linkml_runtime.utils.schemaview import SchemaView
12
- from sqlalchemy import Column, ForeignKey, MetaData, Table, create_mock_engine
13
- from sqlalchemy.types import (Boolean, Date, DateTime, Enum, Float, Integer,
14
- Text, Time)
11
+ from sqlalchemy import Column, ForeignKey, MetaData, Table, UniqueConstraint, create_mock_engine
12
+ from sqlalchemy.types import Boolean, Date, DateTime, Enum, Float, Integer, Text, Time
15
13
 
16
14
  from linkml._version import __version__
17
- from linkml.generators.yamlgen import YAMLGenerator
18
- from linkml.transformers.relmodel_transformer import (
19
- ForeignKeyPolicy, RelationalModelTransformer)
15
+ from linkml.transformers.relmodel_transformer import ForeignKeyPolicy, RelationalModelTransformer
20
16
  from linkml.utils.generator import Generator, shared_arguments
21
17
  from linkml.utils.schemaloader import SchemaLoader
22
18
 
@@ -122,10 +118,11 @@ class SQLTableGenerator(Generator):
122
118
  generatorname = os.path.basename(__file__)
123
119
  generatorversion = "0.1.1"
124
120
  valid_formats = ["sql"]
121
+ file_extension = "sql"
125
122
  uses_schemaloader = False
126
123
 
127
124
  # ObjectVars
128
- use_inherits: bool = False ## postgresql supports inheritance
125
+ use_inherits: bool = False # postgresql supports inheritance
129
126
  dialect: str = field(default_factory=lambda: "sqlite")
130
127
  inject_primary_keys: bool = field(default_factory=lambda: True)
131
128
  use_foreign_keys: bool = field(default_factory=lambda: True)
@@ -133,6 +130,9 @@ class SQLTableGenerator(Generator):
133
130
  direct_mapping: bool = field(default_factory=lambda: False)
134
131
  relative_slot_num: bool = field(default_factory=lambda: 0)
135
132
 
133
+ def serialize(self, **kwargs) -> str:
134
+ return self.generate_ddl(**kwargs)
135
+
136
136
  def generate_ddl(self, naming_policy: SqlNamingPolicy = None, **kwargs) -> str:
137
137
  ddl_str = ""
138
138
 
@@ -140,9 +140,7 @@ class SQLTableGenerator(Generator):
140
140
  nonlocal ddl_str
141
141
  ddl_str += f"{str(sql.compile(dialect=engine.dialect)).rstrip()};"
142
142
 
143
- engine = create_mock_engine(
144
- f"{self.dialect}://./MyDb", strategy="mock", executor=dump
145
- )
143
+ engine = create_mock_engine(f"{self.dialect}://./MyDb", strategy="mock", executor=dump)
146
144
  schema_metadata = MetaData()
147
145
  sqltr = RelationalModelTransformer(SchemaView(self.schema))
148
146
  if not self.use_foreign_keys:
@@ -168,7 +166,8 @@ class SQLTableGenerator(Generator):
168
166
  return ""
169
167
  return txt.replace("\n", "")
170
168
 
171
- # Currently SQLite dialect in SQLA does not generated comments; see https://github.com/sqlalchemy/sqlalchemy/issues/1546#issuecomment-1067389172
169
+ # Currently SQLite dialect in SQLA does not generate comments; see
170
+ # https://github.com/sqlalchemy/sqlalchemy/issues/1546#issuecomment-1067389172
172
171
  # As a workaround we add these as "--" comments via direct string manipulation
173
172
  include_comments = self.dialect == "sqlite"
174
173
  sv = SchemaView(schema)
@@ -197,10 +196,15 @@ class SQLTableGenerator(Generator):
197
196
  nullable=not s.required,
198
197
  )
199
198
  if include_comments:
200
- ddl_str += f"-- * Slot: {sn} Description: {strip_newlines(s.description)}\n"
199
+ ddl_str += (
200
+ f"-- * Slot: {sn} Description: {strip_newlines(s.description)}\n"
201
+ )
201
202
  if s.description:
202
203
  col.comment = s.description
203
204
  cols.append(col)
205
+ for uc_name, uc in c.unique_keys.items():
206
+ sql_uc = UniqueConstraint(*[sql_name(sn) for sn in uc.unique_key_slots])
207
+ cols.append(sql_uc)
204
208
  Table(sql_name(cn), schema_metadata, *cols, comment=str(c.description))
205
209
  schema_metadata.create_all(engine)
206
210
  return ddl_str
@@ -235,9 +239,7 @@ class SQLTableGenerator(Generator):
235
239
  if range_base in RANGEMAP:
236
240
  return RANGEMAP[range_base]
237
241
  else:
238
- logging.error(
239
- f"UNKNOWN range base: {range_base} for {slot.name} = {slot.range}"
240
- )
242
+ logging.error(f"UNKNOWN range base: {range_base} for {slot.name} = {slot.range}")
241
243
  return Text()
242
244
 
243
245
  def get_foreign_key(self, cn: str, sv: SchemaView) -> str:
@@ -271,9 +273,7 @@ class SQLTableGenerator(Generator):
271
273
  "--relmodel-output",
272
274
  help="Path to intermediate LinkML YAML of transformed relational model",
273
275
  )
274
- @click.option(
275
- "--python-import", help="Python import header for generated sql-alchemy code"
276
- )
276
+ @click.option("--python-import", help="Python import header for generated sql-alchemy code")
277
277
  @click.option(
278
278
  "--use-foreign-keys/--no-use-foreign-keys",
279
279
  default=True,
@@ -1,12 +1,16 @@
1
1
  import os
2
2
  from dataclasses import dataclass
3
3
  from datetime import date
4
- from typing import Optional, TextIO, Union
5
4
 
6
5
  import click
7
- from linkml_runtime.linkml_model.meta import (LINKML, ClassDefinition,
8
- Definition, EnumDefinition,
9
- SchemaDefinition, SlotDefinition)
6
+ from linkml_runtime.linkml_model.meta import (
7
+ LINKML,
8
+ ClassDefinition,
9
+ Definition,
10
+ EnumDefinition,
11
+ SchemaDefinition,
12
+ SlotDefinition,
13
+ )
10
14
 
11
15
  from linkml._version import __version__
12
16
  from linkml.utils.generator import Generator, shared_arguments
@@ -19,6 +23,7 @@ class SSSOMGenerator(Generator):
19
23
  """
20
24
  Generates Simple Standard for Sharing Ontology Mappings (SSSOM) TSVs
21
25
  """
26
+
22
27
  # ClassVats
23
28
  generatorname = os.path.basename(__file__)
24
29
  generatorversion = "0.0.1"
@@ -62,7 +67,6 @@ class SSSOMGenerator(Generator):
62
67
  else:
63
68
  self.output_file = DEFAULT_OUTPUT_FILENAME
64
69
 
65
-
66
70
  def make_msdf_list(self, row_as_dict: dict) -> None:
67
71
  list_of_row = []
68
72
  for col_name in self.msdf_columns:
@@ -118,11 +122,7 @@ class SSSOMGenerator(Generator):
118
122
  for k, v in obj.permissible_values.items():
119
123
  if v["meaning"]:
120
124
  subject_id = (
121
- default_prefix
122
- + ":"
123
- + subject_category
124
- + "#"
125
- + k.replace(" ", "")
125
+ default_prefix + ":" + subject_category + "#" + k.replace(" ", "")
126
126
  )
127
127
  subject_label = k.replace(" ", "")
128
128
  object_id = obj.permissible_values[k]["meaning"]
@@ -164,9 +164,7 @@ class SSSOMGenerator(Generator):
164
164
  metadata["mapping_tool"] = LINKML
165
165
  metadata["creator_id"] = "linkml_user"
166
166
  metadata["mapping_date"] = date.today().strftime("%Y-%m-%d")
167
- metadata["curie_map"] = {
168
- k: v.prefix_reference for k, v in schema.prefixes.items()
169
- }
167
+ metadata["curie_map"] = {k: v.prefix_reference for k, v in schema.prefixes.items()}
170
168
  with open(self.output_file, "w", encoding="UTF-8") as sssom_tsv:
171
169
  for k, v in metadata.items():
172
170
  if k != "curie_map":