linkml 1.6.3__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,7 +1,11 @@
1
- {%- if element.title -%}
2
- {%- set title = element.title ~ ' (' ~ gen.name(element) ~ ')' -%}
3
- {%- else -%}
4
- {%- set title = gen.name(element) -%}
1
+ {%- if element.title %}
2
+ {%- set title = element.title ~ ' (' ~ element.name ~ ')' -%}
3
+ {%- else %}
4
+ {%- if gen.use_class_uris -%}
5
+ {%- set title = element.name -%}
6
+ {%- else -%}
7
+ {%- set title = gen.name(element) -%}
8
+ {%- endif -%}
5
9
  {%- endif -%}
6
10
 
7
11
  # Class: {{ title }}
@@ -1,7 +1,11 @@
1
1
  {%- if element.title %}
2
2
  {%- set title = element.title ~ ' (' ~ element.name ~ ')' -%}
3
3
  {%- else %}
4
- {%- set title = gen.name(element) -%}
4
+ {%- if gen.use_slot_uris -%}
5
+ {%- set title = element.name -%}
6
+ {%- else -%}
7
+ {%- set title = gen.name(element) -%}
8
+ {%- endif -%}
5
9
  {%- endif -%}
6
10
 
7
11
  # Slot: {{ title }}
@@ -150,6 +150,7 @@ class DocGenerator(Generator):
150
150
  gen_slots: bool = field(default_factory=lambda: True)
151
151
  no_types_dir: bool = field(default_factory=lambda: False)
152
152
  use_slot_uris: bool = field(default_factory=lambda: False)
153
+ use_class_uris: bool = field(default_factory=lambda: False)
153
154
  hierarchical_class_view: bool = field(default_factory=lambda: False)
154
155
 
155
156
  def __post_init__(self):
@@ -331,6 +332,13 @@ class DocGenerator(Generator):
331
332
  return curie.split(":")[1]
332
333
 
333
334
  return underscore(element.name)
335
+ elif type(element).class_name == "class_definition":
336
+ if self.use_class_uris:
337
+ curie = self.schemaview.get_uri(element)
338
+ if curie:
339
+ return curie.split(":")[1]
340
+
341
+ return camelcase(element.name)
334
342
  else:
335
343
  return camelcase(element.name)
336
344
 
@@ -374,6 +382,10 @@ class DocGenerator(Generator):
374
382
  if self._is_external(e):
375
383
  return self.uri_link(e)
376
384
  elif isinstance(e, ClassDefinition):
385
+ if self.use_class_uris:
386
+ curie = self.schemaview.get_uri(e)
387
+ if curie is not None:
388
+ return self._markdown_link(n=curie.split(":")[1], name=e.name)
377
389
  return self._markdown_link(camelcase(e.name))
378
390
  elif isinstance(e, EnumDefinition):
379
391
  return self._markdown_link(camelcase(e.name))
@@ -474,7 +486,7 @@ class DocGenerator(Generator):
474
486
  ) -> str:
475
487
  indent = " " * depth * 4
476
488
 
477
- if self.use_slot_uris:
489
+ if self.use_slot_uris or self.use_class_uris:
478
490
  name = self.schemaview.get_element(element).name
479
491
  else:
480
492
  name = self.name(element)
@@ -878,6 +890,11 @@ class DocGenerator(Generator):
878
890
  default=False,
879
891
  help="Use IDs from slot_uri instead of names",
880
892
  )
893
+ @click.option(
894
+ "--use-class-uris/--no-use-class-uris",
895
+ default=False,
896
+ help="Use IDs from class_uri instead of names",
897
+ )
881
898
  @click.option(
882
899
  "--hierarchical-class-view/--no-hierarchical-class-view",
883
900
  default=True,
@@ -889,7 +906,17 @@ class DocGenerator(Generator):
889
906
  )
890
907
  @click.version_option(__version__, "-V", "--version")
891
908
  @click.command()
892
- def cli(yamlfile, directory, index_name, dialect, template_directory, use_slot_uris, hierarchical_class_view, **args):
909
+ def cli(
910
+ yamlfile,
911
+ directory,
912
+ index_name,
913
+ dialect,
914
+ template_directory,
915
+ use_slot_uris,
916
+ use_class_uris,
917
+ hierarchical_class_view,
918
+ **args,
919
+ ):
893
920
  """Generate documentation folder from a LinkML YAML schema
894
921
 
895
922
  Currently a default set of templates for markdown is provided (see the
@@ -915,6 +942,7 @@ def cli(yamlfile, directory, index_name, dialect, template_directory, use_slot_u
915
942
  dialect=dialect,
916
943
  template_directory=template_directory,
917
944
  use_slot_uris=use_slot_uris,
945
+ use_class_uris=use_class_uris,
918
946
  hierarchical_class_view=hierarchical_class_view,
919
947
  index_name=index_name,
920
948
  **args,
@@ -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__":
@@ -5,8 +5,8 @@ package {{ doc.package }};
5
5
  import java.math.BigDecimal;{% endif -%}
6
6
  {% if f.range.startswith('Instant') %}
7
7
  import java.time.Instant;{% endif -%}
8
- {% if f.range.startswith('LocalDateTime') %}
9
- import java.time.LocalDateTime;{% endif -%}
8
+ {% if f.range.startswith('LocalDate') %}
9
+ import java.time.LocalDate;{% endif -%}
10
10
  {% if f.range.startswith('ZonedDateTime') %}
11
11
  import java.time.ZonedDateTime;{% endif -%}
12
12
  {% if f.range.startswith('List') %}
@@ -45,9 +45,9 @@ TYPEMAP = {
45
45
  "xsd:float": "Float",
46
46
  "xsd:double": "Double",
47
47
  "xsd:boolean": "boolean",
48
- "xds:dateTime": "ZonedDateTime",
49
- "xds:date": "LocalDateTime",
50
- "xds:time": "Instant",
48
+ "xsd:dateTime": "ZonedDateTime",
49
+ "xsd:date": "LocalDate",
50
+ "xsd:time": "Instant",
51
51
  "xsd:anyURI": "String",
52
52
  "xsd:decimal": "BigDecimal",
53
53
  }
@@ -65,19 +65,23 @@ class JsonSchema(UserDict):
65
65
  identifier_name = self._lax_forward_refs.pop(canonical_name)
66
66
  self.add_lax_def(canonical_name, identifier_name)
67
67
 
68
- def add_lax_def(self, name: str, identifier_name: str) -> None:
68
+ def add_lax_def(self, names: Union[str, List[str]], identifier_name: str) -> None:
69
69
  # JSON-Schema does not have inheritance,
70
70
  # so we duplicate slots from inherited parents and mixins
71
71
  # Maps e.g. Person --> Person__identifier_optional
72
72
  # for use when Person is a range of an inlined-as-dict slot
73
- canonical_name = camelcase(name)
73
+ if isinstance(names, str):
74
+ names = [names]
74
75
 
75
- if "$defs" not in self or canonical_name not in self["$defs"]:
76
- self._lax_forward_refs[canonical_name] = identifier_name
77
- else:
78
- lax_cls = deepcopy(self["$defs"][canonical_name])
79
- lax_cls["required"].remove(identifier_name)
80
- self["$defs"][canonical_name + self.OPTIONAL_IDENTIFIER_SUFFIX] = lax_cls
76
+ for name in names:
77
+ canonical_name = camelcase(name)
78
+
79
+ if "$defs" not in self or canonical_name not in self["$defs"]:
80
+ self._lax_forward_refs[canonical_name] = identifier_name
81
+ else:
82
+ lax_cls = deepcopy(self["$defs"][canonical_name])
83
+ lax_cls["required"].remove(identifier_name)
84
+ self["$defs"][canonical_name + self.OPTIONAL_IDENTIFIER_SUFFIX] = lax_cls
81
85
 
82
86
  def add_property(self, name: str, subschema: "JsonSchema", required: bool = False) -> None:
83
87
  canonical_name = underscore(name)
@@ -32,7 +32,7 @@ from linkml.utils.generator import shared_arguments
32
32
  from linkml.utils.ifabsent_functions import ifabsent_value_declaration
33
33
 
34
34
 
35
- def default_template(pydantic_ver: str = "1") -> str:
35
+ def default_template(pydantic_ver: str = "1", extra_fields: str = "forbid") -> str:
36
36
  """Constructs a default template for pydantic classes based on the version of pydantic"""
37
37
  ### HEADER ###
38
38
  template = """
@@ -64,7 +64,7 @@ version = "{{version if version else None}}"
64
64
  """
65
65
  ### BASE MODEL ###
66
66
  if pydantic_ver == "1":
67
- template += """
67
+ template += f"""
68
68
  class WeakRefShimBaseModel(BaseModel):
69
69
  __slots__ = '__weakref__'
70
70
 
@@ -72,18 +72,18 @@ class ConfiguredBaseModel(WeakRefShimBaseModel,
72
72
  validate_assignment = True,
73
73
  validate_all = True,
74
74
  underscore_attrs_are_private = True,
75
- extra = {% if allow_extra %}'allow'{% else %}'forbid'{% endif %},
75
+ extra = '{extra_fields}',
76
76
  arbitrary_types_allowed = True,
77
77
  use_enum_values = True):
78
78
  pass
79
79
  """
80
80
  else:
81
- template += """
81
+ template += f"""
82
82
  class ConfiguredBaseModel(BaseModel):
83
83
  model_config = ConfigDict(
84
84
  validate_assignment=True,
85
85
  validate_default=True,
86
- extra={% if allow_extra %}'allow'{% else %}'forbid'{% endif %},
86
+ extra = '{extra_fields}',
87
87
  arbitrary_types_allowed=True,
88
88
  use_enum_values = True)
89
89
  """
@@ -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 += """
@@ -196,7 +237,7 @@ class PydanticGenerator(OOCodeGenerator):
196
237
  # ObjectVars
197
238
  pydantic_version: str = field(default_factory=lambda: PYDANTIC_VERSION[0])
198
239
  template_file: str = None
199
- allow_extra: bool = field(default_factory=lambda: False)
240
+ extra_fields: str = field(default_factory=lambda: "forbid")
200
241
  gen_mixin_inheritance: bool = field(default_factory=lambda: True)
201
242
 
202
243
  # ObjectVars (identical to pythongen)
@@ -454,7 +495,7 @@ class PydanticGenerator(OOCodeGenerator):
454
495
  with open(self.template_file) as template_file:
455
496
  template_obj = Template(template_file.read())
456
497
  else:
457
- template_obj = Template(default_template(self.pydantic_version))
498
+ template_obj = Template(default_template(self.pydantic_version, self.extra_fields))
458
499
 
459
500
  sv: SchemaView
460
501
  sv = self.schemaview
@@ -536,7 +577,7 @@ class PydanticGenerator(OOCodeGenerator):
536
577
  underscore=underscore,
537
578
  enums=enums,
538
579
  predefined_slot_values=self.get_predefined_slot_values(),
539
- allow_extra=self.allow_extra,
580
+ extra_fields=self.extra_fields,
540
581
  metamodel_version=self.schema.metamodel_version,
541
582
  version=self.schema.version,
542
583
  class_isa_plus_mixins=self.get_class_isa_plus_mixins(),
@@ -548,13 +589,19 @@ class PydanticGenerator(OOCodeGenerator):
548
589
 
549
590
 
550
591
  @shared_arguments(PydanticGenerator)
551
- @click.option("--template_file", help="Optional jinja2 template to use for class generation")
592
+ @click.option("--template-file", help="Optional jinja2 template to use for class generation")
552
593
  @click.option(
553
- "--pydantic_version",
594
+ "--pydantic-version",
554
595
  type=click.Choice(["1", "2"]),
555
596
  default="1",
556
597
  help="Pydantic version to use (1 or 2)",
557
598
  )
599
+ @click.option(
600
+ "--extra-fields",
601
+ type=click.Choice(["allow", "ignore", "forbid"], case_sensitive=False),
602
+ default="forbid",
603
+ help="How to handle extra fields in BaseModel.",
604
+ )
558
605
  @click.version_option(__version__, "-V", "--version")
559
606
  @click.command()
560
607
  def cli(
@@ -566,6 +613,7 @@ def cli(
566
613
  classvars=True,
567
614
  slots=True,
568
615
  pydantic_version="1",
616
+ extra_fields="forbid",
569
617
  **args,
570
618
  ):
571
619
  """Generate pydantic classes to represent a LinkML model"""
@@ -573,6 +621,7 @@ def cli(
573
621
  yamlfile,
574
622
  template_file=template_file,
575
623
  pydantic_version=pydantic_version,
624
+ extra_fields=extra_fields,
576
625
  emit_metadata=head,
577
626
  genmeta=genmeta,
578
627
  gen_classvars=classvars,
@@ -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
@@ -51,12 +51,12 @@ class ShaclGenerator(Generator):
51
51
  if self.schema.version:
52
52
  print(f"# version: {self.schema.version}")
53
53
 
54
- def serialize(self, **args) -> None:
54
+ def serialize(self, **args) -> str:
55
55
  g = self.as_graph()
56
56
  data = g.serialize(format="turtle" if self.format in ["owl", "ttl"] else self.format)
57
57
  return data
58
58
 
59
- def as_graph(self) -> None:
59
+ def as_graph(self) -> Graph:
60
60
  sv = self.schemaview
61
61
  g = Graph()
62
62
  g.bind("sh", SH)
@@ -161,7 +161,7 @@ class SQLAlchemyGenerator(Generator):
161
161
  # concatenate the python dataclasses with the sqla code
162
162
  if pydantic:
163
163
  # mixin inheritance doesn't get along with SQLAlchemy's imperative (aka classical) mapping
164
- pygen = PydanticGenerator(self.original_schema, allow_extra=True, gen_mixin_inheritance=False)
164
+ pygen = PydanticGenerator(self.original_schema, extra_fields="allow", gen_mixin_inheritance=False)
165
165
  else:
166
166
  pygen = PythonGenerator(self.original_schema)
167
167
  dc_code = pygen.serialize()
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)
@@ -51,12 +51,15 @@ class ValidationContext:
51
51
  return jsonschema_gen.generate()
52
52
 
53
53
  def pydantic_model(self, *, closed: bool):
54
- module = self._pydantic_module(closed)
54
+ module = self._pydantic_module(closed=closed)
55
55
  return module.__dict__[self._target_class]
56
56
 
57
57
  @lru_cache
58
- def _pydantic_module(self, *closed: bool):
59
- return PydanticGenerator(self._schema, allow_extra=not closed).compile_module()
58
+ def _pydantic_module(self, *, closed: bool):
59
+ return PydanticGenerator(
60
+ self._schema,
61
+ extra_fields="forbid" if closed else "ignore" if closed is None else "allow",
62
+ ).compile_module()
60
63
 
61
64
  def _get_target_class(self, target_class: Optional[str] = None) -> str:
62
65
  if target_class is None:
@@ -0,0 +1,13 @@
1
+ Apache-2.0
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: linkml
3
- Version: 1.6.3
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
@@ -6,29 +6,29 @@ linkml/generators/__init__.py,sha256=qa22iTeBrcds6ndst_iTPVWLQtBF6NGyhVngBhbhIpc
6
6
  linkml/generators/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  linkml/generators/common/type_designators.py,sha256=lgVcIRJJ-yCvVGeP9U_gQZpm77UmJBQo9Bh3lGJITno,1956
8
8
  linkml/generators/csvgen.py,sha256=h-Mj-uNrzuETx_G3dW6eFbk1jj0NHSwqbehqR5tA6PM,2955
9
- linkml/generators/docgen/class.md.jinja2,sha256=r8bsve3K5jLXD-cTTiMIiOuekNJBDwYAyb-lcnOrDBk,3168
9
+ linkml/generators/docgen/class.md.jinja2,sha256=05Kbl83ApOMzA7gZeRJjPU7jkZHCIsaKN1BYWNMRTfM,3275
10
10
  linkml/generators/docgen/class_diagram.md.jinja2,sha256=ShRhNnIae54Y_4zrfXuIF6BjX8oCHRZJnovE8G1fjOU,2542
11
11
  linkml/generators/docgen/common_metadata.md.jinja2,sha256=zy8Ua3gDtAAq8VA3e3O3ft9W7eJopVZaq5efP8LU_hU,1321
12
12
  linkml/generators/docgen/enum.md.jinja2,sha256=mXnUrRkleY2bOTEyAZ5c4pcUnqhs6BNa8a-4LVve-eo,1014
13
13
  linkml/generators/docgen/index.md.jinja2,sha256=wXUYTmayPLFltC0vbGE_Mf6m3GkkWav7FOEjCvEpHp4,1466
14
14
  linkml/generators/docgen/index.tex.jinja2,sha256=Go_EA-_N4JUpbOYbk3OY11mz5yV70VF2l2sMtgIPWw4,501
15
15
  linkml/generators/docgen/schema.md.jinja2,sha256=xlENfnzNRYgPT_0tdqNFxgklVM4Qf5BuzhFVvSMDuxs,70
16
- linkml/generators/docgen/slot.md.jinja2,sha256=0A5SDAW34Y9e3pMLjImWFWuMHLh53SGvdXx2XBSmEz0,2704
16
+ linkml/generators/docgen/slot.md.jinja2,sha256=XVd0M4gKx9Q2fONcsUGBRj_bJivyN4P9jhj9IO496jQ,2817
17
17
  linkml/generators/docgen/subset.md.jinja2,sha256=fTNIpAkml5RKFbbtLore3IAzFN1cISVsyL1ru2-Z4oA,2665
18
18
  linkml/generators/docgen/type.md.jinja2,sha256=QmCMJZrFwP33eHkggBVtypbyrxTb-XZn9vHOYojVaYk,635
19
- linkml/generators/docgen.py,sha256=k_nRddHEaQzqYX-qNw_0cUjerW30255S2geMPf1z3PY,32504
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
26
26
  linkml/generators/javagen/example_template.java.jinja2,sha256=ec4CVTv_0zS7V5Y-1E6H4lRraya10gfX7BEMBlu38X4,444
27
- linkml/generators/javagen/java_record_template.jinja2,sha256=yS7vKlgkHOQJfMjeziADDBCICri5JBp85BzreSp8QXw,1737
28
- linkml/generators/javagen.py,sha256=Byf_923aAcWhZKeuTDPIB2o6PLuMHJV5WiD_AG0t_wg,5431
27
+ linkml/generators/javagen/java_record_template.jinja2,sha256=OQZffLSy_xR3FIhQMltvrYyVeut7l2Q-tzK7AOiVmWs,1729
28
+ linkml/generators/javagen.py,sha256=oZfUowKnBQNdklRqOM-yKptO4Te4y4N5ItIc5jTuWKU,5427
29
29
  linkml/generators/jsonldcontextgen.py,sha256=2TUzEzFBX7wDOYJ51Kg0hp2CVXcOzJhgIbq0CrzL2oc,7832
30
30
  linkml/generators/jsonldgen.py,sha256=KQYurjqp3gI0bevjzmrw4WDEgz4Yf4o4TJfZsqK4_vs,7575
31
- linkml/generators/jsonschemagen.py,sha256=2SMrdqS_OazUEQtVSm8_A0FUSx4HRd_0zFhoMQa2Nak,23820
31
+ linkml/generators/jsonschemagen.py,sha256=CWTvj7n8pWomRqeb-vmG-z99TpzzvBu0UIbBRRag4eI,23958
32
32
  linkml/generators/legacy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
33
  linkml/generators/linkmlgen.py,sha256=QhIPA1v2g_g5fien3ZKN-L6TkDk3t7puVFrcoEnwkwY,3540
34
34
  linkml/generators/markdowngen.py,sha256=ZPLahEPjWsrAsKq4CHbVDXeVd0n1NO-2STs068-g0Ac,32948
@@ -39,16 +39,16 @@ 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=xncxCk0mwkvkzyrOIwCm4DKVH34OQnqGDUtCi50ogRA,22068
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
- linkml/generators/shaclgen.py,sha256=CRmIv0hYAFY3Z2tnNcQSq6yJeNkoGn9vqx6s2r6kc8c,8662
45
+ linkml/generators/shaclgen.py,sha256=KxNmDZW2ciCuSqUhJ65TxLTjF8jME1FmN5SaWJCuW9k,8662
46
46
  linkml/generators/shexgen.py,sha256=Awtn5SyjS-TUcVCwMdT0F7hNO4K8VcSCYBaFru45Mwg,8994
47
47
  linkml/generators/sparqlgen.py,sha256=xIT4abjYTjPvAjczZ2mkqfap5z8-AImK_jaCvgZyRGs,6120
48
48
  linkml/generators/sqlalchemy/__init__.py,sha256=mb9AC1rIFkSiNZhhG0TAk45ol9PjS1XvsrvCjgfVUpQ,249
49
49
  linkml/generators/sqlalchemy/sqlalchemy_declarative_template.py,sha256=X_Ws1NUBikMI5HuNgEhl_PIeWM-B-c2B0W9KUBH4QTg,2542
50
50
  linkml/generators/sqlalchemy/sqlalchemy_imperative_template.py,sha256=u4ZpponG1h6XROrOHGOf_0H2e6xL1_s8twAOA-gx94A,1622
51
- linkml/generators/sqlalchemygen.py,sha256=W5BJE-PU7kOaWtfwZw8fjyx9E_-ZQGZl1xBkywSpMCY,9182
51
+ linkml/generators/sqlalchemygen.py,sha256=XjvyCYr9h0EsFaKy4y-lMXunYw4dnk9vSpwlPjZK8gU,9186
52
52
  linkml/generators/sqlddlgen.py,sha256=t7U0-ZOWnulA7CMrlaih73H7fQdaGNRhdVLmvWGxFT4,18436
53
53
  linkml/generators/sqltablegen.py,sha256=h_zVuNC8huRzzTa9nSHNWBxq-4TuLq7nR5nZfpDnWiM,11481
54
54
  linkml/generators/sssomgen.py,sha256=yur3q7so9uwIELWZaZRzkJwNbz_ppBL7IQki9XLIM3k,6879
@@ -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
@@ -113,7 +113,7 @@ linkml/validator/plugins/pydantic_validation_plugin.py,sha256=C-Vp74bz5oqp5V-iuh
113
113
  linkml/validator/plugins/recommended_slots_plugin.py,sha256=kOdoYQyye47nLA7BjorVmydS84nGpiVy3MoCbPq1Ymo,2308
114
114
  linkml/validator/plugins/validation_plugin.py,sha256=9SMHF8b2bgG9-8351e8bY676e0A4aEBJSXvMjMF5kXg,1548
115
115
  linkml/validator/report.py,sha256=kkkuh-IZF9--cO-2wGjwP3PDLvOcjjvC8AOlxXUIOAM,870
116
- linkml/validator/validation_context.py,sha256=dDMXCw6TdJC--bu53sb6-v8ch36rGeGbOndoUJXgXVw,2290
116
+ linkml/validator/validation_context.py,sha256=MmOwLk4cF_Cy7fPdFK61Eti3c3dgzKSIu6r_PmkkoZs,2388
117
117
  linkml/validator/validator.py,sha256=jOSdYyC8QIm1GWmllM7Z1_GV-2VO3hwEwdF2AHE-DNY,5476
118
118
  linkml/validators/__init__.py,sha256=43W3J5NPKhwa3ZFHLRYsJMocwQKWGYCF9Ki9r0ccGbc,202
119
119
  linkml/validators/jsonschemavalidator.py,sha256=_v0finzU2RGPC5xo0CylYge9XkY7oAigcly2SKLwFuI,7865
@@ -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.3.dist-info/WHEEL,sha256=vVCvjcmxuUltf8cYhJ0sJMRDLr1XsPuxEId8YDzbyCY,88
127
- linkml-1.6.3.dist-info/LICENSE,sha256=Nv_Z3AhdUpp-YOEnbXOuWgMLAgMT5sVAhZOmrirzlnM,6555
128
- linkml-1.6.3.dist-info/entry_points.txt,sha256=za8r49Z5gcz3rAYTZLbxw5EPZr1rGuxSe1uiRUpf8R0,2143
129
- linkml-1.6.3.dist-info/METADATA,sha256=lRpwApDha7rSgAHvnFPHBIJtLdFO2sI1uWodl36w42U,3496
130
- linkml-1.6.3.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,,
@@ -1,116 +0,0 @@
1
- CC0 1.0 Universal
2
-
3
- Statement of Purpose
4
-
5
- The laws of most jurisdictions throughout the world automatically confer
6
- exclusive Copyright and Related Rights (defined below) upon the creator and
7
- subsequent owner(s) (each and all, an "owner") of an original work of
8
- authorship and/or a database (each, a "Work").
9
-
10
- Certain owners wish to permanently relinquish those rights to a Work for the
11
- purpose of contributing to a commons of creative, cultural and scientific
12
- works ("Commons") that the public can reliably and without fear of later
13
- claims of infringement build upon, modify, incorporate in other works, reuse
14
- and redistribute as freely as possible in any form whatsoever and for any
15
- purposes, including without limitation commercial purposes. These owners may
16
- contribute to the Commons to promote the ideal of a free culture and the
17
- further production of creative, cultural and scientific works, or to gain
18
- reputation or greater distribution for their Work in part through the use and
19
- efforts of others.
20
-
21
- For these and/or other purposes and motivations, and without any expectation
22
- of additional consideration or compensation, the person associating CC0 with a
23
- Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
24
- and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
25
- and publicly distribute the Work under its terms, with knowledge of his or her
26
- Copyright and Related Rights in the Work and the meaning and intended legal
27
- effect of CC0 on those rights.
28
-
29
- 1. Copyright and Related Rights. A Work made available under CC0 may be
30
- protected by copyright and related or neighboring rights ("Copyright and
31
- Related Rights"). Copyright and Related Rights include, but are not limited
32
- to, the following:
33
-
34
- i. the right to reproduce, adapt, distribute, perform, display, communicate,
35
- and translate a Work;
36
-
37
- ii. moral rights retained by the original author(s) and/or performer(s);
38
-
39
- iii. publicity and privacy rights pertaining to a person's image or likeness
40
- depicted in a Work;
41
-
42
- iv. rights protecting against unfair competition in regards to a Work,
43
- subject to the limitations in paragraph 4(a), below;
44
-
45
- v. rights protecting the extraction, dissemination, use and reuse of data in
46
- a Work;
47
-
48
- vi. database rights (such as those arising under Directive 96/9/EC of the
49
- European Parliament and of the Council of 11 March 1996 on the legal
50
- protection of databases, and under any national implementation thereof,
51
- including any amended or successor version of such directive); and
52
-
53
- vii. other similar, equivalent or corresponding rights throughout the world
54
- based on applicable law or treaty, and any national implementations thereof.
55
-
56
- 2. Waiver. To the greatest extent permitted by, but not in contravention of,
57
- applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
58
- unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
59
- and Related Rights and associated claims and causes of action, whether now
60
- known or unknown (including existing as well as future claims and causes of
61
- action), in the Work (i) in all territories worldwide, (ii) for the maximum
62
- duration provided by applicable law or treaty (including future time
63
- extensions), (iii) in any current or future medium and for any number of
64
- copies, and (iv) for any purpose whatsoever, including without limitation
65
- commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
66
- the Waiver for the benefit of each member of the public at large and to the
67
- detriment of Affirmer's heirs and successors, fully intending that such Waiver
68
- shall not be subject to revocation, rescission, cancellation, termination, or
69
- any other legal or equitable action to disrupt the quiet enjoyment of the Work
70
- by the public as contemplated by Affirmer's express Statement of Purpose.
71
-
72
- 3. Public License Fallback. Should any part of the Waiver for any reason be
73
- judged legally invalid or ineffective under applicable law, then the Waiver
74
- shall be preserved to the maximum extent permitted taking into account
75
- Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
76
- is so judged Affirmer hereby grants to each affected person a royalty-free,
77
- non transferable, non sublicensable, non exclusive, irrevocable and
78
- unconditional license to exercise Affirmer's Copyright and Related Rights in
79
- the Work (i) in all territories worldwide, (ii) for the maximum duration
80
- provided by applicable law or treaty (including future time extensions), (iii)
81
- in any current or future medium and for any number of copies, and (iv) for any
82
- purpose whatsoever, including without limitation commercial, advertising or
83
- promotional purposes (the "License"). The License shall be deemed effective as
84
- of the date CC0 was applied by Affirmer to the Work. Should any part of the
85
- License for any reason be judged legally invalid or ineffective under
86
- applicable law, such partial invalidity or ineffectiveness shall not
87
- invalidate the remainder of the License, and in such case Affirmer hereby
88
- affirms that he or she will not (i) exercise any of his or her remaining
89
- Copyright and Related Rights in the Work or (ii) assert any associated claims
90
- and causes of action with respect to the Work, in either case contrary to
91
- Affirmer's express Statement of Purpose.
92
-
93
- 4. Limitations and Disclaimers.
94
-
95
- a. No trademark or patent rights held by Affirmer are waived, abandoned,
96
- surrendered, licensed or otherwise affected by this document.
97
-
98
- b. Affirmer offers the Work as-is and makes no representations or warranties
99
- of any kind concerning the Work, express, implied, statutory or otherwise,
100
- including without limitation warranties of title, merchantability, fitness
101
- for a particular purpose, non infringement, or the absence of latent or
102
- other defects, accuracy, or the present or absence of errors, whether or not
103
- discoverable, all to the greatest extent permissible under applicable law.
104
-
105
- c. Affirmer disclaims responsibility for clearing rights of other persons
106
- that may apply to the Work or any use thereof, including without limitation
107
- any person's Copyright and Related Rights in the Work. Further, Affirmer
108
- disclaims responsibility for obtaining any necessary consents, permissions
109
- or other rights required for any use of the Work.
110
-
111
- d. Affirmer understands and acknowledges that Creative Commons is not a
112
- party to this document and has no duty or obligation with respect to this
113
- CC0 or use of the Work.
114
-
115
- For more information, please see
116
- <http://creativecommons.org/publicdomain/zero/1.0/>
File without changes