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