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
linkml/utils/generator.py CHANGED
@@ -21,34 +21,36 @@ import re
21
21
  import sys
22
22
  from contextlib import redirect_stdout
23
23
  from dataclasses import dataclass, field
24
+ from functools import lru_cache
24
25
  from io import StringIO
25
26
  from pathlib import Path
26
- from typing import (Callable, Dict, List, Optional, Set, TextIO, Type, Union,
27
- cast, Mapping, ClassVar)
27
+ from typing import Callable, ClassVar, Dict, List, Mapping, Optional, Set, TextIO, Type, Union, cast
28
28
 
29
29
  import click
30
30
  from click import Argument, Command, Option
31
31
  from linkml_runtime import SchemaView
32
32
  from linkml_runtime.dumpers import yaml_dumper
33
- from linkml_runtime.linkml_model.linkml_files import LOCAL_BASE
34
- from linkml_runtime.linkml_model.meta import (ClassDefinition,
35
- ClassDefinitionName, Definition,
36
- Element, ElementName,
37
- EnumDefinition,
38
- EnumDefinitionName,
39
- PrefixPrefixPrefix,
40
- SchemaDefinition, SlotDefinition,
41
- SlotDefinitionName,
42
- SubsetDefinition,
43
- SubsetDefinitionName,
44
- TypeDefinition,
45
- TypeDefinitionName)
46
- import linkml_runtime.linkml_model.meta as metamodel
33
+ from linkml_runtime.linkml_model.meta import (
34
+ ClassDefinition,
35
+ ClassDefinitionName,
36
+ Definition,
37
+ Element,
38
+ ElementName,
39
+ EnumDefinition,
40
+ EnumDefinitionName,
41
+ PrefixPrefixPrefix,
42
+ SchemaDefinition,
43
+ SlotDefinition,
44
+ SlotDefinitionName,
45
+ SubsetDefinition,
46
+ SubsetDefinitionName,
47
+ TypeDefinition,
48
+ TypeDefinitionName,
49
+ )
47
50
  from linkml_runtime.utils.formatutils import camelcase, underscore
48
- from linkml_runtime.utils.introspection import package_schemaview
49
51
  from linkml_runtime.utils.namespaces import Namespaces
50
52
 
51
- from linkml import LOCAL_METAMODEL_YAML_FILE, META_BASE_URI, METAMODEL_YAML_URI
53
+ from linkml import LOCAL_METAMODEL_YAML_FILE
52
54
  from linkml.utils.mergeutils import alias_root
53
55
  from linkml.utils.schemaloader import SchemaLoader
54
56
  from linkml.utils.typereferences import References
@@ -57,6 +59,23 @@ DEFAULT_LOG_LEVEL: str = "WARNING"
57
59
  DEFAULT_LOG_LEVEL_INT: int = logging.WARNING
58
60
 
59
61
 
62
+ @lru_cache
63
+ def _resolved_metamodel(mergeimports):
64
+ if not os.path.exists(LOCAL_METAMODEL_YAML_FILE):
65
+ raise AssertionError(f"{LOCAL_METAMODEL_YAML_FILE} not found")
66
+
67
+ base_dir = str(Path(str(LOCAL_METAMODEL_YAML_FILE)).parent)
68
+ logging.debug(f"BASE={base_dir}")
69
+ metamodel = SchemaLoader(
70
+ LOCAL_METAMODEL_YAML_FILE,
71
+ importmap={"linkml": base_dir},
72
+ base_dir=base_dir,
73
+ mergeimports=mergeimports,
74
+ )
75
+ metamodel.resolve()
76
+ return metamodel
77
+
78
+
60
79
  @dataclass
61
80
  class Generator(metaclass=abc.ABCMeta):
62
81
  """
@@ -79,8 +98,8 @@ class Generator(metaclass=abc.ABCMeta):
79
98
  uses_schemaloader: ClassVar[bool] = True
80
99
  """Old-style generator that uses the SchemaLoader and visitor pattern"""
81
100
 
82
- #uses_schemaview: ClassVar[bool] = True
83
- #"""New-style generator that uses SchemaView"""
101
+ # uses_schemaview: ClassVar[bool] = True
102
+ # """New-style generator that uses SchemaView"""
84
103
 
85
104
  requires_metamodel: ClassVar[bool] = True
86
105
  """Generator queries an instance of the metamodel"""
@@ -104,7 +123,9 @@ class Generator(metaclass=abc.ABCMeta):
104
123
  format: Optional[str] = None
105
124
  """expected output format"""
106
125
 
107
- metadata: bool = field(default_factory= lambda: True)
126
+ file_extension: ClassVar[str] = None
127
+
128
+ metadata: bool = field(default_factory=lambda: True)
108
129
  """True means include date, generator, etc. information in source header if appropriate"""
109
130
 
110
131
  useuris: Optional[bool] = None
@@ -113,7 +134,7 @@ class Generator(metaclass=abc.ABCMeta):
113
134
  log_level: int = DEFAULT_LOG_LEVEL_INT
114
135
  """Logging level, 0 is minimum"""
115
136
 
116
- mergeimports: Optional[bool] = field(default_factory= lambda: True)
137
+ mergeimports: Optional[bool] = field(default_factory=lambda: True)
117
138
  """True means merge non-linkml sources into importing package. False means separate packages"""
118
139
 
119
140
  source_file_date: Optional[str] = None
@@ -129,7 +150,8 @@ class Generator(metaclass=abc.ABCMeta):
129
150
  """Verbosity"""
130
151
 
131
152
  output: Optional[str] = None
132
- """Path to output file. Note all generators may not implement this uniformly, see https://github.com/linkml/linkml/issues/923"""
153
+ """Path to output file. Note all generators may not implement this
154
+ uniformly, see https://github.com/linkml/linkml/issues/923"""
133
155
 
134
156
  namespaces: Optional[Namespaces] = None
135
157
  """All prefix expansions used"""
@@ -140,9 +162,7 @@ class Generator(metaclass=abc.ABCMeta):
140
162
  base_dir: str = None # Base directory of schema
141
163
  """Working directory or base URL of sources"""
142
164
 
143
- metamodel_name_map: Dict[
144
- str, str
145
- ] = None
165
+ metamodel_name_map: Dict[str, str] = None
146
166
  """Allows mapping of names of metamodel elements such as slot, etc"""
147
167
 
148
168
  importmap: Optional[Union[str, Optional[Mapping[str, str]]]] = None
@@ -170,29 +190,21 @@ class Generator(metaclass=abc.ABCMeta):
170
190
  self.source_file_date = None
171
191
  self.source_file_size = None
172
192
  if self.requires_metamodel:
173
- if os.path.exists(LOCAL_METAMODEL_YAML_FILE):
174
- base_dir = str(Path(str(LOCAL_METAMODEL_YAML_FILE)).parent)
175
- logging.debug(f"BASE={base_dir}")
176
- self.metamodel = SchemaLoader(
177
- LOCAL_METAMODEL_YAML_FILE,
178
- importmap={"linkml": base_dir},
179
- base_dir=base_dir,
180
- mergeimports=self.mergeimports,
181
- )
182
- else:
183
- raise AssertionError(f"{LOCAL_METAMODEL_YAML_FILE} not found")
184
- self.metamodel.resolve()
193
+ self.metamodel = _resolved_metamodel(self.mergeimports)
185
194
  schema = self.schema
186
195
  # TODO: remove aliasing
187
196
  self.emit_metadata = self.metadata
188
197
  if self.uses_schemaloader:
189
198
  self._initialize_using_schemaloader(schema)
190
199
  else:
191
- self.schemaview = SchemaView(schema)
200
+ logging.info(f"Using SchemaView with im={self.importmap}")
201
+ self.schemaview = SchemaView(schema, importmap=self.importmap)
192
202
  self.schema = self.schemaview.schema
193
203
  self._init_namespaces()
194
204
 
195
- def _initialize_using_schemaloader(self, schema: Union[str, TextIO, SchemaDefinition, "Generator"]):
205
+ def _initialize_using_schemaloader(
206
+ self, schema: Union[str, TextIO, SchemaDefinition, "Generator"]
207
+ ):
196
208
  # currently generators are very liberal in what they accept, including
197
209
  # other generators.
198
210
  # See https://github.com/linkml/linkml/issues/923 for discussion on how
@@ -251,7 +263,7 @@ class Generator(metaclass=abc.ABCMeta):
251
263
  """
252
264
  Generate output in the required format
253
265
 
254
- :param kwargs: Generater specific parameters
266
+ :param kwargs: Generator specific parameters
255
267
  :return: Generated output
256
268
  """
257
269
  output = StringIO()
@@ -294,9 +306,7 @@ class Generator(metaclass=abc.ABCMeta):
294
306
  ):
295
307
  if self.visit_class(cls):
296
308
  for slot in (
297
- self.all_slots(cls)
298
- if self.visit_all_class_slots
299
- else self.own_slots(cls)
309
+ self.all_slots(cls) if self.visit_all_class_slots else self.own_slots(cls)
300
310
  ):
301
311
  self.visit_class_slot(cls, self.aliased_slot_name(slot), slot)
302
312
  self.end_class(cls)
@@ -376,9 +386,7 @@ class Generator(metaclass=abc.ABCMeta):
376
386
  # =============================
377
387
  # Helper methods
378
388
  # =============================
379
- def own_slots(
380
- self, cls: Union[ClassDefinitionName, ClassDefinition]
381
- ) -> List[SlotDefinition]:
389
+ def own_slots(self, cls: Union[ClassDefinitionName, ClassDefinition]) -> List[SlotDefinition]:
382
390
  """Return the list of slots owned the class definition. An "own slot" is any ``cls`` slot that does not appear
383
391
  in the class is_a parent. Own_slots include:
384
392
 
@@ -440,9 +448,7 @@ class Generator(metaclass=abc.ABCMeta):
440
448
  rval.append(slot)
441
449
  seen.add(sname_base)
442
450
  return rval + (
443
- self.all_slots(parent, cls_slots_first=cls_slots_first, seen=seen)
444
- if parent
445
- else []
451
+ self.all_slots(parent, cls_slots_first=cls_slots_first, seen=seen) if parent else []
446
452
  )
447
453
  else:
448
454
  for sname in cls.slots:
@@ -465,9 +471,7 @@ class Generator(metaclass=abc.ABCMeta):
465
471
  else self.schema.slots[element.is_a]
466
472
  )
467
473
 
468
- def ancestors(
469
- self, element: Union[ClassDefinition, SlotDefinition]
470
- ) -> List[ElementName]:
474
+ def ancestors(self, element: Union[ClassDefinition, SlotDefinition]) -> List[ElementName]:
471
475
  """Return an ordered list of ancestor names for the supplied slot or class
472
476
 
473
477
  @param element: Slot or class name or definition
@@ -477,9 +481,7 @@ class Generator(metaclass=abc.ABCMeta):
477
481
  [] if element.is_a is None else self.ancestors(self.parent(element))
478
482
  )
479
483
 
480
- def neighborhood(
481
- self, elements: Union[str, ElementName, List[ElementName]]
482
- ) -> References:
484
+ def neighborhood(self, elements: Union[str, ElementName, List[ElementName]]) -> References:
483
485
  """Return a list of all slots, classes and types that touch any element in elements, including the element
484
486
  itself
485
487
 
@@ -549,9 +551,7 @@ class Generator(metaclass=abc.ABCMeta):
549
551
  if element in self.schema.subsets:
550
552
  touches.subsetrefs.add(cast(SubsetDefinitionName, element))
551
553
  if element in self.synopsis.subsetrefs:
552
- touches.update(
553
- self.synopsis.subsetrefs[cast(SubsetDefinitionName, element)]
554
- )
554
+ touches.update(self.synopsis.subsetrefs[cast(SubsetDefinitionName, element)])
555
555
  if not bool(touches):
556
556
  self.logger.warning(f"neighborhood({element}) - {element} is undefined")
557
557
 
@@ -566,9 +566,9 @@ class Generator(metaclass=abc.ABCMeta):
566
566
  """
567
567
  formatted_typ_name = self.class_or_type_name(typ.name)
568
568
  if typ.typeof:
569
- return self.range_type_path(
570
- self.schema.types[cast(TypeDefinitionName, typ.typeof)]
571
- ) + [formatted_typ_name]
569
+ return self.range_type_path(self.schema.types[cast(TypeDefinitionName, typ.typeof)]) + [
570
+ formatted_typ_name
571
+ ]
572
572
  elif typ.repr:
573
573
  return [typ.repr, formatted_typ_name]
574
574
  else:
@@ -595,9 +595,7 @@ class Generator(metaclass=abc.ABCMeta):
595
595
  return slotname
596
596
  return None
597
597
 
598
- def enum_identifier_path(
599
- self, enum_or_enumname: Union[str, EnumDefinition]
600
- ) -> List[str]:
598
+ def enum_identifier_path(self, enum_or_enumname: Union[str, EnumDefinition]) -> List[str]:
601
599
  """Return an enum_identifier path"""
602
600
  return [
603
601
  "str",
@@ -656,9 +654,7 @@ class Generator(metaclass=abc.ABCMeta):
656
654
  )
657
655
  if slot.range in self.schema.types:
658
656
  # Type
659
- return self.range_type_path(
660
- self.schema.types[cast(TypeDefinitionName, slot.range)]
661
- )
657
+ return self.range_type_path(self.schema.types[cast(TypeDefinitionName, slot.range)])
662
658
  elif slot.range in self.schema.enums:
663
659
  return self.enum_identifier_path(slot.range)
664
660
  else:
@@ -758,9 +754,7 @@ class Generator(metaclass=abc.ABCMeta):
758
754
  else:
759
755
  return None
760
756
 
761
- def obj_for(
762
- self, el_or_elname: str, is_range_name: bool = False
763
- ) -> Optional[Element]:
757
+ def obj_for(self, el_or_elname: str, is_range_name: bool = False) -> Optional[Element]:
764
758
  if is_range_name:
765
759
  return (
766
760
  self.class_or_type_for(el_or_elname)
@@ -794,8 +788,7 @@ class Generator(metaclass=abc.ABCMeta):
794
788
  return [
795
789
  slot
796
790
  for slot in [self.schema.slots[sn] for sn in cls.slots]
797
- if cls.name in slot.domain_of
798
- or (set(cls.mixins).intersection(slot.domain_of))
791
+ if cls.name in slot.domain_of or (set(cls.mixins).intersection(slot.domain_of))
799
792
  ]
800
793
 
801
794
  def add_mappings(self, defn: Definition) -> None:
@@ -826,9 +819,7 @@ class Generator(metaclass=abc.ABCMeta):
826
819
  else:
827
820
  mapping = mcurie
828
821
  if ":" not in mapping or len(mapping.split(":")) != 2:
829
- raise ValueError(
830
- f"Definition {defn.name} - unrecognized mapping: {mapping}"
831
- )
822
+ raise ValueError(f"Definition {defn.name} - unrecognized mapping: {mapping}")
832
823
  ns = mapping.split(":")[0]
833
824
  logging.debug(f"Adding {ns} from {mapping} // {defn}")
834
825
  if ns:
@@ -873,18 +864,13 @@ class Generator(metaclass=abc.ABCMeta):
873
864
  slot_name_normalized_singular = re.sub(r"s$", "", slot_name_normalized)
874
865
  if slot_name_normalized == "classes":
875
866
  slot_name_normalized_singular = "class"
876
- if (
877
- self.metamodel_name_map is not None
878
- and slot_name_normalized in self.metamodel_name_map
879
- ):
867
+ if self.metamodel_name_map is not None and slot_name_normalized in self.metamodel_name_map:
880
868
  return capitalize(self.metamodel_name_map[slot_name_normalized])
881
869
  elif (
882
870
  self.metamodel_name_map is not None
883
871
  and slot_name_normalized_singular in self.metamodel_name_map
884
872
  ):
885
- return capitalize(
886
- f"{self.metamodel_name_map[slot_name_normalized_singular]}s"
887
- )
873
+ return capitalize(f"{self.metamodel_name_map[slot_name_normalized_singular]}s")
888
874
  else:
889
875
  return slot_name
890
876
 
@@ -926,16 +912,14 @@ def shared_arguments(g: Type[Generator]) -> Callable[[Command], Command]:
926
912
  logging.basicConfig(level=_log_level_string_to_int(value))
927
913
 
928
914
  def decorator(f: Command) -> Command:
929
- f.params.append(
930
- Argument(("yamlfile",), type=click.Path(exists=True, dir_okay=False))
931
- )
915
+ f.params.append(Argument(("yamlfile",), type=click.Path(exists=True, dir_okay=False)))
932
916
  f.params.append(
933
917
  Option(
934
918
  ("--format", "-f"),
935
919
  type=click.Choice(g.valid_formats),
936
920
  default=g.valid_formats[0],
937
921
  show_default=True,
938
- help=f"Output format",
922
+ help="Output format",
939
923
  )
940
924
  )
941
925
  f.params.append(
@@ -955,15 +939,13 @@ def shared_arguments(g: Type[Generator]) -> Callable[[Command], Command]:
955
939
  )
956
940
  )
957
941
  f.params.append(
958
- Option(
959
- ("--importmap", "-im"), type=click.File(), help="Import mapping file"
960
- )
942
+ Option(("--importmap", "-im"), type=click.File(), help="Import mapping file")
961
943
  )
962
944
  f.params.append(
963
945
  Option(
964
946
  ("--log_level",),
965
947
  type=click.Choice(_LOG_LEVEL_STRINGS),
966
- help=f"Logging level",
948
+ help="Logging level",
967
949
  default=DEFAULT_LOG_LEVEL,
968
950
  show_default=True,
969
951
  callback=log_level_callback,
@@ -973,7 +955,7 @@ def shared_arguments(g: Type[Generator]) -> Callable[[Command], Command]:
973
955
  Option(
974
956
  ("--verbose", "-v"),
975
957
  count=True,
976
- help=f"verbosity",
958
+ help="verbosity",
977
959
  callback=verbosity_callback,
978
960
  )
979
961
  )
linkml/utils/helpers.py CHANGED
@@ -1,5 +1,6 @@
1
1
  import re
2
2
 
3
+
3
4
  def remove_duplicates(lst):
4
5
  """Remove duplicate tuples from a list of tuples."""
5
6
  return [t for t in (set(tuple(i) for i in lst))]
@@ -9,6 +10,7 @@ def write_to_file(file_path, data, mode="w", encoding="utf-8"):
9
10
  with open(file_path, mode, encoding=encoding) as f:
10
11
  f.write(data)
11
12
 
13
+
12
14
  def convert_to_snake_case(str):
13
- str = re.sub(r"(?<=[a-z])(?=[A-Z])|[^a-zA-Z]", " ", str).strip().replace(' ', '_')
14
- return ''.join(str.lower())
15
+ str = re.sub(r"(?<=[a-z])(?=[A-Z])|[^a-zA-Z]", " ", str).strip().replace(" ", "_")
16
+ return "".join(str.lower())
@@ -13,20 +13,12 @@ def strval(txt: str) -> str:
13
13
 
14
14
 
15
15
  def default_uri_for(loader: SchemaLoader) -> str:
16
- dflt = (
17
- loader.schema.default_prefix
18
- if loader.schema.default_prefix
19
- else sfx(loader.schema.id)
20
- )
16
+ dflt = loader.schema.default_prefix if loader.schema.default_prefix else sfx(loader.schema.id)
21
17
  return sfx(loader.namespaces.uri_for(dflt))
22
18
 
23
19
 
24
20
  def default_curie_or_uri(loader: SchemaLoader) -> str:
25
- dflt = (
26
- loader.schema.default_prefix
27
- if loader.schema.default_prefix
28
- else sfx(loader.schema.id)
29
- )
21
+ dflt = loader.schema.default_prefix if loader.schema.default_prefix else sfx(loader.schema.id)
30
22
  if ":/" in dflt:
31
23
  prefix = loader.namespaces.prefix_for(loader.schema.default_prefix)
32
24
  if prefix:
@@ -65,6 +57,7 @@ def default_ns_for(loader: SchemaLoader, cls: ClassDefinition) -> str:
65
57
  # cls_id = slotname
66
58
  # return f"sfx(str(self.{cls_id}))" if cls_id else "None"
67
59
 
60
+
68
61
  # Library of named default values -- this is here to prevent code injection
69
62
  # Contents: Match text (as re),
70
63
  # flag that indicates whether we're generating a default value expression or postinig code
@@ -80,12 +73,27 @@ default_library: List[
80
73
  (r"[Tt]rue", False, lambda _, __, ___, ____: "True"),
81
74
  (r"[Ff]alse", False, lambda _, __, ___, ____: "False"),
82
75
  (r"int\(([-+]?[0-9]+)\)", False, lambda m, __, ___, ____: int(m[1])),
83
- (r"float\(([-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)\)", False, lambda m, __, ___, ____: float(m[1])),
84
- (r"date\((\d{4})-(\d{2})-(\d{2})\)", False, lambda m, __, ___, ____: f"datetime.date({m[1]}, {m[2]}, {m[3]})"),
85
- (r"datetime\((\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z\)", False, lambda m, __, ___, ____: f"datetime.datetime({m[1]}, {m[2]}, {m[3]}, {m[4]}, {m[5]}, {m[6]})"),
76
+ (
77
+ r"float\(([-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)\)",
78
+ False,
79
+ lambda m, __, ___, ____: float(m[1]),
80
+ ),
81
+ (
82
+ r"date\((\d{4})-(\d{2})-(\d{2})\)",
83
+ False,
84
+ lambda m, __, ___, ____: f"datetime.date({m[1]}, {m[2]}, {m[3]})",
85
+ ),
86
+ (
87
+ r"datetime\((\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z\)",
88
+ False,
89
+ lambda m, __, ___, ____: f"datetime.datetime({m[1]}, {m[2]}, {m[3]}, {m[4]}, {m[5]}, {m[6]})",
90
+ ),
86
91
  # TODO: We have to make the real URI available before any of these can work
87
- # ("class_uri", True, lambda _, loader, ___, ____: f'"{default_uri_for(loader)}" + camelcase(self.name)'),
88
- # ("slot_uri", True, lambda _, loader, ___, ____: f'"{default_uri_for(loader)}" + underscore(self.alias if self.alias else self.name)'),
92
+ # ("class_uri", True,
93
+ # lambda _, loader, ___, ____: f'"{default_uri_for(loader)}" + camelcase(self.name)'),
94
+ # ("slot_uri", True,
95
+ # lambda _, loader, ___, ____: f'"{default_uri_for(loader)}" +
96
+ # underscore(self.alias if self.alias else self.name)'),
89
97
  # ("class_curie", True, lambda _, loader, ___, ____: curie_for(loader, True)),
90
98
  # ("slot_curie", True, lambda _, loader, ___, ____: curie_for(loader, False)),
91
99
  ("class_uri", True, lambda _, loader, ___, ____: "None"),
@@ -3,12 +3,16 @@ import logging
3
3
  from copy import deepcopy
4
4
  from typing import Dict, List, Optional, Union, cast
5
5
 
6
- from linkml_runtime.linkml_model.meta import (ClassDefinition, Element,
7
- EnumDefinition, SchemaDefinition,
8
- SlotDefinition,
9
- SlotDefinitionName,
10
- TypeDefinition,
11
- TypeDefinitionName)
6
+ from linkml_runtime.linkml_model.meta import (
7
+ ClassDefinition,
8
+ Element,
9
+ EnumDefinition,
10
+ SchemaDefinition,
11
+ SlotDefinition,
12
+ SlotDefinitionName,
13
+ TypeDefinition,
14
+ TypeDefinitionName,
15
+ )
12
16
  from linkml_runtime.utils.formatutils import camelcase, underscore
13
17
  from linkml_runtime.utils.namespaces import Namespaces
14
18
  from linkml_runtime.utils.yamlutils import extended_str
@@ -45,26 +49,14 @@ def merge_schemas(
45
49
  imported_from_uri = imported_from
46
50
  else:
47
51
  imported_from_uri = namespaces.uri_for(imported_from)
48
- merge_dicts(
49
- target.classes, mergee.classes, imported_from, imported_from_uri, merge_imports
50
- )
51
- merge_dicts(
52
- target.slots, mergee.slots, imported_from, imported_from_uri, merge_imports
53
- )
54
- merge_dicts(
55
- target.types, mergee.types, imported_from, imported_from_uri, merge_imports
56
- )
57
- merge_dicts(
58
- target.subsets, mergee.subsets, imported_from, imported_from_uri, merge_imports
59
- )
60
- merge_dicts(
61
- target.enums, mergee.enums, imported_from, imported_from_uri, merge_imports
62
- )
52
+ merge_dicts(target.classes, mergee.classes, imported_from, imported_from_uri, merge_imports)
53
+ merge_dicts(target.slots, mergee.slots, imported_from, imported_from_uri, merge_imports)
54
+ merge_dicts(target.types, mergee.types, imported_from, imported_from_uri, merge_imports)
55
+ merge_dicts(target.subsets, mergee.subsets, imported_from, imported_from_uri, merge_imports)
56
+ merge_dicts(target.enums, mergee.enums, imported_from, imported_from_uri, merge_imports)
63
57
 
64
58
 
65
- def merge_namespaces(
66
- target: SchemaDefinition, mergee: SchemaDefinition, namespaces
67
- ) -> None:
59
+ def merge_namespaces(target: SchemaDefinition, mergee: SchemaDefinition, namespaces) -> None:
68
60
  """
69
61
  Add the mergee namespace definitions to target
70
62
 
@@ -74,7 +66,6 @@ def merge_namespaces(
74
66
  :return:
75
67
  """
76
68
  for prefix in mergee.prefixes.values():
77
-
78
69
  # Handle local prefixes special: we assume that these happen because we are in different (levels of) folders,
79
70
  # and we assume that they reference the same linkml file
80
71
  if "://" not in prefix.prefix_reference:
@@ -110,8 +101,7 @@ def merge_namespaces(
110
101
  # target.prefixes[prefix.prefix_prefix] = prefix
111
102
  if (
112
103
  prefix.prefix_prefix in target.prefixes
113
- and target.prefixes[prefix.prefix_prefix].prefix_reference
114
- != prefix.prefix_reference
104
+ and target.prefixes[prefix.prefix_prefix].prefix_reference != prefix.prefix_reference
115
105
  ):
116
106
  raise ValueError(
117
107
  f"Prefix: {prefix.prefix_prefix} mismatch between {target.name} and {mergee.name}"
@@ -177,11 +167,7 @@ def merge_slots(
177
167
  if skip is None:
178
168
  skip = []
179
169
  for k, v in dataclasses.asdict(source).items():
180
- if (
181
- k not in skip
182
- and v is not None
183
- and (not inheriting or getattr(target, k, None) is None)
184
- ):
170
+ if k not in skip and v is not None and (not inheriting or getattr(target, k, None) is None):
185
171
  if k in source._inherited_slots or not inheriting:
186
172
  setattr(target, k, deepcopy(v))
187
173
  else:
@@ -234,9 +220,7 @@ def merge_classes(
234
220
  if slotbase in target.slot_usage:
235
221
  slotname = slot_usage_name(slotbase, target)
236
222
  if slotbase not in target_base_slots:
237
- target.slots.append(slotname) if at_end else target.slots.insert(
238
- 0, slotname
239
- )
223
+ target.slots.append(slotname) if at_end else target.slots.insert(0, slotname)
240
224
  target_base_slots.add(slotbase)
241
225
 
242
226
 
linkml/utils/rawloader.py CHANGED
@@ -6,9 +6,7 @@ from urllib.parse import urlparse
6
6
  import yaml
7
7
  from dateutil.parser import ParserError, parse
8
8
  from hbreader import FileInfo, HBType, detect_type
9
- from linkml_runtime.linkml_model.meta import (ClassDefinition,
10
- SchemaDefinition, SlotDefinition,
11
- metamodel_version)
9
+ from linkml_runtime.linkml_model.meta import SchemaDefinition, metamodel_version
12
10
  from linkml_runtime.loaders import yaml_loader
13
11
  from linkml_runtime.utils.yamlutils import YAMLMark, YAMLRoot
14
12
 
@@ -56,9 +54,7 @@ def load_raw_schema(
56
54
 
57
55
  # Passing a URL or file name
58
56
  if detect_type(data, base_dir) not in (HBType.STRING, HBType.STRINGABLE):
59
- assert (
60
- source_file is None
61
- ), "source_file parameter not allowed if data is a file or URL"
57
+ assert source_file is None, "source_file parameter not allowed if data is a file or URL"
62
58
  assert (
63
59
  source_file_date is None
64
60
  ), "source_file_date parameter not allowed if data is a file or URL"
@@ -1,11 +1,16 @@
1
- from dataclasses import dataclass, field
2
- from typing import Dict, List, Union, Optional
3
-
4
- from linkml_runtime.linkml_model import (ClassDefinition, EnumDefinition,
5
- PermissibleValue, Prefix,
6
- SchemaDefinition, SlotDefinition,
7
- TypeDefinition)
8
- from linkml_runtime.utils.formatutils import camelcase, underscore
1
+ from dataclasses import dataclass
2
+ from typing import Dict, List, Optional, Union
3
+
4
+ from linkml_runtime.linkml_model import (
5
+ ClassDefinition,
6
+ EnumDefinition,
7
+ PermissibleValue,
8
+ Prefix,
9
+ SchemaDefinition,
10
+ SlotDefinition,
11
+ TypeDefinition,
12
+ )
13
+ from linkml_runtime.utils.formatutils import underscore
9
14
  from linkml_runtime.utils.schema_as_dict import schema_as_dict
10
15
 
11
16
 
@@ -69,6 +74,7 @@ class SchemaBuilder:
69
74
  :param slot_usage: slots keyed by slot name
70
75
  :param replace_if_present: if True, replace existing class if present
71
76
  :param kwargs: additional ClassDefinition properties
77
+ :param use_attributes: if True, add slots as attributes
72
78
  :return: builder
73
79
  :raises ValueError: if class already exists and replace_if_present=False
74
80
  """
@@ -83,10 +89,12 @@ class SchemaBuilder:
83
89
  for s in slots:
84
90
  if isinstance(s, SlotDefinition):
85
91
  cls.attributes[s.name] = s
92
+ elif isinstance(s, dict):
93
+ cls.attributes[s["name"]] = s
94
+ elif isinstance(s, str):
95
+ cls.attributes[s] = SlotDefinition(s)
86
96
  else:
87
- raise ValueError(
88
- f"If use_attributes=True then slots must be SlotDefinitions"
89
- )
97
+ raise ValueError("If use_attributes=True then slots must be SlotDefinitions")
90
98
  else:
91
99
  if slots is not None:
92
100
  for s in slots:
@@ -103,7 +111,11 @@ class SchemaBuilder:
103
111
  return self
104
112
 
105
113
  def add_slot(
106
- self, slot: Union[SlotDefinition, Dict, str], class_name: str = None, replace_if_present=False, **kwargs
114
+ self,
115
+ slot: Union[SlotDefinition, Dict, str],
116
+ class_name: str = None,
117
+ replace_if_present=False,
118
+ **kwargs,
107
119
  ) -> "SchemaBuilder":
108
120
  """
109
121
  Adds the slot to the schema.
@@ -170,7 +182,7 @@ class SchemaBuilder:
170
182
  enum_def.permissible_values[pv.text] = pv
171
183
  return self
172
184
 
173
- def add_prefix(self, prefix: str, url: str, replace_if_present = False) -> "SchemaBuilder":
185
+ def add_prefix(self, prefix: str, url: str, replace_if_present=False) -> "SchemaBuilder":
174
186
  """
175
187
  Adds a prefix for use with CURIEs
176
188
 
@@ -205,12 +217,12 @@ class SchemaBuilder:
205
217
  return self
206
218
 
207
219
  def add_type(
208
- self,
209
- type: Union[TypeDefinition, Dict, str],
210
- typeof: str = None,
211
- uri: str = None,
212
- replace_if_present=False,
213
- **kwargs
220
+ self,
221
+ type: Union[TypeDefinition, Dict, str],
222
+ typeof: str = None,
223
+ uri: str = None,
224
+ replace_if_present=False,
225
+ **kwargs,
214
226
  ) -> "SchemaBuilder":
215
227
  """
216
228
  Adds the type to the schema