diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs index 99f26c0..5e838b3 100644 --- a/.settings/org.eclipse.core.resources.prefs +++ b/.settings/org.eclipse.core.resources.prefs @@ -1,2 +1,3 @@ eclipse.preferences.version=1 +encoding//src/pychangelogfactory/changelogfactory.py=utf-8 encoding/=UTF-8 diff --git a/docs-static/usage.md b/docs-static/usage.md index 472d0d0..8550bd6 100644 --- a/docs-static/usage.md +++ b/docs-static/usage.md @@ -42,30 +42,170 @@ From master git repository: ### Sample code ``` py -from pychangelogfactory import ChangeLogFormater +from pychangelogfactory import ChangeLogFactory -raw_changelog=''' -feat: add a nice feature to the project -style: reindent the full Foo class -security: fix a security leak on the Foo2 component -''' -ChangeLogFormater.FactoryProcessFullChangelog(raw_changelog) -changelog = ChangeLogFormater.RenderFullChangelog() +raw_changelog = ( + "feat: add a nice feature to the project\n" + "style: reindent the full Foo class\n" + "security: fix a security issue on the Foo2 component\n" + "security: fix another security problem on the Foo2 component\n" + "improve core performances by reducing complexity\n" + "some random changes in the text content\n" +) +hdlr = ChangeLogFactory() +hdlr.ProcessFullChangelog(self.raw_changelog) +changelog = hdlr.RenderFullChangelog() print(changelog) ``` + +#### Or shorted version: + +``` py +hdlr = ChangeLogFactory(self.raw_changelog) +changelog = hdlr.RenderFullChangelog() +``` +#### Or one-liner version: + +``` py +changelog = ChangeLogFactory(self.raw_changelog).RenderFullChangelog() +``` + ### Output(Raw) - #### Features :sparkles:: + #### Features :sparkles: : > add a nice feature to the project - #### Security :shield:: - > fix a security leak on the Foo2 component - #### Style :art:: + #### Security :shield: : + > security: fix a security issue on the Foo2 component + > security: fix another security problem on the Foo2 component + #### Performance Enhancements :rocket: : + > improve core performances by reducing complexity + #### Style :art: : > reindent the full Foo class +### Output (rendered) +#### Features :sparkles: : +> add a nice feature to the project +#### Security :shield: : +> security: fix a security issue on the Foo2 component + +> security: fix another security problem on the Foo2 component +#### Performance Enhancements :rocket: : +> improve core performances by reducing complexity +#### Style :art: : +> reindent the full Foo class + +### Options +#### Display unknown messages types +``` py +from pychangelogfactory import ChangeLogFormater + +raw_changelog = ( + "feat: add a nice feature to the project\n" + "style: reindent the full Foo class\n" + "security: fix a security issue on the Foo2 component\n" + "security: fix another security problem on the Foo2 component\n" + "improve core performances by reducing complexity\n" + "some random changes in the text content\n" +) +changelog = ChangeLogFactory(self.raw_changelog).RenderFullChangelog(include_unknown=True) +print(changelog) +``` ### Output (rendered) #### Features :sparkles:: > add a nice feature to the project #### Security :shield:: -> fix a security leak on the Foo2 component +> fix a security issue on the Foo2 component +> fix another security problem on the Foo2 component +#### Performance Enhancements :rocket:: +> improve core performances by reducing complexity #### Style :art:: -> reindent the full Foo class \ No newline at end of file +> reindent the full Foo class +#### Others :question:: +> some random changes in the text content + +## Supported types + +| Type/Tag | Priority | Keywords | Title | +|-----------|----------|----------------------------------------|-------------------------------------------------------| +| break | 20 | break | :rotating_light: Breaking changes :rotating_light: : | +| feat | 20 | feat, new, create, add | Features :sparkles: : | +| fix | 0 | fix, issue, problem | Fixes :wrench: : | +| security | 20 | safe, leak | Security :shield: : | +| chore | 10 | task, refactor, build, better, improve | Chore :building_construction: : | +| perf | 15 | fast, perf | Performance Enhancements :rocket: : | +| wip | 0 | temp | Work in progress changes :construction: : | +| doc | 0 | doc, manual | Documentations :book: : | +| style | 5 | beautify | Style :art: : | +| refactor | 0 | | Refactorings :recycle: : | +| ci | 0 | jenkins, git | Continuous Integration :cyclone: : | +| test | -5 | unittest, check, testing | Testings :vertical_traffic_light: : | +| build | 0 | compile, version | Builds :package: : | +| revert | 0 | revert, fallback | Reverts :back: : | +| other | -20 | | Others :question: : | + +## Add new types + +New formaters can be easily added by subclassing `ChangeLogFormater`: + +### Inject custom formater locally (prefered way) + +``` py +from pychangelogfactory import ChangeLogFormater,ChangeLogFactory + +class ChangeLogFormater_others(ChangeLogFormater): + """My formater""" + + prefix: str = "mytag" + title: str = "My Title :" + keywords: list[str] = ["foo","42"] + priority: int = 10 + +hdlr = ChangeLogFactory() +hdlr.RegisterFormater(ChangeLogFormater_others) +... +``` + +### Inject custom formater module-wide + +``` py +from pychangelogfactory import ChangeLogFormater,ChangeLogFormaterRecordType + +@ChangeLogFormaterRecordType +class ChangeLogFormater_others(ChangeLogFormater): + """My formater""" + + prefix: str = "mytag" + title: str = "My Title :" + keywords: list[str] = ["foo","42"] + priority: int = 10 + +hdlr = ChangeLogFactory() +... +``` + +/// note | Scope +This will register your new formater for all next new factories +/// +### Test + +``` py +raw_changelog = ("mytag: add a nice feature to the project\n" + "foo modification in my file\n" + "need 42 coffee\n" + ) +hdlr = ChangeLogFactory(raw_changelog) +changelog = hdlr.RenderFullChangelog(include_unknown=True) +print(changelog) +``` + +### Output +#### My Title : +> add a nice feature to the project + +> foo modification in my file + +> need 42 coffee + + + + diff --git a/mkdocs.yml b/mkdocs.yml index b474ff2..d730766 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,11 +1,12 @@ docs_dir: docs site_name: pychangelogfactory -site_url: 'https://chacha.ddns.net/mkdocs-web/chacha/pychangelogfactory/latest/' -site_description: 'A simple changelog builder that you can feed with your repository change history' +site_url: https://chacha.ddns.net/mkdocs-web/chacha/pychangelogfactory/latest/ +site_description: A simple changelog builder that you can feed with your repository + change history site_author: chacha -repo_url: 'https://chacha.ddns.net/gitea/chacha/pychangelogfactory' +repo_url: https://chacha.ddns.net/gitea/chacha/pychangelogfactory use_directory_urls: false -copyright: 'CC BY-NC-SA 4.0' +copyright: CC BY-NC-SA 4.0 theme: name: material features: @@ -57,6 +58,13 @@ plugins: show_root_full_path: false merge_init_into_class: true separate_signature: true +- with-pdf: + cover_subtitle: User Manual + cover_logo: C:\Users\chacha\git\pychangelogfactory\docs-static\Library.jpg + verbose: false + exclude_pages: + - LICENSE + output_path: C:\Users\chacha\git\pychangelogfactory\helpers-results\doc_gen\site\pdf\manual.pdf markdown_extensions: - def_list - tables diff --git a/src/pychangelogfactory/__init__.py b/src/pychangelogfactory/__init__.py index 2babfbc..18d2fd7 100644 --- a/src/pychangelogfactory/__init__.py +++ b/src/pychangelogfactory/__init__.py @@ -20,4 +20,4 @@ except PackageNotFoundError: # pragma: no cover warnings.warn("can not read __version__, assuming local test context, setting it to ?.?.?") __version__ = "?.?.?" -from .changelogfactory import ChangeLogFormater +from .changelogfactory import ChangeLogFactory, ChangeLogFormaterRecordType, ChangeLogFormater diff --git a/src/pychangelogfactory/changelogfactory.py b/src/pychangelogfactory/changelogfactory.py index e900e28..22f732d 100644 --- a/src/pychangelogfactory/changelogfactory.py +++ b/src/pychangelogfactory/changelogfactory.py @@ -9,7 +9,7 @@ # You should have received a copy of the license along with this # work. If not, see . -"""A simple changelog formater that consume merged commit message and produce nice pre-formated changelogs. +"""A simple changelog formater that consume merged message and produce nice pre-formated changelogs. """ @@ -25,83 +25,80 @@ def ChangeLogFormaterRecordType(Klass: type) -> type: Klass: class to register in the factory Returns: untouched class""" - ChangeLogFormater.ar_Klass.append(Klass) + ChangeLogFactory.ar_FormaterKlass.add(Klass) return Klass class ChangeLogFormater(ABC): - """The main changelog class that define nearly everythings. - This was supposed to be a very shorty script this is why it is all-in-one... - /// warning - Factory and base-objects are mixed in the same class. - /// - """ - - ar_Klass: list[ChangeLogFormater] = [] - ar_LinesResult: list[ChangeLogFormater] = [] prefix: str = "^\s+" title: str = "Others :" - checkCommentPattern: str = r"^[ \t]*(?:\/\/|#)" keywords: list[str] = [] priority: int = 0 - def __init__(self, scope: str | None, ChangelogString: str): - """Main ChangeLogFormater class constructor + _lines: list[str] = [] + + def __init__(self): + """ChangeLogFormater class constructor + + This class is the base formater class. + + This class is for: + + - classifying message: CheckLine() and CheckLine_keywords() + - storing lines: Clear() and PushLine() + - returning the formated output: Render() and RenderLines() - This class contain both formater and factory. /// warning this class does not aim to be instantiated by user. /// + + """ + self._lines: list[str] = [] + + def Clear(self) -> None: + """Clear the formater content""" + self._lines: list[str] = [] + + def PushLine(self, ChangelogString: str) -> None: + """Push a new line in the formater + Args: - scope: scope of the formater (tag) - ChangelogString: formater rendered title + ChangelogString: the new line to insert """ - self._scope = scope - self._ChangelogString = ChangelogString.strip() + self._lines.append(ChangelogString.strip()) - def RenderLine(self): - """Get a rendered line - Returns: - the rendered line - """ - return self._ChangelogString.strip() - - @classmethod - def RenderLines(cls) -> str: - """Render all lines + def Render(self) -> str: + """Render all lines + title Returns: the rendered lines """ - changelog_category: str = "" - lines = cls.GetLines() - if len(lines) > 0: - changelog_category = f"#### {cls.title}\n" - for line in lines: - changelog_category = changelog_category + f"> {line.RenderLine()}" - if (scope := line.GetScope()) != "": - changelog_category = changelog_category + f"\t*[{scope}]*" - changelog_category = changelog_category + "\n" + changelog_category = "" + if len(self._lines) > 0: + changelog_category = f"#### {self.title}\n" + changelog_category = changelog_category + self.RenderLines() return changelog_category - def GetScope(self) -> str: - """Return the current scope (category) + def RenderLines(self) -> str: + """Render only lines Returns: - the current scope + the rendered lines """ - return self._scope if self._scope is not None else "" - - @classmethod - def Clear(cls) -> None: - """Clear internal memory""" - ChangeLogFormater.ar_LinesResult = [] + full_lines = "" + for line in self._lines: + full_lines = full_lines + f"> {line}" + "\n" + return full_lines @classmethod def CheckLine(cls, content: str) -> re.Match: - """Check if a line is in the current scope (lazy identification) - only formal tags are parsed by this function - eg: (): + """Check if a line match the current formater (lazy identification) + + /// warning + Only formal tags are parsed by this function + eg: `(): ` + /// + Args: content: line to parse Returns: @@ -113,13 +110,14 @@ class ChangeLogFormater(ABC): @classmethod def CheckLine_keywords(cls, content: str) -> bool: - """Check if a line is in the current scope (deeper in-word identification) - any word in the message can be used to categorize this message. - this function test only for the current category. + """Check if a line match the current formater (deeper in-content identification) + + Any word in the message can be used to categorize this message. + Args: content: line to parse Returns: - True if a keyword has matched + True if a keyword has matched, False otherwise """ keyword_list = cls.keywords for _keyword in keyword_list: @@ -127,91 +125,130 @@ class ChangeLogFormater(ABC): return True return False - @classmethod - def FactoryProcessLineMain(cls, RawChangelogLine: str) -> ChangeLogFormater: + +class ChangeLogFactory: + """The main changelog class""" + + ar_FormaterKlass: set[type[ChangeLogFormater]] = set() + ar_Formater: dict[ChangeLogFormater] = dict() + checkCommentPattern: str = r"^[ \t]*(?:\/\/|#)" + + def __init__(self, ChangelogString: None | str = None): + """Main ChangeLogFormater class constructor + + Args: + ChangelogString: optionnal input string to start with + """ + for FormaterKlass in ChangeLogFactory.ar_FormaterKlass: + self.ar_Formater[FormaterKlass.__name__] = FormaterKlass() + + if isinstance(ChangelogString, str): + self.ProcessFullChangelog(ChangelogString) + + def RegisterFormater(self, FormaterKlass: ChangeLogFormater) -> None: + """Register a new formater + + Args: + FormaterKlass: class of the formater to be added + Returns: + self class for convenience + """ + self.ar_FormaterKlass.add(FormaterKlass) + self.ar_Formater[FormaterKlass.__name__] = FormaterKlass() + return self + + def unRegisterFormater(self, FormaterKlass: ChangeLogFormater) -> None: + """Register a new formater + + Args: + FormaterKlass: class of the formater to be removed + Returns: + self class for convenience + """ + self.ar_FormaterKlass.remove(FormaterKlass) + del self.ar_Formater[FormaterKlass.__name__] + return self + + def Clear(self) -> ChangeLogFactory: + """Clear internal memory + Returns: + self class for convenience + """ + for formater in self.ar_Formater.values(): + formater.Clear() + return self + + def _ProcessLineMain(self, RawChangelogLine: str) -> bool: """Process a line and look for identified ones - this function will try to apply every available formater for the 1st search round: formal search - order of search is set according to formater's configuration + + This function will try to apply every available formater for the 1st search round: formal search + If a matching formater is found, line is inserted. + Args: RawChangelogLine: line to parse Returns: - a corresponding ChangeLogFormater_XXX() object, or a ChangeLogFormater_others() + True if successfully matched, False otherwise """ - for Klass in sorted(ChangeLogFormater.ar_Klass, key=lambda x: x.priority): - content = Klass.CheckLine(RawChangelogLine) + for formater in sorted(self.ar_Formater.values(), key=lambda x: x.priority): + content = formater.CheckLine(RawChangelogLine) if content is not None: - return Klass(content.group(1), content.group(2)) - return ChangeLogFormater_others(None, RawChangelogLine) + formater.PushLine(content.group(2)) + return True - @classmethod - def FactoryProcessLineSecond(cls, RawChangelogLine: str) -> ChangeLogFormater: + return False + + def _ProcessLineSecond(self, RawChangelogLine: str) -> bool: """Process a line and look for non-identified ones - this function will try to apply every available formater for the 2ns search round: any keyword - order of search is set according to formater's configuration + + This function will try to apply every available formater for the 2ns search round: any keyword + If a matching formater is found, line is inserted. + Args: RawChangelogLine: line to parse Returns: - a corresponding ChangeLogFormater_XXX() object, or a ChangeLogFormater_others() + True if successfully matched, False otherwise """ - for Klass in sorted(ChangeLogFormater.ar_Klass, key=lambda x: x.priority, reverse=True): - if Klass.CheckLine_keywords(RawChangelogLine): - return Klass(None, RawChangelogLine) + for formater in sorted(self.ar_Formater.values(), key=lambda x: x.priority, reverse=True): + if formater.CheckLine_keywords(RawChangelogLine): + formater.PushLine(RawChangelogLine) + return True - return ChangeLogFormater_others(None, RawChangelogLine) + self.ar_Formater[ChangeLogFormater_others.__name__].PushLine(RawChangelogLine) + return False - @classmethod - def FactoryProcessFullChangelog(cls, RawChangelogMessage: str) -> list[ChangeLogFormater]: + def ProcessFullChangelog(self, RawChangelogMessage: str) -> ChangeLogFactory: """Process all input lines - This function handle the main 2-round changes search algo. - Tt takes care of search-order and automatically skip any non-relevants message line. + + This function handles the main 2-round changes search algo. + It takes care of search-order and automatically skip any non-relevants message line. A non relevant line can be a commented one, or a to short one. Available comment patterns are: // and # A relevant commit line must contain: - at least 2 words for formal - at least 3 words for keywords + Args: RawChangelogMessage: The full raw changelog (merged commit-history) Returns: - a list of ChangeLogFormater_XXX() object + self class for convenience """ - LinesResult = [] + Lines2ndRound = [] for line in RawChangelogMessage.split("\n"): lineWordsCount = len(line.split()) - if (lineWordsCount > 1) and (not re.match(cls.checkCommentPattern, line)): - res = cls.FactoryProcessLineMain(line) - if type(res) is not ChangeLogFormater_others: - LinesResult.append(res) + if (lineWordsCount > 1) and (not re.match(self.checkCommentPattern, line)): + if self._ProcessLineMain(line) is True: + continue elif lineWordsCount > 2: Lines2ndRound.append(line) for line in Lines2ndRound: - LinesResult.append(cls.FactoryProcessLineSecond(line)) + self._ProcessLineSecond(line) - ChangeLogFormater.ar_LinesResult = LinesResult - return ChangeLogFormater.ar_LinesResult + return self - @classmethod - def GetLinesOfType(cls, Klass: type) -> list[ChangeLogFormater]: - """Retrieve all lines of specified formater type - Args: - Klass: type of formater to get - Returns: - a list of ChangeLogFormater_XXX() object - """ - return [_ for _ in ChangeLogFormater.ar_LinesResult if isinstance(_, Klass)] - - @classmethod - def GetLines(cls) -> list[ChangeLogFormater]: - """Retrieve all lines for the current formater - Returns: - a list of ChangeLogFormater_XXX() object - """ - return ChangeLogFormater.GetLinesOfType(cls) - - @classmethod - def RenderFullChangelog(cls, include_unknown: bool = False) -> str: + def RenderFullChangelog(self, include_unknown: bool = False) -> str: """Render the main changelog Args: include_unknown: includes unknown lines in an Unknown category @@ -219,10 +256,11 @@ class ChangeLogFormater(ABC): the final formated changelog """ full_changelog = "" - for Klass in sorted(ChangeLogFormater.ar_Klass, key=lambda x: x.priority, reverse=True): - if (include_unknown is False) and (Klass == ChangeLogFormater_others): + for formater in sorted(self.ar_Formater.values(), key=lambda x: x.priority, reverse=True): + if (include_unknown is False) and (isinstance(formater, ChangeLogFormater_others)): continue - full_changelog = full_changelog + Klass.RenderLines() + full_changelog = full_changelog + formater.Render() + return full_changelog @@ -237,43 +275,43 @@ class ChangeLogFormater(ABC): for RecordType, Config in { # fmt: off "break": ( 20, ["break"], - ":rotating_light: Breaking changes :rotating_light::", + ":rotating_light: Breaking changes :rotating_light: :", ), - "feat": ( 20, ["feat", "new", "create", "add"], - "Features :sparkles::" + "feat": ( 25, ["feat", "new", "create", "add"], + "Features :sparkles: :" ), - "fix": ( 0, ["fix","issue", "problem"], - "Fixes :wrench::" + "fix": ( 0, ["fix","issue", "problem"], + "Fixes :wrench: :" ), "security": ( 20, ["safe", "leak"], - "Security :shield::" + "Security :shield: :" ), - "chore": ( 20, ["task", "refactor", "build", "better", "improve"], - "Chore :building_construction::", + "chore": ( 10, ["task", "refactor", "build", "better", "improve"], + "Chore :building_construction: :", ), - "perf": ( 0, ["fast", ], - "Performance Enhancements :rocket::", + "perf": ( 15, ["fast","perf" ], + "Performance Enhancements :rocket: :", ), "wip": ( 0, ["temp", ], - "Work in progress changes :construction::", + "Work in progress changes :construction: :", ), - "docs": ( 0, [ "doc", ], - "Documentations :book::", + "doc": ( 0, [ "doc", "manual"], + "Documentations :book: :", ), "style": ( 5, ["beautify", ], - "Style :art::", + "Style :art: :", ), "refactor": ( 0, [], - "Refactorings :recycle::" + "Refactorings :recycle: :" ), "ci": ( 0, ["jenkins", "git"], - "Continuous Integration :cyclone::" + "Continuous Integration :cyclone: :" ), - "test": ( -5, ["unittest", "check", r"^(?:\s)*test(?:\s)*$"], - "Testings :vertical_traffic_light::" + "test": ( -5, ["unittest", "check", "testing"], + "Testings :vertical_traffic_light: :" ), "build": ( 0, ["compile", "version"], - "Builds :package:" + "Builds :package: :" ), # fmt: on }.items(): @@ -289,7 +327,7 @@ for RecordType, Config in { "priority": Config[0], }, ) - ChangeLogFormater.ar_Klass.append(_tmp) + ChangeLogFactory.ar_FormaterKlass.add(_tmp) @ChangeLogFormaterRecordType @@ -297,16 +335,19 @@ class ChangeLogFormater_revert(ChangeLogFormater): """Revert scope formater""" prefix: str = "revert" - title: str = "Reverts :back::" - keywords: list[str] = ["fallback"] + title: str = "Reverts :back: :" + keywords: list[str] = ["revert", "fallback"] priority: int = 0 - def RenderLine(self) -> str: - """an overloaded RenderLine implementation that adds surrounding '~~' + def RenderLines(self) -> str: + """Render all lines Returns: - the rendered pattern + the rendered lines """ - return "~~" + super().RenderLine() + "~~" + full_lines = "" + for line in self._lines: + full_lines = full_lines + f"> ~~{line}~~" + "\n" + return full_lines @ChangeLogFormaterRecordType @@ -314,6 +355,6 @@ class ChangeLogFormater_others(ChangeLogFormater): """Others / unknown scope formater""" prefix: str = "other" - title: str = "Others :question::" + title: str = "Others :question: :" keywords: list[str] = [""] priority: int = -20 diff --git a/test/test_changelogfactory.py b/test/test_changelogfactory.py index fcab973..1ba3db4 100644 --- a/test/test_changelogfactory.py +++ b/test/test_changelogfactory.py @@ -13,16 +13,17 @@ from src import pychangelogfactory class Testtest_module(unittest.TestCase): def simplegeneration(self, inputstr, teststrs: list[str]): - pychangelogfactory.ChangeLogFormater.FactoryProcessFullChangelog(inputstr) - changelog = pychangelogfactory.ChangeLogFormater.RenderFullChangelog() + hdlr = pychangelogfactory.ChangeLogFactory() + hdlr.ProcessFullChangelog(inputstr) + changelog = hdlr.RenderFullChangelog() for test in teststrs: self.assertIn(test, changelog) def test_simplegeneration_ignored2(self): - raw = "break: testbreak break" + "\n" + "#docs: testdoc doc" + "\n" + "#style: teststyle beautify" + "\n" + "//test: testtest check" + raw = "break: testbreak break" + "\n" + "#doc: testdoc doc" + "\n" + "#style: teststyle beautify" + "\n" + "//test: testtest check" - pychangelogfactory.ChangeLogFormater.FactoryProcessFullChangelog(raw) - changelog = pychangelogfactory.ChangeLogFormater.RenderFullChangelog() + hdlr = pychangelogfactory.ChangeLogFactory(raw) + changelog = hdlr.RenderFullChangelog() self.assertIn("testbreak", changelog) self.assertNotIn("testdoc", changelog) @@ -30,26 +31,26 @@ class Testtest_module(unittest.TestCase): self.assertNotIn("testtest", changelog) def test_simplegeneration_ignored(self): - raw = "break: testbreak" + "\n" + "#docs: testdoc" + "\n" + "#style: teststyle" + "\n" + "//test: testtest" + raw = "break: testbreak" + "\n" + "#doc: testdoc" + "\n" + "#style: teststyle" + "\n" + "//test: testtest" - pychangelogfactory.ChangeLogFormater.FactoryProcessFullChangelog(raw) - changelog = pychangelogfactory.ChangeLogFormater.RenderFullChangelog() + hdlr = pychangelogfactory.ChangeLogFactory(raw) + changelog = hdlr.RenderFullChangelog() self.assertIn("testbreak", changelog) self.assertNotIn("testdoc", changelog) self.assertNotIn("teststyle", changelog) self.assertNotIn("testtest", changelog) def test_simplegeneration_order(self): - raw = "break: testbreak" + "\n" + "docs: testdoc" + "\n" + "style: teststyle" + "\n" + "test: testtest" - pychangelogfactory.ChangeLogFormater.FactoryProcessFullChangelog(raw) - changelog = pychangelogfactory.ChangeLogFormater.RenderFullChangelog().splitlines() + raw = "break: testbreak" + "\n" + "doc: testdoc" + "\n" + "style: teststyle" + "\n" + "test: testtest" + hdlr = pychangelogfactory.ChangeLogFactory(raw) + changelog = hdlr.RenderFullChangelog().splitlines() self.assertIn("testbreak", changelog[1]) self.assertIn("teststyle", changelog[3]) self.assertIn("testdoc", changelog[5]) self.assertIn("testtest", changelog[7]) def test_simplegeneration_multiple(self): - raw = "break: testbreak" + "\n" + "docs: testdoc" + "\n" + "style: teststyle" + raw = "break: testbreak" + "\n" + "doc: testdoc" + "\n" + "style: teststyle" self.simplegeneration(raw, ["testbreak", "testdoc", "teststyle"]) def test_simplegeneration_breaking(self): @@ -90,7 +91,7 @@ class Testtest_module(unittest.TestCase): self.simplegeneration("test temp dummy1 dummy2", ["test temp"]) def test_simplegeneration_docs(self): - self.simplegeneration("docs: teststring", ["teststring"]) + self.simplegeneration("doc: teststring", ["teststring"]) self.simplegeneration("test doc dummy1 dummy2", ["test doc"]) def test_simplegeneration_style(self): @@ -118,22 +119,74 @@ class Testtest_module(unittest.TestCase): def test_simplegeneration_revert(self): self.simplegeneration("revert: teststring", ["~~teststring~~"]) + # fmt: off + raw_changelog = ( + "feat: add a nice feature to the project\n" + "style: reindent the full Foo class\n" + "security: fix a security issue on the Foo2 component\n" + "security: fix another security problem on the Foo2 component\n" + "improve core performances by reducing complexity\n" + "some random changes in the text content\n" + ) + expected_formated = ( + "#### Features :sparkles: :\n" + "> add a nice feature to the project\n" + "#### Security :shield: :\n" + "> fix a security issue on the Foo2 component\n" + "> fix another security problem on the Foo2 component\n" + "#### Performance Enhancements :rocket: :\n" + "> improve core performances by reducing complexity\n" + "#### Style :art: :\n" + "> reindent the full Foo class\n" + "#### Others :question: :\n" + "> some random changes in the text content\n" + ) + # fmt: on + def test_sample(self): - raw_changelog = ( - "feat: add a nice feature to the project\n" - "style: reindent the full Foo class\n" - "security: fix a security leak on the Foo2 component" - ) - pychangelogfactory.ChangeLogFormater.FactoryProcessFullChangelog(raw_changelog) - changelog = pychangelogfactory.ChangeLogFormater.RenderFullChangelog() + hdlr = pychangelogfactory.ChangeLogFactory(self.raw_changelog) + changelog = hdlr.RenderFullChangelog(include_unknown=True) + self.assertEqual(changelog, self.expected_formated) + def test_sample_aio(self): + changelog = pychangelogfactory.ChangeLogFactory(self.raw_changelog).RenderFullChangelog(include_unknown=True) + print(changelog) + self.assertEqual(changelog, self.expected_formated) + + def test_sample_exploded(self): + hdlr = pychangelogfactory.ChangeLogFactory() + hdlr.ProcessFullChangelog(self.raw_changelog) + changelog = hdlr.RenderFullChangelog(include_unknown=True) + self.assertEqual(changelog, self.expected_formated) + + +class Testtest_module_othercontext(unittest.TestCase): + def test_custom(self): + + from src.pychangelogfactory import ChangeLogFormater, ChangeLogFormaterRecordType, ChangeLogFactory + + @ChangeLogFormaterRecordType + class ChangeLogFormater_TEST(ChangeLogFormater): + """My formater""" + + prefix: str = "mytag" + title: str = "My Title :" + keywords: list[str] = ["foo", "42"] + priority: int = 10 + + # fmt: off + raw_changelog = ("mytag: add a nice feature to the project\n" + "foo modification in my file\n" + "need 42 coffee\n" + ) expected_formated = ( - "#### Features :sparkles::\n" - "> add a nice feature to the project\n" - "#### Security :shield::\n" - "> fix a security leak on the Foo2 component\n" - "#### Style :art::\n" - "> reindent the full Foo class\n" + "#### My Title :\n" + "> add a nice feature to the project\n" + "> foo modification in my file\n" + "> need 42 coffee\n" ) + # fmt: on + hdlr = ChangeLogFactory(raw_changelog) + changelog = hdlr.RenderFullChangelog(include_unknown=True) self.assertEqual(changelog, expected_formated)