From 02999fc639d80bc72f124195228a10fed80e7b8e Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Sat, 6 Jun 2020 15:29:18 +0800 Subject: [PATCH 01/13] refactor(defaults): move bump_map, bump_pattern, commit_parser from defaults to ConventionalCommitsCz These defaults are not defaults for other cz --- commitizen/bump.py | 13 ++--------- commitizen/changelog.py | 2 +- .../conventional_commits.py | 18 +++++++++++---- tests/commands/conftest.py | 2 +- tests/test_bump_find_increment.py | 7 +++++- tests/test_changelog.py | 23 +++++++++++-------- tests/test_cz_base.py | 2 +- tests/test_cz_conventional_commits.py | 2 +- tests/test_cz_jira.py | 2 +- tests/test_factory.py | 2 +- 10 files changed, 41 insertions(+), 32 deletions(-) diff --git a/commitizen/bump.py b/commitizen/bump.py index 9312491044..f3319fc1cc 100644 --- a/commitizen/bump.py +++ b/commitizen/bump.py @@ -6,22 +6,13 @@ from packaging.version import Version -from commitizen.defaults import ( - MAJOR, - MINOR, - PATCH, - bump_map, - bump_message, - bump_pattern, -) +from commitizen.defaults import MAJOR, MINOR, PATCH, bump_message from commitizen.exceptions import CurrentVersionNotFoundError from commitizen.git import GitCommit def find_increment( - commits: List[GitCommit], - regex: str = bump_pattern, - increments_map: Union[dict, OrderedDict] = bump_map, + commits: List[GitCommit], regex: str, increments_map: Union[dict, OrderedDict] ) -> Optional[str]: if isinstance(increments_map, dict): diff --git a/commitizen/changelog.py b/commitizen/changelog.py index 7bb9007cdc..d854219a28 100644 --- a/commitizen/changelog.py +++ b/commitizen/changelog.py @@ -69,7 +69,7 @@ def generate_tree_from_commits( commits: List[GitCommit], tags: List[GitTag], commit_parser: str, - changelog_pattern: str = defaults.bump_pattern, + changelog_pattern: str, unreleased_version: Optional[str] = None, change_type_map: Optional[Dict[str, str]] = None, changelog_message_builder_hook: Optional[Callable] = None, diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py index 61f7b7f94b..f9890d46ba 100644 --- a/commitizen/cz/conventional_commits/conventional_commits.py +++ b/commitizen/cz/conventional_commits/conventional_commits.py @@ -1,5 +1,6 @@ import os import re +from collections import OrderedDict from typing import Any, Dict, List from commitizen import defaults @@ -28,10 +29,19 @@ def parse_subject(text): class ConventionalCommitsCz(BaseCommitizen): - bump_pattern = defaults.bump_pattern - bump_map = defaults.bump_map - commit_parser = defaults.commit_parser - changelog_pattern = defaults.bump_pattern + bump_pattern = r"^(BREAKING[\-\ ]CHANGE|feat|fix|refactor|perf)(\(.+\))?(!)?" + bump_map = OrderedDict( + ( + (r"^.+!$", defaults.MAJOR), + (r"^BREAKING[\-\ ]CHANGE", defaults.MAJOR), + (r"^feat", defaults.MINOR), + (r"^fix", defaults.PATCH), + (r"^refactor", defaults.PATCH), + (r"^perf", defaults.PATCH), + ) + ) + commit_parser = r"^(?Pfeat|fix|refactor|perf|BREAKING CHANGE)(?:\((?P[^()\r\n]*)\)|\()?(?P!)?:\s(?P.*)?" # noqa + version_parser = defaults.version_parser change_type_map = { "feat": "Feat", "fix": "Fix", diff --git a/tests/commands/conftest.py b/tests/commands/conftest.py index faae727918..46e9500365 100644 --- a/tests/commands/conftest.py +++ b/tests/commands/conftest.py @@ -9,7 +9,7 @@ @pytest.fixture() def config(): _config = BaseConfig() - _config.settings.update({"name": defaults.name}) + _config.settings.update({"name": defaults.DEFAULT_SETTINGS["name"]}) return _config diff --git a/tests/test_bump_find_increment.py b/tests/test_bump_find_increment.py index 1f98694a2b..826490a3ba 100644 --- a/tests/test_bump_find_increment.py +++ b/tests/test_bump_find_increment.py @@ -5,6 +5,7 @@ import pytest from commitizen import bump +from commitizen.cz import ConventionalCommitsCz from commitizen.git import GitCommit NONE_INCREMENT_CC = ["docs(README): motivation", "ci: added travis"] @@ -72,7 +73,11 @@ ) def test_find_increment(messages, expected_type): commits = [GitCommit(rev="test", title=message) for message in messages] - increment_type = bump.find_increment(commits) + increment_type = bump.find_increment( + commits, + regex=ConventionalCommitsCz.bump_pattern, + increments_map=ConventionalCommitsCz.bump_map, + ) assert increment_type == expected_type diff --git a/tests/test_changelog.py b/tests/test_changelog.py index 055e4fc916..da05eaf2b7 100644 --- a/tests/test_changelog.py +++ b/tests/test_changelog.py @@ -1,6 +1,9 @@ import pytest from commitizen import changelog, defaults, git +from commitizen.cz.conventional_commits.conventional_commits import ( + ConventionalCommitsCz, +) from commitizen.exceptions import InvalidConfigurationError COMMITS_DATA = [ @@ -841,8 +844,8 @@ def test_order_changelog_tree_raises(): def test_render_changelog(gitcommits, tags, changelog_content): - parser = defaults.commit_parser - changelog_pattern = defaults.bump_pattern + parser = ConventionalCommitsCz.commit_parser + changelog_pattern = ConventionalCommitsCz.bump_pattern tree = changelog.generate_tree_from_commits( gitcommits, tags, parser, changelog_pattern ) @@ -852,8 +855,8 @@ def test_render_changelog(gitcommits, tags, changelog_content): def test_render_changelog_unreleased(gitcommits): some_commits = gitcommits[:7] - parser = defaults.commit_parser - changelog_pattern = defaults.bump_pattern + parser = ConventionalCommitsCz.commit_parser + changelog_pattern = ConventionalCommitsCz.bump_pattern tree = changelog.generate_tree_from_commits( some_commits, [], parser, changelog_pattern ) @@ -867,8 +870,8 @@ def test_render_changelog_tag_and_unreleased(gitcommits, tags): tag for tag in tags if tag.rev == "56c8a8da84e42b526bcbe130bd194306f7c7e813" ] - parser = defaults.commit_parser - changelog_pattern = defaults.bump_pattern + parser = ConventionalCommitsCz.commit_parser + changelog_pattern = ConventionalCommitsCz.bump_pattern tree = changelog.generate_tree_from_commits( some_commits, single_tag, parser, changelog_pattern ) @@ -881,8 +884,8 @@ def test_render_changelog_tag_and_unreleased(gitcommits, tags): def test_render_changelog_with_change_type(gitcommits, tags): new_title = ":some-emoji: feature" change_type_map = {"feat": new_title} - parser = defaults.commit_parser - changelog_pattern = defaults.bump_pattern + parser = ConventionalCommitsCz.commit_parser + changelog_pattern = ConventionalCommitsCz.bump_pattern tree = changelog.generate_tree_from_commits( gitcommits, tags, parser, changelog_pattern, change_type_map=change_type_map ) @@ -897,8 +900,8 @@ def changelog_message_builder_hook(message: dict, commit: git.GitCommit) -> dict ] = f"{message['message']} [link](github.com/232323232) {commit.author} {commit.author_email}" return message - parser = defaults.commit_parser - changelog_pattern = defaults.bump_pattern + parser = ConventionalCommitsCz.commit_parser + changelog_pattern = ConventionalCommitsCz.bump_pattern tree = changelog.generate_tree_from_commits( gitcommits, tags, diff --git a/tests/test_cz_base.py b/tests/test_cz_base.py index 04cc7d9e83..80450d4476 100644 --- a/tests/test_cz_base.py +++ b/tests/test_cz_base.py @@ -8,7 +8,7 @@ @pytest.fixture() def config(): _config = BaseConfig() - _config.settings.update({"name": defaults.name}) + _config.settings.update({"name": defaults.DEFAULT_SETTINGS["name"]}) return _config diff --git a/tests/test_cz_conventional_commits.py b/tests/test_cz_conventional_commits.py index 27445cd57c..82a123b8a2 100644 --- a/tests/test_cz_conventional_commits.py +++ b/tests/test_cz_conventional_commits.py @@ -23,7 +23,7 @@ @pytest.fixture() def config(): _config = BaseConfig() - _config._settings["name"] = defaults.name + _config._settings["name"] = defaults.DEFAULT_SETTINGS["name"] return _config diff --git a/tests/test_cz_jira.py b/tests/test_cz_jira.py index b725a46fa9..0060613961 100644 --- a/tests/test_cz_jira.py +++ b/tests/test_cz_jira.py @@ -8,7 +8,7 @@ @pytest.fixture() def config(): _config = BaseConfig() - _config.settings.update({"name": defaults.name}) + _config.settings.update({"name": defaults.DEFAULT_SETTINGS["name"]}) return _config diff --git a/tests/test_factory.py b/tests/test_factory.py index 1a2eb7178d..5fbd2deebb 100644 --- a/tests/test_factory.py +++ b/tests/test_factory.py @@ -7,7 +7,7 @@ def test_factory(): config = BaseConfig() - config.settings.update({"name": defaults.name}) + config.settings.update({"name": defaults.DEFAULT_SETTINGS["name"]}) r = factory.commiter_factory(config) assert isinstance(r, BaseCommitizen) From 316695b3a07e4fd0313cdfec8744996f82e6028f Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Sat, 6 Jun 2020 15:33:31 +0800 Subject: [PATCH 02/13] test: move default config to top level conftest.py --- tests/conftest.py | 17 ++++++++++++++++- tests/test_cz_base.py | 9 --------- tests/test_cz_conventional_commits.py | 9 --------- tests/test_cz_jira.py | 11 ----------- 4 files changed, 16 insertions(+), 30 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index f293d5631b..aec79c2db3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,9 @@ +import os + import pytest -from commitizen import cmd +from commitizen import cmd, defaults +from commitizen.config import BaseConfig @pytest.fixture(scope="function") @@ -18,3 +21,15 @@ def tmp_commitizen_project(tmp_git_project): tmp_commitizen_cfg_file.write("[tool.commitizen]\n" 'version="0.1.0"\n') yield tmp_git_project + + +@pytest.fixture() +def config(): + _config = BaseConfig() + _config.settings.update({"name": defaults.DEFAULT_SETTINGS["name"]}) + return _config + + +@pytest.fixture() +def config_path() -> str: + return os.path.join(os.getcwd(), "pyproject.toml") diff --git a/tests/test_cz_base.py b/tests/test_cz_base.py index 80450d4476..891ee01167 100644 --- a/tests/test_cz_base.py +++ b/tests/test_cz_base.py @@ -1,17 +1,8 @@ import pytest -from commitizen import defaults -from commitizen.config import BaseConfig from commitizen.cz.base import BaseCommitizen -@pytest.fixture() -def config(): - _config = BaseConfig() - _config.settings.update({"name": defaults.DEFAULT_SETTINGS["name"]}) - return _config - - class DummyCz(BaseCommitizen): def questions(self): return [{"type": "input", "name": "commit", "message": "Initial commit:\n"}] diff --git a/tests/test_cz_conventional_commits.py b/tests/test_cz_conventional_commits.py index 82a123b8a2..04d0522174 100644 --- a/tests/test_cz_conventional_commits.py +++ b/tests/test_cz_conventional_commits.py @@ -1,7 +1,5 @@ import pytest -from commitizen import defaults -from commitizen.config import BaseConfig from commitizen.cz.conventional_commits.conventional_commits import ( ConventionalCommitsCz, parse_scope, @@ -20,13 +18,6 @@ invalid_subjects = ["", " ", ".", " .", "", None] -@pytest.fixture() -def config(): - _config = BaseConfig() - _config._settings["name"] = defaults.DEFAULT_SETTINGS["name"] - return _config - - def test_parse_scope_valid_values(): for valid_scope in valid_scopes: assert valid_scope == parse_scope(valid_scope) diff --git a/tests/test_cz_jira.py b/tests/test_cz_jira.py index 0060613961..03055c14b1 100644 --- a/tests/test_cz_jira.py +++ b/tests/test_cz_jira.py @@ -1,17 +1,6 @@ -import pytest - -from commitizen import defaults -from commitizen.config import BaseConfig from commitizen.cz.jira import JiraSmartCz -@pytest.fixture() -def config(): - _config = BaseConfig() - _config.settings.update({"name": defaults.DEFAULT_SETTINGS["name"]}) - return _config - - def test_questions(config): cz = JiraSmartCz(config) questions = cz.questions() From 4c02aa9f3b50f6a9110f042e57d49d8f606ebe5c Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Thu, 24 Dec 2020 20:47:50 +0800 Subject: [PATCH 03/13] style(mypy): disable disallow_untyped_decorators for tests --- setup.cfg | 15 ++++++++------- tests/test_changelog.py | 6 +++--- tests/test_changelog_parser.py | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/setup.cfg b/setup.cfg index d4e21ad5b3..e85ca41fff 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,19 +4,20 @@ norecursedirs = .* build dist CVS _darcs {arch} *.egg venv env virtualenv [mypy] -files = commitizen, tests +files = commitizen ignore_missing_imports = true # disallow_untyped_calls = True # disallow_untyped_defs = True # disallow_incomplete_defs = True -disallow_untyped_decorators = True +disallow_untyped_decorators = true # disallow_any_generics = True -disallow_subclassing_any = True +disallow_subclassing_any = true # warn_return_any = True -warn_redundant_casts = True -warn_unused_ignores = True -warn_unused_configs = True - +warn_redundant_casts = true +warn_unused_ignores = true +warn_unused_configs = true +[mypy-tests.*] +disallow_untyped_decorators = false [flake8] ignore = diff --git a/tests/test_changelog.py b/tests/test_changelog.py index da05eaf2b7..b3071ed5fc 100644 --- a/tests/test_changelog.py +++ b/tests/test_changelog.py @@ -472,7 +472,7 @@ ] -@pytest.fixture # type: ignore +@pytest.fixture def gitcommits() -> list: commits = [ git.GitCommit( @@ -487,13 +487,13 @@ def gitcommits() -> list: return commits -@pytest.fixture # type: ignore +@pytest.fixture def tags() -> list: tags = [git.GitTag(*tag) for tag in TAGS] return tags -@pytest.fixture # type: ignore +@pytest.fixture def changelog_content() -> str: changelog_path = "tests/CHANGELOG_FOR_TEST.md" with open(changelog_path, "r") as f: diff --git a/tests/test_changelog_parser.py b/tests/test_changelog_parser.py index 438b2f766d..ae6df53912 100644 --- a/tests/test_changelog_parser.py +++ b/tests/test_changelog_parser.py @@ -25,7 +25,7 @@ """ -@pytest.fixture # type: ignore +@pytest.fixture def changelog_content() -> str: changelog_path = "tests/CHANGELOG_FOR_TEST.md" with open(changelog_path, "r") as f: From 75a6d559fd3d1e00465fe1779bcac6325fe1e50f Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Fri, 25 Dec 2020 17:55:30 +0800 Subject: [PATCH 04/13] style(mypy): add return type to all functions --- commitizen/bump.py | 4 ++-- commitizen/config/base_config.py | 6 +++--- commitizen/config/json_config.py | 2 +- commitizen/config/toml_config.py | 2 +- commitizen/config/yaml_config.py | 2 +- commitizen/defaults.py | 19 +++++++++++++++++-- commitizen/git.py | 4 ++-- commitizen/out.py | 10 +++++----- 8 files changed, 32 insertions(+), 17 deletions(-) diff --git a/commitizen/bump.py b/commitizen/bump.py index f3319fc1cc..6c09e2f16d 100644 --- a/commitizen/bump.py +++ b/commitizen/bump.py @@ -132,7 +132,7 @@ def generate_version( def update_version_in_files( current_version: str, new_version: str, files: List[str], *, check_consistency=False -): +) -> None: """Change old version to the new one in every file given. Note that this version is not the tag formatted one. @@ -196,7 +196,7 @@ def _version_to_regex(version: str): return re.compile(f"{clean_regex}") -def create_tag(version: Union[Version, str], tag_format: Optional[str] = None): +def create_tag(version: Union[Version, str], tag_format: Optional[str] = None) -> str: """The tag and the software version might be different. That's why this function exists. diff --git a/commitizen/config/base_config.py b/commitizen/config/base_config.py index 76cd1706e1..2c6e3cf21b 100644 --- a/commitizen/config/base_config.py +++ b/commitizen/config/base_config.py @@ -25,11 +25,11 @@ def set_key(self, key, value): """ raise NotImplementedError() - def update(self, data: dict): + def update(self, data: dict) -> None: self._settings.update(data) - def add_path(self, path: Union[str, Path]): + def add_path(self, path: Union[str, Path]) -> None: self._path = Path(path) - def _parse_setting(self, data: Union[bytes, str]) -> dict: + def _parse_setting(self, data: Union[bytes, str]) -> None: raise NotImplementedError() diff --git a/commitizen/config/json_config.py b/commitizen/config/json_config.py index 445f2aac5f..a40a8ceb20 100644 --- a/commitizen/config/json_config.py +++ b/commitizen/config/json_config.py @@ -30,7 +30,7 @@ def set_key(self, key, value): json.dump(parser, f, indent=2) return self - def _parse_setting(self, data: Union[bytes, str]): + def _parse_setting(self, data: Union[bytes, str]) -> None: """We expect to have a section in .cz.json looking like ``` diff --git a/commitizen/config/toml_config.py b/commitizen/config/toml_config.py index b5b7f7b2a1..c2a90cec9f 100644 --- a/commitizen/config/toml_config.py +++ b/commitizen/config/toml_config.py @@ -41,7 +41,7 @@ def set_key(self, key, value): f.write(parser.as_string().encode("utf-8")) return self - def _parse_setting(self, data: Union[bytes, str]): + def _parse_setting(self, data: Union[bytes, str]) -> None: """We expect to have a section in pyproject looking like ``` diff --git a/commitizen/config/yaml_config.py b/commitizen/config/yaml_config.py index 5d199c1929..1d44e64f3b 100644 --- a/commitizen/config/yaml_config.py +++ b/commitizen/config/yaml_config.py @@ -17,7 +17,7 @@ def init_empty_config_content(self): with open(self.path, "a") as json_file: yaml.dump({"commitizen": {}}, json_file) - def _parse_setting(self, data: Union[bytes, str]): + def _parse_setting(self, data: Union[bytes, str]) -> None: """We expect to have a section in cz.yaml looking like ``` diff --git a/commitizen/defaults.py b/commitizen/defaults.py index e460bbd6f7..5557a3fdc4 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -1,5 +1,20 @@ from collections import OrderedDict -from typing import Any, Dict, List +from typing import List, Optional, TypedDict + + +# Type +class Settings(TypedDict): + name: str + version: Optional[str] + version_files: List[str] + tag_format: Optional[str] + bump_message: Optional[str] + changelog_file: str + changelog_incremental: bool + changelog_start_rev: Optional[str] + update_changelog_on_bump: bool + use_shortcuts: bool + name: str = "cz_conventional_commits" config_files: List[str] = [ @@ -11,7 +26,7 @@ "cz.yaml", ] -DEFAULT_SETTINGS: Dict[str, Any] = { +DEFAULT_SETTINGS: Settings = { "name": "cz_conventional_commits", "version": None, "version_files": [], diff --git a/commitizen/git.py b/commitizen/git.py index b196f15115..03bb780c1b 100644 --- a/commitizen/git.py +++ b/commitizen/git.py @@ -54,12 +54,12 @@ def from_line(cls, line: str, inner_delimiter: str) -> "GitTag": return cls(name=name, rev=obj, date=date) -def tag(tag: str, annotated: bool = False): +def tag(tag: str, annotated: bool = False) -> cmd.Command: c = cmd.run(f"git tag -a {tag} -m {tag}" if annotated else f"git tag {tag}") return c -def commit(message: str, args: str = ""): +def commit(message: str, args: str = "") -> cmd.Command: f = NamedTemporaryFile("wb", delete=False) f.write(message.encode("utf-8")) f.close() diff --git a/commitizen/out.py b/commitizen/out.py index 7ac5ba420b..a25f0f2e32 100644 --- a/commitizen/out.py +++ b/commitizen/out.py @@ -3,27 +3,27 @@ from termcolor import colored -def write(value: str, *args): +def write(value: str, *args) -> None: """Intended to be used when value is multiline.""" print(value, *args) -def line(value: str, *args, **kwargs): +def line(value: str, *args, **kwargs) -> None: """Wrapper in case I want to do something different later.""" print(value, *args, **kwargs) -def error(value: str): +def error(value: str) -> None: message = colored(value, "red") line(message, file=sys.stderr) -def success(value: str): +def success(value: str) -> None: message = colored(value, "green") line(message) -def info(value: str): +def info(value: str) -> None: message = colored(value, "blue") line(message) From 71a13ee13e99be1d30770a3b5760696564f6b234 Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Sat, 27 Nov 2021 11:51:48 +0800 Subject: [PATCH 05/13] refactor(defaults): add Settings typeddict --- commitizen/config/base_config.py | 10 +++++----- commitizen/defaults.py | 6 ++++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/commitizen/config/base_config.py b/commitizen/config/base_config.py index 2c6e3cf21b..8935c4875d 100644 --- a/commitizen/config/base_config.py +++ b/commitizen/config/base_config.py @@ -1,16 +1,16 @@ from pathlib import Path -from typing import Any, Dict, Optional, Union +from typing import Optional, Union -from commitizen.defaults import DEFAULT_SETTINGS +from commitizen.defaults import DEFAULT_SETTINGS, Settings class BaseConfig: def __init__(self): - self._settings: Dict[str, Any] = DEFAULT_SETTINGS.copy() + self._settings: Settings = DEFAULT_SETTINGS.copy() self._path: Optional[Path] = None @property - def settings(self) -> Dict[str, Any]: + def settings(self) -> Settings: return self._settings @property @@ -25,7 +25,7 @@ def set_key(self, key, value): """ raise NotImplementedError() - def update(self, data: dict) -> None: + def update(self, data: Settings) -> None: self._settings.update(data) def add_path(self, path: Union[str, Path]) -> None: diff --git a/commitizen/defaults.py b/commitizen/defaults.py index 5557a3fdc4..f3dd319c6e 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -1,9 +1,9 @@ from collections import OrderedDict -from typing import List, Optional, TypedDict +from typing import Any, List, Optional, Tuple, TypedDict # Type -class Settings(TypedDict): +class Settings(TypedDict, total=False): name: str version: Optional[str] version_files: List[str] @@ -14,6 +14,8 @@ class Settings(TypedDict): changelog_start_rev: Optional[str] update_changelog_on_bump: bool use_shortcuts: bool + style: Optional[List[Tuple[str, str]]] + customize: Any name: str = "cz_conventional_commits" From 3e9e78984e9de1279c3085622188beea6ee9e438 Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Wed, 8 Dec 2021 09:20:47 +0800 Subject: [PATCH 06/13] refactor(config): add CzSettings and Questions TypedDict --- commitizen/commands/init.py | 4 +- commitizen/cz/base.py | 5 ++- .../conventional_commits.py | 6 +-- commitizen/cz/customize/customize.py | 9 +++-- commitizen/cz/jira/jira.py | 4 +- commitizen/defaults.py | 40 ++++++++++++++++--- commitizen/git.py | 2 +- 7 files changed, 50 insertions(+), 20 deletions(-) diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index d4a4180aa3..d290612949 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -49,7 +49,7 @@ def __call__(self): out.line(f"Config file {self.config.path} already exists") def _ask_config_path(self) -> str: - name = questionary.select( + name: str = questionary.select( "Please choose a supported config file: (default: pyproject.toml)", choices=config_files, default="pyproject.toml", @@ -58,7 +58,7 @@ def _ask_config_path(self) -> str: return name def _ask_name(self) -> str: - name = questionary.select( + name: str = questionary.select( "Please choose a cz (commit rule): (default: cz_conventional_commits)", choices=list(registry.keys()), default="cz_conventional_commits", diff --git a/commitizen/cz/base.py b/commitizen/cz/base.py index 734852c868..a5abe35f16 100644 --- a/commitizen/cz/base.py +++ b/commitizen/cz/base.py @@ -5,11 +5,12 @@ from commitizen import git from commitizen.config.base_config import BaseConfig +from commitizen.defaults import Questions class BaseCommitizen(metaclass=ABCMeta): bump_pattern: Optional[str] = None - bump_map: Optional[dict] = None + bump_map: Optional[Dict[str, str]] = None default_style_config: List[Tuple[str, str]] = [ ("qmark", "fg:#ff9d00 bold"), ("question", "bold"), @@ -45,7 +46,7 @@ def __init__(self, config: BaseConfig): self.config.settings.update({"style": BaseCommitizen.default_style_config}) @abstractmethod - def questions(self) -> list: + def questions(self) -> Questions: """Questions regarding the commit message.""" @abstractmethod diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py index f9890d46ba..47ce0d16c8 100644 --- a/commitizen/cz/conventional_commits/conventional_commits.py +++ b/commitizen/cz/conventional_commits/conventional_commits.py @@ -1,11 +1,11 @@ import os import re from collections import OrderedDict -from typing import Any, Dict, List from commitizen import defaults from commitizen.cz.base import BaseCommitizen from commitizen.cz.utils import multiple_line_breaker, required_validator +from commitizen.defaults import Questions __all__ = ["ConventionalCommitsCz"] @@ -49,8 +49,8 @@ class ConventionalCommitsCz(BaseCommitizen): "perf": "Perf", } - def questions(self) -> List[Dict[str, Any]]: - questions: List[Dict[str, Any]] = [ + def questions(self) -> Questions: + questions: Questions = [ { "type": "list", "name": "prefix", diff --git a/commitizen/cz/customize/customize.py b/commitizen/cz/customize/customize.py index acf205d06e..7a6f03a825 100644 --- a/commitizen/cz/customize/customize.py +++ b/commitizen/cz/customize/customize.py @@ -3,11 +3,12 @@ except ImportError: from string import Template # type: ignore -from typing import Any, Dict, List, Optional +from typing import Optional from commitizen import defaults from commitizen.config import BaseConfig from commitizen.cz.base import BaseCommitizen +from commitizen.defaults import Questions from commitizen.exceptions import MissingCzCustomizeConfigError __all__ = ["CustomizeCommitsCz"] @@ -37,11 +38,11 @@ def __init__(self, config: BaseConfig): if custom_change_type_order: self.change_type_order = custom_change_type_order - def questions(self) -> List[Dict[str, Any]]: - return self.custom_settings.get("questions") + def questions(self) -> Questions: + return self.custom_settings.get("questions", [{}]) def message(self, answers: dict) -> str: - message_template = Template(self.custom_settings.get("message_template")) + message_template = Template(self.custom_settings.get("message_template", "")) if getattr(Template, "substitute", None): return message_template.substitute(**answers) # type: ignore else: diff --git a/commitizen/cz/jira/jira.py b/commitizen/cz/jira/jira.py index 46c5965c46..bd3cd3c7ee 100644 --- a/commitizen/cz/jira/jira.py +++ b/commitizen/cz/jira/jira.py @@ -1,13 +1,13 @@ import os -from typing import Any, Dict, List from commitizen.cz.base import BaseCommitizen +from commitizen.defaults import Questions __all__ = ["JiraSmartCz"] class JiraSmartCz(BaseCommitizen): - def questions(self) -> List[Dict[str, Any]]: + def questions(self) -> Questions: questions = [ { "type": "input", diff --git a/commitizen/defaults.py b/commitizen/defaults.py index f3dd319c6e..f508f6b9a7 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -1,8 +1,35 @@ -from collections import OrderedDict -from typing import Any, List, Optional, Tuple, TypedDict - +import collections +import pathlib +from typing import ( + Any, + Iterable, + List, + MutableMapping, + Optional, + OrderedDict, + Tuple, + TypedDict, + Union, +) # Type +Questions = Iterable[MutableMapping[str, Any]] + + +class CzSettings(TypedDict, total=False): + bump_pattern: str + bump_map: OrderedDict[str, str] + change_type_order: List[str] + + questions: Questions + example: Optional[str] + schema_pattern: Optional[str] + schema: Optional[str] + info_path: Union[str, pathlib.Path] + info: str + message_template: str + + class Settings(TypedDict, total=False): name: str version: Optional[str] @@ -15,7 +42,7 @@ class Settings(TypedDict, total=False): update_changelog_on_bump: bool use_shortcuts: bool style: Optional[List[Tuple[str, str]]] - customize: Any + customize: CzSettings name: str = "cz_conventional_commits" @@ -46,7 +73,7 @@ class Settings(TypedDict, total=False): PATCH = "PATCH" bump_pattern = r"^(BREAKING[\-\ ]CHANGE|feat|fix|refactor|perf)(\(.+\))?(!)?" -bump_map = OrderedDict( +bump_map = collections.OrderedDict( ( (r"^.+!$", MAJOR), (r"^BREAKING[\-\ ]CHANGE", MAJOR), @@ -56,9 +83,10 @@ class Settings(TypedDict, total=False): (r"^perf", PATCH), ) ) +change_type_order = ["BREAKING CHANGE", "feat", "fix", "refactor", "perf"] + bump_message = "bump: version $current_version → $new_version" -change_type_order = ["BREAKING CHANGE", "feat", "fix", "refactor", "perf"] commit_parser = r"^(?Pfeat|fix|refactor|perf|BREAKING CHANGE)(?:\((?P[^()\r\n]*)\)|\()?(?P!)?:\s(?P.*)?" # noqa version_parser = r"(?P([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+)?(\w+)?)" diff --git a/commitizen/git.py b/commitizen/git.py index 03bb780c1b..4e60b68793 100644 --- a/commitizen/git.py +++ b/commitizen/git.py @@ -14,7 +14,7 @@ class GitObject: def __eq__(self, other) -> bool: if not hasattr(other, "rev"): return False - return self.rev == other.rev + return self.rev == other.rev # type: ignore class GitCommit(GitObject): From 1a7a28261b4d67f4a7260808825806e2b183dea6 Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Wed, 8 Dec 2021 09:32:53 +0800 Subject: [PATCH 07/13] style(mypy): enforce warn_return_any check --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index e85ca41fff..0c3a421421 100644 --- a/setup.cfg +++ b/setup.cfg @@ -12,7 +12,7 @@ ignore_missing_imports = true disallow_untyped_decorators = true # disallow_any_generics = True disallow_subclassing_any = true -# warn_return_any = True +warn_return_any = True warn_redundant_casts = true warn_unused_ignores = true warn_unused_configs = true From 87fa25b753667abcc855a97c35dd9b3564249dfb Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Wed, 8 Dec 2021 09:43:19 +0800 Subject: [PATCH 08/13] fix: import TypedDict from type_extensions for backward compatibility --- commitizen/defaults.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/commitizen/defaults.py b/commitizen/defaults.py index f508f6b9a7..e4429216be 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -8,10 +8,11 @@ Optional, OrderedDict, Tuple, - TypedDict, Union, ) +from typing_extensions import TypedDict + # Type Questions = Iterable[MutableMapping[str, Any]] From 8be81d310db98d2349f6a4e018379d22a545caba Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Wed, 8 Dec 2021 10:03:29 +0800 Subject: [PATCH 09/13] style(defaults): annoate OrderedDict through 'OrderedDict' for 3.6 compatibility --- commitizen/defaults.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/commitizen/defaults.py b/commitizen/defaults.py index e4429216be..3fa7c5256d 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -1,15 +1,6 @@ -import collections import pathlib -from typing import ( - Any, - Iterable, - List, - MutableMapping, - Optional, - OrderedDict, - Tuple, - Union, -) +from collections import OrderedDict +from typing import Any, Iterable, List, MutableMapping, Optional, Tuple, Union from typing_extensions import TypedDict @@ -19,7 +10,7 @@ class CzSettings(TypedDict, total=False): bump_pattern: str - bump_map: OrderedDict[str, str] + bump_map: "OrderedDict[str, str]" change_type_order: List[str] questions: Questions @@ -74,7 +65,7 @@ class Settings(TypedDict, total=False): PATCH = "PATCH" bump_pattern = r"^(BREAKING[\-\ ]CHANGE|feat|fix|refactor|perf)(\(.+\))?(!)?" -bump_map = collections.OrderedDict( +bump_map = OrderedDict( ( (r"^.+!$", MAJOR), (r"^BREAKING[\-\ ]CHANGE", MAJOR), From 9ab3912a15ebe4a6ee7d209b29d5b005de378f66 Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Wed, 8 Dec 2021 10:21:22 +0800 Subject: [PATCH 10/13] refactor(conventional_commits): remove duplicate patterns and import from defaults --- .../conventional_commits/conventional_commits.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py index 47ce0d16c8..248df0f319 100644 --- a/commitizen/cz/conventional_commits/conventional_commits.py +++ b/commitizen/cz/conventional_commits/conventional_commits.py @@ -1,6 +1,5 @@ import os import re -from collections import OrderedDict from commitizen import defaults from commitizen.cz.base import BaseCommitizen @@ -29,18 +28,9 @@ def parse_subject(text): class ConventionalCommitsCz(BaseCommitizen): - bump_pattern = r"^(BREAKING[\-\ ]CHANGE|feat|fix|refactor|perf)(\(.+\))?(!)?" - bump_map = OrderedDict( - ( - (r"^.+!$", defaults.MAJOR), - (r"^BREAKING[\-\ ]CHANGE", defaults.MAJOR), - (r"^feat", defaults.MINOR), - (r"^fix", defaults.PATCH), - (r"^refactor", defaults.PATCH), - (r"^perf", defaults.PATCH), - ) - ) - commit_parser = r"^(?Pfeat|fix|refactor|perf|BREAKING CHANGE)(?:\((?P[^()\r\n]*)\)|\()?(?P!)?:\s(?P.*)?" # noqa + bump_pattern = defaults.bump_pattern + bump_map = defaults.bump_map + commit_parser = defaults.commit_parser version_parser = defaults.version_parser change_type_map = { "feat": "Feat", From e8703e5bff3ba9e0795c032d57bffb816e5478a5 Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Wed, 8 Dec 2021 23:14:06 +0800 Subject: [PATCH 11/13] docs(customization): update question type to Questions (from commitizen.defaults) --- docs/customization.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/customization.md b/docs/customization.md index 74ac0707b6..f9cae2a10a 100644 --- a/docs/customization.md +++ b/docs/customization.md @@ -140,7 +140,7 @@ commitizen: | Parameter | Type | Default | Description | | ------------------- | ------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `questions` | `dict` | `None` | Questions regarding the commit message. Detailed below. | +| `questions` | `Questions` | `None` | Questions regarding the commit message. Detailed below. The type `Questions` is an alias to `Iterable[MutableMapping[str, Any]]` which is definied in `commitizen.defaults`. | | `message_template` | `str` | `None` | The template for generating message from the given answers. `message_template` should either follow [Jinja2][jinja2] formatting specification, and all the variables in this template should be defined in `name` in `questions` | | `example` | `str` | `None` | (OPTIONAL) Provide an example to help understand the style. Used by `cz example`. | | `schema` | `str` | `None` | (OPTIONAL) Show the schema used. Used by `cz schema`. | @@ -152,6 +152,7 @@ commitizen: | `change_type_order` | `str` | `None` | (OPTIONAL) List of strings used to order the Changelog. All other types will be sorted alphabetically. Default is `["BREAKING CHANGE", "feat", "fix", "refactor", "perf"]` | [jinja2]: https://jinja.palletsprojects.com/en/2.10.x/ + #### Detailed `questions` content | Parameter | Type | Default | Description | @@ -162,7 +163,6 @@ commitizen: | `choices` | `list` | `None` | (OPTIONAL) The choices when `type = list`. Either use a list of values or a list of dictionaries with `name` and `value` keys. Keyboard shortcuts can be defined via `key`. See examples above. | | `default` | `Any` | `None` | (OPTIONAL) The default value for this question. | | `filter` | `str` | `None` | (Optional) Validator for user's answer. **(Work in Progress)** | - [different-question-types]: https://github.com/tmbo/questionary#different-question-types #### Shortcut keys @@ -194,15 +194,15 @@ See [commitizen_cz_template](https://github.com/commitizen-tools/commitizen_cz_t Create a file starting with `cz_`, for example `cz_jira.py`. This prefix is used to detect the plug-in. Same method [flask uses] -Inherit from `BaseCommitizen`, and you must define `questions` and -`message`. The others are optional. +Inherit from `BaseCommitizen`, and you must define `questions` and `message`. The others are optional. ```python from commitizen.cz.base import BaseCommitizen +from commitizen.defaults import Questions class JiraCz(BaseCommitizen): - - def questions(self) -> list: + # Questions = Iterable[MutableMapping[str, Any]] + def questions(self) -> Questions: """Questions regarding the commit message.""" questions = [ { From d7062d59ea412c074cedc01768fb74237da50e12 Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Wed, 8 Dec 2021 23:43:45 +0800 Subject: [PATCH 12/13] docs(customization): detailed what a Questoin expect --- docs/customization.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/customization.md b/docs/customization.md index f9cae2a10a..58303e4f14 100644 --- a/docs/customization.md +++ b/docs/customization.md @@ -140,7 +140,7 @@ commitizen: | Parameter | Type | Default | Description | | ------------------- | ------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `questions` | `Questions` | `None` | Questions regarding the commit message. Detailed below. The type `Questions` is an alias to `Iterable[MutableMapping[str, Any]]` which is definied in `commitizen.defaults`. | +| `questions` | `Questions` | `None` | Questions regarding the commit message. Detailed below. The type `Questions` is an alias to `Iterable[MutableMapping[str, Any]]` which is definied in `commitizen.defaults`. It expects a list of dictionaries. | | `message_template` | `str` | `None` | The template for generating message from the given answers. `message_template` should either follow [Jinja2][jinja2] formatting specification, and all the variables in this template should be defined in `name` in `questions` | | `example` | `str` | `None` | (OPTIONAL) Provide an example to help understand the style. Used by `cz example`. | | `schema` | `str` | `None` | (OPTIONAL) Show the schema used. Used by `cz schema`. | @@ -202,6 +202,7 @@ from commitizen.defaults import Questions class JiraCz(BaseCommitizen): # Questions = Iterable[MutableMapping[str, Any]] + # It expect a list with dictionaries. def questions(self) -> Questions: """Questions regarding the commit message.""" questions = [ From c29873af20fede59ed73008136ca33a73d0be70e Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Thu, 9 Dec 2021 08:53:35 +0800 Subject: [PATCH 13/13] docs(customization): fix typo --- docs/customization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/customization.md b/docs/customization.md index 58303e4f14..2c69e037b4 100644 --- a/docs/customization.md +++ b/docs/customization.md @@ -202,7 +202,7 @@ from commitizen.defaults import Questions class JiraCz(BaseCommitizen): # Questions = Iterable[MutableMapping[str, Any]] - # It expect a list with dictionaries. + # It expects a list with dictionaries. def questions(self) -> Questions: """Questions regarding the commit message.""" questions = [