diff --git a/src/core/model_utils.py b/src/core/model_utils.py index a7cf22c..bdf936e 100644 --- a/src/core/model_utils.py +++ b/src/core/model_utils.py @@ -366,6 +366,137 @@ def FileLine__clear( return __class__.Helper__ClearFileLine(cfgData, fileLineData) + # -------------------------------------------------------------------- + class tagFindCtx: + m_processedFiles: typing.Optional[typing.Set[str]] + + def __init__(self): + self.m_processedFiles = None + return + + def reg_file( + self, + filePath: str, + ) -> bool: + assert type(filePath) is str + assert filePath != "" + + if self.m_processedFiles is None: + self.m_processedFiles = set() + elif filePath in self.m_processedFiles: + return False + + self.m_processedFiles.add(filePath) + return True + + # -------------------------------------------------------------------- + @staticmethod + def File__FindSimpleOption( + findCtx: tagFindCtx, + fileData: PgCfgModel__FileData, + optionName: str, + enterIntoIncludes: bool, + ) -> typing.Optional[PgCfgModel__OptionData]: + assert type(findCtx) is __class__.tagFindCtx + assert type(fileData) is PgCfgModel__FileData + assert type(fileData.m_Lines) is list + assert type(optionName) is str + assert type(enterIntoIncludes) is bool + + if not findCtx.reg_file(fileData.m_Path): + return None + + stack: typing.List[ + typing.Tuple[ + PgCfgModel__FileData, + int, + ] + ] = [] + + top_fileData = fileData + top_index = len(fileData.m_Lines) + + while True: + assert top_index <= len(top_fileData.m_Lines) + assert top_index >= 0 + + nextFile: typing.Optional[PgCfgModel__FileData] = None + + while top_index > 0: + top_index -= 1 + + lineData = top_fileData.m_Lines[top_index] + + if len(lineData.m_Items) == 0: + continue + + assert len(lineData.m_Items) > 0 + assert len(lineData.m_Items) <= 2 + + firstItem = lineData.m_Items[0] + assert firstItem is not None + assert firstItem.m_Element is not None + + # Debug checks + if len(lineData.m_Items) == 1: + pass + else: + assert len(lineData.m_Items) == 2 + assert lineData.m_Items[1] is not None + assert lineData.m_Items[1].m_Element is not None + assert ( + type(lineData.m_Items[1].m_Element) is PgCfgModel__CommentData + ) + pass + + if type(firstItem.m_Element) is PgCfgModel__OptionData: + if firstItem.m_Element.m_Name != optionName: + continue + + return firstItem.m_Element # <--------- BINGO + + if type(firstItem.m_Element) is PgCfgModel__IncludeData: + if not enterIntoIncludes: + # Stop searching upwards: any option defined above an include + # might be overridden by that included file. + return None # <--------- FAIL + + if not findCtx.reg_file(firstItem.m_Element.m_File.m_Path): + continue + + nextFile = firstItem.m_Element.m_File + break + + if type(firstItem.m_Element) is PgCfgModel__CommentData: + assert len(lineData.m_Items) == 1 + continue + + BugCheckError.UnkObjectDataType( + type(firstItem.m_Element), + ) + + if nextFile is None: + assert top_index == 0 + + if len(stack) == 0: + return None # <--------- FAIL + + top = stack.pop() + assert top is not None + top_fileData = top[0] + top_index = top[1] + continue + + assert nextFile is not None + assert top_index >= 0 + assert top_index < len(top_fileData.m_Lines) + + stack.append((top_fileData, top_index)) + + top_fileData = nextFile + top_index = len(nextFile.m_Lines) + continue + # -------------------------------------------------------------------- @staticmethod def File__add_Option( diff --git a/src/implementation/v00/configuration_base_ds_strict.py b/src/implementation/v00/configuration_base_ds_strict.py index 9c0d5d9..e10588b 100644 --- a/src/implementation/v00/configuration_base_ds_strict.py +++ b/src/implementation/v00/configuration_base_ds_strict.py @@ -129,22 +129,25 @@ def GetOptionValue__Simple( # -------------------------------------- FILE DATA if type(sourceData) is PgCfgModel__FileData: - fileData = sourceData + assert sourceData.IsAlive() - assert fileData.IsAlive() - assert fileData.m_OptionsByName is not None - assert type(fileData.m_OptionsByName) is PgCfgModel__MultiIndex + if optionName not in cfg.m_Data.m_AllOptionsByName.keys(): + return None - optionData = __class__.Helper__FindSimpleOption( - cfg, - fileData.m_OptionsByName, + findCtx = PgCfgModel__ModelUtils.tagFindCtx() + + optionData = PgCfgModel__ModelUtils.File__FindSimpleOption( + findCtx, + sourceData, optionName, + enterIntoIncludes=True, ) if optionData is not None: assert type(optionData) is PgCfgModel__OptionData assert optionData.IsAlive() assert optionData.m_Value is not None + assert optionData.m_Name == optionName return __class__.Helper__PrepareGetValue( cfg, optionData.m_Name, diff --git a/tests/implementation/v00/configuration_std/Objects/PostgresConfigurationReader_Base/LoadConfigurationFile/test_set001__common.py b/tests/implementation/v00/configuration_std/Objects/PostgresConfigurationReader_Base/LoadConfigurationFile/test_set001__common.py index 9fe4b52..d82044a 100644 --- a/tests/implementation/v00/configuration_std/Objects/PostgresConfigurationReader_Base/LoadConfigurationFile/test_set001__common.py +++ b/tests/implementation/v00/configuration_std/Objects/PostgresConfigurationReader_Base/LoadConfigurationFile/test_set001__common.py @@ -227,7 +227,7 @@ def test_002__two_files( assert file1.m_FileData is load_r1.m_FileData assert file2.m_FileData is load_r2.m_FileData - assert file1.GetOptionValue("port") is None + assert file1.GetOptionValue("port") == 123 assert file2.GetOptionValue("port") == 123 return @@ -296,7 +296,7 @@ def test_003__two_files__duplication_and_cycles( file1 = cfg.get_AllFiles().GetFileByName(C_FILE_NAME1) file2 = cfg.get_AllFiles().GetFileByName(C_FILE_NAME2) - assert file1.GetOptionValue("port") is None + assert file1.GetOptionValue("port") == 123 assert file2.GetOptionValue("port") == 123 return