Compare commits

...

7 Commits

Author SHA1 Message Date
cclecle
407282f70e fix all typing warnings and maximize typing coverage 2023-03-26 20:21:22 +01:00
cclecle
198337e877 fix typing coverage and completion 2023-03-26 11:15:20 +01:00
cclecle
d1a34fafe3 add type check ! 2023-03-26 03:14:03 +01:00
cclecle
010145a0a8 applying quality warning messages 2023-03-26 02:38:12 +01:00
cclecle
1ed87d05f6 add class names 2023-03-26 02:35:40 +01:00
cclecle
2d8d2f2a74 fix introduced error in ResetFormaterList() 2023-03-26 02:28:51 +01:00
cclecle
8d68edd4ab fix ChangeLog => Changelog everywhere 2023-03-26 02:25:08 +01:00
10 changed files with 256 additions and 172 deletions

32
Jenkinsfile vendored
View File

@@ -397,14 +397,23 @@ pipeline {
}
post {
always {
dir("gitrepo") {
publishHTML([
reportDir: "helpers-results/quality_check",
reportFiles: "report.html",
reportName: "quality-report",
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true])
dir("gitrepo") {
publishCoverage adapters: [cobertura(mergeToOneReport: true, path: "helpers-results/types_check/cobertura.xml")]
junit 'helpers-results/types_check/junit.xml'
publishHTML([
reportDir: "helpers-results/quality_check",
reportFiles: "report.html",
reportName: "quality-report",
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true])
publishHTML([
reportDir: "helpers-results/types_check",
reportFiles: "index.html",
reportName: "types_check",
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true])
}
}
}
@@ -481,12 +490,7 @@ pipeline {
sz_full_rate = full_rate.setScale(2, RoundingMode.HALF_EVEN).toString()
badge_coverage.setStatus(sz_full_rate)
badge_coverage.setColor(getColorScale(full_rate))
//complexity = new BigDecimal( 10*GetCoverageValue_complexity(coverage_report_path))
//sz_complexity = complexity.setScale(2, RoundingMode.HALF_EVEN).toString()
//badge_complexity.setStatus(sz_complexity)
//badge_quality.setColor(getColorScale_reversed(complexity))
//badge_maintainability
records = readCSV file: 'helpers-results/complexity_check/MI.csv'
maintainability = records[1][1]

View File

@@ -8,7 +8,7 @@
![](docs-static/Library.jpg)
# pyChangeLogFactory
# pyChangelogFactory
A simple changelog formater that consume raw changes list text and produce nice pre-formated changelogs.
The input data mainly aim to be a merged commit report.

View File

@@ -42,7 +42,7 @@ From master git repository:
### Sample code
``` py
from pychangelogfactory import ChangeLogFactory
from pychangelogfactory import ChangelogFactory
raw_changelog = (
"feat: add a nice feature to the project\n"
@@ -52,7 +52,7 @@ raw_changelog = (
"improve core performances by reducing complexity\n"
"some random changes in the text content\n"
)
hdlr = ChangeLogFactory()
hdlr = ChangelogFactory()
hdlr.ProcessFullChangelog(self.raw_changelog)
changelog = hdlr.RenderFullChangelog()
print(changelog)
@@ -61,13 +61,13 @@ print(changelog)
#### Or shorted version:
``` py
hdlr = ChangeLogFactory(self.raw_changelog)
hdlr = ChangelogFactory(self.raw_changelog)
changelog = hdlr.RenderFullChangelog()
```
#### Or one-liner version:
``` py
changelog = ChangeLogFactory(self.raw_changelog).RenderFullChangelog()
changelog = ChangelogFactory(self.raw_changelog).RenderFullChangelog()
```
### Output(Raw)
@@ -97,7 +97,7 @@ changelog = ChangeLogFactory(self.raw_changelog).RenderFullChangelog()
### Options
#### Display unknown messages types
``` py
from pychangelogfactory import ChangeLogFormater
from pychangelogfactory import ChangelogFormater
raw_changelog = (
"feat: add a nice feature to the project\n"
@@ -107,7 +107,7 @@ raw_changelog = (
"improve core performances by reducing complexity\n"
"some random changes in the text content\n"
)
changelog = ChangeLogFactory(self.raw_changelog).RenderFullChangelog(include_unknown=True)
changelog = ChangelogFactory(self.raw_changelog).RenderFullChangelog(include_unknown=True)
print(changelog)
```
### Output (rendered)
@@ -125,34 +125,34 @@ print(changelog)
## 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: : |
| Type/Tag | Priority | Keywords | Title | Class Name |
|-----------|----------|----------------------------------------|-------------------------------------------------------|-------------------------------|
| break | 20 | break | :rotating_light: Breaking changes :rotating_light: : | `ChangelogFormater_break` |
| feat | 20 | feat, new, create, add | Features :sparkles: : | `ChangelogFormater_feat` |
| fix | 0 | fix, issue, problem | Fixes :wrench: : | `ChangelogFormater_fix` |
| security | 20 | safe, leak | Security :shield: : | `ChangelogFormater_security` |
| chore | 10 | task, refactor, build, better, improve | Chore :building_construction: : | `ChangelogFormater_chore` |
| perf | 15 | fast, perf | Performance Enhancements :rocket: : | `ChangelogFormater_perf` |
| wip | 0 | temp | Work in progress changes :construction: : | `ChangelogFormater_wip` |
| doc | 0 | doc, manual | Documentations :book: : | `ChangelogFormater_wip` |
| style | 5 | beautify | Style :art: : | `ChangelogFormater_style` |
| refactor | 0 | | Refactorings :recycle: : | `ChangelogFormater_refactor` |
| ci | 0 | jenkins, git | Continuous Integration :cyclone: : | `ChangelogFormater_ci` |
| test | -5 | unittest, check, testing | Testings :vertical_traffic_light: : | `ChangelogFormater_test` |
| build | 0 | compile, version | Builds :package: : | `ChangelogFormater_build` |
| revert | 0 | revert, fallback | Reverts :back: : | `ChangelogFormater_revert` |
| other | -20 | | Others :question: : | `ChangelogFormater_others` |
## Add new types
New formaters can be easily added by subclassing `ChangeLogFormater`:
New formaters can be easily added by subclassing `ChangelogFormater`:
### Inject custom formater locally (prefered way)
``` py
from pychangelogfactory import ChangeLogFormater,ChangeLogFactory
from pychangelogfactory import ChangelogFormater,ChangelogFactory
class ChangeLogFormater_others(ChangeLogFormater):
class ChangelogFormater_others(ChangelogFormater):
"""My formater"""
prefix: str = "mytag"
@@ -160,18 +160,18 @@ class ChangeLogFormater_others(ChangeLogFormater):
keywords: list[str] = ["foo","42"]
priority: int = 10
hdlr = ChangeLogFactory()
hdlr.RegisterFormater(ChangeLogFormater_others)
hdlr = ChangelogFactory()
hdlr.RegisterFormater(ChangelogFormater_others)
...
```
### Inject custom formater module-wide
``` py
from pychangelogfactory import ChangeLogFormater,ChangelogFormaterRecordType
from pychangelogfactory import ChangelogFormater,ChangelogFormaterRecordType
@ChangelogFormaterRecordType
class ChangeLogFormater_others(ChangeLogFormater):
class ChangelogFormater_others(ChangelogFormater):
"""My formater"""
prefix: str = "mytag"
@@ -179,7 +179,7 @@ class ChangeLogFormater_others(ChangeLogFormater):
keywords: list[str] = ["foo","42"]
priority: int = 10
hdlr = ChangeLogFactory()
hdlr = ChangelogFactory()
...
```
@@ -193,7 +193,7 @@ 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)
hdlr = ChangelogFactory(raw_changelog)
changelog = hdlr.RenderFullChangelog(include_unknown=True)
print(changelog)
```
@@ -207,24 +207,24 @@ print(changelog)
> need 42 coffee
### revert changes
### Revert changes
#### Reset to original list class-wise (all modules):
``` py
ChangeLogFactory.ResetFormaterList()
ChangelogFactory.ResetBaseFormaterList()
...
```
#### Reset to original list instance-wise:
``` py
hdlr = ChangeLogFactory()
hdlr = ChangelogFactory()
hdlr.ResetFormaterList()
...
```
#### Removing a specific formater:
``` py
hdlr = ChangeLogFactory()
hdlr.unRegisterFormater(ChangeLogFormater_others)
hdlr = ChangelogFactory()
hdlr.unRegisterFormater(ChangelogFormater_others)
...
```
/// warning
There is no way to remove a specific formater class-wise (all modules).
There is no way to remove a specific formater class-wise (all modules) except using ResetFormaterList().
///

View File

@@ -57,10 +57,11 @@ class helper_base(ABC):
return process.stdout
@classmethod
def run_cmd(cls, cmdarray):
def run_cmd(cls, cmdarray, silent: bool = False):
p = subprocess.run(cmdarray, capture_output=True)
print(p.stdout)
print(p.stderr)
if not silent:
print(p.stdout.decode())
print(p.stderr.decode())
return p.stdout

View File

@@ -37,7 +37,7 @@ class quality_check(helper_withresults_base):
def GetPylintMessageList(cls):
Messagelist = dict()
regex = r"^:([a-zA-Z-]+) \(([^\)]+)\)"
for line in cls.run_cmd([sys.executable, "-m", "pylint", "--list-msgs"]).splitlines():
for line in cls.run_cmd([sys.executable, "-m", "pylint", "--list-msgs"], True).splitlines():
if res := re.search(regex, line.decode()):
Messagelist[res.group(1)] = res.group(2)
cls.PylintMessageList = Messagelist
@@ -52,6 +52,7 @@ class quality_check(helper_withresults_base):
@classmethod
def do_job(cls):
print("checking code quality ...")
cls.GetPylintMessageList()
RES_all = dict()

View File

@@ -24,22 +24,18 @@ class types_check(helper_withresults_base):
print("checking code typing ...")
result = api.run(
[ # project path
"-m",
"src." + str(cls.pyproject["project"]["name"]),
"-p",
"src.pychangelogfactory",
# analysis configuration
"--ignore-missing-imports",
"--strict-equality",
# "--show-traceback",
"--explicit-package-bases",
# "--strict-equality",
# "--check-untyped-defs",
# reports generation
"--cobertura-xml-report",
str(cls.get_result_dir()),
"--html-report",
str(cls.get_result_dir()),
"--linecount-report",
str(cls.get_result_dir()),
"--linecoverage-report",
str(cls.get_result_dir()),
"--lineprecision-report",
str(cls.get_result_dir()),
"--txt-report",
str(cls.get_result_dir()),
"--xml-report",
@@ -52,6 +48,9 @@ class types_check(helper_withresults_base):
if result[0]:
print("\nType checking report:\n")
print(result[0]) # stdout
# converting the report using pylint_json2html (/!\ internal API, but as their is no leading '_' ...)
with open(cls.get_result_dir() / "raw_eport.txt", "w+", encoding="utf-8") as Outfile:
Outfile.write(result[0])
if result[1]:
print("\nError report:\n")

View File

@@ -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 ChangeLogFactory, ChangelogFormaterRecordType, ChangeLogFormater
from .changelogfactory import ChangelogFactory, ChangelogFormaterRecordType, ChangelogFormater

View File

@@ -15,36 +15,93 @@
from __future__ import annotations
import re
from re import Match, search, compile as _compile, match
from abc import ABC
from copy import deepcopy
_savedFormaterList = set()
from typing import TYPE_CHECKING
from typing import Generic, TypeVar, cast
if TYPE_CHECKING:
from typing import Optional, ClassVar, Type, Dict
T_ChangelogFormater = TypeVar("T_ChangelogFormater", bound="ChangelogFormater")
def ChangelogFormaterRecordType(Klass: type) -> type:
class _ChangelogFormatersCtx(Generic[T_ChangelogFormater]):
"""Storage class that manage Formaters"""
def __init__(self) -> None:
"""Storage class init method"""
self._savedFormaterList: set[Type[T_ChangelogFormater]] = set()
def add(self, record: Type[T_ChangelogFormater]) -> None:
"""Add a Formater to the storage class
Args:
record: the Formater class to be added
"""
self._savedFormaterList.add(record)
def remove(self, record: Type[T_ChangelogFormater]) -> None:
"""Remove a Formater from the storage class
Args:
record: the Formater class to be removed
"""
self._savedFormaterList.remove(record)
def reset(self) -> None:
"""Reset the storage class"""
self._savedFormaterList = set()
def get(self) -> set[Type[T_ChangelogFormater]]:
"""Get the storage data set
Returns:
The internal storage class (set)
"""
return self._savedFormaterList
def __copy__(self) -> _ChangelogFormatersCtx[T_ChangelogFormater]:
"""Copy the class"""
cls = self.__class__
result = cls.__new__(cls)
result._savedFormaterList = self._savedFormaterList
return result
def __deepcopy__(self, memo: Dict[int, object]) -> _ChangelogFormatersCtx[T_ChangelogFormater]:
"""Deep-Copy the class
Args:
memo: __deepcopy__ interface impl"""
result = self.__copy__()
memo[id(self)] = result
result._savedFormaterList = self._savedFormaterList.copy()
return result
def ChangelogFormaterRecordType(Klass: Type[T_ChangelogFormater]) -> Type[T_ChangelogFormater]:
"""Decorator function that registers formater implementation in factory
Args:
Klass: class to register in the factory
Returns:
untouched class"""
ChangeLogFactory.ar_FormaterKlass.add(Klass)
ChangelogFactory.RegisterBaseFormater(Klass)
return Klass
def _ChangelogFormaterRecordType(Klass: type) -> type:
def _ChangelogFormaterRecordType(Klass: Type[T_ChangelogFormater]) -> Type[T_ChangelogFormater]:
"""Internal decorator function that registers formater implementation in factory
Args:
Klass: class to register in the factory
Returns:
untouched class"""
_savedFormaterList.add(Klass)
cast(ChangelogFactory[ChangelogFormater], ChangelogFactory)._ar_SavedFormaterKlass.add(Klass)
return ChangelogFormaterRecordType(Klass)
class ChangeLogFormater(ABC):
"""ChangeLogFormater class
class ChangelogFormater(ABC):
"""ChangelogFormater class
This class is the base formater class.
This class is the formater base class.
This class is for:
@@ -58,16 +115,15 @@ class ChangeLogFormater(ABC):
///
"""
prefix: None | str = None
title: None | str = None
keywords: None | list[str] = None
priority: int = 0
prefix: ClassVar[Optional[str]] = None
title: ClassVar[Optional[str]] = None
keywords: ClassVar[Optional[list[str]]] = None
priority: ClassVar[int] = 0
_lines: list[None | str] = []
_lines: list[str] = []
def __init__(self):
"""ChangeLogFormater class constructor"""
self._lines = []
def __init__(self) -> None:
"""ChangelogFormater class constructor"""
self._lines: list[None | str] = []
def Clear(self) -> None:
"""Clear the formater content"""
@@ -103,7 +159,7 @@ class ChangeLogFormater(ABC):
return full_lines
@classmethod
def CheckLine(cls, content: str) -> re.Match:
def CheckLine(cls, content: str) -> None | Match[str]:
"""Check if a line match the current formater (lazy identification)
/// warning
@@ -116,7 +172,7 @@ class ChangeLogFormater(ABC):
Returns:
match object
"""
regex = re.compile(r"^(?:-\s+)?(?:{0})(?:\((.*)\))?(?::)(?:\s*)([^\s].+)".format(cls.prefix))
regex = _compile(rf"^(?:-\s+)?(?:{cls.prefix})(?:\((.*)\))?(?::)(?:\s*)([^\s].+)")
_match = regex.match(content)
return _match
@@ -132,55 +188,69 @@ class ChangeLogFormater(ABC):
True if a keyword has matched, False otherwise
"""
keyword_list = cls.keywords
for _keyword in keyword_list:
if (_keyword != "") and re.search(_keyword, content):
return True
if keyword_list:
for _keyword in keyword_list:
if _keyword and search(_keyword, content):
return True
return False
class ChangeLogFactory:
class ChangelogFactory(Generic[T_ChangelogFormater]):
"""The main changelog class"""
ar_FormaterKlass: set[type[ChangeLogFormater]] = set()
ar_Formater: None | dict[ChangeLogFormater] = None
_ar_SavedFormaterKlass: ClassVar[_ChangelogFormatersCtx[ChangelogFormater]] = _ChangelogFormatersCtx[ChangelogFormater]()
ar_FormaterKlass: _ChangelogFormatersCtx[T_ChangelogFormater] = _ChangelogFormatersCtx[T_ChangelogFormater]()
ar_Formater: Dict[str, T_ChangelogFormater] = {}
checkCommentPattern: str = r"^[ \t]*(?:\/\/|#)"
def __init__(self, ChangelogString: None | str = None):
"""Main ChangeLogFormater class constructor
def __init__(self, ChangelogString: Optional[str] = None) -> None:
"""Main ChangelogFormater class constructor
Args:
ChangelogString: optionnal input string to start with
ChangelogString: optional input string to be processed
"""
self.ar_Formater = dict()
self.ar_FormaterKlass = self.ar_FormaterKlass.copy()
self.ar_Formater: Dict[str, T_ChangelogFormater] = {}
self.ar_FormaterKlass = deepcopy(type(self).ar_FormaterKlass)
for FormaterKlass in self.ar_FormaterKlass:
for FormaterKlass in self.ar_FormaterKlass.get():
self.ar_Formater[FormaterKlass.__name__] = FormaterKlass()
# missing mypy coverage here because of internal bad isinstance() handling
# could be fixed using 'type(ChangelogString) is str' but then quality check will bad
# so let quality advise
if isinstance(ChangelogString, str):
self.ProcessFullChangelog(ChangelogString)
def ResetFormaterList(self=None) -> None | ChangeLogFactory:
"""Reset the formater class list to original
This method can be call both from class or from instance.
> If call from class it will reset the whole list.
> If call from instance only the instance will be reseted.
def ResetFormaterList(self) -> ChangelogFactory[T_ChangelogFormater]:
"""Reset the formater class list to original (Instance wise)
Returns:
self for convenience or None if call from class
self for convenience
"""
if self is not None:
self.ar_FormaterKlass = _savedFormaterList.copy()
self.ar_Formater = {}
for FormaterKlass in self.ar_FormaterKlass:
self.ar_Formater[FormaterKlass.__name__] = FormaterKlass()
ChangeLogFactory.ar_FormaterKlass = _savedFormaterList.copy()
self.ar_FormaterKlass: T_ChangelogFormater = deepcopy(
cast(_ChangelogFormatersCtx[T_ChangelogFormater], ChangelogFactory._ar_SavedFormaterKlass)
)
self.ar_Formater = {}
for FormaterKlass in self.ar_FormaterKlass.get():
self.ar_Formater[FormaterKlass.__name__] = FormaterKlass()
return self
@classmethod
def RegisterBaseFormater(cls, FormaterKlass: Type[T_ChangelogFormater]) -> None:
"""Register a new formater in the current instance
Args:
FormaterKlass: class of the formater to be added
"""
cls.ar_FormaterKlass.add(FormaterKlass)
@classmethod
def ResetBaseFormaterList(cls) -> None:
"""Reset the formater class list to original (BaseClass wise)"""
cls.ar_FormaterKlass = deepcopy(cls._ar_SavedFormaterKlass)
return None
def RegisterFormater(self, FormaterKlass: ChangeLogFormater) -> None:
def RegisterFormater(self, FormaterKlass: Type[T_ChangelogFormater]) -> ChangelogFactory[T_ChangelogFormater]:
"""Register a new formater in the current instance
Args:
@@ -192,7 +262,7 @@ class ChangeLogFactory:
self.ar_Formater[FormaterKlass.__name__] = FormaterKlass()
return self
def unRegisterFormater(self, FormaterKlass: ChangeLogFormater) -> None:
def unRegisterFormater(self, FormaterKlass: Type[T_ChangelogFormater]) -> ChangelogFactory[T_ChangelogFormater]:
"""unRegister a new formater in the current instance
Args:
@@ -204,7 +274,7 @@ class ChangeLogFactory:
del self.ar_Formater[FormaterKlass.__name__]
return self
def Clear(self) -> ChangeLogFactory:
def Clear(self) -> ChangelogFactory[T_ChangelogFormater]:
"""Clear internal memory
Returns:
self for convenience
@@ -220,14 +290,16 @@ class ChangeLogFactory:
If a matching formater is found, line is inserted.
Args:
RawChangelogLine: line to parse
RawChangelogLine: line to process
Returns:
True if successfully matched, False otherwise
"""
for formater in sorted(self.ar_Formater.values(), key=lambda x: x.priority):
content = formater.CheckLine(RawChangelogLine)
if content is not None:
formater.PushLine(content.group(2))
content: Optional[Match[str]] = formater.CheckLine(RawChangelogLine)
# missing mypy coverage here because of internal bad isinstance() handling AND Match type
if isinstance(content, Match) and (len(content.groups()) == 2):
res: str = content.group(2)
formater.PushLine(res)
return True
return False
@@ -239,7 +311,7 @@ class ChangeLogFactory:
If a matching formater is found, line is inserted.
Args:
RawChangelogLine: line to parse
RawChangelogLine: line to process
Returns:
True if successfully matched, False otherwise
"""
@@ -248,22 +320,25 @@ class ChangeLogFactory:
formater.PushLine(RawChangelogLine)
return True
self.ar_Formater[ChangeLogFormater_others.__name__].PushLine(RawChangelogLine)
self.ar_Formater[ChangelogFormater_others.__name__].PushLine(RawChangelogLine)
return False
def ProcessFullChangelog(self, RawChangelogMessage: str) -> ChangeLogFactory:
def ProcessFullChangelog(self, RawChangelogMessage: str) -> ChangelogFactory[T_ChangelogFormater]:
"""Process all input lines
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 #
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)
RawChangelogMessage: The full raw changelog to be processed
Returns:
self for convenience
"""
@@ -272,7 +347,7 @@ class ChangeLogFactory:
for line in RawChangelogMessage.split("\n"):
lineWordsCount = len(line.split())
if (lineWordsCount > 1) and (not re.match(self.checkCommentPattern, line)):
if (lineWordsCount > 1) and (not match(self.checkCommentPattern, line)):
if self._ProcessLineMain(line) is True:
continue
if lineWordsCount > 2:
@@ -292,7 +367,8 @@ class ChangeLogFactory:
"""
full_changelog = ""
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)):
# missing mypy coverage here because of internal bad isinstance() handling
if (include_unknown is False) and (isinstance(formater, ChangelogFormater_others)):
continue
full_changelog = full_changelog + formater.Render()
@@ -334,7 +410,7 @@ for RecordType, Config in {
"Documentations :book: :",
),
"style": ( 5, ["beautify", ],
"Style :art: :",
"Style :art: :",
),
"refactor": ( 0, [],
"Refactorings :recycle: :"
@@ -351,10 +427,12 @@ for RecordType, Config in {
# fmt: on
}.items():
# then we instantiate all of them
_name = f"ChangeLogFormater_{RecordType}"
_tmp = globals()[_name] = type(
_name = f"ChangelogFormater_{RecordType}"
# can not change globals definition so mypy will keep complaining
_tmp = type(
_name,
(ChangeLogFormater,),
(ChangelogFormater,),
{
"prefix": RecordType,
"title": Config[2],
@@ -362,18 +440,19 @@ for RecordType, Config in {
"priority": Config[0],
},
)
ChangeLogFactory.ar_FormaterKlass.add(_tmp)
_savedFormaterList.add(_tmp)
globals()[_name] = _tmp
cast(ChangelogFactory[ChangelogFormater], ChangelogFactory).RegisterBaseFormater(_tmp)
cast(ChangelogFactory[ChangelogFormater], ChangelogFactory)._ar_SavedFormaterKlass.add(_tmp)
@_ChangelogFormaterRecordType
class ChangeLogFormater_revert(ChangeLogFormater):
class ChangelogFormater_revert(ChangelogFormater):
"""Revert scope formater"""
prefix: str = "revert"
title: str = "Reverts :back: :"
keywords: list[str] = ["revert", "fallback"]
priority: int = 0
prefix: ClassVar[Optional[str]] = "revert"
title: ClassVar[Optional[str]] = "Reverts :back: :"
keywords: ClassVar[Optional[list[str]]] = ["revert", "fallback"]
priority: ClassVar[int] = 0
def RenderLines(self) -> str:
"""Render all lines
@@ -387,10 +466,10 @@ class ChangeLogFormater_revert(ChangeLogFormater):
@_ChangelogFormaterRecordType
class ChangeLogFormater_others(ChangeLogFormater):
class ChangelogFormater_others(ChangelogFormater):
"""Others / unknown scope formater"""
prefix: str = "other"
title: str = "Others :question: :"
keywords: list[str] = [""]
priority: int = -20
prefix: ClassVar[Optional[str]] = "other"
title: ClassVar[Optional[str]] = "Others :question: :"
keywords: ClassVar[Optional[list[str]]] = [""]
priority: ClassVar[int] = -20

View File

@@ -8,15 +8,15 @@
import unittest
from src.pychangelogfactory import ChangeLogFormater, ChangeLogFactory, ChangelogFormaterRecordType
from src.pychangelogfactory import ChangelogFormater, ChangelogFactory, ChangelogFormaterRecordType
class Testtest_module(unittest.TestCase):
def setUp(self):
ChangeLogFactory.ResetFormaterList()
ChangelogFactory.ResetBaseFormaterList()
def simplegeneration(self, inputstr, teststrs: list[str], withunknown: bool = False):
hdlr = ChangeLogFactory()
hdlr = ChangelogFactory()
hdlr.ProcessFullChangelog(inputstr)
changelog = hdlr.RenderFullChangelog(include_unknown=withunknown)
for test in teststrs:
@@ -25,7 +25,7 @@ class Testtest_module(unittest.TestCase):
def test_simplegeneration_ignored2(self):
raw = "break: testbreak break" + "\n" + "#doc: testdoc doc" + "\n" + "#style: teststyle beautify" + "\n" + "//test: testtest check"
hdlr = ChangeLogFactory(raw)
hdlr = ChangelogFactory(raw)
changelog = hdlr.RenderFullChangelog()
self.assertIn("testbreak", changelog)
@@ -36,7 +36,7 @@ class Testtest_module(unittest.TestCase):
def test_simplegeneration_ignored(self):
raw = "break: testbreak" + "\n" + "#doc: testdoc" + "\n" + "#style: teststyle" + "\n" + "//test: testtest"
hdlr = ChangeLogFactory(raw)
hdlr = ChangelogFactory(raw)
changelog = hdlr.RenderFullChangelog()
self.assertIn("testbreak", changelog)
self.assertNotIn("testdoc", changelog)
@@ -45,7 +45,7 @@ class Testtest_module(unittest.TestCase):
def test_simplegeneration_order(self):
raw = "break: testbreak" + "\n" + "doc: testdoc" + "\n" + "style: teststyle" + "\n" + "test: testtest"
hdlr = ChangeLogFactory(raw)
hdlr = ChangelogFactory(raw)
changelog = hdlr.RenderFullChangelog().splitlines()
self.assertIn("testbreak", changelog[1])
self.assertIn("teststyle", changelog[3])
@@ -156,22 +156,22 @@ class Testtest_module(unittest.TestCase):
# fmt: on
def test_sample(self):
hdlr = ChangeLogFactory(self.raw_changelog)
hdlr = ChangelogFactory(self.raw_changelog)
changelog = hdlr.RenderFullChangelog(include_unknown=True)
self.assertEqual(changelog, self.expected_formated)
def test_sample_aio(self):
changelog = ChangeLogFactory(self.raw_changelog).RenderFullChangelog(include_unknown=True)
changelog = ChangelogFactory(self.raw_changelog).RenderFullChangelog(include_unknown=True)
self.assertEqual(changelog, self.expected_formated)
def test_sample_exploded(self):
hdlr = ChangeLogFactory()
hdlr = ChangelogFactory()
hdlr.ProcessFullChangelog(self.raw_changelog)
changelog = hdlr.RenderFullChangelog(include_unknown=True)
self.assertEqual(changelog, self.expected_formated)
def test_sample_clear(self):
hdlr = ChangeLogFactory()
hdlr = ChangelogFactory()
hdlr.ProcessFullChangelog(self.raw_changelog)
changelog = hdlr.RenderFullChangelog(include_unknown=True)
self.assertEqual(changelog, self.expected_formated)
@@ -182,7 +182,7 @@ class Testtest_module(unittest.TestCase):
class Testtest_module_othercontext(unittest.TestCase):
def setUp(self):
ChangeLogFactory.ResetFormaterList()
ChangelogFactory.ResetBaseFormaterList()
def test_custom(self):
"""
@@ -190,7 +190,7 @@ class Testtest_module_othercontext(unittest.TestCase):
"""
@ChangelogFormaterRecordType
class ChangeLogFormater_TEST(ChangeLogFormater):
class ChangelogFormater_TEST(ChangelogFormater):
"""My formater"""
prefix: str = "mytag"
@@ -211,7 +211,7 @@ class Testtest_module_othercontext(unittest.TestCase):
)
# fmt: on
hdlr = ChangeLogFactory(raw_changelog)
hdlr = ChangelogFactory(raw_changelog)
changelog = hdlr.RenderFullChangelog(include_unknown=True)
self.assertEqual(changelog, expected_formated_orig)
@@ -219,7 +219,7 @@ class Testtest_module_othercontext(unittest.TestCase):
2nd PART: cheking the custom formater is still here after new object creation
"""
hdlr = ChangeLogFactory(raw_changelog)
hdlr = ChangelogFactory(raw_changelog)
changelog = hdlr.RenderFullChangelog(include_unknown=True)
self.assertEqual(changelog, expected_formated_orig)
@@ -227,8 +227,8 @@ class Testtest_module_othercontext(unittest.TestCase):
3rd PART: removing the custom formater at runtime
"""
hdlr = ChangeLogFactory()
hdlr.unRegisterFormater(ChangeLogFormater_TEST)
hdlr = ChangelogFactory()
hdlr.unRegisterFormater(ChangelogFormater_TEST)
hdlr.ProcessFullChangelog(raw_changelog)
changelog = hdlr.RenderFullChangelog(include_unknown=True)
@@ -248,7 +248,7 @@ class Testtest_module_othercontext(unittest.TestCase):
4th PART: checking it is back when create new obj
"""
hdlr = ChangeLogFactory()
hdlr = ChangelogFactory()
hdlr.ProcessFullChangelog(raw_changelog)
changelog = hdlr.RenderFullChangelog(include_unknown=True)
self.assertEqual(changelog, expected_formated_orig)
@@ -257,7 +257,7 @@ class Testtest_module_othercontext(unittest.TestCase):
3.1rd PART: removing the custom formater at runtime
"""
hdlr = ChangeLogFactory()
hdlr = ChangelogFactory()
hdlr.ResetFormaterList()
hdlr.ProcessFullChangelog(raw_changelog)
changelog = hdlr.RenderFullChangelog(include_unknown=True)
@@ -267,7 +267,7 @@ class Testtest_module_othercontext(unittest.TestCase):
4.1th PART: checking it is back when create new obj
"""
hdlr = ChangeLogFactory()
hdlr = ChangelogFactory()
hdlr.ProcessFullChangelog(raw_changelog)
changelog = hdlr.RenderFullChangelog(include_unknown=True)
self.assertEqual(changelog, expected_formated_orig)
@@ -275,8 +275,8 @@ class Testtest_module_othercontext(unittest.TestCase):
"""
5th PART: reseting class list globally
"""
ChangeLogFactory.ResetFormaterList()
hdlr = ChangeLogFactory()
ChangelogFactory.ResetBaseFormaterList()
hdlr = ChangelogFactory()
hdlr.ProcessFullChangelog(raw_changelog)
changelog = hdlr.RenderFullChangelog(include_unknown=True)
self.assertEqual(changelog, expected_formated)
@@ -284,7 +284,7 @@ class Testtest_module_othercontext(unittest.TestCase):
"""
6th PART: checking it is still not here
"""
hdlr = ChangeLogFactory()
hdlr = ChangelogFactory()
hdlr.ProcessFullChangelog(raw_changelog)
changelog = hdlr.RenderFullChangelog(include_unknown=True)
self.assertEqual(changelog, expected_formated)
@@ -292,10 +292,10 @@ class Testtest_module_othercontext(unittest.TestCase):
class Testtest_module_othercontext2(unittest.TestCase):
def setUp(self):
ChangeLogFactory.ResetFormaterList()
ChangelogFactory.ResetBaseFormaterList()
def test_custom2(self):
class ChangeLogFormater_TEST2(ChangeLogFormater):
class ChangelogFormater_TEST2(ChangelogFormater):
"""My formater"""
prefix: str = "mytag"
@@ -316,9 +316,9 @@ class Testtest_module_othercontext2(unittest.TestCase):
)
# fmt: on
hdlr = ChangeLogFactory()
hdlr = ChangelogFactory()
hdlr.RegisterFormater(ChangeLogFormater_TEST2)
hdlr.RegisterFormater(ChangelogFormater_TEST2)
hdlr.ProcessFullChangelog(raw_changelog)
changelog = hdlr.RenderFullChangelog(include_unknown=True)