Skip to content

Commit 7302eb9

Browse files
authored
py/envoy.base.utils: Add changelogs.yaml (#4562)
Signed-off-by: Ryan Northey <ryan@synca.io>
1 parent 1d49dea commit 7302eb9

5 files changed

Lines changed: 95 additions & 30 deletions

File tree

py/envoy.base.utils/envoy/base/utils/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ toolshed_library(
1616
"//py/deps:reqs#pyyaml",
1717
"//py/deps:reqs#trycast",
1818
"//py/deps:reqs#types-protobuf",
19+
"//py/deps:reqs#types-pyyaml",
1920
"//py/deps:reqs#zstandard",
2021
],
2122
sources=[

py/envoy.base.utils/envoy/base/utils/abstract/project/changelog.py

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
CHANGELOG_CURRENT_PATH = "changelogs/current.yaml"
3131
CHANGELOG_CURRENT_DIR_PATH = "changelogs/current"
3232
CHANGELOG_ENTRY_GLOB = "*/*.rst"
33-
CHANGELOG_SECTIONS_PATH = "changelogs/sections.yaml"
33+
CHANGELOG_CONFIG_PATH = "changelogs/changelogs.yaml"
3434
ENTRY_SEPARATOR = "__"
3535
CHANGELOG_SUMMARY_PATH = "changelogs/summary.md"
3636
CHANGELOG_URL_TPL = (
@@ -316,20 +316,28 @@ def section_re(self) -> re.Pattern[str]:
316316
return re.compile(r"\n[a-z_]*:")
317317

318318
@cached_property
319-
def sections(self) -> typing.ChangelogSectionsDict:
319+
def config(self) -> typing.ChangelogConfigDict:
320320
try:
321321
return utils.from_yaml(
322-
self.sections_path,
323-
typing.ChangelogSectionsDict)
322+
self.config_path,
323+
typing.ChangelogConfigDict)
324324
except _yaml.reader.ReaderError as e:
325325
raise exceptions.ChangelogError(
326-
"Failed to parse changelog sections "
327-
f"({self.sections_path}): {e}")
326+
"Failed to parse changelog config "
327+
f"({self.config_path}): {e}")
328328
except utils.TypeCastingError as e:
329329
logger.warning(
330-
"Changelog section parsing error: "
331-
f"({self.sections_path})\n{e}")
332-
return cast(typing.ChangelogSectionsDict, e.value)
330+
"Changelog config parsing error: "
331+
f"({self.config_path})\n{e}")
332+
return cast(typing.ChangelogConfigDict, e.value)
333+
334+
@cached_property
335+
def sections(self) -> typing.ChangelogSectionsDict:
336+
return self.config["sections"]
337+
338+
@cached_property
339+
def areas(self) -> typing.ChangelogAreasDict:
340+
return self.config["areas"]
333341

334342
def validate_sections(
335343
self,
@@ -353,12 +361,12 @@ def validate_sections(
353361
raise exceptions.ChangelogParseError(
354362
f"Unknown changelog section(s){where}: "
355363
f"{', '.join(unknown)}. "
356-
f"Valid sections come from {CHANGELOG_SECTIONS_PATH}.")
364+
f"Valid sections come from {CHANGELOG_CONFIG_PATH}.")
357365
return data
358366

359367
@property
360-
def sections_path(self) -> pathlib.Path:
361-
return self.project.path.joinpath(CHANGELOG_SECTIONS_PATH)
368+
def config_path(self) -> pathlib.Path:
369+
return self.project.path.joinpath(CHANGELOG_CONFIG_PATH)
362370

363371
@property
364372
def summary_path(self) -> pathlib.Path:

py/envoy.base.utils/envoy/base/utils/interface.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,12 @@ async def is_pending(self) -> bool:
233233
`Pending`."""
234234
raise NotImplementedError
235235

236+
@property
237+
@abstracts.interfacemethod
238+
def areas(self) -> typing.ChangelogAreasDict:
239+
"""Changelog areas."""
240+
raise NotImplementedError
241+
236242
@property
237243
@abstracts.interfacemethod
238244
def sections(self) -> typing.ChangelogSectionsDict:

py/envoy.base.utils/envoy/base/utils/typing.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,19 @@ class ChangelogSectionDict(BaseChangelogSectionDict, total=False):
5454

5555
ChangelogSectionsDict = dict[str, ChangelogSectionDict]
5656

57+
58+
class ChangelogAreaDict(BaseChangelogSectionDict):
59+
pass
60+
61+
62+
ChangelogAreasDict = dict[str, ChangelogAreaDict]
63+
64+
65+
class ChangelogConfigDict(TypedDict):
66+
sections: ChangelogSectionsDict
67+
areas: ChangelogAreasDict
68+
69+
5770
VersionConfigDict = dict[str, str]
5871

5972

py/envoy.base.utils/tests/test_abstract_project_changelogs.py

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -334,13 +334,13 @@ def test_abstract_changelogs_rel_current_dir_path(patches):
334334
@pytest.mark.parametrize(
335335
"raises",
336336
[None, Exception, yaml.reader.ReaderError, exceptions.TypeCastingError])
337-
def test_abstract_changelogs_sections(patches, raises):
337+
def test_abstract_changelogs_config(patches, raises):
338338
changelogs = DummyChangelogs("PROJECT")
339339
patched = patches(
340340
"cast",
341341
"utils.from_yaml",
342342
"logger",
343-
("AChangelogs.sections_path",
343+
("AChangelogs.config_path",
344344
dict(new_callable=PropertyMock)),
345345
prefix="envoy.base.utils.abstract.project.changelog")
346346

@@ -350,53 +350,89 @@ def test_abstract_changelogs_sections(patches, raises):
350350
m_yaml.side_effect = error
351351
if raises == Exception:
352352
with pytest.raises(Exception):
353-
changelogs.sections
353+
changelogs.config
354354
elif raises == yaml.reader.ReaderError:
355355
with pytest.raises(exceptions.ChangelogError) as e:
356-
changelogs.sections
356+
changelogs.config
357357
else:
358358
assert (
359-
changelogs.sections
359+
changelogs.config
360360
== (m_yaml.return_value
361361
if not raises
362362
else m_cast.return_value))
363363

364364
if raises == exceptions.TypeCastingError:
365365
assert (
366366
m_cast.call_args
367-
== [(typing.ChangelogSectionsDict, error.value), {}])
367+
== [(typing.ChangelogConfigDict, error.value), {}])
368368
assert (
369369
m_logger.warning.call_args
370-
== [("Changelog section parsing error: "
370+
== [("Changelog config parsing error: "
371371
f"({m_path.return_value})\n{error}", ), {}])
372372
else:
373373
assert not m_logger.called
374374
assert not m_cast.called
375375

376376
assert (
377377
m_yaml.call_args
378-
== [(m_path.return_value, typing.ChangelogSectionsDict), {}])
378+
== [(m_path.return_value, typing.ChangelogConfigDict), {}])
379379
assert (
380-
("sections" in changelogs.__dict__)
380+
("config" in changelogs.__dict__)
381381
== (not raises or raises == exceptions.TypeCastingError))
382382
if raises != yaml.reader.ReaderError:
383383
return
384384
assert (
385385
e.value.args[0]
386-
== ("Failed to parse changelog sections "
386+
== ("Failed to parse changelog config "
387387
f"({m_path.return_value}): {str(error)}"))
388388

389389

390-
def test_abstract_changelogs_sections_path():
390+
def test_abstract_changelogs_sections(patches):
391+
changelogs = DummyChangelogs("PROJECT")
392+
patched = patches(
393+
("AChangelogs.config",
394+
dict(new_callable=PropertyMock)),
395+
prefix="envoy.base.utils.abstract.project.changelog")
396+
397+
with patched as (m_config, ):
398+
assert (
399+
changelogs.sections
400+
== m_config.return_value.__getitem__.return_value)
401+
402+
assert (
403+
m_config.return_value.__getitem__.call_args
404+
== [("sections", ), {}])
405+
assert "sections" in changelogs.__dict__
406+
407+
408+
def test_abstract_changelogs_areas(patches):
409+
changelogs = DummyChangelogs("PROJECT")
410+
patched = patches(
411+
("AChangelogs.config",
412+
dict(new_callable=PropertyMock)),
413+
prefix="envoy.base.utils.abstract.project.changelog")
414+
415+
with patched as (m_config, ):
416+
assert (
417+
changelogs.areas
418+
== m_config.return_value.__getitem__.return_value)
419+
420+
assert (
421+
m_config.return_value.__getitem__.call_args
422+
== [("areas", ), {}])
423+
assert "areas" in changelogs.__dict__
424+
425+
426+
def test_abstract_changelogs_config_path():
391427
project = MagicMock()
392428
changelogs = DummyChangelogs(project)
393429
assert (
394-
changelogs.sections_path
430+
changelogs.config_path
395431
== project.path.joinpath.return_value)
396432
assert (
397433
project.path.joinpath.call_args
398-
== [(abstract.project.changelog.CHANGELOG_SECTIONS_PATH, ), {}])
399-
assert "sections_path" not in changelogs.__dict__
434+
== [(abstract.project.changelog.CHANGELOG_CONFIG_PATH, ), {}])
435+
assert "config_path" not in changelogs.__dict__
400436

401437

402438
def test_abstract_changelogs_validate_sections():
@@ -423,7 +459,7 @@ def test_abstract_changelogs_validate_sections_unknown(path):
423459

424460
message = e.value.args[0]
425461
assert "unknown" in message
426-
assert abstract.project.changelog.CHANGELOG_SECTIONS_PATH in message
462+
assert abstract.project.changelog.CHANGELOG_CONFIG_PATH in message
427463
if path is None:
428464
assert "changelogs/current.yaml" not in message
429465
assert "(None)" not in message
@@ -1737,9 +1773,10 @@ async def test_abstract_changelog_data_unknown_section(tmp_path):
17371773
"unknown:\n"
17381774
" - area: api\n"
17391775
" change: changed\n")
1740-
tmp_path.joinpath("changelogs/sections.yaml").write_text(
1741-
"known:\n"
1742-
" title: Known\n")
1776+
tmp_path.joinpath("changelogs/changelogs.yaml").write_text(
1777+
"sections:\n"
1778+
" known:\n"
1779+
" title: Known\n")
17431780

17441781
project = MagicMock()
17451782
project.path = tmp_path

0 commit comments

Comments
 (0)