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
@@ -8,26 +8,37 @@ from typing import Callable, Dict, Iterator, List, Optional, Set, Tuple, Union
8
8
 
9
9
  import click
10
10
  from linkml_runtime.linkml_model import linkml_files
11
- from linkml_runtime.linkml_model.meta import (ClassDefinition,
12
- ClassDefinitionName,
13
- DefinitionName, Element,
14
- EnumDefinition, PermissibleValue,
15
- SlotDefinition,
16
- SlotDefinitionName,
17
- TypeDefinition)
11
+ from linkml_runtime.linkml_model.meta import (
12
+ ClassDefinition,
13
+ ClassDefinitionName,
14
+ DefinitionName,
15
+ Element,
16
+ EnumDefinition,
17
+ PermissibleValue,
18
+ SlotDefinition,
19
+ SlotDefinitionName,
20
+ TypeDefinition,
21
+ )
18
22
  from linkml_runtime.utils.compile_python import compile_python
19
- from linkml_runtime.utils.formatutils import (be, camelcase, sfx, split_col,
20
- underscore, wrapped_annotation)
23
+ from linkml_runtime.utils.formatutils import (
24
+ be,
25
+ camelcase,
26
+ sfx,
27
+ split_col,
28
+ underscore,
29
+ wrapped_annotation,
30
+ )
21
31
  from linkml_runtime.utils.metamodelcore import builtinnames
22
32
  from rdflib import URIRef
23
33
 
24
34
  import linkml
25
35
  from linkml._version import __version__
26
- from linkml.generators import PYTHON_GEN_VERSION
27
36
  from linkml.utils.generator import Generator, shared_arguments
28
- from linkml.utils.ifabsent_functions import (default_curie_or_uri,
29
- ifabsent_postinit_declaration,
30
- ifabsent_value_declaration)
37
+ from linkml.utils.ifabsent_functions import (
38
+ default_curie_or_uri,
39
+ ifabsent_postinit_declaration,
40
+ ifabsent_value_declaration,
41
+ )
31
42
 
32
43
 
33
44
  @dataclass
@@ -40,8 +51,9 @@ class PythonGenerator(Generator):
40
51
 
41
52
  # ClassVars
42
53
  generatorname = os.path.basename(__file__)
43
- generatorversion = PYTHON_GEN_VERSION
54
+ generatorversion = "0.0.1"
44
55
  valid_formats = ["py"]
56
+ file_extension = "py"
45
57
  visit_all_class_slots = False
46
58
  uses_schemaloader = True
47
59
 
@@ -57,9 +69,7 @@ class PythonGenerator(Generator):
57
69
  if self.format is None:
58
70
  self.format = self.valid_formats[0]
59
71
  if self.schema.default_prefix == "linkml" and not self.genmeta:
60
- logging.error(
61
- f"Generating metamodel without --genmeta is highly inadvisable!"
62
- )
72
+ logging.error("Generating metamodel without --genmeta is highly inadvisable!")
63
73
  if (
64
74
  not self.schema.source_file
65
75
  and isinstance(self.sourcefile, str)
@@ -82,18 +92,14 @@ class PythonGenerator(Generator):
82
92
 
83
93
  def visit_schema(self, **kwargs) -> None:
84
94
  # Add explicitly declared prefixes
85
- self.emit_prefixes.update(
86
- [p.prefix_prefix for p in self.schema.prefixes.values()]
87
- )
95
+ self.emit_prefixes.update([p.prefix_prefix for p in self.schema.prefixes.values()])
88
96
 
89
97
  # Add all emit statements
90
98
  self.emit_prefixes.update(self.schema.emit_prefixes)
91
99
 
92
100
  # Add the default prefix
93
101
  if self.schema.default_prefix:
94
- self.emit_prefixes.add(
95
- self.namespaces.prefix_for(self.schema.default_prefix)
96
- )
102
+ self.emit_prefixes.add(self.namespaces.prefix_for(self.schema.default_prefix))
97
103
 
98
104
  def visit_class(self, cls: ClassDefinition) -> bool:
99
105
  if not cls.imported_from:
@@ -123,9 +129,7 @@ class PythonGenerator(Generator):
123
129
  if self.genmeta
124
130
  else "from linkml_runtime.linkml_model.meta import EnumDefinition, PermissibleValue, PvFormulaOptions\n"
125
131
  )
126
- handlerimport = (
127
- "from linkml_runtime.utils.enumerations import EnumDefinitionImpl"
128
- )
132
+ handlerimport = "from linkml_runtime.utils.enumerations import EnumDefinitionImpl"
129
133
  split_description = ""
130
134
  if self.schema.description:
131
135
  split_description = "\n# ".join(
@@ -191,9 +195,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
191
195
  )
192
196
 
193
197
  def gen_imports(self) -> str:
194
- listents = [
195
- f"from {k} import {', '.join(v)}" for k, v in self.gen_import_list().items()
196
- ]
198
+ listents = [f"from {k} import {', '.join(v)}" for k, v in self.gen_import_list().items()]
197
199
  return "\n".join(listents)
198
200
 
199
201
  def gen_import_list(self) -> Dict[str, List[str]]:
@@ -220,17 +222,13 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
220
222
  model_base + path[len(linkml_files.LINKML_NAMESPACE) :], set()
221
223
  ).add(name)
222
224
  elif path == linkml.BIOLINK_MODEL_URI:
223
- innerself.v.setdefault(linkml.BIOLINK_MODEL_PYTHON_LOC, set()).add(
224
- name
225
- )
225
+ innerself.v.setdefault(linkml.BIOLINK_MODEL_PYTHON_LOC, set()).add(name)
226
226
  elif "://" in path:
227
- raise ValueError(
228
- f"Cannot map {path} into a python import statement"
229
- )
227
+ raise ValueError(f"Cannot map {path} into a python import statement")
230
228
  elif "/" in path:
231
- innerself.v.setdefault(
232
- path.replace("./", ".").replace("/", "."), set()
233
- ).add(name)
229
+ innerself.v.setdefault(path.replace("./", ".").replace("/", "."), set()).add(
230
+ name
231
+ )
234
232
  elif "." in path:
235
233
  innerself.v.setdefault(path, set()).add(name)
236
234
  else:
@@ -262,9 +260,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
262
260
  cls = self.schema.classes[slot.range]
263
261
  if cls.imported_from:
264
262
  if self.class_identifier(cls):
265
- identifier_range = self.class_identifier_path(cls, False)[
266
- -1
267
- ]
263
+ identifier_range = self.class_identifier_path(cls, False)[-1]
268
264
  if identifier_range in self.schema.types:
269
265
  add_type_ref(TypeDefinition(identifier_range))
270
266
  else:
@@ -313,7 +309,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
313
309
  )
314
310
  return "\n".join(
315
311
  [
316
- f"{pfx.upper().replace('.', '_').replace('-', '_')} = CurieNamespace('{pfx.replace('.', '_')}', '{self.namespaces[pfx]}')"
312
+ f"{pfx.upper().replace('.', '_').replace('-', '_')} = CurieNamespace('{pfx.replace('.', '_')}', '{self.namespaces[pfx]}')" # noqa: E501
317
313
  for pfx in sorted(self.emit_prefixes)
318
314
  if pfx in self.namespaces
319
315
  ]
@@ -328,22 +324,15 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
328
324
  pkeys = self.primary_keys_for(cls)
329
325
  if pkeys:
330
326
  for pk in pkeys:
331
- classname = camelcase(cls.name) + camelcase(
332
- self.aliased_slot_name(pk)
333
- )
327
+ classname = camelcase(cls.name) + camelcase(self.aliased_slot_name(pk))
334
328
  # If we've got a parent slot and the range of the parent is the range of the child, the
335
329
  # child slot is a subclass of the parent. Otherwise, the child range has been overridden,
336
- # so the inheritence chain has been broken
337
- parent_pk = (
338
- self.class_identifier(cls.is_a) if cls.is_a else None
339
- )
340
- parent_pk_slot = (
341
- self.schema.slots[parent_pk] if parent_pk else None
342
- )
330
+ # so the inheritance chain has been broken
331
+ parent_pk = self.class_identifier(cls.is_a) if cls.is_a else None
332
+ parent_pk_slot = self.schema.slots[parent_pk] if parent_pk else None
343
333
  pk_slot = self.schema.slots[pk]
344
334
  if parent_pk_slot and (
345
- parent_pk_slot.name == pk
346
- or pk_slot.range == parent_pk_slot.range
335
+ parent_pk_slot.name == pk or pk_slot.range == parent_pk_slot.range
347
336
  ):
348
337
  parents = self.class_identifier_path(cls.is_a, False)
349
338
  else:
@@ -360,29 +349,25 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
360
349
  def gen_typedefs(self) -> str:
361
350
  """Generate python type declarations for all defined types"""
362
351
  rval = []
363
- defs_to_generate = [
364
- x for x in self.schema.types.values() if not x.imported_from
365
- ]
352
+ defs_to_generate = [x for x in self.schema.types.values() if not x.imported_from]
366
353
  emitted_types = []
367
- ## all imported_from types are already considered generated
368
- emitted_types.extend(
369
- [x.name for x in self.schema.types.values() if x.imported_from]
370
- )
354
+ # all imported_from types are already considered generated
355
+ emitted_types.extend([x.name for x in self.schema.types.values() if x.imported_from])
371
356
  for typ in [x for x in defs_to_generate if not x.typeof]:
372
357
  self._gen_typedef(typ, typ.base.rsplit(".")[-1], rval, emitted_types)
373
358
 
374
359
  while True:
375
360
  defs_to_generate_typeof = [
376
- x for x in defs_to_generate if x.typeof and not x.name in emitted_types
361
+ x for x in defs_to_generate if x.typeof and x.name not in emitted_types
377
362
  ]
378
363
  if len(defs_to_generate_typeof) == 0:
379
364
  break
380
- defs_can_generate = [
381
- x for x in defs_to_generate_typeof if x.typeof in emitted_types
382
- ]
365
+ defs_can_generate = [x for x in defs_to_generate_typeof if x.typeof in emitted_types]
383
366
  if len(defs_can_generate) == 0:
384
367
  raise ValueError(
385
- f"Cannot generate type definition for {[f'{x.name} of {x.typeof}' for x in defs_to_generate_typeof]}. Forgot a link in the type hierarchy chain?"
368
+ "Cannot generate type definition for "
369
+ f"{[f'{x.name} of {x.typeof}' for x in defs_to_generate_typeof]}. "
370
+ "Forgot a link in the type hierarchy chain?"
386
371
  )
387
372
  for typ in defs_can_generate:
388
373
  self._gen_typedef(typ, camelcase(typ.typeof), rval, emitted_types)
@@ -395,9 +380,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
395
380
  if typ.description:
396
381
  description = typ.description.replace('"""', "---")
397
382
  desc = f'\n\t""" {description} """'
398
- rval.append(
399
- f"class {typname}({superclass}):{desc}\n\t{self.gen_type_meta(typ)}\n\n"
400
- )
383
+ rval.append(f"class {typname}({superclass}):{desc}\n\t{self.gen_type_meta(typ)}\n\n")
401
384
  emitted_types.append(typ.name)
402
385
 
403
386
  def gen_classdefs(self) -> str:
@@ -459,9 +442,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
459
442
  class_class_curie = None
460
443
  if class_class_curie:
461
444
  class_class_curie = f'"{class_class_curie}"'
462
- class_class_uri = (
463
- cls_python_uri if cls_python_uri else f'URIRef("{class_class_uri}")'
464
- )
445
+ class_class_uri = cls_python_uri if cls_python_uri else f'URIRef("{class_class_uri}")'
465
446
  class_model_uri = self.namespaces.uri_or_curie_for(
466
447
  self.schema.default_prefix or "DEFAULT_", camelcase(cls.name)
467
448
  )
@@ -493,9 +474,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
493
474
  type_class_curie = None
494
475
  if type_class_curie:
495
476
  type_class_curie = f'"{type_class_curie}"'
496
- type_class_uri = (
497
- type_python_uri if type_python_uri else f'URIRef("{type_class_uri}")'
498
- )
477
+ type_class_uri = type_python_uri if type_python_uri else f'URIRef("{type_class_uri}")'
499
478
  type_model_uri = self.namespaces.uri_or_curie_for(
500
479
  self.schema.default_prefix, camelcase(typ.name)
501
480
  )
@@ -517,7 +496,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
517
496
  """
518
497
  Generate the variable declarations for a dataclass.
519
498
 
520
- :param cls: class containing variables to be rendered in inheritence hierarchy
499
+ :param cls: class containing variables to be rendered in inheritance hierarchy
521
500
  :return: variable declarations for target class and its ancestors
522
501
  """
523
502
  initializers = []
@@ -532,9 +511,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
532
511
  lambda slot: (slot.identifier or slot.key) and not slot.ifabsent,
533
512
  first_hit_only=True,
534
513
  )
535
- initializers += [
536
- self.gen_class_variable(cls, slot, not is_root) for slot in slot_variables
537
- ]
514
+ initializers += [self.gen_class_variable(cls, slot, not is_root) for slot in slot_variables]
538
515
 
539
516
  # Required slots
540
517
  slot_variables = self._slot_iter(
@@ -544,25 +521,17 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
544
521
  and not slot.key
545
522
  and not slot.ifabsent,
546
523
  )
547
- initializers += [
548
- self.gen_class_variable(cls, slot, not is_root) for slot in slot_variables
549
- ]
524
+ initializers += [self.gen_class_variable(cls, slot, not is_root) for slot in slot_variables]
550
525
 
551
526
  # Required or key slots with default values
552
- slot_variables = self._slot_iter(
553
- cls, lambda slot: slot.ifabsent and slot.required
554
- )
555
- initializers += [
556
- self.gen_class_variable(cls, slot, False) for slot in slot_variables
557
- ]
527
+ slot_variables = self._slot_iter(cls, lambda slot: slot.ifabsent and slot.required)
528
+ initializers += [self.gen_class_variable(cls, slot, False) for slot in slot_variables]
558
529
 
559
530
  # Followed by everything else
560
531
  slot_variables = self._slot_iter(
561
532
  cls, lambda slot: not slot.required and slot in domain_slots
562
533
  )
563
- initializers += [
564
- self.gen_class_variable(cls, slot, False) for slot in slot_variables
565
- ]
534
+ initializers += [self.gen_class_variable(cls, slot, False) for slot in slot_variables]
566
535
 
567
536
  return "\n\t".join(initializers)
568
537
 
@@ -618,9 +587,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
618
587
  pkey = self.class_identifier(slot.range)
619
588
  # Special case, inlined, identified range
620
589
  if pkey and slot.inlined and slot.multivalued:
621
- base_key = self.gen_class_reference(
622
- self.class_identifier_path(slot.range, False)
623
- )
590
+ base_key = self.gen_class_reference(self.class_identifier_path(slot.range, False))
624
591
  num_elements = len(self.schema.classes[slot.range].slots)
625
592
  dflt = None if slot.required and positional_allowed else "empty_dict()"
626
593
  if num_elements == 1:
@@ -709,12 +676,8 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
709
676
  if slot.ifabsent:
710
677
  dflt = ifabsent_postinit_declaration(slot.ifabsent, self, cls, slot)
711
678
  if dflt and dflt != "None":
712
- post_inits_pre_super.append(
713
- f"if self.{self.slot_name(slot.name)} is None:"
714
- )
715
- post_inits_pre_super.append(
716
- f"\tself.{self.slot_name(slot.name)} = {dflt}"
717
- )
679
+ post_inits_pre_super.append(f"if self.{self.slot_name(slot.name)} is None:")
680
+ post_inits_pre_super.append(f"\tself.{self.slot_name(slot.name)} = {dflt}")
718
681
 
719
682
  post_inits = []
720
683
  if not (cls.mixin or cls.abstract):
@@ -737,9 +700,9 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
737
700
  if slot.name not in pkeys and (not slot.ifabsent or True):
738
701
  post_inits.append(self.gen_postinit(cls, slot))
739
702
 
740
- post_inits_pre_super_line = "\n\t\t".join(
741
- [p for p in post_inits_pre_super if p]
742
- ) + ("\n\t\t" if post_inits_pre_super else "")
703
+ post_inits_pre_super_line = "\n\t\t".join([p for p in post_inits_pre_super if p]) + (
704
+ "\n\t\t" if post_inits_pre_super else ""
705
+ )
743
706
  post_inits_line = "\n\t\t".join([p for p in post_inits if p])
744
707
  return (
745
708
  (
@@ -771,9 +734,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
771
734
  del clist[i]
772
735
  break
773
736
  if not can_add:
774
- raise (
775
- f"could not find suitable element in {clist} that does not ref {slist}"
776
- )
737
+ raise (f"could not find suitable element in {clist} that does not ref {slist}")
777
738
  return slist
778
739
 
779
740
  def is_key_value_class(self, range_name: DefinitionName) -> bool:
@@ -801,8 +762,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
801
762
  aliased_slot_name = self.slot_name(
802
763
  slot.name
803
764
  ) # Mangled name by which the slot is known in python
804
- range_type, base_type, base_type_name = self.class_reference_type(slot, cls)
805
- slot_identifier = self.class_identifier(slot.range)
765
+ _, _, base_type_name = self.class_reference_type(slot, cls)
806
766
 
807
767
  # Generate existence check for required slots. Note that inherited classes have to do post init checks because
808
768
  # You can't have required elements after optional elements in the parent class
@@ -811,23 +771,17 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
811
771
  rlines.append(f'\tself.MissingRequiredField("{aliased_slot_name}")')
812
772
 
813
773
  # Generate the type co-orcion for the various types.
814
- indent = len(f"self.{aliased_slot_name} = [") * " "
815
774
  # NOTE: if you set this to true, we will cast all types. This may be what we really want
816
775
  if not slot.multivalued:
817
776
  if slot.required:
818
- rlines.append(
819
- f"if not isinstance(self.{aliased_slot_name}, {base_type_name}):"
820
- )
777
+ rlines.append(f"if not isinstance(self.{aliased_slot_name}, {base_type_name}):")
821
778
  else:
822
779
  rlines.append(
823
780
  f"if self.{aliased_slot_name} is not None and "
824
781
  f"not isinstance(self.{aliased_slot_name}, {base_type_name}):"
825
782
  )
826
- # A really wierd case -- a class that has no properties
827
- if (
828
- slot.range in self.schema.classes
829
- and not self.schema.classes[slot.range].slots
830
- ):
783
+ # A really weird case -- a class that has no properties
784
+ if slot.range in self.schema.classes and not self.schema.classes[slot.range].slots:
831
785
  rlines.append(f"\tself.{aliased_slot_name} = {base_type_name}()")
832
786
  else:
833
787
  if (
@@ -928,8 +882,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
928
882
  return [
929
883
  slot_name
930
884
  for slot_name in cls.slots
931
- if self.schema.slots[slot_name].key
932
- or self.schema.slots[slot_name].identifier
885
+ if self.schema.slots[slot_name].key or self.schema.slots[slot_name].identifier
933
886
  ]
934
887
 
935
888
  def key_name_for(self, class_name: ClassDefinitionName) -> Optional[str]:
@@ -949,12 +902,8 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
949
902
  """Determine whether slot_range is a forward reference"""
950
903
  # logging.info(f"CHECKING: {slot_range} {owning_class}")
951
904
  if (
952
- slot_range in self.schema.classes
953
- and self.schema.classes[slot_range].imported_from
954
- ) or (
955
- slot_range in self.schema.enums
956
- and self.schema.enums[slot_range].imported_from
957
- ):
905
+ slot_range in self.schema.classes and self.schema.classes[slot_range].imported_from
906
+ ) or (slot_range in self.schema.enums and self.schema.enums[slot_range].imported_from):
958
907
  logging.info(
959
908
  f"FALSE: FORWARD: {slot_range} {owning_class} // IMP={self.schema.classes[slot_range].imported_from}"
960
909
  )
@@ -963,9 +912,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
963
912
  return True
964
913
  for cname in self.schema.classes:
965
914
  if cname == owning_class:
966
- logging.info(
967
- f"TRUE: OCCURS SAME: {cname} == {slot_range} owning: {owning_class}"
968
- )
915
+ logging.info(f"TRUE: OCCURS SAME: {cname} == {slot_range} owning: {owning_class}")
969
916
  return True # Occurs on or after
970
917
  elif cname == slot_range:
971
918
  logging.info(
@@ -974,9 +921,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
974
921
  return False # Occurs before
975
922
  return True
976
923
 
977
- def python_uri_for(
978
- self, uriorcurie: Union[str, URIRef]
979
- ) -> Tuple[str, Optional[str]]:
924
+ def python_uri_for(self, uriorcurie: Union[str, URIRef]) -> Tuple[str, Optional[str]]:
980
925
  """Return the python form of uriorcurie
981
926
  :param uriorcurie:
982
927
  :return: URI and CURIE form
@@ -985,7 +930,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
985
930
  if ns == "":
986
931
  ns = "DEFAULT_"
987
932
  if ns is None:
988
- return f'"str(uriorcurie)"', None
933
+ return '"str(uriorcurie)"', None
989
934
  return (
990
935
  ns.upper() + (f".{ln}" if ln.isidentifier() else f"['{ln}']"),
991
936
  ns.upper() + f".curie('{ln}')",
@@ -1007,9 +952,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
1007
952
  python_slot_name = underscore(slot.name)
1008
953
  slot_uri, slot_curie = self.python_uri_for(slot.slot_uri)
1009
954
  slot_model_uri, slot_model_curie = self.python_uri_for(
1010
- self.namespaces.uri_or_curie_for(
1011
- self.schema.default_prefix, python_slot_name
1012
- )
955
+ self.namespaces.uri_or_curie_for(self.schema.default_prefix, python_slot_name)
1013
956
  )
1014
957
  domain = (
1015
958
  camelcase(slot.domain)
@@ -1038,20 +981,14 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
1038
981
  else:
1039
982
  mappings = ""
1040
983
  pattern = (
1041
- f",\n pattern=re.compile(r'{slot.pattern}')"
1042
- if slot.pattern
1043
- else ""
984
+ f",\n pattern=re.compile(r'{slot.pattern}')" if slot.pattern else ""
1044
985
  )
1045
986
  return f"""slots.{python_slot_name} = Slot(uri={slot_uri}, name="{slot.name}", curie={slot_curie},
1046
987
  model_uri={slot_model_uri}, domain={domain}, range={rnge}{mappings}{pattern})"""
1047
988
 
1048
989
  def gen_enumerations(self) -> str:
1049
990
  return "\n\n".join(
1050
- [
1051
- self.gen_enum(enum)
1052
- for enum in self.schema.enums.values()
1053
- if not enum.imported_from
1054
- ]
991
+ [self.gen_enum(enum) for enum in self.schema.enums.values() if not enum.imported_from]
1055
992
  )
1056
993
 
1057
994
  def gen_enum(self, enum: EnumDefinition) -> str:
@@ -1088,17 +1025,16 @@ class {enum_name}(EnumDefinitionImpl):
1088
1025
  else None
1089
1026
  )
1090
1027
  desc = f"{enum_desc},\n" if enum.description else ""
1091
- cs = (
1092
- f"\t\tcode_set={self.namespaces.curie_for(self.namespaces.uri_for(enum.code_set), default_ok=False, pythonform=True)},\n"
1028
+ enum_code_set = (
1029
+ self.namespaces.curie_for(
1030
+ self.namespaces.uri_for(enum.code_set), default_ok=False, pythonform=True
1031
+ )
1093
1032
  if enum.code_set
1094
- else ""
1033
+ else None
1095
1034
  )
1035
+ cs = f"\t\tcode_set={enum_code_set},\n" if enum_code_set else ""
1096
1036
  tag = f'\t\tcode_set_tag="{enum.code_set_tag}",\n' if enum.code_set_tag else ""
1097
- ver = (
1098
- f'\t\tcode_set_version="{enum.code_set_version}",\n'
1099
- if enum.code_set_version
1100
- else ""
1101
- )
1037
+ ver = f'\t\tcode_set_version="{enum.code_set_version}",\n' if enum.code_set_version else ""
1102
1038
  vf = (
1103
1039
  f"\t\tpv_formula=PvFormulaOptions.{enum.pv_formula.code.text},\n"
1104
1040
  if enum.pv_formula
@@ -1147,9 +1083,7 @@ class {enum_name}(EnumDefinitionImpl):
1147
1083
  indent_str = indent * " "
1148
1084
  pv_text = pv.text.replace('"', '\\"').replace(r"\n", r"\\n")
1149
1085
  pv_parts = self.gen_pv_constructor(pv, indent)
1150
- init_list.append(
1151
- f' setattr(cls, "{pv_text}",\n{indent_str}{pv_parts})'
1152
- )
1086
+ init_list.append(f' setattr(cls, "{pv_text}",\n{indent_str}{pv_parts})')
1153
1087
 
1154
1088
  add_vals_text = "\n".join(init_list).rstrip()
1155
1089
 
@@ -1212,9 +1146,7 @@ class {enum_name}(EnumDefinitionImpl):
1212
1146
 
1213
1147
  @shared_arguments(PythonGenerator)
1214
1148
  @click.command()
1215
- @click.option(
1216
- "--head/--no-head", default=True, show_default=True, help="Emit metadata heading"
1217
- )
1149
+ @click.option("--head/--no-head", default=True, show_default=True, help="Emit metadata heading")
1218
1150
  @click.option(
1219
1151
  "--genmeta/--no-genmeta",
1220
1152
  default=False,
@@ -7,10 +7,9 @@ Generate a JSON LD representation of the model
7
7
  import os
8
8
  import urllib.parse as urlparse
9
9
  from dataclasses import dataclass, field
10
- from typing import Optional, TextIO, Union, List
10
+ from typing import List, Optional
11
11
 
12
12
  import click
13
- from linkml_runtime.linkml_model.meta import SchemaDefinition
14
13
  from rdflib import Graph
15
14
  from rdflib.plugin import Parser as rdflib_Parser
16
15
  from rdflib.plugin import plugins as rdflib_plugins
@@ -23,11 +22,11 @@ from linkml.utils.generator import Generator, shared_arguments
23
22
 
24
23
  @dataclass
25
24
  class RDFGenerator(Generator):
26
-
27
25
  # ClassVars
28
26
  generatorname = os.path.basename(__file__)
29
27
  generatorversion = "0.1.1"
30
- # TODO: we leave ttl as default for backwards compatibility but nt is recommended, see https://github.com/linkml/linkml/issues/163
28
+ # TODO: we leave ttl as default for backwards compatibility but nt is
29
+ # recommended, see https://github.com/linkml/linkml/issues/163
31
30
  valid_formats = ["ttl"] + sorted(
32
31
  [x.name for x in rdflib_plugins(None, rdflib_Parser) if "/" not in str(x.name)]
33
32
  )
@@ -38,15 +37,10 @@ class RDFGenerator(Generator):
38
37
  emit_metadata: bool = field(default_factory=lambda: False)
39
38
  context: List[str] = None
40
39
 
41
-
42
40
  def _data(self, g: Graph) -> str:
43
- return g.serialize(
44
- format="turtle" if self.format == "ttl" else self.format
45
- ).decode()
41
+ return g.serialize(format="turtle" if self.format == "ttl" else self.format).decode()
46
42
 
47
- def end_schema(
48
- self, output: Optional[str] = None, context: str = None, **_
49
- ) -> None:
43
+ def end_schema(self, output: Optional[str] = None, context: str = None, **_) -> None:
50
44
  gen = JSONLDGenerator(
51
45
  self,
52
46
  format=JSONLDGenerator.valid_formats[0],
@@ -1,28 +1,32 @@
1
1
  import logging
2
2
  import os
3
- from copy import copy, deepcopy
4
- from dataclasses import field
5
- from typing import (Callable, Dict, Iterator, List, Optional, Set, TextIO,
6
- Tuple, Union)
7
3
 
8
4
  import click
9
- from linkml_runtime.linkml_model.meta import (Annotation, ClassDefinition,
10
- SchemaDefinition, TypeDefinition)
11
- from linkml_runtime.utils.formatutils import camelcase, underscore
5
+ from linkml_runtime.utils.formatutils import underscore
12
6
  from linkml_runtime.utils.schemaview import SchemaView
13
7
  from rdflib import BNode, Graph, Literal, URIRef
14
8
  from rdflib.collection import Collection
15
- from rdflib.namespace import RDF, RDFS, SH, XSD
9
+ from rdflib.namespace import RDF, SH
16
10
 
17
11
  from linkml._version import __version__
18
12
  from linkml.utils.generator import Generator, shared_arguments
19
13
 
14
+ LINK_ML_TYPES_STRING = URIRef("http://www.w3.org/2001/XMLSchema#string")
15
+ LINK_ML_TYPES_BOOL = URIRef("http://www.w3.org/2001/XMLSchema#boolean")
16
+ LINK_ML_TYPES_DECIMAL = URIRef("http://www.w3.org/2001/XMLSchema#decimal")
17
+ LINK_ML_TYPES_INTEGER = URIRef("http://www.w3.org/2001/XMLSchema#integer")
18
+ LINK_ML_TYPES_DURATION = URIRef("http://www.w3.org/2001/XMLSchema#duration")
19
+ LINK_ML_TYPES_DATETIME = URIRef("http://www.w3.org/2001/XMLSchema#dateTime")
20
+ LINK_ML_TYPES_DATE = URIRef("http://www.w3.org/2001/XMLSchema#date")
21
+ LINK_ML_TYPES_TIME = URIRef("http://www.w3.org/2001/XMLSchema#time")
22
+
20
23
 
21
24
  class ShaclGenerator(Generator):
22
25
  # ClassVars
23
26
  generatorname = os.path.basename(__file__)
24
27
  generatorversion = "0.0.1"
25
28
  valid_formats = ["ttl"]
29
+ file_extension = "shacl.ttl"
26
30
  visit_all_class_slots = False
27
31
  uses_schemaloader = True
28
32
 
@@ -58,7 +62,7 @@ class ShaclGenerator(Generator):
58
62
 
59
63
  class_uri = URIRef(sv.get_uri(c, expand=True))
60
64
  shape_pv(RDF.type, SH.NodeShape)
61
- shape_pv(SH.targetClass, class_uri) ## TODO
65
+ shape_pv(SH.targetClass, class_uri) # TODO
62
66
  if c.mixin or c.abstract:
63
67
  shape_pv(SH.closed, Literal(False))
64
68
  else:
@@ -68,9 +72,8 @@ class ShaclGenerator(Generator):
68
72
  if c.description is not None:
69
73
  shape_pv(SH.description, Literal(c.description))
70
74
  list_node = BNode()
71
- coll = Collection(g, list_node, [RDF.type])
75
+ Collection(g, list_node, [RDF.type])
72
76
  shape_pv(SH.ignoredProperties, list_node)
73
- type_designator = None
74
77
  order = 0
75
78
  for s in sv.class_induced_slots(c.name):
76
79
  # fixed in linkml-runtime 1.1.3
@@ -118,23 +121,34 @@ class ShaclGenerator(Generator):
118
121
  elif r in sv.all_enums():
119
122
  e = sv.get_enum(r)
120
123
  pv_node = BNode()
121
- pv_coll = Collection(
124
+ Collection(
122
125
  g,
123
126
  pv_node,
124
127
  [
125
- URIRef(sv.expand_curie(pv.meaning))
126
- if pv.meaning
127
- else Literal(pv_name)
128
+ URIRef(sv.expand_curie(pv.meaning)) if pv.meaning else Literal(pv_name)
128
129
  for pv_name, pv in e.permissible_values.items()
129
130
  ],
130
131
  )
131
132
  prop_pv(SH["in"], pv_node)
132
133
  else:
133
- None # TODO
134
+ if r == "string":
135
+ prop_pv(SH.datatype, LINK_ML_TYPES_STRING)
136
+ elif r == "boolean":
137
+ prop_pv(SH.datatype, LINK_ML_TYPES_BOOL)
138
+ elif r == "duration":
139
+ prop_pv(SH.datatype, LINK_ML_TYPES_DURATION)
140
+ elif r == "datetime":
141
+ prop_pv(SH.datatype, LINK_ML_TYPES_DATETIME)
142
+ elif r == "date":
143
+ prop_pv(SH.datatype, LINK_ML_TYPES_DATE)
144
+ elif r == "time":
145
+ prop_pv(SH.datatype, LINK_ML_TYPES_TIME)
146
+ elif r == "decimal":
147
+ prop_pv(SH.datatype, LINK_ML_TYPES_DECIMAL)
148
+ elif r == "integer":
149
+ prop_pv(SH.datatype, LINK_ML_TYPES_INTEGER)
134
150
  if s.pattern:
135
151
  prop_pv(SH.pattern, Literal(s.pattern))
136
- if s.designates_type:
137
- type_designator = s
138
152
 
139
153
  return g
140
154