def _get_import_priorities(self) -> 'ImportPriorities': if not self._import_priorities: import_priorities = ImportPriorities() import_priorities.import_priority[self] = ImportInfo(0, False) self._prioritize_imports(set(), import_priorities, 1, False) self._import_priorities = import_priorities # make a copy so the caller doesn't mess these up return self._import_priorities.copy()
def _prioritize_imports(self, processed_set: Set['CdmDocumentDefinition'], import_priorities: 'ImportPriorities', sequence: int, \ skip_monikered: bool) -> int: # goal is to make a map from the reverse order of imports (breadth first) to the first (aka last) sequence number in that list. # This gives the semantic that the 'last/shallowest' definition for a duplicate symbol wins, # the lower in this list a document shows up, the higher priority its definitions are for resolving conflicts. # for 'moniker' imports, keep track of the 'last/shallowest' use of each moniker tag. # maps document to priority. priority_map = import_priorities.import_priority # type: Dict[CdmDocumentDefinition, ImportInfo] # maps moniker to document. moniker_map = import_priorities.moniker_priority_map # type: Dict[str, CdmDocumentDefinition] # if already in list, don't do this again if self in processed_set: # if the first document in the priority map is this then the document was the starting point of the recursion. # and if this document is present in the processedSet we know that there is a circular list of imports. if self in priority_map and priority_map[self].priority == 0: import_priorities.has_circular_import = True return sequence processed_set.add(self) if self.imports: # reverse order. # first add the imports done at this level only. # reverse the list reversed_imports = self.imports[::-1] # type: List[CdmImport] moniker_imports = [] # type: List[CdmDocumentDefinition] for imp in reversed_imports: imp_doc = imp._document # type: CdmDocumentDefinition # moniker imports will be added to the end of the priority list later. if imp_doc: if not imp.moniker and imp_doc not in priority_map: # add doc priority_map[imp_doc] = ImportInfo(sequence, False) sequence += 1 else: moniker_imports.append(imp_doc) else: logger.warning( self.ctx, self._TAG, CdmDocumentDefinition._prioritize_imports.__name__, self.at_corpus_path, CdmLogCode.WARN_DOC_IMPORT_NOT_LOADED, imp.corpus_path) # now add the imports of the imports. for imp in reversed_imports: imp_doc = imp._document # type: CdmDocumentDefinition is_moniker = bool(imp.moniker) if not imp_doc: logger.warning( self.ctx, self._TAG, CdmDocumentDefinition._prioritize_imports.__name__, self.at_corpus_path, CdmLogCode.WARN_DOC_IMPORT_NOT_LOADED, imp.corpus_path) # if the document has circular imports its order on the impDoc.ImportPriorities list is not correct # since the document itself will always be the first one on the list. if imp_doc and imp_doc._import_priorities and not imp_doc._import_priorities.has_circular_import: # lucky, already done so avoid recursion and copy imp_pri_sub = imp_doc._get_import_priorities() imp_pri_sub.import_priority.pop( imp_doc) # because already added above imports = list(imp_pri_sub.import_priority.keys()) imports.sort(key=lambda doc: imp_pri_sub.import_priority[ doc].priority) for key in imports: # if the document is imported with moniker in another document do not include it in the priority list of this one. # moniker imports are only added to the priority list of the document that directly imports them. if key not in priority_map and not imp_pri_sub.import_priority[ key].is_moniker: # add doc priority_map[key] = ImportInfo(sequence, False) sequence += 1 # if the import is not monikered then merge its monikerMap to this one. if not is_moniker: for key, value in imp_pri_sub.moniker_priority_map.items( ): moniker_map[key] = value elif imp_doc: # skip the monikered imports from here if this is a monikered import itself and we are only collecting the dependencies sequence = imp_doc._prioritize_imports( processed_set, import_priorities, sequence, is_moniker) if not skip_monikered: # moniker imports are prioritized by the 'closest' use of the moniker to the starting doc. # so last one found in this recursion for imp in self.imports: if imp._document and imp.moniker: moniker_map[imp.moniker] = imp._document # if the document index is zero, the document being processed is the root of the imports chain. # in this case add the monikered imports to the end of the priorityMap. if self in priority_map and priority_map[self].priority == 0: for doc in moniker_imports: if doc not in priority_map: priority_map[doc] = ImportInfo(sequence, True) sequence += 1 return sequence