linkml 1.5.8rc1__py3-none-any.whl → 1.6.0__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.
@@ -2,11 +2,13 @@ import keyword
2
2
  import logging
3
3
  import os
4
4
  import re
5
+ from copy import copy
5
6
  from dataclasses import dataclass, field
6
7
  from types import ModuleType
7
8
  from typing import Callable, Dict, Iterator, List, Optional, Set, Tuple, Union
8
9
 
9
10
  import click
11
+ from linkml_runtime import SchemaView
10
12
  from linkml_runtime.linkml_model import linkml_files
11
13
  from linkml_runtime.linkml_model.meta import (
12
14
  ClassDefinition,
@@ -65,6 +67,7 @@ class PythonGenerator(Generator):
65
67
 
66
68
  def __post_init__(self) -> None:
67
69
  self.sourcefile = self.schema
70
+ self.schemaview = SchemaView(self.schema)
68
71
  super().__post_init__()
69
72
  if self.format is None:
70
73
  self.format = self.valid_formats[0]
@@ -307,15 +310,25 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
307
310
  if ":/" in dflt_prefix
308
311
  else dflt_prefix.upper()
309
312
  )
310
- return "\n".join(
311
- [
312
- f"{pfx.upper().replace('.', '_').replace('-', '_')} = CurieNamespace('{pfx.replace('.', '_')}', '{self.namespaces[pfx]}')" # noqa: E501
313
- for pfx in sorted(self.emit_prefixes)
314
- if pfx in self.namespaces
315
- ]
313
+ curienamespace_defs = [
314
+ {
315
+ "variable": f"{pfx.upper().replace('.', '_').replace('-', '_')}",
316
+ "value": f"CurieNamespace('{pfx.replace('.', '_')}', '{self.namespaces[pfx]}')",
317
+ }
318
+ for pfx in sorted(self.emit_prefixes)
319
+ if pfx in self.namespaces
320
+ ]
321
+ curienamespace_declarations = "\n".join(
322
+ [f"{ns['variable']} = {ns['value']}" for ns in curienamespace_defs]
316
323
  + [f"DEFAULT_ = {dflt}"]
317
324
  )
318
325
 
326
+ ",".join([x["variable"] for x in curienamespace_defs])
327
+ # catalog_declaration = f"\nnamespace_catalog = CurieNamespaceCatalog.create({curienamespace_vars})\n"
328
+ catalog_declaration = ""
329
+
330
+ return curienamespace_declarations + catalog_declaration
331
+
319
332
  def gen_references(self) -> str:
320
333
  """Generate python type declarations for all identifiers (primary keys)"""
321
334
  rval = []
@@ -396,6 +409,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
396
409
  parentref = f'({self.formatted_element_name(cls.is_a, True) if cls.is_a else "YAMLRoot"})'
397
410
  slotdefs = self.gen_class_variables(cls)
398
411
  postinits = self.gen_postinits(cls)
412
+ constructor = self.gen_constructor(cls)
399
413
 
400
414
  wrapped_description = (
401
415
  f'\n\t"""\n\t{wrapped_annotation(be(cls.description))}\n\t"""'
@@ -406,15 +420,18 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
406
420
  if self.is_class_unconstrained(cls):
407
421
  return f"\n{self.class_or_type_name(cls.name)} = Any"
408
422
 
409
- return (
423
+ cd_str = (
410
424
  ("\n@dataclass" if slotdefs else "")
411
425
  + f"\nclass {self.class_or_type_name(cls.name)}{parentref}:{wrapped_description}"
412
426
  + f"{self.gen_inherited_slots(cls)}"
413
427
  + f"{self.gen_class_meta(cls)}"
414
428
  + (f"\n\t{slotdefs}" if slotdefs else "")
415
429
  + (f"\n{postinits}" if postinits else "")
430
+ + (f"\n{constructor}" if constructor else "")
416
431
  )
417
432
 
433
+ return cd_str
434
+
418
435
  def gen_inherited_slots(self, cls: ClassDefinition) -> str:
419
436
  if not self.gen_classvars:
420
437
  return ""
@@ -648,7 +665,6 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
648
665
  prox_type_name = rangelist[-1]
649
666
 
650
667
  # Quote forward references - note that enums always gen at the end
651
- logging.info(f"CHECK: {slot.name} => {slot.range} ")
652
668
  if slot.range in self.schema.enums or (
653
669
  cls
654
670
  and slot.inlined
@@ -699,20 +715,30 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
699
715
  # TODO: Remove the bypass whenever we get default_range fixed
700
716
  if slot.name not in pkeys and (not slot.ifabsent or True):
701
717
  post_inits.append(self.gen_postinit(cls, slot))
718
+ post_inits_designators = []
719
+
720
+ domain_slot_names = [s.name for s in self.domain_slots(cls)]
721
+ for slot in self.schemaview.class_induced_slots(cls.name):
722
+ # This is for all type designators that were defined at a parent class
723
+ # We need to treat them specially: the initialisation should come
724
+ # AFTER the call to super() because we want to override the super behaviour
725
+ if slot.name not in domain_slot_names and slot.designates_type:
726
+ post_inits_designators.append(self.gen_postinit(cls, slot))
702
727
 
703
728
  post_inits_pre_super_line = "\n\t\t".join([p for p in post_inits_pre_super if p]) + (
704
729
  "\n\t\t" if post_inits_pre_super else ""
705
730
  )
731
+ post_inits_post_super_line = "\n\t\t".join(post_inits_designators)
706
732
  post_inits_line = "\n\t\t".join([p for p in post_inits if p])
707
733
  return (
708
734
  (
709
735
  f"""
710
736
  def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
711
737
  {post_inits_pre_super_line}{post_inits_line}
712
- super().__post_init__(**kwargs)"""
713
- + "\n"
738
+ super().__post_init__(**kwargs)
739
+ {post_inits_post_super_line}"""
714
740
  )
715
- if post_inits_line or post_inits_pre_super_line
741
+ if post_inits_line or post_inits_pre_super_line or post_inits_post_super_line
716
742
  else ""
717
743
  )
718
744
 
@@ -751,6 +777,67 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
751
777
  return len(rng.slots) - len(pkeys) == 1
752
778
  return False
753
779
 
780
+ def _roll_up_type(self, typ_name: str) -> str:
781
+ if typ_name in self.schemaview.all_types():
782
+ t = self.schemaview.get_type(typ_name)
783
+ if t.typeof:
784
+ return self._roll_up_type(t.typeof)
785
+ return typ_name
786
+
787
+ def gen_constructor(self, cls: ClassDefinition) -> Optional[str]:
788
+ rlines: List[str] = []
789
+ designators = [x for x in self.domain_slots(cls) if x.designates_type]
790
+ if len(designators) > 0:
791
+ descendants = self.schemaview.class_descendants(cls.name)
792
+ if len(descendants) > 1:
793
+ slot = designators[0]
794
+ aliased_slot_name = self.slot_name(slot.name)
795
+ slot_range = self._roll_up_type(slot.range)
796
+
797
+ rlines.append("def __new__(cls, *args, **kwargs):")
798
+ td_val_expression = "kwargs[type_designator]"
799
+ if slot_range == "string":
800
+ lookup_by_props = ["class_name"]
801
+ elif slot_range == "uri":
802
+ lookup_by_props = ["class_class_uri", "class_model_uri"]
803
+ td_val_expression = (
804
+ f"URIRef({td_val_expression}) if "
805
+ f"isinstance({td_val_expression}, str) else {td_val_expression}"
806
+ )
807
+ elif slot_range == "uriorcurie":
808
+ lookup_by_props = ["class_class_curie", "class_class_uri", "class_model_uri"]
809
+ else:
810
+ raise ValueError(f"Unsupported type designator range: {slot.range}")
811
+ rlines.append(
812
+ f"""
813
+ type_designator = "{aliased_slot_name}"
814
+ if not type_designator in kwargs:
815
+ return super().__new__(cls,*args,**kwargs)
816
+ else:
817
+ type_designator_value = {td_val_expression}
818
+ target_cls = cls._class_for("{lookup_by_props[0]}", type_designator_value)
819
+ """
820
+ )
821
+ for prop in lookup_by_props[1:]:
822
+ rlines.append(
823
+ f"""
824
+ if target_cls is None:
825
+ target_cls = cls._class_for("{prop}", type_designator_value)
826
+ """
827
+ )
828
+ rlines.append(
829
+ f"""
830
+ if target_cls is None:
831
+ raise ValueError(f"Wrong type designator value: class {{cls.__name__}} "
832
+ f"has no subclass with {lookup_by_props}='{{kwargs[type_designator]}}'")
833
+ return super().__new__(target_cls,*args,**kwargs)
834
+ """
835
+ )
836
+
837
+ if rlines and copy(rlines[-1]).strip() != "":
838
+ rlines.append("")
839
+ return ("\n\t" if len(rlines) > 0 else "") + "\n\t".join(rlines)
840
+
754
841
  def gen_postinit(self, cls: ClassDefinition, slot: SlotDefinition) -> Optional[str]:
755
842
  """Generate python post init rules for slot in class"""
756
843
  rlines: List[str] = []
@@ -770,18 +857,34 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
770
857
  rlines.append(f"if self._is_empty(self.{aliased_slot_name}):")
771
858
  rlines.append(f'\tself.MissingRequiredField("{aliased_slot_name}")')
772
859
 
773
- # Generate the type co-orcion for the various types.
860
+ # Generate the type co-ercion for the various types.
774
861
  # NOTE: if you set this to true, we will cast all types. This may be what we really want
775
862
  if not slot.multivalued:
776
- if slot.required:
863
+ if slot.designates_type:
864
+ pass
865
+ elif slot.required:
777
866
  rlines.append(f"if not isinstance(self.{aliased_slot_name}, {base_type_name}):")
778
867
  else:
779
868
  rlines.append(
780
869
  f"if self.{aliased_slot_name} is not None and "
781
870
  f"not isinstance(self.{aliased_slot_name}, {base_type_name}):"
782
871
  )
783
- # A really weird case -- a class that has no properties
784
- if slot.range in self.schema.classes and not self.schema.classes[slot.range].slots:
872
+ if slot.designates_type:
873
+ slot_range = self._roll_up_type(slot.range)
874
+ if slot_range == "string":
875
+ td_value_classvar = "class_name"
876
+ elif slot_range == "uri":
877
+ td_value_classvar = "class_model_uri"
878
+ elif slot_range == "uriorcurie":
879
+ td_value_classvar = "class_class_curie"
880
+ else:
881
+ raise ValueError(f"Unsupported type designator range: {slot_range}")
882
+ rlines.append(f"self.{aliased_slot_name} = str(self.{td_value_classvar})")
883
+ elif (
884
+ # A really weird case -- a class that has no properties
885
+ slot.range in self.schema.classes
886
+ and not self.schema.classes[slot.range].slots
887
+ ):
785
888
  rlines.append(f"\tself.{aliased_slot_name} = {base_type_name}()")
786
889
  else:
787
890
  if (
@@ -847,8 +950,9 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
847
950
  f"{sn} = [v if isinstance(v, {base_type_name}) "
848
951
  f"else {base_type_name}(v) for v in {sn}]"
849
952
  )
850
- if rlines:
851
- rlines.append("")
953
+ while rlines and copy(rlines[-1]).strip() == "":
954
+ rlines.pop()
955
+ rlines.append("")
852
956
  return "\n\t\t".join(rlines)
853
957
 
854
958
  def _slot_iter(
linkml/utils/generator.py CHANGED
@@ -210,7 +210,6 @@ class Generator(metaclass=abc.ABCMeta):
210
210
  # See https://github.com/linkml/linkml/issues/923 for discussion on how
211
211
  # to simplify the overall framework
212
212
  if isinstance(schema, Generator):
213
- logging.info("Instantiating generator with another generator is deprecated")
214
213
  gen = schema
215
214
  self.schema = gen.schema
216
215
  self.synopsis = gen.synopsis
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: linkml
3
- Version: 1.5.8rc1
3
+ Version: 1.6.0
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
7
7
  Author: Chris Mungall
8
8
  Author-email: cjmungall@lbl.gov
9
- Requires-Python: >=3.7.6,<4.0.0
9
+ Requires-Python: >=3.8,<4.0
10
10
  Classifier: Development Status :: 5 - Production/Stable
11
11
  Classifier: Environment :: Console
12
12
  Classifier: Intended Audience :: Developers
@@ -33,7 +33,7 @@ Requires-Dist: jinja2 (>=3.1.0)
33
33
  Requires-Dist: jsonasobj2 (>=1.0.3,<2.0.0)
34
34
  Requires-Dist: jsonschema[format] (>=4.0.0)
35
35
  Requires-Dist: linkml-dataops
36
- Requires-Dist: linkml-runtime (>=1.5.4)
36
+ Requires-Dist: linkml-runtime (>=1.6.0)
37
37
  Requires-Dist: openpyxl
38
38
  Requires-Dist: parse
39
39
  Requires-Dist: prefixcommons (>=0.1.7)
@@ -28,18 +28,19 @@ linkml/generators/javagen/java_record_template.jinja2,sha256=yS7vKlgkHOQJfMjeziA
28
28
  linkml/generators/javagen.py,sha256=Byf_923aAcWhZKeuTDPIB2o6PLuMHJV5WiD_AG0t_wg,5431
29
29
  linkml/generators/jsonldcontextgen.py,sha256=3JnBns93ESW-sYb0AADJ-lcUpg6bQP8JzVDetOHLV3g,7932
30
30
  linkml/generators/jsonldgen.py,sha256=OYxh7humrpcopw67P4fSiX4P0aO_iSZCE3xjgXrHNvk,7637
31
- linkml/generators/jsonschemagen.py,sha256=3uvR8Ffyi7dgQCaqw2oA2_3ZKD_2iajD_CEfI7hCmtA,23666
31
+ linkml/generators/jsonschemagen.py,sha256=-qwTKrncSyVCGKikrBkfwI2jBBBw50CNQAeS21mfeLw,24194
32
+ linkml/generators/legacy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
33
  linkml/generators/linkmlgen.py,sha256=LaVTMPsGdQmC_zUPCjHg9fr6RsLuPLQK9nGyqqAUkPU,3556
33
34
  linkml/generators/markdowngen.py,sha256=uDgSgPEaeeTExHCe4o9X0ul5YUKMwDKIpku5FzY-_4I,33468
34
35
  linkml/generators/namespacegen.py,sha256=pPjUQweKRCtDfTTtK4kfuRohef3uKPrbhNUfkmYB8bA,6472
35
36
  linkml/generators/oocodegen.py,sha256=l-5zck_NNAUxJiApJrnDPkqxCv3b6v5s4hC_smelszU,7620
36
- linkml/generators/owlgen.py,sha256=Ry7Qps_CSaqnfbkhCQPNGjP46Kisx8YSFR6Fdv5Cq9U,28984
37
+ linkml/generators/owlgen.py,sha256=JQgs0MvTbcNoFROvzBY0KmtisrKdzd9J0j3Fu7M-iDM,39742
37
38
  linkml/generators/plantumlgen.py,sha256=56AnrRE92pwxmPsYgB4Rbjx0AuxO-iwpIKl7DDApuhE,15360
38
39
  linkml/generators/prefixmapgen.py,sha256=AWQTHPcuPlj-rDgSCbKcX_-4eLiDww9huZoOTVels6c,4694
39
40
  linkml/generators/projectgen.py,sha256=_7jP7WFLoxX_kByj7pS5PA48VXDRJ_JDmY7HaWTnnr4,9674
40
41
  linkml/generators/protogen.py,sha256=MzrrwD55YH5-GMUFhSRv8Z4DIt-5mcHAih4HO_oR6zM,2342
41
42
  linkml/generators/pydanticgen.py,sha256=uAvq03QKkQBY1YJemcPbssnZvsxn-IX6tFzyi6j1VMY,22763
42
- linkml/generators/pythongen.py,sha256=BFUkTdis9CnqBfmCAFx0R0joNpr2uAU8wnVBu851Ifg,48737
43
+ linkml/generators/pythongen.py,sha256=N-rr63YYkUmHuB7i0t4o3m-8XZ9tD3TH8Y68df35sus,53536
43
44
  linkml/generators/rdfgen.py,sha256=ZWn70B9e79eV91GiZsnHmN-QQ4-CoMBd8SvKSwUd63Q,2716
44
45
  linkml/generators/shaclgen.py,sha256=AyabyHUju1rAfrjSYyoxxFwiRjUmhFgbgPIN4Qe56zE,7060
45
46
  linkml/generators/shexgen.py,sha256=IotuSUVNboLSx7tF6uOqdT8Rb53iqoyFu2mNTnP6eNM,9191
@@ -85,7 +86,7 @@ linkml/utils/converter.py,sha256=lhhFVk_t4mZ2oxGum_9VLs0Uxb8qWbS8b3xed0LkmyY,630
85
86
  linkml/utils/datautils.py,sha256=wTVIGHkqbG2kuHp8IPVPsrZ7we99ZH5RGrVdBMdzAQs,3740
86
87
  linkml/utils/datavalidator.py,sha256=kxlBZRkctIoVnrdwOULvtE7NrJK9qRtqW1Ds4LSfnlU,486
87
88
  linkml/utils/execute_tutorial.py,sha256=AdjHMUUVdeYAvJr_yWVPdBJEW_AWrCuPvnqh9fAHDfU,6950
88
- linkml/utils/generator.py,sha256=N-jD1kuvx1gchoaCjVlwZsM5llKDtU6dfEDKggNqwno,38805
89
+ linkml/utils/generator.py,sha256=qrD1kfcLvhC4Uaf4bjUaKMoMy1Zyc9wshuGLxEUF6bU,38716
89
90
  linkml/utils/helpers.py,sha256=yR8n4zFA5wPcYC7xzRuNF3wO16vG80v6j7DM3qTNmIc,447
90
91
  linkml/utils/ifabsent_functions.py,sha256=VILoHk6VajhAVRb1bBukrxl1-imrrYzfrtAV0fr_-Rs,5833
91
92
  linkml/utils/logictools.py,sha256=McrU80trvdpFs68L2xcbgCiiiCRdVXzDil5HdZwJ84w,21364
@@ -122,8 +123,8 @@ linkml/workspaces/datamodel/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMp
122
123
  linkml/workspaces/datamodel/workspaces.py,sha256=1M6yqcTnlpmKhUdI-QVxL40wNC1MSRAqrOwvYoLesh0,15073
123
124
  linkml/workspaces/datamodel/workspaces.yaml,sha256=EjVrwPpeRZqJRjuGyyDRxxFzuv55SiLIXPBRUG6HStU,4233
124
125
  linkml/workspaces/example_runner.py,sha256=hkGHGVPsRp4VQjUU8CQibpnRCMhrULktZ7_A3asj_nw,11648
125
- linkml-1.5.8rc1.dist-info/LICENSE,sha256=Nv_Z3AhdUpp-YOEnbXOuWgMLAgMT5sVAhZOmrirzlnM,6555
126
- linkml-1.5.8rc1.dist-info/WHEEL,sha256=vVCvjcmxuUltf8cYhJ0sJMRDLr1XsPuxEId8YDzbyCY,88
127
- linkml-1.5.8rc1.dist-info/entry_points.txt,sha256=Y_WcIenx01tAlAi192U1eDjpL7LZ4D9b72GqrCZSr9Q,2160
128
- linkml-1.5.8rc1.dist-info/METADATA,sha256=I1DBl9GtQ-okN_XnqIHashZmQeRshdhidBaHPY0mJWU,3532
129
- linkml-1.5.8rc1.dist-info/RECORD,,
126
+ linkml-1.6.0.dist-info/LICENSE,sha256=Nv_Z3AhdUpp-YOEnbXOuWgMLAgMT5sVAhZOmrirzlnM,6555
127
+ linkml-1.6.0.dist-info/WHEEL,sha256=vVCvjcmxuUltf8cYhJ0sJMRDLr1XsPuxEId8YDzbyCY,88
128
+ linkml-1.6.0.dist-info/METADATA,sha256=f1cWdCoGnqXPXmMwg-5GojfkYwppx2XYLlylK8XP8zY,3525
129
+ linkml-1.6.0.dist-info/entry_points.txt,sha256=Y_WcIenx01tAlAi192U1eDjpL7LZ4D9b72GqrCZSr9Q,2160
130
+ linkml-1.6.0.dist-info/RECORD,,