88from pathlib import Path
99from typing import Mapping , Optional , Tuple , TypeVar
1010import warnings
11+ from pprint import pprint
1112
1213from linkml_runtime .utils .namespaces import Namespaces
1314from deprecated .classic import deprecated
1718from linkml_runtime .linkml_model .meta import *
1819from linkml_runtime .exceptions import OrderingError
1920from enum import Enum
21+ from linkml_runtime .linkml_model .meta import ClassDefinition , SlotDefinition , ClassDefinitionName
22+ from dataclasses import asdict , is_dataclass , fields
2023
2124logger = logging .getLogger (__name__ )
2225
3639ENUM_NAME = Union [EnumDefinitionName , str ]
3740
3841ElementType = TypeVar ("ElementType" , bound = Element )
39- ElementNameType = TypeVar ("ElementNameType" , bound = Union [ElementName ,str ])
42+ ElementNameType = TypeVar ("ElementNameType" , bound = Union [ElementName , str ])
4043DefinitionType = TypeVar ("DefinitionType" , bound = Definition )
41- DefinitionNameType = TypeVar ("DefinitionNameType" , bound = Union [DefinitionName ,str ])
44+ DefinitionNameType = TypeVar ("DefinitionNameType" , bound = Union [DefinitionName , str ])
4245ElementDict = Dict [ElementNameType , ElementType ]
4346DefDict = Dict [DefinitionNameType , DefinitionType ]
4447
@@ -53,7 +56,6 @@ class OrderedBy(Enum):
5356 """
5457
5558
56-
5759def _closure (f , x , reflexive = True , depth_first = True , ** kwargs ):
5860 if reflexive :
5961 rv = [x ]
@@ -84,7 +86,7 @@ def load_schema_wrap(path: str, **kwargs):
8486 schema : SchemaDefinition
8587 schema = yaml_loader .load (path , target_class = SchemaDefinition , ** kwargs )
8688 if "\n " not in path :
87- # if "\n" not in path and "://" not in path:
89+ # if "\n" not in path and "://" not in path:
8890 # only set path if the input is not a yaml string or URL.
8991 # Setting the source path is necessary for relative imports;
9092 # while initializing a schema with a yaml string is possible, there
@@ -146,6 +148,7 @@ def get_anonymous_class_definition(class_as_dict: ClassDefinition) -> AnonymousC
146148 :return: An AnonymousClassExpression.
147149 """
148150 an_expr = AnonymousClassExpression ()
151+ print (class_as_dict )
149152 valid_fields = {field .name for field in fields (an_expr )}
150153 for k , v in class_as_dict .items ():
151154 if k in valid_fields :
@@ -154,6 +157,7 @@ def get_anonymous_class_definition(class_as_dict: ClassDefinition) -> AnonymousC
154157 setattr (an_expr , k , v )
155158 return an_expr
156159
160+
157161@dataclass
158162class SchemaView (object ):
159163 """
@@ -265,7 +269,8 @@ def load_import(self, imp: str, from_schema: SchemaDefinition = None):
265269 return schema
266270
267271 @lru_cache (None )
268- def imports_closure (self , imports : bool = True , traverse : Optional [bool ] = None , inject_metadata = True ) -> List [SchemaDefinitionName ]:
272+ def imports_closure (self , imports : bool = True , traverse : Optional [bool ] = None , inject_metadata = True ) -> List [
273+ SchemaDefinitionName ]:
269274 """
270275 Return all imports
271276
@@ -350,7 +355,7 @@ def imports_closure(self, imports: bool = True, traverse: Optional[bool] = None,
350355 visited .add (sn )
351356
352357 # filter duplicates, keeping first entry
353- closure = list ({k :None for k in closure }.keys ())
358+ closure = list ({k : None for k in closure }.keys ())
354359
355360 if inject_metadata :
356361 for s in self .schema_map .values ():
@@ -456,7 +461,6 @@ def _order_inheritance(self, elements: DefDict) -> DefDict:
456461
457462 return {s .name : s for s in slist }
458463
459-
460464 @lru_cache (None )
461465 def all_classes (self , ordered_by = OrderedBy .PRESERVE , imports = True ) -> Dict [ClassDefinitionName , ClassDefinition ]:
462466 """
@@ -901,15 +905,14 @@ def permissible_value_ancestors(self, permissible_value_text: str,
901905
902906 @lru_cache (None )
903907 def permissible_value_descendants (self , permissible_value_text : str ,
904- enum_name : ENUM_NAME ,
905- reflexive = True ,
906- depth_first = True ) -> List [str ]:
908+ enum_name : ENUM_NAME ,
909+ reflexive = True ,
910+ depth_first = True ) -> List [str ]:
907911 """
908912 Closure of permissible_value_children method
909913 :enum
910914 """
911915
912-
913916 return _closure (lambda x : self .permissible_value_children (x , enum_name ),
914917 permissible_value_text ,
915918 reflexive = reflexive ,
@@ -1368,7 +1371,6 @@ def induced_slot(self, slot_name: SLOT_NAME, class_name: CLASS_NAME = None, impo
13681371 :param slot_name: slot to be queries
13691372 :param class_name: class used as context
13701373 :param imports: include imports closure
1371- :param mangle_name: if True, the slot name will be mangled to include the class name
13721374 :return: dynamic slot constructed by inference
13731375 """
13741376 if class_name :
@@ -1412,53 +1414,18 @@ def induced_slot(self, slot_name: SLOT_NAME, class_name: CLASS_NAME = None, impo
14121414 }
14131415 # iterate through all metaslots, and potentially populate metaslot value for induced slot
14141416 for metaslot_name in self ._metaslots_for_slot ():
1415-
14161417 # inheritance of slots; priority order
14171418 # slot-level assignment < ancestor slot_usage < self slot_usage
14181419 v = getattr (induced_slot , metaslot_name , None )
14191420 if cls is None :
14201421 propagated_from = []
14211422 else :
14221423 propagated_from = self .class_ancestors (class_name , reflexive = True , mixins = True )
1423-
14241424 for an in reversed (propagated_from ):
14251425 induced_slot .owner = an
1426- a = self .get_element (an , imports )
1427- # slot usage of the slot in the ancestor class, last ancestor iterated through here is "self"
1428- # so that self.slot_usage overrides ancestor slot_usage at the conclusion of the loop.
1426+ a = self .get_class (an , imports )
14291427 anc_slot_usage = a .slot_usage .get (slot_name , {})
1430- # slot name in the ancestor class
1431- # getattr(x, 'y') is equivalent to x.y. None here means raise an error if x.y is not found
14321428 v2 = getattr (anc_slot_usage , metaslot_name , None )
1433- # v2 is the value of the metaslot in slot_usage in the ancestor class, which in the loop, means that
1434- # the class itself is the last slot_usage to be considered and applied.
1435- if metaslot_name in ["any_of" , "exactly_one_of" ]:
1436- if anc_slot_usage != {}:
1437- for ao in anc_slot_usage .any_of :
1438- if ao .range is not None :
1439- ao_range = self .get_element (ao .range )
1440- if ao_range :
1441- print (ao_range )
1442- acd = get_anonymous_class_definition (to_dict (ao_range ))
1443- if induced_slot .range_expression is None :
1444- induced_slot .range_expression = AnonymousClassExpression ()
1445- if induced_slot .range_expression .any_of is None :
1446- induced_slot .range_expression .any_of = []
1447- # Check for duplicates before appending
1448- if acd not in induced_slot .range_expression .any_of :
1449- induced_slot .range_expression .any_of .append (acd )
1450- for eoo in anc_slot_usage .exactly_one_of :
1451- if eoo .range is not None :
1452- eoo_range = self .get_element (eoo .range )
1453- print (eoo_range )
1454- acd = get_anonymous_class_definition (as_dict (eoo_range ))
1455- if induced_slot .range_expression is None :
1456- induced_slot .range_expression = AnonymousClassExpression ()
1457- if induced_slot .range_expression .exactly_one_of is None :
1458- induced_slot .range_expression .exactly_one_of = []
1459- # Check for duplicates before appending
1460- if acd not in induced_slot .range_expression .exactly_one_of :
1461- induced_slot .range_expression .exactly_one_of .append (acd )
14621429 if v is None :
14631430 v = v2
14641431 else :
@@ -1494,33 +1461,8 @@ def induced_slot(self, slot_name: SLOT_NAME, class_name: CLASS_NAME = None, impo
14941461 if induced_slot .name in c .slots or induced_slot .name in c .attributes :
14951462 if c .name not in induced_slot .domain_of :
14961463 induced_slot .domain_of .append (c .name )
1497- if induced_slot .range is not None :
1498- if induced_slot .range_expression is None :
1499- induced_slot .range_expression = AnonymousClassExpression ()
1500- induced_slot .range_expression .any_of = []
1501- induced_slot .range_expression .any_of .append (
1502- get_anonymous_class_definition (to_dict (self .get_element (induced_slot .range )))
1503- )
1504- return induced_slot
1505- else :
1506- any_of_ancestors = []
1507- if induced_slot .range_expression .any_of is not None :
1508- for ao_range in induced_slot .range_expression .any_of :
1509- ao_range_class = self .get_class (ao_range .name )
1510- ao_anc = self .class_ancestors (ao_range_class .name )
1511- for a in ao_anc :
1512- if a not in any_of_ancestors :
1513- any_of_ancestors .append (a )
1514- if induced_slot .range in any_of_ancestors :
1515- return induced_slot
1516- else :
1517- induced_slot .range_expression .any_of .append (
1518- get_anonymous_class_definition (to_dict (self .get_element (induced_slot .range )))
1519- )
1520- return induced_slot
15211464 return induced_slot
15221465
1523-
15241466 @lru_cache (None )
15251467 def _metaslots_for_slot (self ):
15261468 fake_slot = SlotDefinition ('__FAKE' )
@@ -1645,7 +1587,7 @@ def is_inlined(self, slot: SlotDefinition, imports=True) -> bool:
16451587 return True
16461588 elif slot .inlined_as_list :
16471589 return True
1648-
1590+
16491591 id_slot = self .get_identifier_slot (range , imports = imports )
16501592 if id_slot is None :
16511593 # must be inlined as has no identifier
@@ -1689,7 +1631,7 @@ def slot_range_as_union(self, slot: SlotDefinition) -> List[ElementName]:
16891631 """
16901632 Returns all applicable ranges for a slot
16911633
1692- Typically any given slot has exactly one range, and one metamodel element type,
1634+ Typically, any given slot has exactly one range, and one metamodel element type,
16931635 but a proposed feature in LinkML 1.2 is range expressions, where ranges can be defined as unions
16941636
16951637 :param slot:
@@ -1701,9 +1643,9 @@ def slot_range_as_union(self, slot: SlotDefinition) -> List[ElementName]:
17011643 if x .range :
17021644 range_union_of .append (x .range )
17031645 return range_union_of
1704-
1646+
17051647 def get_classes_by_slot (
1706- self , slot : SlotDefinition , include_induced : bool = False
1648+ self , slot : SlotDefinition , include_induced : bool = False
17071649 ) -> List [ClassDefinitionName ]:
17081650 """Get all classes that use a given slot, either as a direct or induced slot.
17091651
0 commit comments