1010from typing import Optional , Union , Iterable
1111
1212from .registry import lookup
13- from .base import Node , IRI , Link , Node
13+ from .base import Node , IRI , Link
14+
15+
16+ def _could_be_instance (value , types ):
17+ """
18+ True if a Link's allowed types are consistent with the given types
19+ """
20+ return isinstance (value , Link ) and value .allowed_types and set (value .allowed_types ).issubset (types )
1421
1522
1623class Property :
@@ -89,6 +96,10 @@ def types(self):
8996 self ._resolved_types = True
9097 return self ._types
9198
99+ @property
100+ def is_link (self ) -> bool :
101+ return issubclass (self .types [0 ], Node )
102+
92103 def validate (self , value , ignore = None ):
93104 """
94105 Check whether `value` satisfies all constraints.
@@ -113,7 +124,7 @@ def validate(self, value, ignore=None):
113124 if not isinstance (value , (list , tuple )):
114125 value = [value ]
115126 for item in value :
116- if not isinstance (item , self .types ):
127+ if not ( isinstance (item , self .types ) or _could_be_instance ( item , self . types ) ):
117128 if "type" not in ignore :
118129 failures ["type" ].append (
119130 f"{ self .name } : Expected { ', ' .join (t .__name__ for t in self .types )} , "
@@ -145,7 +156,7 @@ def validate(self, value, ignore=None):
145156 failures ["multiplicity" ].append (
146157 f"{ self .name } does not accept multiple values, but contains { len (value )} "
147158 )
148- elif not isinstance (value , self .types ):
159+ elif not ( isinstance (value , self .types ) or _could_be_instance ( value , self . types ) ):
149160 if "type" not in ignore :
150161 failures ["type" ].append (
151162 f"{ self .name } : Expected { ', ' .join (t .__name__ for t in self .types )} , "
@@ -163,8 +174,9 @@ def deserialize(self, data):
163174 Args:
164175 data: the JSON-LD data
165176 """
166-
167177 # todo: check data type
178+ link_keys = set (("@id" , "@type" ))
179+
168180 def deserialize_item (item ):
169181 if self .types == (str ,):
170182 if self .formatting != "text/plain" :
@@ -188,9 +200,18 @@ def deserialize_item(item):
188200 if "@type" in item :
189201 for cls in self .types :
190202 if cls .type_ == item ["@type" ]:
191- return cls .from_jsonld (item )
203+ if set (item .keys ()) == link_keys :
204+ # if we only have @id and @type, it's a Link
205+ return Link (item ["@id" ], allowed_types = [cls ])
206+ else :
207+ # otherwise it's a Node
208+ return cls .from_jsonld (item )
209+ raise TypeError (
210+ f"Mismatched types. Data has '{ item ['@type' ]} ' "
211+ f"but property only allows { [cls .type_ for cls in self .types ]} "
212+ )
192213 else :
193- return Link (item ["@id" ])
214+ return Link (item ["@id" ], allowed_types = self . types )
194215 else :
195216 raise NotImplementedError ()
196217
0 commit comments