@@ -94,6 +94,7 @@ def __init__(self, ctx, schemagraph=None, foreign_properties=None,
9494 self .cache = {}
9595
9696 self .url_fields = None # type: Set[str]
97+ self .scoped_ref_fields = None # type: Set[str]
9798 self .vocab_fields = None # type: Set[str]
9899 self .identifiers = None # type: Set[str]
99100 self .identity_links = None # type: Set[str]
@@ -186,6 +187,7 @@ def add_context(self, newcontext, baseuri=""):
186187 "Refreshing context that already has stuff in it" )
187188
188189 self .url_fields = set ()
190+ self .scoped_ref_fields = set ()
189191 self .vocab_fields = set ()
190192 self .identifiers = set ()
191193 self .identity_links = set ()
@@ -206,6 +208,8 @@ def add_context(self, newcontext, baseuri=""):
206208 self .identity_links .add (key )
207209 elif isinstance (value , dict ) and value .get ("@type" ) == "@id" :
208210 self .url_fields .add (key )
211+ if value .get ("scopedRef" , False ):
212+ self .scoped_ref_fields .add (key )
209213 if value .get ("identity" , False ):
210214 self .identity_links .add (key )
211215 elif isinstance (value , dict ) and value .get ("@type" ) == "@vocab" :
@@ -235,7 +239,7 @@ def add_context(self, newcontext, baseuri=""):
235239 _logger .debug ("vocab_fields is %s" , self .vocab_fields )
236240 _logger .debug ("vocab is %s" , self .vocab )
237241
238- def resolve_ref (self , ref , base_url = None ):
242+ def resolve_ref (self , ref , base_url = None , toplevel = True ):
239243 # type: (Union[Dict[str, Any], str, unicode], Union[str, unicode]) -> Tuple[Union[Dict[str, Any], str, unicode], Dict[str, Any]]
240244 base_url = base_url or 'file://%s/' % os .path .abspath ('.' )
241245
@@ -297,7 +301,7 @@ def resolve_ref(self, ref, base_url=None):
297301 doc = self .fetch (doc_url )
298302
299303 # Recursively expand urls and resolve directives
300- obj , metadata = self .resolve_all (doc if doc else obj , doc_url )
304+ obj , metadata = self .resolve_all (doc if doc else obj , doc_url , toplevel = toplevel )
301305
302306 # Requested reference should be in the index now, otherwise it's a bad
303307 # reference
@@ -318,7 +322,7 @@ def resolve_ref(self, ref, base_url=None):
318322 except TypeError :
319323 return obj , metadata
320324
321- def resolve_all (self , document , base_url , file_base = None ):
325+ def resolve_all (self , document , base_url , file_base = None , toplevel = True ):
322326 # type: (Any, Union[str, unicode], Union[str, unicode]) -> Tuple[Any, Dict[str, Any]]
323327 loader = self
324328 metadata = {} # type: Dict[str, Any]
@@ -328,7 +332,7 @@ def resolve_all(self, document, base_url, file_base=None):
328332 if isinstance (document , dict ):
329333 # Handle $import and $include
330334 if ('$import' in document or '$include' in document ):
331- return self .resolve_ref (document , file_base )
335+ return self .resolve_ref (document , base_url = file_base , toplevel = toplevel )
332336 elif isinstance (document , list ):
333337 pass
334338 else :
@@ -364,7 +368,7 @@ def resolve_all(self, document, base_url, file_base=None):
364368 if "$graph" in document :
365369 metadata = _copy_dict_without_key (document , "$graph" )
366370 document = document ["$graph" ]
367- metadata , _ = loader .resolve_all (metadata , base_url , file_base )
371+ metadata , _ = loader .resolve_all (metadata , base_url , file_base = file_base , toplevel = False )
368372
369373 if isinstance (document , dict ):
370374 for idmapField in loader .idmap :
@@ -412,6 +416,8 @@ def resolve_all(self, document, base_url, file_base=None):
412416 del document [d ]
413417
414418 for d in loader .url_fields :
419+ if d in self .scoped_ref_fields :
420+ continue
415421 if d in document :
416422 if isinstance (document [d ], basestring ):
417423 document [d ] = loader .expand_url (
@@ -427,7 +433,7 @@ def resolve_all(self, document, base_url, file_base=None):
427433 try :
428434 for key , val in document .items ():
429435 document [key ], _ = loader .resolve_all (
430- val , base_url , file_base )
436+ val , base_url , file_base = file_base , toplevel = False )
431437 except validate .ValidationException as v :
432438 _logger .debug ("loader is %s" , id (loader ))
433439 raise validate .ValidationException ("(%s) (%s) Validation error in field %s:\n %s" % (
@@ -439,7 +445,7 @@ def resolve_all(self, document, base_url, file_base=None):
439445 while i < len (document ):
440446 val = document [i ]
441447 if isinstance (val , dict ) and "$import" in val :
442- l , _ = loader .resolve_ref (val , file_base )
448+ l , _ = loader .resolve_ref (val , base_url = file_base , toplevel = False )
443449 if isinstance (l , list ):
444450 del document [i ]
445451 for item in aslist (l ):
@@ -450,7 +456,7 @@ def resolve_all(self, document, base_url, file_base=None):
450456 i += 1
451457 else :
452458 document [i ], _ = loader .resolve_all (
453- val , base_url , file_base )
459+ val , base_url , file_base = file_base , toplevel = False )
454460 i += 1
455461 except validate .ValidationException as v :
456462 raise validate .ValidationException ("(%s) (%s) Validation error in position %i:\n %s" % (
@@ -463,6 +469,9 @@ def resolve_all(self, document, base_url, file_base=None):
463469 metadata [identifer ], base_url , scoped = True )
464470 loader .idx [metadata [identifer ]] = document
465471
472+ if toplevel :
473+ self .validate_links (document , "" )
474+
466475 return document , metadata
467476
468477 def fetch_text (self , url ):
@@ -522,36 +531,51 @@ def check_file(self, fn): # type: (Union[str, unicode]) -> bool
522531 else :
523532 return False
524533
525- def validate_link (self , field , link ):
534+ def validate_link (self , field , link , docid ):
526535 # type: (str, Union[str, unicode, List[str], Dict[str, Any]]) -> bool
527536 if field in self .nolinkcheck :
528- return True
537+ return link
529538 if isinstance (link , (str , unicode )):
530539 if field in self .vocab_fields :
531540 if link not in self .vocab and link not in self .idx and link not in self .rvocab :
532541 if not self .check_file (link ):
533542 raise validate .ValidationException (
534543 "Field `%s` contains undefined reference to `%s`" % (field , link ))
535544 elif link not in self .idx and link not in self .rvocab :
536- if not self .check_file (link ):
545+ if field in self .scoped_ref_fields :
546+ split = urlparse .urlsplit (docid )
547+ sp = split .fragment .split ("/" )
548+ while len (sp ) > 0 :
549+ sp .pop ()
550+ sp .append (link )
551+ url = urlparse .urlunsplit (
552+ (split .scheme , split .netloc , split .path , split .query , "/" .join (sp )))
553+ if url in self .idx :
554+ print link , "is" , url
555+ return url
556+ else :
557+ sp .pop ()
558+ raise validate .ValidationException (
559+ "Field `%s` contains undefined reference to `%s`" % (field , link ))
560+ elif not self .check_file (link ):
537561 raise validate .ValidationException (
538562 "Field `%s` contains undefined reference to `%s`" % (field , link ))
539563 elif isinstance (link , list ):
540564 errors = []
541- for i in link :
565+ for n , i in enumerate ( link ) :
542566 try :
543- self .validate_link (field , i )
567+ link [ n ] = self .validate_link (field , i , docid )
544568 except validate .ValidationException as v :
545569 errors .append (v )
546570 if errors :
547571 raise validate .ValidationException (
548572 "\n " .join ([str (e ) for e in errors ]))
549573 elif isinstance (link , dict ):
550- self .validate_links (link )
574+ self .validate_links (link , docid )
551575 else :
552576 raise validate .ValidationException ("Link must be a str, unicode, "
553577 "list, or a dict." )
554- return True
578+ return link
555579
556580 def getid (self , d ): # type: (Any) -> Union[basestring, None]
557581 if isinstance (d , dict ):
@@ -561,10 +585,10 @@ def getid(self, d): # type: (Any) -> Union[basestring, None]
561585 return d [i ]
562586 return None
563587
564- def validate_links (self , document ): # type: (Any) -> None
588+ def validate_links (self , document , base_url ): # type: (Any) -> None
565589 docid = self .getid (document )
566- if docid is None :
567- docid = ""
590+ if not docid :
591+ docid = base_url
568592
569593 errors = []
570594 iterator = None # type: Any
@@ -573,26 +597,26 @@ def validate_links(self, document): # type: (Any) -> None
573597 elif isinstance (document , dict ):
574598 try :
575599 for d in self .url_fields :
576- if d not in self . identity_links and d in document :
577- self .validate_link (d , document [d ])
600+ if d in document and d not in self . identity_links :
601+ document [ d ] = self .validate_link (d , document [d ], docid )
578602 except validate .ValidationException as v :
579603 errors .append (v )
580604 if hasattr (document , "iteritems" ):
581605 iterator = document .iteritems ()
582606 else :
583607 iterator = document .items ()
584608 else :
585- return
609+ return document
586610
587611 for key , val in iterator :
588612 try :
589- self .validate_links (val )
613+ document [ key ] = self .validate_links (val , docid )
590614 except validate .ValidationException as v :
591615 if key not in self .nolinkcheck :
592- docid = self .getid (val )
593- if docid :
616+ docid2 = self .getid (val )
617+ if docid2 :
594618 errors .append (validate .ValidationException (
595- "While checking object `%s`\n %s" % (docid , validate .indent (str (v )))))
619+ "While checking object `%s`\n %s" % (docid2 , validate .indent (str (v )))))
596620 else :
597621 if isinstance (key , basestring ):
598622 errors .append (validate .ValidationException (
@@ -607,7 +631,7 @@ def validate_links(self, document): # type: (Any) -> None
607631 "\n " .join ([str (e ) for e in errors ]))
608632 else :
609633 raise errors [0 ]
610- return
634+ return document
611635
612636
613637def _copy_dict_without_key (from_dict , filtered_key ):
0 commit comments