7171_logger = logging .getLogger (__name__ )
7272
7373
74+ def get_model_dir_path () -> str :
75+ """The directory the fetched json schemas are written to"""
76+ return os .path .join (os .path .dirname (os .path .abspath (__file__ )), "model" )
77+
78+
79+ def write_schema_stub (model_dir_path : str , schema_name : str ) -> str :
80+ """Writes an empty schema, so a $ref pointing at it still resolves
81+
82+ A $ref is rewritten to a local file name before the page it names is
83+ fetched. When that page turns out not to exist, nothing writes the file,
84+ and datamodel-code-generator later fails with FileNotFoundError far away
85+ from the cause. An empty schema keeps generation going, and the reason is
86+ reported in the FetchSchemaResult instead.
87+ """
88+ schema_path = os .path .join (model_dir_path , schema_name + ".json" )
89+ os .makedirs (os .path .dirname (schema_path ), exist_ok = True )
90+ with open (schema_path , "w" , encoding = "utf-8" ) as f :
91+ f .write ("{}" )
92+ return schema_path
93+
94+
95+ def collect_messages (
96+ target : Optional [List [str ]], source : Optional [List [str ]]
97+ ) -> Optional [List [str ]]:
98+ """Adds the messages in source to target, skipping duplicates
99+
100+ _FetchSchemaParam is copied shallowly for a recursive fetch, so target and
101+ source can be the very same list. That case is already merged and is left
102+ alone rather than iterated while being appended to.
103+ """
104+ if not source or target is source :
105+ return target
106+ if target is None :
107+ target = []
108+ for message in source :
109+ if message not in target :
110+ target .append (message )
111+ return target
112+
113+
74114# Reusable type definitions
75115class OverwriteOptions (Enum ):
76116 """Options for overwriting properties"""
@@ -477,13 +517,15 @@ def fetch_schema(
477517
478518 # merge unique results and return
479519 merged_result = OSW .FetchSchemaResult (
480- fetched_schema_titles = [], error_messages = []
520+ fetched_schema_titles = [], error_messages = [], warning_messages = []
481521 )
482522 for result in results :
483523 if result .fetched_schema_titles :
484524 merged_result .fetched_schema_titles .extend (result .fetched_schema_titles )
485525 if result .error_messages :
486526 merged_result .error_messages .extend (result .error_messages )
527+ if result .warning_messages :
528+ merged_result .warning_messages .extend (result .warning_messages )
487529 return OSW .FetchSchemaResult (
488530 fetched_schema_titles = (
489531 list (set (merged_result .fetched_schema_titles ))
@@ -495,6 +537,11 @@ def fetch_schema(
495537 if len (merged_result .error_messages ) > 0
496538 else None
497539 ),
540+ warning_messages = (
541+ list (set (merged_result .warning_messages ))
542+ if len (merged_result .warning_messages ) > 0
543+ else None
544+ ),
498545 )
499546
500547 class _FetchSchemaParam (BaseModel ):
@@ -569,6 +616,8 @@ def _fetch_schema(
569616 ]
570617 if not page .exists :
571618 print (f"Error: Page { schema_title } does not exist" )
619+ # the $ref that led here was already rewritten to this file name
620+ write_schema_stub (get_model_dir_path (), schema_name )
572621 return OSW .FetchSchemaResult (
573622 fetched_schema_titles = fetchSchemaParam .fetched_schema_titles ,
574623 warning_messages = fetchSchemaParam .warning_messages ,
@@ -606,6 +655,7 @@ def _fetch_schema(
606655 schema = json .loads (schema_str .replace ("$ref" , "dollarref" ))
607656
608657 jsonpath_expr = parse ("$..dollarref" )
658+ ref_error_messages = None
609659 for match in jsonpath_expr .find (schema ):
610660 # value = "https://" + self.mw_site.host + match.value
611661 if match .value .startswith ("#" ):
@@ -628,11 +678,18 @@ def _fetch_schema(
628678 _param = fetchSchemaParam .copy ()
629679 _param .root = False
630680 _param .schema_title = ref_schema_title
631- self ._fetch_schema (_param ) # resolve references recursive
681+ ref_result = self ._fetch_schema (_param ) # resolve refs recursive
682+ # the recursive call is the only place that knows why a
683+ # referenced schema could not be fetched, so its messages have
684+ # to be carried up rather than dropped
685+ ref_error_messages = collect_messages (
686+ ref_error_messages , ref_result .error_messages
687+ )
688+ fetchSchemaParam .warning_messages = collect_messages (
689+ fetchSchemaParam .warning_messages , ref_result .warning_messages
690+ )
632691
633- model_dir_path = os .path .join (
634- os .path .dirname (os .path .abspath (__file__ )), "model"
635- ) # src/model
692+ model_dir_path = get_model_dir_path () # src/model
636693 schema_path = os .path .join (model_dir_path , schema_name + ".json" )
637694 os .makedirs (os .path .dirname (schema_path ), exist_ok = True )
638695 with open (schema_path , "w" , encoding = "utf-8" ) as f :
@@ -1005,6 +1062,7 @@ def _fetch_schema(
10051062 return OSW .FetchSchemaResult (
10061063 fetched_schema_titles = fetchSchemaParam .fetched_schema_titles ,
10071064 warning_messages = fetchSchemaParam .warning_messages ,
1065+ error_messages = ref_error_messages ,
10081066 )
10091067
10101068 def install_dependencies (
0 commit comments