linkml 1.8.1__py3-none-any.whl → 1.8.3__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 (78) hide show
  1. linkml/cli/__init__.py +0 -0
  2. linkml/cli/__main__.py +4 -0
  3. linkml/cli/main.py +130 -0
  4. linkml/generators/common/build.py +105 -0
  5. linkml/generators/common/ifabsent_processor.py +286 -0
  6. linkml/generators/common/lifecycle.py +124 -0
  7. linkml/generators/common/template.py +89 -0
  8. linkml/generators/csvgen.py +1 -1
  9. linkml/generators/docgen/slot.md.jinja2 +4 -0
  10. linkml/generators/docgen.py +1 -1
  11. linkml/generators/dotgen.py +1 -1
  12. linkml/generators/erdiagramgen.py +1 -1
  13. linkml/generators/excelgen.py +1 -1
  14. linkml/generators/golanggen.py +1 -1
  15. linkml/generators/golrgen.py +1 -1
  16. linkml/generators/graphqlgen.py +1 -1
  17. linkml/generators/javagen.py +1 -1
  18. linkml/generators/jsonldcontextgen.py +4 -5
  19. linkml/generators/jsonldgen.py +1 -1
  20. linkml/generators/jsonschemagen.py +69 -22
  21. linkml/generators/linkmlgen.py +1 -1
  22. linkml/generators/markdowngen.py +1 -1
  23. linkml/generators/namespacegen.py +1 -1
  24. linkml/generators/oocodegen.py +2 -1
  25. linkml/generators/owlgen.py +1 -1
  26. linkml/generators/plantumlgen.py +1 -1
  27. linkml/generators/prefixmapgen.py +1 -1
  28. linkml/generators/projectgen.py +1 -1
  29. linkml/generators/protogen.py +1 -1
  30. linkml/generators/pydanticgen/__init__.py +8 -3
  31. linkml/generators/pydanticgen/array.py +114 -194
  32. linkml/generators/pydanticgen/build.py +64 -25
  33. linkml/generators/pydanticgen/includes.py +1 -31
  34. linkml/generators/pydanticgen/pydanticgen.py +621 -276
  35. linkml/generators/pydanticgen/template.py +152 -184
  36. linkml/generators/pydanticgen/templates/attribute.py.jinja +9 -7
  37. linkml/generators/pydanticgen/templates/base_model.py.jinja +0 -13
  38. linkml/generators/pydanticgen/templates/class.py.jinja +2 -2
  39. linkml/generators/pydanticgen/templates/footer.py.jinja +2 -10
  40. linkml/generators/pydanticgen/templates/module.py.jinja +2 -2
  41. linkml/generators/pydanticgen/templates/validator.py.jinja +0 -4
  42. linkml/generators/python/__init__.py +1 -0
  43. linkml/generators/python/python_ifabsent_processor.py +92 -0
  44. linkml/generators/pythongen.py +19 -23
  45. linkml/generators/rdfgen.py +1 -1
  46. linkml/generators/shacl/__init__.py +1 -3
  47. linkml/generators/shacl/shacl_data_type.py +1 -1
  48. linkml/generators/shacl/shacl_ifabsent_processor.py +89 -0
  49. linkml/generators/shaclgen.py +11 -5
  50. linkml/generators/shexgen.py +1 -1
  51. linkml/generators/sparqlgen.py +1 -1
  52. linkml/generators/sqlalchemygen.py +1 -1
  53. linkml/generators/sqltablegen.py +1 -1
  54. linkml/generators/sssomgen.py +1 -1
  55. linkml/generators/summarygen.py +1 -1
  56. linkml/generators/terminusdbgen.py +7 -4
  57. linkml/generators/typescriptgen.py +1 -1
  58. linkml/generators/yamlgen.py +1 -1
  59. linkml/generators/yumlgen.py +1 -1
  60. linkml/linter/cli.py +1 -1
  61. linkml/transformers/logical_model_transformer.py +117 -18
  62. linkml/utils/converter.py +1 -1
  63. linkml/utils/execute_tutorial.py +2 -0
  64. linkml/utils/logictools.py +142 -29
  65. linkml/utils/schema_builder.py +7 -6
  66. linkml/utils/schema_fixer.py +1 -1
  67. linkml/utils/sqlutils.py +1 -1
  68. linkml/validator/cli.py +4 -1
  69. linkml/validators/jsonschemavalidator.py +1 -1
  70. linkml/validators/sparqlvalidator.py +1 -1
  71. linkml/workspaces/example_runner.py +1 -1
  72. {linkml-1.8.1.dist-info → linkml-1.8.3.dist-info}/METADATA +2 -2
  73. {linkml-1.8.1.dist-info → linkml-1.8.3.dist-info}/RECORD +76 -68
  74. {linkml-1.8.1.dist-info → linkml-1.8.3.dist-info}/entry_points.txt +1 -1
  75. linkml/generators/shacl/ifabsent_processor.py +0 -59
  76. linkml/utils/ifabsent_functions.py +0 -138
  77. {linkml-1.8.1.dist-info → linkml-1.8.3.dist-info}/LICENSE +0 -0
  78. {linkml-1.8.1.dist-info → linkml-1.8.3.dist-info}/WHEEL +0 -0
@@ -1,11 +1,13 @@
1
- from copy import copy
2
1
  from importlib.util import find_spec
3
- from typing import Any, ClassVar, Dict, Generator, List, Literal, Optional, Union, overload
2
+ from typing import Any, ClassVar, Dict, Generator, List, Literal, Optional, Union
4
3
 
5
4
  from jinja2 import Environment, PackageLoader
6
- from pydantic import BaseModel, Field
5
+ from pydantic import BaseModel, Field, field_validator
7
6
  from pydantic.version import VERSION as PYDANTIC_VERSION
8
7
 
8
+ from linkml.generators.common.template import TemplateModel
9
+ from linkml.utils.deprecation import deprecation_warning
10
+
9
11
  try:
10
12
  if find_spec("black") is not None:
11
13
  from linkml.generators.pydanticgen.black import format_black
@@ -19,10 +21,14 @@ except ImportError:
19
21
  if int(PYDANTIC_VERSION[0]) >= 2:
20
22
  from pydantic import computed_field
21
23
  else:
22
- from pydantic.fields import ModelField
24
+ deprecation_warning("pydantic-v1")
25
+
26
+ def computed_field(f):
27
+ """No-op decorator to allow this module to not break imports until 1.9.0"""
28
+ return f
23
29
 
24
30
 
25
- class TemplateModel(BaseModel):
31
+ class PydanticTemplateModel(TemplateModel):
26
32
  """
27
33
  Metaclass to render pydantic models with jinja templates.
28
34
 
@@ -59,15 +65,14 @@ class TemplateModel(BaseModel):
59
65
  loader=PackageLoader("linkml.generators.pydanticgen", "templates"), trim_blocks=True, lstrip_blocks=True
60
66
  )
61
67
 
62
- pydantic_ver: int = int(PYDANTIC_VERSION[0])
63
68
  meta_exclude: ClassVar[List[str]] = None
64
69
 
65
70
  def render(self, environment: Optional[Environment] = None, black: bool = False) -> str:
66
71
  """
67
72
  Recursively render a template model to a string.
68
73
 
69
- For each field in the model, recurse through, rendering each :class:`.TemplateModel`
70
- using the template set in :attr:`.TemplateModel.template` , but preserving the structure
74
+ For each field in the model, recurse through, rendering each :class:`.PydanticTemplateModel`
75
+ using the template set in :attr:`.PydanticTemplateModel.template` , but preserving the structure
71
76
  of lists and dictionaries. Regular :class:`.BaseModel` s are rendered to dictionaries.
72
77
  Any other value is passed through unchanged.
73
78
 
@@ -76,16 +81,10 @@ class TemplateModel(BaseModel):
76
81
  black (bool): if ``True`` , format template with black. (default False)
77
82
  """
78
83
  if environment is None:
79
- environment = TemplateModel.environment()
84
+ environment = self.environment()
80
85
 
81
- if int(PYDANTIC_VERSION[0]) >= 2:
82
- fields = {**self.model_fields, **self.model_computed_fields}
83
- else:
84
- fields = self.model_fields
86
+ rendered = super().render(environment=environment)
85
87
 
86
- data = {k: _render(getattr(self, k, None), environment) for k in fields}
87
- template = environment.get_template(self.template)
88
- rendered = template.render(**data)
89
88
  if format_black is not None and black:
90
89
  try:
91
90
  return format_black(rendered)
@@ -97,72 +96,6 @@ class TemplateModel(BaseModel):
97
96
  else:
98
97
  return rendered
99
98
 
100
- @classmethod
101
- def environment(cls) -> Environment:
102
- """
103
- Default environment for Template models.
104
- uses a :class:`jinja2.PackageLoader` for the templates directory within this module
105
- with the ``trim_blocks`` and ``lstrip_blocks`` parameters set to ``True`` so that the
106
- default templates could be written in a more readable way.
107
- """
108
- return copy(cls._environment)
109
-
110
- if int(PYDANTIC_VERSION[0]) < 2:
111
- # simulate pydantic 2's model_fields behavior
112
- # without using classmethod + property decorators
113
- # see:
114
- # - https://docs.python.org/3/whatsnew/3.11.html#language-builtins
115
- # - https://github.com/python/cpython/issues/89519
116
- # and:
117
- # - https://docs.python.org/3/reference/datamodel.html#customizing-class-creation
118
- # for this version.
119
- model_fields: ClassVar[Dict[str, "ModelField"]]
120
-
121
- def __init_subclass__(cls, **kwargs):
122
- super().__init_subclass__(**kwargs)
123
- cls.model_fields = cls.__fields__
124
-
125
- @overload
126
- def model_dump(self, mode: Literal["python"] = "python") -> dict: ...
127
-
128
- @overload
129
- def model_dump(self, mode: Literal["json"] = "json") -> str: ...
130
-
131
- def model_dump(self, mode: Literal["python", "json"] = "python", **kwargs) -> Union[dict, str]:
132
- if mode == "json":
133
- return self.json(**kwargs)
134
- return self.dict(**kwargs)
135
-
136
- @classmethod
137
- def exclude_from_meta(cls: "TemplateModel") -> List[str]:
138
- """
139
- Attributes in the source definition to exclude from linkml_meta
140
- """
141
- ret = [*cls.model_fields.keys()]
142
- if cls.meta_exclude is not None:
143
- ret = ret + cls.meta_exclude
144
- return ret
145
-
146
-
147
- def _render(
148
- item: Union[TemplateModel, Any, List[Union[Any, TemplateModel]], Dict[str, Union[Any, TemplateModel]]],
149
- environment: Environment,
150
- ) -> Union[str, List[str], Dict[str, str]]:
151
- if isinstance(item, TemplateModel):
152
- return item.render(environment)
153
- elif isinstance(item, list):
154
- return [_render(i, environment) for i in item]
155
- elif isinstance(item, dict):
156
- return {k: _render(v, environment) for k, v in item.items()}
157
- elif isinstance(item, BaseModel):
158
- if int(PYDANTIC_VERSION[0]) >= 2:
159
- fields = item.model_fields
160
- else:
161
- fields = item.__fields__
162
- return {k: _render(getattr(item, k, None), environment) for k in fields.keys()}
163
- else:
164
- return item
165
-
166
99
 
167
100
  class EnumValue(BaseModel):
168
101
  """
@@ -174,7 +107,7 @@ class EnumValue(BaseModel):
174
107
  description: Optional[str] = None
175
108
 
176
109
 
177
- class PydanticEnum(TemplateModel):
110
+ class PydanticEnum(PydanticTemplateModel):
178
111
  """
179
112
  Model used to render a :class:`enum.Enum`
180
113
  """
@@ -186,7 +119,7 @@ class PydanticEnum(TemplateModel):
186
119
  values: Dict[str, EnumValue] = Field(default_factory=dict)
187
120
 
188
121
 
189
- class PydanticBaseModel(TemplateModel):
122
+ class PydanticBaseModel(PydanticTemplateModel):
190
123
  """
191
124
  Parameterization of the base model that generated pydantic classes inherit from
192
125
  """
@@ -207,24 +140,24 @@ class PydanticBaseModel(TemplateModel):
207
140
  strict: bool = False
208
141
  """
209
142
  Enable strict mode in the base model.
210
-
143
+
211
144
  .. note::
212
-
145
+
213
146
  Pydantic 2 only! Pydantic 1 only has strict types, not strict mode. See: https://github.com/linkml/linkml/issues/1955
214
-
147
+
215
148
  References:
216
149
  https://docs.pydantic.dev/latest/concepts/strict_mode
217
150
  """
218
151
 
219
152
 
220
- class PydanticAttribute(TemplateModel):
153
+ class PydanticAttribute(PydanticTemplateModel):
221
154
  """
222
155
  Reduced version of SlotDefinition that carries all and only the information
223
156
  needed by the template
224
157
  """
225
158
 
226
159
  template: ClassVar[str] = "attribute.py.jinja"
227
- meta_exclude: ClassVar[List[str]] = ["from_schema", "owner", "range", "multivalued", "inlined", "inlined_as_list"]
160
+ meta_exclude: ClassVar[List[str]] = ["from_schema", "owner", "range", "inlined", "inlined_as_list"]
228
161
 
229
162
  name: str
230
163
  required: bool = False
@@ -232,51 +165,32 @@ class PydanticAttribute(TemplateModel):
232
165
  key: bool = False
233
166
  predefined: Optional[str] = None
234
167
  """Fixed string to use in body of field"""
235
- annotations: Optional[dict] = None
236
- """
237
- Of the form::
238
-
239
- annotations = {'python_range': {'value': 'int'}}
240
-
241
- .. todo::
242
-
243
- simplify when refactoring pydanticgen, should just be a string or a model
244
-
245
- """
168
+ range: Optional[str] = None
169
+ """Type annotation used for model field"""
246
170
  title: Optional[str] = None
247
171
  description: Optional[str] = None
248
172
  equals_number: Optional[Union[int, float]] = None
249
173
  minimum_value: Optional[Union[int, float]] = None
250
174
  maximum_value: Optional[Union[int, float]] = None
175
+ exact_cardinality: Optional[int] = None
176
+ minimum_cardinality: Optional[int] = None
177
+ maximum_cardinality: Optional[int] = None
178
+ multivalued: Optional[bool] = None
251
179
  pattern: Optional[str] = None
252
180
  meta: Optional[Dict[str, Any]] = None
253
181
  """
254
182
  Metadata for the slot to be included in a Field annotation
255
183
  """
256
184
 
257
- if int(PYDANTIC_VERSION[0]) >= 2:
258
-
259
- @computed_field
260
- def field(self) -> str:
261
- """Computed value to use inside of the generated Field"""
262
- if self.predefined:
263
- return self.predefined
264
- elif self.required or self.identifier or self.key:
265
- return "..."
266
- else:
267
- return "None"
268
-
269
- else:
270
- field: Optional[str] = None
271
-
272
- def __init__(self, **kwargs):
273
- super(PydanticAttribute, self).__init__(**kwargs)
274
- if self.predefined:
275
- self.field = self.predefined
276
- elif self.required or self.identifier or self.key:
277
- self.field = "..."
278
- else:
279
- self.field = "None"
185
+ @computed_field
186
+ def field(self) -> str:
187
+ """Computed value to use inside of the generated Field"""
188
+ if self.predefined:
189
+ return self.predefined
190
+ elif self.required or self.identifier or self.key:
191
+ return "..."
192
+ else:
193
+ return "None"
280
194
 
281
195
 
282
196
  class PydanticValidator(PydanticAttribute):
@@ -287,7 +201,7 @@ class PydanticValidator(PydanticAttribute):
287
201
  template: ClassVar[str] = "validator.py.jinja"
288
202
 
289
203
 
290
- class PydanticClass(TemplateModel):
204
+ class PydanticClass(PydanticTemplateModel):
291
205
  """
292
206
  Reduced version of ClassDefinition that carries all and only the information
293
207
  needed by the template.
@@ -316,29 +230,14 @@ class PydanticClass(TemplateModel):
316
230
 
317
231
  return {k: PydanticValidator(**v.model_dump()) for k, v in self.attributes.items() if v.pattern is not None}
318
232
 
319
- if int(PYDANTIC_VERSION[0]) >= 2:
233
+ @computed_field
234
+ def validators(self) -> Optional[Dict[str, PydanticValidator]]:
235
+ return self._validators()
320
236
 
321
- @computed_field
322
- def validators(self) -> Optional[Dict[str, PydanticValidator]]:
323
- return self._validators()
324
-
325
- @computed_field
326
- def slots(self) -> Optional[Dict[str, PydanticAttribute]]:
327
- """alias of attributes"""
328
- return self.attributes
329
-
330
- else:
331
- validators: Optional[Dict[str, PydanticValidator]]
332
-
333
- def __init__(self, **kwargs):
334
- super(PydanticClass, self).__init__(**kwargs)
335
- self.validators = self._validators()
336
-
337
- def render(self, environment: Optional[Environment] = None, black: bool = False) -> str:
338
- """Overridden in pydantic 1 to ensure that validators are regenerated at rendering time"""
339
- # refresh in case attributes have changed since init
340
- self.validators = self._validators()
341
- return super(PydanticClass, self).render(environment, black)
237
+ @computed_field
238
+ def slots(self) -> Optional[Dict[str, PydanticAttribute]]:
239
+ """alias of attributes"""
240
+ return self.attributes
342
241
 
343
242
 
344
243
  class ObjectImport(BaseModel):
@@ -352,7 +251,7 @@ class ObjectImport(BaseModel):
352
251
  alias: Optional[str] = None
353
252
 
354
253
 
355
- class Import(TemplateModel):
254
+ class Import(PydanticTemplateModel):
356
255
  """
357
256
  A python module, or module and classes to be imported.
358
257
 
@@ -386,6 +285,12 @@ class Import(TemplateModel):
386
285
  module: str
387
286
  alias: Optional[str] = None
388
287
  objects: Optional[List[ObjectImport]] = None
288
+ is_schema: bool = False
289
+ """
290
+ Whether or not this ``Import`` is importing another schema imported by the main schema --
291
+ ie. that it is not expected to be provided by the environment, but imported locally from within the package.
292
+ Used primarily in split schema generation, see :func:`.pydanticgen.generate_split` for example usage.
293
+ """
389
294
 
390
295
  def merge(self, other: "Import") -> List["Import"]:
391
296
  """
@@ -429,7 +334,14 @@ class Import(TemplateModel):
429
334
  }
430
335
  self_objs.update(other_objs)
431
336
 
432
- return [Import(module=self.module, alias=alias, objects=list(self_objs.values()))]
337
+ return [
338
+ Import(
339
+ module=self.module,
340
+ alias=alias,
341
+ objects=list(self_objs.values()),
342
+ is_schema=self.is_schema or other.is_schema,
343
+ )
344
+ ]
433
345
  else:
434
346
  # one is a module, the other imports objects, keep both
435
347
  return [self, other]
@@ -480,7 +392,7 @@ class ConditionalImport(Import):
480
392
  alternative: Import
481
393
 
482
394
 
483
- class Imports(TemplateModel):
395
+ class Imports(PydanticTemplateModel):
484
396
  """
485
397
  Container class for imports that can handle merging!
486
398
 
@@ -516,19 +428,38 @@ class Imports(TemplateModel):
516
428
 
517
429
  imports: List[Union[Import, ConditionalImport]] = Field(default_factory=list)
518
430
 
519
- def __add__(self, other: Union[Import, "Imports", List[Import]]) -> "Imports":
431
+ @classmethod
432
+ def _merge(
433
+ cls, imports: List[Union[Import, ConditionalImport]], other: Union[Import, "Imports", List[Import]]
434
+ ) -> List[Union[Import, ConditionalImport]]:
435
+ """
436
+ Add a new import to an existing imports list, handling deduplication and flattening.
437
+
438
+ Mutates and returns ``imports``
439
+
440
+ Generally will prefer the imports in ``other`` , updating those in ``imports``.
441
+ If ``other`` ...
442
+ - doesn't match any ``module`` in ``imports``, add it!
443
+ - matches a single ``module`` in imports, :meth:`.Import.merge` the object imports
444
+ - matches multiple ``module``s in imports, then there must have been another
445
+ :class:`.ConditionalImport` already present, so we :meth:`.Import.merge` the existing
446
+ :class:`.Import` if it is one, and if it's a :class:`.ConditionalImport` just YOLO
447
+ and append it since there isn't a principled way to merge them from strings.
448
+ - is :class:`.Imports` or a list of :class:`.Import` s, call this recursively for each
449
+ item.
450
+
451
+ Since imports can be merged in an undefined order depending on the generator configuration,
452
+ default behavior for imports with matching ``module`` is to remove them and append to the
453
+ end of the imports list (rather than keeping it in the position of the existing
454
+ :class:`.Import` ). :class:`.ConditionalImports` make it possible to have namespace
455
+ conflicts, so in imperative import style we assume the most recently added :class:`.Import`
456
+ is the one that should prevail.
457
+ """
458
+ #
520
459
  if isinstance(other, Imports) or (isinstance(other, list) and all([isinstance(i, Import) for i in other])):
521
- if hasattr(self, "model_copy"):
522
- self_copy = self.model_copy(deep=True)
523
- else:
524
- self_copy = self.copy()
525
-
526
460
  for i in other:
527
- self_copy += i
528
- return self_copy
529
-
530
- # check if we have one of these already
531
- imports = self.imports.copy()
461
+ imports = cls._merge(imports, i)
462
+ return imports
532
463
 
533
464
  existing = [i for i in imports if i.module == other.module]
534
465
 
@@ -554,8 +485,12 @@ class Imports(TemplateModel):
554
485
 
555
486
  # SPECIAL CASE - __future__ annotations must happen at the top of a file
556
487
  imports = sorted(imports, key=lambda i: i.module == "__future__", reverse=True)
488
+ return imports
557
489
 
558
- return Imports(imports=imports)
490
+ def __add__(self, other: Union[Import, "Imports", List[Import]]) -> "Imports":
491
+ imports = self.imports.copy()
492
+ imports = self._merge(imports, other)
493
+ return Imports.model_construct(imports=imports)
559
494
 
560
495
  def __len__(self) -> int:
561
496
  return len(self.imports)
@@ -564,11 +499,61 @@ class Imports(TemplateModel):
564
499
  for i in self.imports:
565
500
  yield i
566
501
 
567
- def __getitem__(self, item: int) -> Import:
568
- return self.imports[item]
502
+ def __getitem__(self, item: Union[int, str]) -> Import:
503
+ if isinstance(item, int):
504
+ return self.imports[item]
505
+ elif isinstance(item, str):
506
+ # the name of the module
507
+ an_import = [i for i in self.imports if i.module == item]
508
+ if len(an_import) == 0:
509
+ raise KeyError(f"No import with module {item} was found.\nWe have: {self.imports}")
510
+ return an_import[0]
511
+ else:
512
+ raise TypeError(f"Can only index with an int or a string as the name of the module,\nGot: {type(item)}")
513
+
514
+ def __contains__(self, item: Union[Import, "Imports", List[Import]]) -> bool:
515
+ """
516
+ Check if all the objects are imported from the given module(s)
517
+
518
+ If the import is a bare module import (ie its :attr:`~.Import.objects` is ``None`` )
519
+ then we must also have a bare module import in this Imports (because even if
520
+ we import from the module, unless we import it specifically its name won't be
521
+ available in the namespace.
569
522
 
523
+ :attr:`.Import.alias` must always match for the same reason.
524
+ """
525
+ if isinstance(item, Imports):
526
+ return all([i in self for i in item.imports])
527
+ elif isinstance(item, list):
528
+ return all([i in self for i in item])
529
+ elif isinstance(item, Import):
530
+ try:
531
+ an_import = self[item.module]
532
+ except KeyError:
533
+ return False
534
+ if item.objects is None:
535
+ return an_import == item
536
+ else:
537
+ return all([obj in an_import.objects for obj in item.objects])
538
+ else:
539
+ raise TypeError("Imports only contains single Import objects or other Imports\n" f"Got: {type(item)}")
570
540
 
571
- class PydanticModule(TemplateModel):
541
+ @field_validator("imports", mode="after")
542
+ @classmethod
543
+ def imports_are_merged(
544
+ cls, imports: List[Union[Import, ConditionalImport]]
545
+ ) -> List[Union[Import, ConditionalImport]]:
546
+ """
547
+ When creating from a list of imports, construct model as if we have done so by iteratively
548
+ constructing with __add__ calls
549
+ """
550
+ merged_imports = []
551
+ for i in imports:
552
+ merged_imports = cls._merge(merged_imports, i)
553
+ return merged_imports
554
+
555
+
556
+ class PydanticModule(PydanticTemplateModel):
572
557
  """
573
558
  Top-level container model for generating a pydantic module :)
574
559
  """
@@ -588,23 +573,6 @@ class PydanticModule(TemplateModel):
588
573
  Metadata for the schema to be included in a linkml_meta module-level instance of LinkMLMeta
589
574
  """
590
575
 
591
- if int(PYDANTIC_VERSION[0]) >= 2:
592
-
593
- @computed_field
594
- def class_names(self) -> List[str]:
595
- return [c.name for c in self.classes.values()]
596
-
597
- else:
598
- class_names: List[str] = Field(default_factory=list)
599
-
600
- def __init__(self, **kwargs):
601
- super(PydanticModule, self).__init__(**kwargs)
602
- self.class_names = [c.name for c in self.classes.values()]
603
-
604
- def render(self, environment: Optional[Environment] = None, black: bool = False) -> str:
605
- """
606
- Trivial override of parent method for pydantic 1 to ensure that
607
- :attr:`.class_names` are correct at render time
608
- """
609
- self.class_names = [c.name for c in self.classes.values()]
610
- return super(PydanticModule, self).render(environment, black)
576
+ @computed_field
577
+ def class_names(self) -> List[str]:
578
+ return [c.name for c in self.classes.values()]
@@ -1,4 +1,4 @@
1
- {{name}}: {{ annotations['python_range'].value }} = Field({{ field }}
1
+ {{name}}: {{ range }} = Field({{ field }}
2
2
  {%- if title != None %}, title="{{title}}"{% endif -%}
3
3
  {%- if description %}, description="""{{description}}"""{% endif -%}
4
4
  {%- if equals_number != None %}
@@ -7,10 +7,12 @@
7
7
  {%- if minimum_value != None %}, ge={{minimum_value}}{% endif -%}
8
8
  {%- if maximum_value != None %}, le={{maximum_value}}{% endif -%}
9
9
  {%- endif -%}
10
- {%- if meta != None -%}
11
- {%- if pydantic_ver == 1 -%}
12
- , linkml_meta = {{ meta | pprint | indent(width=8) }}
13
- {% else -%}
10
+ {%- if multivalued and exact_cardinality != None -%}
11
+ , min_length={{exact_cardinality}}, max_length={{exact_cardinality}}
12
+ {%- elif multivalued -%}
13
+ {%- if minimum_cardinality != None %}, min_length={{minimum_cardinality}}{% endif -%}
14
+ {%- if maximum_cardinality != None %}, max_length={{maximum_cardinality}}{% endif -%}
15
+ {%- endif -%}
16
+ {%- if meta -%}
14
17
  , json_schema_extra = { "linkml_meta": {{ meta | pprint | indent(width=8) }} }
15
- {%- endif -%}
16
- {%- endif -%})
18
+ {%- endif -%})
@@ -1,15 +1,3 @@
1
- {% if pydantic_ver == 1 %}
2
- class WeakRefShimBaseModel(BaseModel):
3
- __slots__ = '__weakref__'
4
-
5
- class {{ name }}(WeakRefShimBaseModel,
6
- validate_assignment = True,
7
- validate_all = True,
8
- underscore_attrs_are_private = True,
9
- extra = "{{ extra_fields }}",
10
- arbitrary_types_allowed = True,
11
- use_enum_values = True):
12
- {% else %}
13
1
  class {{ name }}(BaseModel):
14
2
  model_config = ConfigDict(
15
3
  validate_assignment = True,
@@ -19,7 +7,6 @@ class {{ name }}(BaseModel):
19
7
  use_enum_values = True,
20
8
  strict = {{ strict }},
21
9
  )
22
- {% endif %}
23
10
  {% if fields is not none %}
24
11
  {% for field in fields %}
25
12
  {{ field }}
@@ -4,8 +4,8 @@ class {{ name }}({% if bases is string %}{{ bases }}{% else %}{{ bases | join(',
4
4
  {{ description | indent(width=4) }}
5
5
  """
6
6
  {% endif -%}
7
- {% if meta != None %}
8
- linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({% if pydantic_ver == 1 %}__root__={% endif %}{{ meta | pprint | indent(width=8) }})
7
+ {% if meta %}
8
+ linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({{ meta | pprint | indent(width=8) }})
9
9
 
10
10
  {% endif %}
11
11
  {% if attributes or validators %}
@@ -1,13 +1,5 @@
1
- {% if pydantic_ver == 1 %}
2
- # Update forward refs
3
- # see https://pydantic-docs.helpmanual.io/usage/postponed_annotations/
4
- {% for c in class_names -%}
5
- {{ c }}.update_forward_refs()
6
- {% endfor %}
7
- {% else %}
8
1
  # Model rebuild
9
2
  # see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model
10
- {% for c in class_names -%}
3
+ {% for c in class_names -%}
11
4
  {{ c }}.model_rebuild()
12
- {% endfor %}
13
- {% endif %}
5
+ {% endfor %}
@@ -14,8 +14,8 @@ version = "{{version if version else None}}"
14
14
  {{ c }}
15
15
  {% endfor %}
16
16
  {% endif %}
17
- {% if meta != None %}
18
- linkml_meta = LinkMLMeta({% if pydantic_ver == 1 %}__root__={% endif %}{{ meta | pprint | indent(width=4) }} )
17
+ {% if meta %}
18
+ linkml_meta = LinkMLMeta({{ meta | pprint | indent(width=4) }} )
19
19
  {% else %}
20
20
  linkml_meta = None
21
21
  {% endif %}
@@ -1,8 +1,4 @@
1
- {% if pydantic_ver == 1 %}
2
- @validator('{{name}}', allow_reuse=True)
3
- {% else %}
4
1
  @field_validator('{{name}}')
5
- {% endif %}
6
2
  def pattern_{{name}}(cls, v):
7
3
  pattern=re.compile(r"{{pattern}}")
8
4
  if isinstance(v,list):
@@ -0,0 +1 @@
1
+ __all__ = ["python_ifabsent_processor"]