linkml 1.6.4__py3-none-any.whl → 1.6.5__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.
@@ -1,6 +1,6 @@
1
1
  import logging
2
- import os
3
- from dataclasses import dataclass
2
+ from dataclasses import dataclass, field
3
+ from pathlib import Path
4
4
  from typing import List
5
5
 
6
6
  import click
@@ -8,78 +8,80 @@ from linkml_runtime.utils.schemaview import SchemaView
8
8
  from openpyxl import Workbook
9
9
  from openpyxl.utils import get_column_letter
10
10
  from openpyxl.worksheet.datavalidation import DataValidation
11
- from openpyxl.worksheet.worksheet import Worksheet
12
11
 
13
12
  from linkml._version import __version__
14
13
  from linkml.utils.generator import Generator, shared_arguments
15
- from linkml.utils.helpers import convert_to_snake_case
16
14
 
17
15
 
18
16
  @dataclass
19
17
  class ExcelGenerator(Generator):
20
18
  # ClassVars
21
- generatorname = os.path.basename(__file__)
19
+ generatorname = Path(__file__).name
22
20
  generatorversion = "0.1.1"
23
21
  valid_formats = ["xlsx"]
24
22
  uses_schemaloader = False
25
23
  requires_metamodel = False
26
24
 
25
+ split_workbook_by_class: bool = field(default_factory=lambda: False)
26
+
27
27
  def __post_init__(self) -> None:
28
28
  super().__post_init__()
29
29
  self.logger = logging.getLogger(__name__)
30
30
  self.schemaview = SchemaView(self.schema)
31
31
 
32
- def create_workbook(self, workbook_name: str) -> Workbook:
32
+ def create_workbook(self, workbook_path: Path) -> Workbook:
33
33
  """
34
34
  Creates an Excel workbook using the openpyxl library and returns it.
35
35
 
36
- :param workbook_name: Name of the workbook to be created.
36
+ :param workbook_path: Path of the workbook to be created.
37
37
  :return: An openpyxl Workbook object representing the newly created workbook.
38
38
  """
39
39
  workbook = Workbook()
40
- workbook.title = workbook_name
40
+ workbook.save(workbook_path)
41
41
  return workbook
42
42
 
43
- def get_workbook_name(self, workbook: Workbook) -> str:
44
- """
45
- Returns the name of the given workbook.
46
-
47
- :param workbook: The workbook whose name should be returned.
48
- :return: Name of the workbook.
49
- """
50
- return workbook.title
51
-
52
- def remove_worksheet_by_name(self, workbook: Workbook, worksheet_name: str) -> None:
53
- """
54
- Remove worksheet from workbook by name.
55
- """
56
- worksheet = workbook[worksheet_name]
57
- workbook.remove(worksheet)
58
-
59
- def create_worksheet(self, workbook: Workbook, worksheet_name: str) -> Worksheet:
43
+ def create_workbook_and_worksheets(self, output_path: Path, classes: List[str]) -> None:
60
44
  """
61
- Creates an Excel worksheet with the given name in the given workbook.
45
+ Creates a workbook with worksheets for each class.
62
46
 
63
- :param workbook: The workbook to which the worksheet should be added.
64
- :param worksheet_name: Name of the worksheet to be created.
47
+ :param output_path: The path where the workbook should be created.
48
+ :param classes: List of class names for which worksheets should be created.
65
49
  """
66
- worksheet = workbook.create_sheet(worksheet_name)
67
- workbook_name = self.get_workbook_name(workbook)
68
- workbook.save(workbook_name)
50
+ workbook = self.create_workbook(output_path)
51
+ workbook.remove(workbook.active)
52
+ sv = self.schemaview
69
53
 
70
- return worksheet
54
+ for cls_name in classes:
55
+ cls = sv.get_class(class_name=cls_name, imports=self.mergeimports)
56
+ if not cls.mixin and not cls.abstract:
57
+ workbook.create_sheet(cls_name)
71
58
 
72
- def create_schema_worksheets(self, workbook: str) -> None:
73
- """
74
- Creates worksheets in a given Excel workbook based on the classes in the
75
- schema.
59
+ # Add columns to the worksheet for the current class
60
+ slots = [s.name for s in sv.class_induced_slots(cls_name, self.mergeimports)]
61
+ self.add_columns_to_worksheet(workbook, cls_name, slots)
62
+ workbook.save(output_path)
76
63
 
77
- :param workbook: The workbook to which the worksheet should be added.
78
- """
79
- sv = self.schemaview
80
- for cls_name, cls in sv.all_classes(imports=self.mergeimports).items():
81
- if not cls.mixin and not cls.abstract:
82
- self.create_worksheet(workbook, cls_name)
64
+ # Add enum validation for columns with enum types
65
+ enum_list = list(sv.all_enums(imports=self.mergeimports).keys())
66
+ for s in sv.class_induced_slots(cls_name, self.mergeimports):
67
+ if s.range in enum_list:
68
+ pv_list = list(sv.get_enum(s.range).permissible_values.keys())
69
+
70
+ # Check if the total length of permissible values is <= 255 characters
71
+ enum_length = sum(len(value) for value in pv_list)
72
+ if enum_length <= 255:
73
+ self.column_enum_validation(workbook, cls_name, s.name, pv_list)
74
+ else:
75
+ self.logger.warning(
76
+ f"'{s.range}' has permissible values with total "
77
+ "length > 255 characters. Dropdowns may not work properly "
78
+ f"in {output_path}"
79
+ )
80
+ workbook.save(output_path)
81
+
82
+ workbook.save(output_path)
83
+ if self.split_workbook_by_class:
84
+ self.logger.info(f"The Excel workbooks have been written to {output_path}")
83
85
 
84
86
  def add_columns_to_worksheet(self, workbook: Workbook, worksheet_name: str, sheet_headings: List[str]) -> None:
85
87
  """
@@ -96,10 +98,6 @@ class ExcelGenerator(Generator):
96
98
  for i, heading in enumerate(sheet_headings):
97
99
  worksheet.cell(row=1, column=i + 1, value=heading)
98
100
 
99
- # Save the changes to the workbook
100
- workbook_name = self.get_workbook_name(workbook)
101
- workbook.save(workbook_name)
102
-
103
101
  def column_enum_validation(
104
102
  self,
105
103
  workbook: Workbook,
@@ -129,48 +127,52 @@ class ExcelGenerator(Generator):
129
127
 
130
128
  dv.add(f"{column_letter}2:{column_letter}1048576")
131
129
 
132
- workbook_name = self.get_workbook_name(workbook)
133
- workbook.save(workbook_name)
134
-
135
130
  def serialize(self, **kwargs) -> str:
136
- self.output = (
137
- os.path.abspath(convert_to_snake_case(self.schema.name) + ".xlsx") if not self.output else self.output
138
- )
131
+ sv = self.schemaview
132
+ classes_to_process = [
133
+ cls_name
134
+ for cls_name, cls in sv.all_classes(imports=self.mergeimports).items()
135
+ if not cls.mixin and not cls.abstract
136
+ ]
139
137
 
140
- workbook = self.create_workbook(self.output)
141
- self.remove_worksheet_by_name(workbook, "Sheet")
142
- self.create_schema_worksheets(workbook)
138
+ if self.split_workbook_by_class:
139
+ output_path = Path(self.schema.name + "_worksheets") if not self.output else Path(self.output)
140
+ output_path = output_path.absolute()
143
141
 
144
- sv = self.schemaview
145
- for cls_name, cls in sv.all_classes(imports=self.mergeimports).items():
146
- if not cls.mixin and not cls.abstract:
147
- slots = [s.name for s in sv.class_induced_slots(cls_name, imports=self.mergeimports)]
148
- self.add_columns_to_worksheet(workbook, cls_name, slots)
142
+ if not output_path.is_dir():
143
+ output_path.mkdir(parents=True, exist_ok=True)
149
144
 
150
- enum_list = [e_name for e_name, _ in sv.all_enums(imports=self.mergeimports).items()]
151
- for cls_name, cls in sv.all_classes(imports=self.mergeimports).items():
152
- if not cls.mixin and not cls.abstract:
153
- for s in sv.class_induced_slots(cls_name, imports=self.mergeimports):
154
- if s.range in enum_list:
155
- pv_list = []
156
- for pv_name, _ in sv.get_enum(s.range).permissible_values.items():
157
- pv_list.append(pv_name)
158
- self.column_enum_validation(workbook, cls_name, s.name, pv_list)
159
- self.logger.info(f"The Excel workbook has been written to {self.output}")
145
+ for cls_name in classes_to_process:
146
+ cls_output_path = output_path.joinpath(f"{cls_name}.xlsx")
147
+ self.create_workbook_and_worksheets(cls_output_path, [cls_name])
148
+ self.logger.info(f"The Excel workbook for class '{cls_name}' has been written to {cls_output_path}")
149
+ else:
150
+ output_path = Path(self.schema.name + ".xlsx") if not self.output else Path(self.output)
151
+ output_path = output_path.absolute()
152
+
153
+ self.create_workbook_and_worksheets(output_path, classes_to_process)
154
+
155
+ self.logger.info(f"The Excel workbook has been written to {output_path}")
160
156
 
161
157
 
162
158
  @shared_arguments(ExcelGenerator)
163
159
  @click.command()
160
+ @click.option(
161
+ "--split-workbook-by-class",
162
+ is_flag=True,
163
+ default=False,
164
+ help="""Split model into separate Excel workbooks/files, one for each class""",
165
+ )
164
166
  @click.option(
165
167
  "-o",
166
168
  "--output",
167
169
  type=click.Path(),
168
- help="""Name of Excel spreadsheet to be created""",
170
+ help="""Name of Excel spreadsheet to be created, or name of directory to create split workbooks in""",
169
171
  )
170
172
  @click.version_option(__version__, "-V", "--version")
171
- def cli(yamlfile, **kwargs):
173
+ def cli(yamlfile, split_workbook_by_class, **kwargs):
172
174
  """Generate Excel representation of a LinkML model"""
173
- ExcelGenerator(yamlfile, **kwargs).serialize(**kwargs)
175
+ ExcelGenerator(yamlfile, split_workbook_by_class=split_workbook_by_class, **kwargs).serialize(**kwargs)
174
176
 
175
177
 
176
178
  if __name__ == "__main__":
@@ -108,7 +108,46 @@ class {{ e.name }}(str{% if e['values'] %}, Enum{% endif %}):
108
108
  {% endfor %}
109
109
  """
110
110
  ### CLASSES ###
111
- template += """
111
+ if pydantic_ver == "1":
112
+ template += """
113
+ {%- for c in schema.classes.values() %}
114
+ class {{ c.name }}
115
+ {%- if class_isa_plus_mixins[c.name] -%}
116
+ ({{class_isa_plus_mixins[c.name]|join(', ')}})
117
+ {%- else -%}
118
+ (ConfiguredBaseModel)
119
+ {%- endif -%}
120
+ :
121
+ {% if c.description -%}
122
+ \"\"\"
123
+ {{ c.description }}
124
+ \"\"\"
125
+ {%- endif %}
126
+ {% for attr in c.attributes.values() if c.attributes -%}
127
+ {{attr.name}}: {{ attr.annotations['python_range'].value }} = Field(
128
+ {%- if predefined_slot_values[c.name][attr.name] -%}
129
+ {{ predefined_slot_values[c.name][attr.name] }}
130
+ {%- elif (attr.required or attr.identifier or attr.key) -%}
131
+ ...
132
+ {%- else -%}
133
+ None
134
+ {%- endif -%}
135
+ {%- if attr.title != None %}, title="{{attr.title}}"{% endif -%}
136
+ {%- if attr.description %}, description=\"\"\"{{attr.description}}\"\"\"{% endif -%}
137
+ {%- if attr.pattern %}, regex=\"{{attr.pattern}}\"{% endif -%}
138
+ {%- if attr.equals_number != None %}, le={{attr.equals_number}}, ge={{attr.equals_number}}
139
+ {%- else -%}
140
+ {%- if attr.minimum_value != None %}, ge={{attr.minimum_value}}{% endif -%}
141
+ {%- if attr.maximum_value != None %}, le={{attr.maximum_value}}{% endif -%}
142
+ {%- endif -%}
143
+ )
144
+ {% else -%}
145
+ None
146
+ {% endfor %}
147
+ {% endfor %}
148
+ """
149
+ elif pydantic_ver == "2":
150
+ template += """
112
151
  {%- for c in schema.classes.values() %}
113
152
  class {{ c.name }}
114
153
  {%- if class_isa_plus_mixins[c.name] -%}
@@ -133,6 +172,7 @@ class {{ c.name }}
133
172
  {%- endif -%}
134
173
  {%- if attr.title != None %}, title="{{attr.title}}"{% endif -%}
135
174
  {%- if attr.description %}, description=\"\"\"{{attr.description}}\"\"\"{% endif -%}
175
+ {%- if attr.pattern %}, pattern=\"{{attr.pattern}}\"{% endif -%}
136
176
  {%- if attr.equals_number != None %}, le={{attr.equals_number}}, ge={{attr.equals_number}}
137
177
  {%- else -%}
138
178
  {%- if attr.minimum_value != None %}, ge={{attr.minimum_value}}{% endif -%}
@@ -144,6 +184,7 @@ class {{ c.name }}
144
184
  {% endfor %}
145
185
  {% endfor %}
146
186
  """
187
+
147
188
  ### FWD REFS / REBUILD MODEL ###
148
189
  if pydantic_ver == "1":
149
190
  template += """
@@ -509,6 +509,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
509
509
  initializers += [self.gen_class_variable(cls, slot, False) for slot in slot_variables]
510
510
 
511
511
  # Followed by everything else
512
+
512
513
  slot_variables = self._slot_iter(cls, lambda slot: not slot.required and slot in domain_slots)
513
514
  initializers += [self.gen_class_variable(cls, slot, False) for slot in slot_variables]
514
515
 
@@ -604,7 +605,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
604
605
 
605
606
  def class_reference_type(self, slot: SlotDefinition, cls: Optional[ClassDefinition]) -> Tuple[str, str, str]:
606
607
  """
607
- Return the type of a slot referencing a class
608
+ Return the type of slot referencing a class
608
609
 
609
610
  :param slot: slot to be typed
610
611
  :param cls: owning class. Used for generating key references
@@ -734,6 +735,12 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
734
735
  return typ_name
735
736
 
736
737
  def gen_constructor(self, cls: ClassDefinition) -> Optional[str]:
738
+ """
739
+ Generate python constructor for class
740
+
741
+ :param cls: class to generate constructor for
742
+ :return: python constructor
743
+ """
737
744
  rlines: List[str] = []
738
745
  designators = [x for x in self.domain_slots(cls) if x.designates_type]
739
746
  if len(designators) > 0:
@@ -845,7 +852,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
845
852
  elif slot.inlined:
846
853
  slot_range_cls = self.schema.classes[slot.range]
847
854
  identifier = self.class_identifier(slot_range_cls)
848
- # If we don't have an identifier and we are expecting to be inlined first class elements
855
+ # If we don't have an identifier, and we are expecting to be inlined first class elements
849
856
  # (inlined_as_list is not True), we will use the first required field as the key.
850
857
  # Note that this may not always work, but the workaround is straight forward -- set inlined_as_list to
851
858
  # True
linkml/utils/generator.py CHANGED
@@ -223,7 +223,7 @@ class Generator(metaclass=abc.ABCMeta):
223
223
  else:
224
224
  if isinstance(schema, SchemaDefinition):
225
225
  # schemaloader based methods require schemas to have been created via SchemaLoader,
226
- # which prepopulates some fields (e.g definition_url). If the schema has not been processed through the
226
+ # which prepopulates some fields (e.g. definition_url). If the schema has not been processed through the
227
227
  # loader, then roundtrip
228
228
  if any(c for c in schema.classes.values() if not c.definition_uri):
229
229
  schema = yaml_dumper.dumps(schema)
@@ -339,11 +339,11 @@ class Generator(metaclass=abc.ABCMeta):
339
339
 
340
340
  def visit_class_slot(self, cls: ClassDefinition, aliased_slot_name: str, slot: SlotDefinition) -> None:
341
341
  """Visited for each slot in a class. If class level visit_all_slots is true, this is visited once
342
- for any class that is inherited (class itself, is_a, mixin, apply_to). Otherwise just the own slots.
342
+ for any class that is inherited (class itself, is_a, mixin, apply_to). Otherwise, just the own slots.
343
343
 
344
344
  @param cls: containing class
345
345
  @param aliased_slot_name: Aliased slot name. May not be unique across all class slots
346
- @param slot: slot being visited
346
+ @param slot: being visited
347
347
  """
348
348
  ...
349
349
 
@@ -758,11 +758,28 @@ class Generator(metaclass=abc.ABCMeta):
758
758
  # TODO: add lru cache once we get identity into the classes
759
759
  def domain_slots(self, cls: ClassDefinition) -> List[SlotDefinition]:
760
760
  """Return all slots in the class definition that are owned by the class"""
761
- return [
762
- slot
763
- for slot in [self.schema.slots[sn] for sn in cls.slots]
764
- if cls.name in slot.domain_of or (set(cls.mixins).intersection(slot.domain_of))
765
- ]
761
+ domain_slots = []
762
+ for slot_name in cls.slots:
763
+ slot = self.schema.slots[slot_name]
764
+
765
+ # add any mixin ancestors here so that slots will be distributed to descendents correctly via mixin
766
+ # hierarchy.
767
+ mixin_ancestors = []
768
+ if cls.mixins:
769
+ for mixin in cls.mixins:
770
+ for ancestor in self.schemaview.class_ancestors(mixin, mixins=False):
771
+ if ancestor not in mixin_ancestors:
772
+ mixin_ancestors.append(ancestor)
773
+
774
+ for mixin_ancestor in mixin_ancestors:
775
+ if mixin_ancestor not in cls.mixins:
776
+ cls.mixins.append(mixin_ancestor)
777
+
778
+ # Check if the class is in the domain of the slot or if any of its mixins are in the domain
779
+ if cls.name in slot.domain_of or (set(cls.mixins).intersection(slot.domain_of)):
780
+ domain_slots.append(slot)
781
+
782
+ return domain_slots
766
783
 
767
784
  def add_mappings(self, defn: Definition) -> None:
768
785
  """
@@ -691,7 +691,7 @@ class SchemaLoader:
691
691
  cls.is_a,
692
692
  )
693
693
  for mixin in cls.mixins:
694
- # Note that apply_to has ben injected as a faux mixin so it gets covered here
694
+ # Note that apply_to has been injected as a faux mixin, so it gets covered here
695
695
  if mixin in self.schema.classes:
696
696
  self.merge_class(self.schema.classes[mixin], merged_classes)
697
697
  merge_classes(self.schema, cls, self.schema.classes[mixin], True)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: linkml
3
- Version: 1.6.4
3
+ Version: 1.6.5
4
4
  Summary: Linked Open Data Modeling Language
5
5
  Home-page: https://linkml.io/linkml/
6
6
  Keywords: schema,linked data,data modeling,rdf,owl,biolink
@@ -19,7 +19,7 @@ linkml/generators/docgen/type.md.jinja2,sha256=QmCMJZrFwP33eHkggBVtypbyrxTb-XZn9
19
19
  linkml/generators/docgen.py,sha256=zsLzbXN2t9pafQubEnb7QshufMKBMqUWjdLE1OyFyq8,33296
20
20
  linkml/generators/dotgen.py,sha256=CnbVY6CO1OMuiYXYnvxgNN2IW1mtOQW-J-QnwZlXkUI,5012
21
21
  linkml/generators/erdiagramgen.py,sha256=Gu-_nhLuEPTsYYaoV6tNS1V6cZ2dNJdm6YwxC0VGl7g,10315
22
- linkml/generators/excelgen.py,sha256=uFc8xOCeBhWhWdHPTepotBBaIQPtBA6MkFfVtiKWGhw,6699
22
+ linkml/generators/excelgen.py,sha256=ClzWs3wO-QjH2XzzQVUsGF2n6fnoozmXopxPYDEJyPs,7232
23
23
  linkml/generators/golanggen.py,sha256=Dnl7dhmb1AIK3Is7KRaUbxPd3kBTjWuspFqardiBTJ8,5751
24
24
  linkml/generators/golrgen.py,sha256=tIsbsr4SM9HxeK7TCUwnq-GdSKZ_qW5f7fybg_aqCZE,3436
25
25
  linkml/generators/graphqlgen.py,sha256=6qZpI0rwg3ypsv_KrLVzXgdsJfR8LNPqgMwaRwzwnDs,2151
@@ -39,8 +39,8 @@ linkml/generators/plantumlgen.py,sha256=Vs__5x9ioiT4IBTbvZUpgT8MsYJ0amfBL64MB_nm
39
39
  linkml/generators/prefixmapgen.py,sha256=JJ7hgzuqKVfFZrbDV76Dk8dR2NHsmpp-eNUAspXkfwA,4626
40
40
  linkml/generators/projectgen.py,sha256=EVgS5bDzFTm3WAuMg3lC3rzdcaW-hgpq99qZA4nksSY,9544
41
41
  linkml/generators/protogen.py,sha256=9YfxBZkQdBWwsUbstxEUR4xRWNuAKSfz9zXPhgIYePU,2328
42
- linkml/generators/pydanticgen.py,sha256=CiUHkThpYcVzSkWb4IODhteKau3z-ZQ7yQpFA9dSRGw,22298
43
- linkml/generators/pythongen.py,sha256=DBsZNa9T3fGW4Qw0_l8N72f_DeUEugvcbRN9FJLi1QM,52166
42
+ linkml/generators/pydanticgen.py,sha256=rH__R3Pt7TKQzG2DBo1R5OehQG3FaBMCjXBzZlAWMqw,23742
43
+ linkml/generators/pythongen.py,sha256=7XNlOpScOOpxpo9Q_sasDOZFYaKifbXWQL-OKczpkfY,52327
44
44
  linkml/generators/rdfgen.py,sha256=LxzYBaFEkV7rlf54nWv_6H6AGcWMRXwkaeVXq9VYEc8,2693
45
45
  linkml/generators/shaclgen.py,sha256=KxNmDZW2ciCuSqUhJ65TxLTjF8jME1FmN5SaWJCuW9k,8662
46
46
  linkml/generators/shexgen.py,sha256=Awtn5SyjS-TUcVCwMdT0F7hNO4K8VcSCYBaFru45Mwg,8994
@@ -86,7 +86,7 @@ linkml/utils/converter.py,sha256=rdhCI7Tsjddr3o1rVBfMq5gQubk_GE6fqlBBmyxI5_M,627
86
86
  linkml/utils/datautils.py,sha256=2XWM9LBSVp8v3SwIZECrX3SjDUYzdnP-syjp6YdL89E,3734
87
87
  linkml/utils/datavalidator.py,sha256=kBdWaVi8IZT1bOwEJgJYx-wZAb_PTBObB9nHpYORfKA,472
88
88
  linkml/utils/execute_tutorial.py,sha256=T4kHTSyz3ItJGEUZxVjR-3yLVKnOr5Ix4NMGE47-IuE,6912
89
- linkml/utils/generator.py,sha256=74g3s5FVpOqfOtkMTrpk0k95ny2-kSFc0AvcSlKgka0,38320
89
+ linkml/utils/generator.py,sha256=dW9kfVL-7an0ieXR5HLe8bqOyEgDGAoIzdLOu_TFQhc,39101
90
90
  linkml/utils/helpers.py,sha256=yR8n4zFA5wPcYC7xzRuNF3wO16vG80v6j7DM3qTNmIc,447
91
91
  linkml/utils/ifabsent_functions.py,sha256=FZwceqwlq81lMPDsdNfSHhtzDXSf8F4cbbhRdnDzjss,5765
92
92
  linkml/utils/logictools.py,sha256=GSmBiobC49TcQjE08RtXEE3JwJEOV7eEREio25uJiFs,21184
@@ -94,7 +94,7 @@ linkml/utils/mergeutils.py,sha256=QVm2iQB4v_L2rSvPBsPe9C865R03BgV3TzlPoTTTwWQ,90
94
94
  linkml/utils/rawloader.py,sha256=QB7Rdvy4o4ZJEWBWa2_2xzz2TOh_6Oe4slvUn8IBVIc,4329
95
95
  linkml/utils/schema_builder.py,sha256=WLSit3J4lTifaFLLWTwjqIRiTru1pqvTIUuC1TrxS6Y,9902
96
96
  linkml/utils/schema_fixer.py,sha256=rjwJB5ukfrgc0Z-j3mKSNzRMkHPp_k_zFKaFNIPeIv8,15086
97
- linkml/utils/schemaloader.py,sha256=Ju1QCJPdNbLkXzTyvXmv7Bpk6EQ07Hzh7qgeYdiyTU8,46419
97
+ linkml/utils/schemaloader.py,sha256=bBSTqimMDTFH2FcKtRz99dKNJzV_myPsZSkIFp_6-A0,46421
98
98
  linkml/utils/schemasynopsis.py,sha256=6NKa89bkZfZQE9QM0St-6xQcrsHPkYmBgnnWnlgAcQ4,18455
99
99
  linkml/utils/sqlutils.py,sha256=86XeEbfY0Dk-EObw4q5-dxyzSeBtmIhjqqyDcR8ALS0,16591
100
100
  linkml/utils/typereferences.py,sha256=8Yfuz9-HAwOPoJLbIcO_sY9zf32hvPRzGeSOzECfMWA,2232
@@ -123,8 +123,8 @@ linkml/workspaces/datamodel/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMp
123
123
  linkml/workspaces/datamodel/workspaces.py,sha256=4HdkqweGNfMPqnB1_Onc9DcTfkhoagTRcqruh08nRoI,14905
124
124
  linkml/workspaces/datamodel/workspaces.yaml,sha256=EjVrwPpeRZqJRjuGyyDRxxFzuv55SiLIXPBRUG6HStU,4233
125
125
  linkml/workspaces/example_runner.py,sha256=hblnsZVntuwFO4vqcwl_K5XH6jxb52xCtvdc7Sfq_Yc,11452
126
- linkml-1.6.4.dist-info/LICENSE,sha256=kORMoywK6j9_iy0UvLR-a80P1Rvc9AOM4gsKlUNZABg,535
127
- linkml-1.6.4.dist-info/METADATA,sha256=rXsZaS97nlj9NvQH1poCuvB_xzVjmzg6UGzydhsrwSA,3496
128
- linkml-1.6.4.dist-info/WHEEL,sha256=vVCvjcmxuUltf8cYhJ0sJMRDLr1XsPuxEId8YDzbyCY,88
129
- linkml-1.6.4.dist-info/entry_points.txt,sha256=za8r49Z5gcz3rAYTZLbxw5EPZr1rGuxSe1uiRUpf8R0,2143
130
- linkml-1.6.4.dist-info/RECORD,,
126
+ linkml-1.6.5.dist-info/WHEEL,sha256=vVCvjcmxuUltf8cYhJ0sJMRDLr1XsPuxEId8YDzbyCY,88
127
+ linkml-1.6.5.dist-info/LICENSE,sha256=kORMoywK6j9_iy0UvLR-a80P1Rvc9AOM4gsKlUNZABg,535
128
+ linkml-1.6.5.dist-info/METADATA,sha256=dB0BnsijpuHie4nRiNeQ2N5mLQrr0pCu_6msKi2eWZo,3496
129
+ linkml-1.6.5.dist-info/entry_points.txt,sha256=za8r49Z5gcz3rAYTZLbxw5EPZr1rGuxSe1uiRUpf8R0,2143
130
+ linkml-1.6.5.dist-info/RECORD,,
File without changes