diff --git a/py/envoy.base.utils/envoy/base/utils/abstract/project/changelog.py b/py/envoy.base.utils/envoy/base/utils/abstract/project/changelog.py index 82ad2cd0a..284ba2550 100644 --- a/py/envoy.base.utils/envoy/base/utils/abstract/project/changelog.py +++ b/py/envoy.base.utils/envoy/base/utils/abstract/project/changelog.py @@ -192,9 +192,15 @@ def base_version(self) -> str: @async_property(cache=True) async def data(self) -> typing.ChangelogDict: - return self.project.changelogs.validate_sections( - await self.project.execute(self.get_data, self.path), - self.path) + changelogs = self.project.changelogs + if changelogs._entries_layout and self._is_current: + parsed = await self.project.execute( + self.get_data_from_entries, + self.path, + changelogs.current_dir_path) + else: + parsed = await self.project.execute(self.get_data, self.path) + return changelogs.validate_sections(parsed, self.path) @property @abstracts.interfacemethod @@ -213,6 +219,10 @@ async def release_date(self) -> str: def version(self) -> _version.Version: return self._version + @property + def _is_current(self) -> bool: + return self.version == self.project.changelogs.current + async def entries(self, section: str) -> list[interface.IChangelogEntry]: return sorted( self.entry_class(section, entry) @@ -264,6 +274,10 @@ def current(self) -> _version.Version: def current_path(self) -> pathlib.Path: return self.project.path.joinpath(self.rel_current_path) + @property + def current_dir_path(self) -> pathlib.Path: + return self.project.path.joinpath(self.rel_current_dir_path) + @cached_property def current_tpl(self) -> jinja2.Template: return jinja2.Template(CHANGELOG_CURRENT_TPL) @@ -292,6 +306,10 @@ def paths(self) -> tuple[pathlib.Path, ...]: def rel_current_path(self) -> pathlib.Path: return pathlib.Path(CHANGELOG_CURRENT_PATH) + @property + def rel_current_dir_path(self) -> pathlib.Path: + return pathlib.Path(CHANGELOG_CURRENT_DIR_PATH) + @cached_property def section_re(self) -> re.Pattern[str]: return re.compile(r"\n[a-z_]*:") @@ -474,6 +492,11 @@ def yaml_change_presenter( def _yaml_changelogs_version(self) -> _version.Version: return _version.Version(YAML_CHANGELOGS_VERSION) + @property + def _entries_layout(self) -> bool: + return ( + self.project.path.joinpath(CHANGELOG_CURRENT_DIR_PATH).is_dir()) + def _is_rst_changelog(self, version: _version.Version) -> bool: return version < self._yaml_changelogs_version diff --git a/py/envoy.base.utils/tests/test_abstract_project_changelogs.py b/py/envoy.base.utils/tests/test_abstract_project_changelogs.py index 44a6ffecb..d69c658ba 100644 --- a/py/envoy.base.utils/tests/test_abstract_project_changelogs.py +++ b/py/envoy.base.utils/tests/test_abstract_project_changelogs.py @@ -191,6 +191,24 @@ def test_abstract_changelogs_current_path(patches): assert "current_path" not in changelogs.__dict__ +def test_abstract_changelogs_current_dir_path(patches): + project = MagicMock() + changelogs = DummyChangelogs(project) + patched = patches( + ("AChangelogs.rel_current_dir_path", + dict(new_callable=PropertyMock)), + prefix="envoy.base.utils.abstract.project.changelog") + + with patched as (m_rel, ): + assert ( + changelogs.current_dir_path + == project.path.joinpath.return_value) + assert ( + project.path.joinpath.call_args + == [(m_rel.return_value, ), {}]) + assert "current_dir_path" not in changelogs.__dict__ + + def test_abstract_changelogs_current_tpl(patches): changelogs = DummyChangelogs("PROJECT") patched = patches( @@ -295,6 +313,24 @@ def test_abstract_changelogs_rel_current_path(patches): == [(m_path, ), {}]) +def test_abstract_changelogs_rel_current_dir_path(patches): + changelogs = DummyChangelogs("PROJECT") + patched = patches( + "pathlib", + "CHANGELOG_CURRENT_DIR_PATH", + prefix="envoy.base.utils.abstract.project.changelog") + + with patched as (m_plib, m_path): + assert ( + changelogs.rel_current_dir_path + == m_plib.Path.return_value) + + assert ( + m_plib.Path.call_args + == [(m_path, ), {}]) + assert "rel_current_dir_path" not in changelogs.__dict__ + + @pytest.mark.parametrize( "raises", [None, Exception, yaml.reader.ReaderError, exceptions.TypeCastingError]) @@ -1006,6 +1042,27 @@ def test_abstract_changelogs_changelogs_methods(patches, method): == [(), {}]) +@pytest.mark.parametrize("is_dir", [True, False]) +def test_abstract_changelogs_entries_layout(patches, is_dir): + project = MagicMock() + changelogs = DummyChangelogs(project) + patched = patches( + "CHANGELOG_CURRENT_DIR_PATH", + prefix="envoy.base.utils.abstract.project.changelog") + + with patched as (m_dir_path, ): + project.path.joinpath.return_value.is_dir.return_value = is_dir + assert changelogs._entries_layout == is_dir + + assert ( + project.path.joinpath.call_args + == [(m_dir_path, ), {}]) + assert ( + project.path.joinpath.return_value.is_dir.call_args + == [(), {}]) + assert "_entries_layout" not in changelogs.__dict__ + + def test_abstract_changelogs__yaml_changelogs_version(patches): changelogs = DummyChangelogs("PROJECT") patched = patches( @@ -1509,18 +1566,35 @@ def test_abstract_changelog_base_version(patches): assert "base_version" not in changelog.__dict__ -async def test_abstract_changelog_data(patches): +@pytest.mark.parametrize("match", [True, False]) +def test_abstract_changelog__is_current(match): + project = MagicMock() + version = "VERSION" + changelog = DummyChangelog(project, version, "PATH") + project.changelogs.current = version if match else MagicMock() + assert changelog._is_current == match + assert "_is_current" not in changelog.__dict__ + + +@pytest.mark.parametrize("entries_layout", [True, False]) +@pytest.mark.parametrize("is_current", [True, False]) +async def test_abstract_changelog_data(patches, entries_layout, is_current): project = MagicMock() project.execute = AsyncMock() project.changelogs.validate_sections.return_value = "VALIDATED" + project.changelogs._entries_layout = entries_layout changelog = DummyChangelog(project, "VERSION", "PATH") patched = patches( + ("AChangelog._is_current", + dict(new_callable=PropertyMock)), "AChangelog.get_data", + "AChangelog.get_data_from_entries", ("AChangelog.path", dict(new_callable=PropertyMock)), prefix="envoy.base.utils.abstract.project.changelog") - with patched as (m_get, m_path): + with patched as (m_is_current, m_get, m_get_entries, m_path): + m_is_current.return_value = is_current assert ( await changelog.data == project.changelogs.validate_sections.return_value @@ -1528,9 +1602,19 @@ async def test_abstract_changelog_data(patches): changelog, abstract.AChangelog.data.cache_name)["data"]) - assert ( - project.execute.call_args - == [(m_get, m_path.return_value), {}]) + use_entries = is_current and entries_layout + if use_entries: + assert ( + project.execute.call_args + == [(m_get_entries, + m_path.return_value, + project.changelogs.current_dir_path), {}]) + assert not m_get.called + else: + assert ( + project.execute.call_args + == [(m_get, m_path.return_value), {}]) + assert not m_get_entries.called assert ( project.changelogs.validate_sections.call_args == [(project.execute.return_value, m_path.return_value), {}])