7
7
import requests
8
8
import urlparse
9
9
import re
10
+ import copy
10
11
import ruamel .yaml as yaml
11
12
try :
12
13
from ruamel .yaml import CSafeLoader as SafeLoader
@@ -259,25 +260,30 @@ def resolve_ref(self, ref, base_url=None, checklinks=True):
259
260
260
261
obj = None # type: Dict[unicode, Any]
261
262
inc = False
263
+ mixin = None
262
264
263
265
# If `ref` is a dict, look for special directives.
264
266
if isinstance (ref , dict ):
265
267
obj = ref
266
- if u"$import" in ref :
268
+ if u"$import" in obj :
267
269
if len (obj ) == 1 :
268
270
ref = obj [u"$import" ]
269
271
obj = None
270
272
else :
271
273
raise ValueError (
272
- "'$import' must be the only field in %s" % (str (obj )))
274
+ u "'$import' must be the only field in %s" % (str (obj )))
273
275
elif u"$include" in obj :
274
276
if len (obj ) == 1 :
275
277
ref = obj [u"$include" ]
276
278
inc = True
277
279
obj = None
278
280
else :
279
281
raise ValueError (
280
- "'$include' must be the only field in %s" % (str (obj )))
282
+ u"'$include' must be the only field in %s" % (str (obj )))
283
+ elif u"$mixin" in obj :
284
+ ref = obj [u"$mixin" ]
285
+ mixin = obj
286
+ obj = None
281
287
else :
282
288
ref = None
283
289
for identifier in self .identifiers :
@@ -286,15 +292,17 @@ def resolve_ref(self, ref, base_url=None, checklinks=True):
286
292
break
287
293
if not ref :
288
294
raise ValueError (
289
- "Object `%s` does not have identifier field in %s" % (obj , self .identifiers ))
295
+ u "Object `%s` does not have identifier field in %s" % (obj , self .identifiers ))
290
296
291
297
if not isinstance (ref , (str , unicode )):
292
- raise ValueError ("Must be string: `%s`" % str (ref ))
298
+ raise ValueError (u "Must be string: `%s`" % str (ref ))
293
299
300
+ print ref , base_url
294
301
url = self .expand_url (ref , base_url , scoped_id = (obj is not None ))
302
+ print url
295
303
296
304
# Has this reference been loaded already?
297
- if url in self .idx :
305
+ if url in self .idx and ( not mixin ) :
298
306
return self .idx [url ], {}
299
307
300
308
# "$include" directive means load raw text
@@ -309,14 +317,25 @@ def resolve_ref(self, ref, base_url=None, checklinks=True):
309
317
else :
310
318
# Load structured document
311
319
doc_url , frg = urlparse .urldefrag (url )
312
- if doc_url in self .idx :
320
+ if doc_url in self .idx and (not mixin ):
321
+ # If the base document is in the index, it was already loaded,
322
+ # so if we didn't find the reference earlier then it must not
323
+ # exist.
313
324
raise validate .ValidationException (
314
- "Reference `#%s` not found in file `%s`." % (frg , doc_url ))
315
- doc = self .fetch (doc_url )
325
+ u "Reference `#%s` not found in file `%s`." % (frg , doc_url ))
326
+ doc = self .fetch (doc_url , inject_ids = ( not mixin ) )
316
327
317
328
# Recursively expand urls and resolve directives
318
- resolved_obj , metadata = self .resolve_all (
319
- doc if doc else obj , doc_url , checklinks = checklinks )
329
+ if mixin :
330
+ doc = copy .deepcopy (doc )
331
+ doc .update (mixin )
332
+ del doc ["$mixin" ]
333
+ url = None
334
+ resolved_obj , metadata = self .resolve_all (
335
+ doc , base_url , file_base = doc_url , checklinks = checklinks )
336
+ else :
337
+ resolved_obj , metadata = self .resolve_all (
338
+ doc if doc else obj , doc_url , checklinks = checklinks )
320
339
321
340
# Requested reference should be in the index now, otherwise it's a bad
322
341
# reference
@@ -477,6 +496,8 @@ def resolve_all(self, document, base_url, file_base=None, checklinks=True):
477
496
# Handle $import and $include
478
497
if (u'$import' in document or u'$include' in document ):
479
498
return self .resolve_ref (document , base_url = file_base , checklinks = checklinks )
499
+ elif u'$mixin' in document :
500
+ return self .resolve_ref (document , base_url = base_url , checklinks = checklinks )
480
501
elif isinstance (document , list ):
481
502
pass
482
503
else :
@@ -534,7 +555,7 @@ def resolve_all(self, document, base_url, file_base=None, checklinks=True):
534
555
document [key ], _ = loader .resolve_all (
535
556
val , base_url , file_base = file_base , checklinks = False )
536
557
except validate .ValidationException as v :
537
- _logger .debug ("loader is %s" , id (loader ))
558
+ _logger .warn ("loader is %s" , id (loader ), exc_info = v )
538
559
raise validate .ValidationException ("(%s) (%s) Validation error in field %s:\n %s" % (
539
560
id (loader ), file_base , key , validate .indent (str (v ))))
540
561
@@ -543,7 +564,7 @@ def resolve_all(self, document, base_url, file_base=None, checklinks=True):
543
564
try :
544
565
while i < len (document ):
545
566
val = document [i ]
546
- if isinstance (val , dict ) and u"$import" in val :
567
+ if isinstance (val , dict ) and ( u"$import" in val or u"$mixin" in val ) :
547
568
l , _ = loader .resolve_ref (val , base_url = file_base , checklinks = False )
548
569
if isinstance (l , list ): # never true?
549
570
del document [i ]
@@ -558,6 +579,7 @@ def resolve_all(self, document, base_url, file_base=None, checklinks=True):
558
579
val , base_url , file_base = file_base , checklinks = False )
559
580
i += 1
560
581
except validate .ValidationException as v :
582
+ _logger .warn ("failed" , exc_info = v )
561
583
raise validate .ValidationException ("(%s) (%s) Validation error in position %i:\n %s" % (
562
584
id (loader ), file_base , i , validate .indent (str (v ))))
563
585
@@ -601,7 +623,7 @@ def fetch_text(self, url):
601
623
else :
602
624
raise ValueError ('Unsupported scheme in url: %s' % url )
603
625
604
- def fetch (self , url ): # type: (unicode) -> Any
626
+ def fetch (self , url , inject_ids = True ): # type: (unicode) -> Any
605
627
if url in self .idx :
606
628
return self .idx [url ]
607
629
try :
@@ -614,7 +636,7 @@ def fetch(self, url): # type: (unicode) -> Any
614
636
result = yaml .load (textIO , Loader = SafeLoader )
615
637
except yaml .parser .ParserError as e : # type: ignore
616
638
raise validate .ValidationException ("Syntax error %s" % (e ))
617
- if isinstance (result , dict ) and self .identifiers :
639
+ if isinstance (result , dict ) and inject_ids and self .identifiers :
618
640
for identifier in self .identifiers :
619
641
if identifier not in result :
620
642
result [identifier ] = url
0 commit comments