From 36b28827dfb2c9849da63137e1c601295c2ce300 Mon Sep 17 00:00:00 2001 From: Tim Hsiung Date: Tue, 3 Feb 2026 23:53:18 +0800 Subject: [PATCH 1/4] refactor(tags): extract version resolution method (#1839) --- commitizen/tags.py | 47 ++++++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/commitizen/tags.py b/commitizen/tags.py index dcddbd335f..11b9899bed 100644 --- a/commitizen/tags.py +++ b/commitizen/tags.py @@ -154,24 +154,13 @@ def extract_version(self, tag: GitTag) -> Version: candidates = ( m for regex in self.version_regexes if (m := regex.fullmatch(tag.name)) ) - if not (m := next(candidates, None)): + if not (match := next(candidates, None)): raise InvalidVersion(self._version_tag_error(tag.name)) - if "version" in m.groupdict(): - return self.scheme(m.group("version")) - parts = m.groupdict() - version = parts["major"] + if version := match.groupdict().get("version"): + return self.scheme(version) - if minor := parts.get("minor"): - version = f"{version}.{minor}" - if patch := parts.get("patch"): - version = f"{version}.{patch}" - - if parts.get("prerelease"): - version = f"{version}-{parts['prerelease']}" - if parts.get("devrelease"): - version = f"{version}{parts['devrelease']}" - return self.scheme(version) + return self.scheme(self._extract_version(match)) def include_in_changelog(self, tag: GitTag) -> bool: """Check if a tag should be included in the changelog""" @@ -195,19 +184,14 @@ def search_version(self, text: str, last: bool = False) -> VersionTag | None: match = matches[-1 if last else 0] - if "version" in match.groupdict(): - return VersionTag(match.group("version"), match.group(0)) + groups = match.groupdict() + if version := groups.get("version"): + return VersionTag(version, match.group(0)) - parts = match.groupdict() - try: - version = f"{parts['major']}.{parts['minor']}.{parts['patch']}" - except KeyError: + if not all(value in groups for value in ["major", "minor", "patch"]): return None - if parts.get("prerelease"): - version = f"{version}-{parts['prerelease']}" - if parts.get("devrelease"): - version = f"{version}{parts['devrelease']}" + version = self._extract_version(match) return VersionTag(version, match.group(0)) def normalize_tag( @@ -284,3 +268,16 @@ def from_settings(cls, settings: Settings) -> Self: ignored_tag_formats=settings["ignored_tag_formats"], merge_prereleases=settings["changelog_merge_prerelease"], ) + + def _extract_version(self, match: re.Match[str]) -> str: + groups = match.groupdict() + parts: list[str] = [groups["major"]] + if minor := groups.get("minor"): + parts.append(f".{minor}") + if patch := groups.get("patch"): + parts.append(f".{patch}") + if prerelease := groups.get("prerelease"): + parts.append(f"-{prerelease}") + if devrelease := groups.get("devrelease"): + parts.append(devrelease) + return "".join(parts) From 0731f76cbdebb593a6ff1f29950f7a6b7c20f1fb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 3 Feb 2026 15:54:09 +0000 Subject: [PATCH 2/4] =?UTF-8?q?bump:=20version=204.13.0=20=E2=86=92=204.13?= =?UTF-8?q?.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .pre-commit-config.yaml | 2 +- CHANGELOG.md | 6 ++++++ commitizen/__version__.py | 2 +- pyproject.toml | 2 +- uv.lock | 2 +- 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0f69f0a42e..9ff73d544d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -56,7 +56,7 @@ repos: - tomli - repo: https://github.com/commitizen-tools/commitizen - rev: v4.13.0 # automatically updated by Commitizen + rev: v4.13.1 # automatically updated by Commitizen hooks: - id: commitizen - id: commitizen-branch diff --git a/CHANGELOG.md b/CHANGELOG.md index db91c63b39..15d690d15a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## v4.13.1 (2026-02-03) + +### Refactor + +- **config**: replace is_empty_config with contains_commitizen_section, improve multi config resolution algorithm (#1842) + ## v4.13.0 (2026-02-01) ### Feat diff --git a/commitizen/__version__.py b/commitizen/__version__.py index 5b886240e3..ab9d6f875c 100644 --- a/commitizen/__version__.py +++ b/commitizen/__version__.py @@ -1 +1 @@ -__version__ = "4.13.0" +__version__ = "4.13.1" diff --git a/pyproject.toml b/pyproject.toml index ad639ba9bd..4a2b3850dc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "commitizen" -version = "4.13.0" +version = "4.13.1" description = "Python commitizen client tool" authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }] maintainers = [ diff --git a/uv.lock b/uv.lock index 69e2f8e9e4..e1482cb2dc 100644 --- a/uv.lock +++ b/uv.lock @@ -195,7 +195,7 @@ wheels = [ [[package]] name = "commitizen" -version = "4.13.0" +version = "4.13.1" source = { editable = "." } dependencies = [ { name = "argcomplete" }, From 8d05b9ef93dd5ae3ee7e392cec35561b287ba49b Mon Sep 17 00:00:00 2001 From: Tim Hsiung Date: Wed, 4 Feb 2026 00:12:59 +0800 Subject: [PATCH 3/4] refactor: simplify code with pathlib Path object (#1840) --- commitizen/changelog_formats/base.py | 16 +++++++--------- commitizen/commands/changelog.py | 11 +++++------ commitizen/commands/commit.py | 4 +--- commitizen/commands/init.py | 7 ++++--- commitizen/config/__init__.py | 6 ++---- commitizen/config/toml_config.py | 11 +++++------ commitizen/config/yaml_config.py | 2 +- .../conventional_commits/conventional_commits.py | 7 +++---- commitizen/cz/jira/jira.py | 7 +++---- commitizen/providers/cargo_provider.py | 2 +- commitizen/providers/npm_provider.py | 4 ++-- docs/config/version_provider.md | 2 +- 12 files changed, 35 insertions(+), 44 deletions(-) diff --git a/commitizen/changelog_formats/base.py b/commitizen/changelog_formats/base.py index da6f63bd1f..dcddb5d7e0 100644 --- a/commitizen/changelog_formats/base.py +++ b/commitizen/changelog_formats/base.py @@ -1,7 +1,7 @@ from __future__ import annotations -import os from abc import ABCMeta +from pathlib import Path from typing import IO, TYPE_CHECKING, Any, ClassVar from commitizen.changelog import IncrementalMergeInfo, Metadata @@ -36,12 +36,11 @@ def __init__(self, config: BaseConfig) -> None: ) def get_metadata(self, filepath: str) -> Metadata: - if not os.path.isfile(filepath): + file = Path(filepath) + if not file.is_file(): return Metadata() - with open( - filepath, encoding=self.config.settings["encoding"] - ) as changelog_file: + with file.open(encoding=self.config.settings["encoding"]) as changelog_file: return self.get_metadata_from_file(changelog_file) def get_metadata_from_file(self, file: IO[Any]) -> Metadata: @@ -74,12 +73,11 @@ def get_metadata_from_file(self, file: IO[Any]) -> Metadata: return meta def get_latest_full_release(self, filepath: str) -> IncrementalMergeInfo: - if not os.path.isfile(filepath): + file = Path(filepath) + if not file.is_file(): return IncrementalMergeInfo() - with open( - filepath, encoding=self.config.settings["encoding"] - ) as changelog_file: + with file.open(encoding=self.config.settings["encoding"]) as changelog_file: return self.get_latest_full_release_from_file(changelog_file) def get_latest_full_release_from_file(self, file: IO[Any]) -> IncrementalMergeInfo: diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py index 33dca6b6c1..777fd30493 100644 --- a/commitizen/commands/changelog.py +++ b/commitizen/commands/changelog.py @@ -1,7 +1,5 @@ from __future__ import annotations -import os -import os.path from difflib import SequenceMatcher from operator import itemgetter from pathlib import Path @@ -66,7 +64,7 @@ def __init__(self, config: BaseConfig, arguments: ChangelogArgs) -> None: f"or the setting `changelog_file` in {self.config.path}" ) self.file_name = ( - os.path.join(str(self.config.path.parent), changelog_file_name) + Path(self.config.path.parent, changelog_file_name).as_posix() if self.config.path is not None else changelog_file_name ) @@ -282,9 +280,10 @@ def __call__(self) -> None: raise DryRunExit() lines = [] - if self.incremental and os.path.isfile(self.file_name): - with open( - self.file_name, encoding=self.config.settings["encoding"] + changelog_path = Path(self.file_name) + if self.incremental and changelog_path.is_file(): + with changelog_path.open( + encoding=self.config.settings["encoding"] ) as changelog_file: lines = changelog_file.readlines() diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index 5776af4201..e7f337b604 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -61,9 +61,7 @@ def _read_backup_message(self) -> str | None: return None # Read commit message from backup - with open( - self.backup_file_path, encoding=self.config.settings["encoding"] - ) as f: + with self.backup_file_path.open(encoding=self.config.settings["encoding"]) as f: return f.read().strip() def _get_message_by_prompt_commit_questions(self) -> str: diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index 9d33e7081a..b0df9381dc 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -288,11 +288,12 @@ def _get_config_data(self) -> dict[str, Any]: ], } - if not Path(".pre-commit-config.yaml").is_file(): + pre_commit_config_path = Path(self._PRE_COMMIT_CONFIG_PATH) + if not pre_commit_config_path.is_file(): return {"repos": [CZ_HOOK_CONFIG]} - with open( - self._PRE_COMMIT_CONFIG_PATH, encoding=self.config.settings["encoding"] + with pre_commit_config_path.open( + encoding=self.config.settings["encoding"] ) as config_file: config_data: dict[str, Any] = yaml.safe_load(config_file) or {} diff --git a/commitizen/config/__init__.py b/commitizen/config/__init__.py index 85414496f7..24a9881bc7 100644 --- a/commitizen/config/__init__.py +++ b/commitizen/config/__init__.py @@ -28,10 +28,8 @@ def _resolve_config_candidates() -> list[BaseConfig]: def _create_config_from_path(path: Path) -> BaseConfig: - with open(path, "rb") as f: - data: bytes = f.read() - - return create_config(data=data, path=path) + with path.open("rb") as f: + return create_config(data=f.read(), path=path) def read_cfg(filepath: str | None = None) -> BaseConfig: diff --git a/commitizen/config/toml_config.py b/commitizen/config/toml_config.py index ed0bf73efb..8324bd03a5 100644 --- a/commitizen/config/toml_config.py +++ b/commitizen/config/toml_config.py @@ -1,6 +1,5 @@ from __future__ import annotations -import os from typing import TYPE_CHECKING from tomlkit import TOMLDocument, exceptions, parse, table @@ -33,25 +32,25 @@ def contains_commitizen_section(self) -> bool: def init_empty_config_content(self) -> None: config_doc = TOMLDocument() - if os.path.isfile(self.path): - with open(self.path, "rb") as input_toml_file: + if self.path.is_file(): + with self.path.open("rb") as input_toml_file: config_doc = parse(input_toml_file.read()) if config_doc.get("tool") is None: config_doc["tool"] = table() config_doc["tool"]["commitizen"] = table() # type: ignore[index] - with open(self.path, "wb") as output_toml_file: + with self.path.open("wb") as output_toml_file: output_toml_file.write( config_doc.as_string().encode(self._settings["encoding"]) ) def set_key(self, key: str, value: object) -> Self: - with open(self.path, "rb") as f: + with self.path.open("rb") as f: config_doc = parse(f.read()) config_doc["tool"]["commitizen"][key] = value # type: ignore[index] - with open(self.path, "wb") as f: + with self.path.open("wb") as f: f.write(config_doc.as_string().encode(self._settings["encoding"])) return self diff --git a/commitizen/config/yaml_config.py b/commitizen/config/yaml_config.py index dd1c2c6308..0e79735f2f 100644 --- a/commitizen/config/yaml_config.py +++ b/commitizen/config/yaml_config.py @@ -56,7 +56,7 @@ def _parse_setting(self, data: bytes | str) -> None: pass def set_key(self, key: str, value: object) -> Self: - with open(self.path, "rb") as yaml_file: + with self.path.open("rb") as yaml_file: config_doc = yaml.load(yaml_file, Loader=yaml.FullLoader) config_doc["commitizen"][key] = value diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py index 1d1930595b..6431edec95 100644 --- a/commitizen/cz/conventional_commits/conventional_commits.py +++ b/commitizen/cz/conventional_commits/conventional_commits.py @@ -1,6 +1,6 @@ from __future__ import annotations -import os +from pathlib import Path from typing import TYPE_CHECKING, TypedDict from commitizen import defaults @@ -214,7 +214,6 @@ def schema_pattern(self) -> str: ) def info(self) -> str: - dir_path = os.path.dirname(os.path.realpath(__file__)) - filepath = os.path.join(dir_path, "conventional_commits_info.txt") - with open(filepath, encoding=self.config.settings["encoding"]) as f: + filepath = Path(__file__).parent / "conventional_commits_info.txt" + with filepath.open(encoding=self.config.settings["encoding"]) as f: return f.read() diff --git a/commitizen/cz/jira/jira.py b/commitizen/cz/jira/jira.py index bbe4fc5ee3..78504fbb82 100644 --- a/commitizen/cz/jira/jira.py +++ b/commitizen/cz/jira/jira.py @@ -1,6 +1,6 @@ from __future__ import annotations -import os +from pathlib import Path from typing import TYPE_CHECKING from commitizen.cz.base import BaseCommitizen @@ -73,7 +73,6 @@ def schema_pattern(self) -> str: return r".*[A-Z]{2,}\-[0-9]+( #| .* #).+( #.+)*" def info(self) -> str: - dir_path = os.path.dirname(os.path.realpath(__file__)) - filepath = os.path.join(dir_path, "jira_info.txt") - with open(filepath, encoding=self.config.settings["encoding"]) as f: + filepath = Path(__file__).parent / "jira_info.txt" + with filepath.open(encoding=self.config.settings["encoding"]) as f: return f.read() diff --git a/commitizen/providers/cargo_provider.py b/commitizen/providers/cargo_provider.py index ca00f05e7b..e7b127c4bd 100644 --- a/commitizen/providers/cargo_provider.py +++ b/commitizen/providers/cargo_provider.py @@ -39,7 +39,7 @@ def set(self, document: TOMLDocument, version: str) -> None: def set_version(self, version: str) -> None: super().set_version(version) - if self.lock_file.exists(): + if self.lock_file.is_file(): self.set_lock_version(version) def set_lock_version(self, version: str) -> None: diff --git a/commitizen/providers/npm_provider.py b/commitizen/providers/npm_provider.py index 7aeb0ee7df..597fce4b6e 100644 --- a/commitizen/providers/npm_provider.py +++ b/commitizen/providers/npm_provider.py @@ -46,14 +46,14 @@ def set_version(self, version: str) -> None: self.package_file.write_text( json.dumps(package_document, indent=self.indent) + "\n" ) - if self.lock_file.exists(): + if self.lock_file.is_file(): lock_document = self.set_lock_version( json.loads(self.lock_file.read_text()), version ) self.lock_file.write_text( json.dumps(lock_document, indent=self.indent) + "\n" ) - if self.shrinkwrap_file.exists(): + if self.shrinkwrap_file.is_file(): shrinkwrap_document = self.set_shrinkwrap_version( json.loads(self.shrinkwrap_file.read_text()), version ) diff --git a/docs/config/version_provider.md b/docs/config/version_provider.md index 48a49049df..ccbbe6cfd3 100644 --- a/docs/config/version_provider.md +++ b/docs/config/version_provider.md @@ -217,7 +217,7 @@ class MyProvider(VersionProvider): def get_version(self) -> str: """Read version from VERSION file.""" version_file = Path("VERSION") - if not version_file.exists(): + if not version_file.is_file(): return "0.0.0" return version_file.read_text().strip() From 1230c7acc5bcd9e2751c217f1833b43eb1934776 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 3 Feb 2026 16:21:06 +0000 Subject: [PATCH 4/4] =?UTF-8?q?bump:=20version=204.13.1=20=E2=86=92=204.13?= =?UTF-8?q?.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .pre-commit-config.yaml | 2 +- CHANGELOG.md | 7 +++++++ commitizen/__version__.py | 2 +- pyproject.toml | 2 +- uv.lock | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9ff73d544d..2b4a030fc6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -56,7 +56,7 @@ repos: - tomli - repo: https://github.com/commitizen-tools/commitizen - rev: v4.13.1 # automatically updated by Commitizen + rev: v4.13.2 # automatically updated by Commitizen hooks: - id: commitizen - id: commitizen-branch diff --git a/CHANGELOG.md b/CHANGELOG.md index 15d690d15a..2ba2ed463c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## v4.13.2 (2026-02-03) + +### Refactor + +- simplify code with pathlib Path object (#1840) +- **tags**: extract version resolution method (#1839) + ## v4.13.1 (2026-02-03) ### Refactor diff --git a/commitizen/__version__.py b/commitizen/__version__.py index ab9d6f875c..13398e442b 100644 --- a/commitizen/__version__.py +++ b/commitizen/__version__.py @@ -1 +1 @@ -__version__ = "4.13.1" +__version__ = "4.13.2" diff --git a/pyproject.toml b/pyproject.toml index 4a2b3850dc..fd8c1dfc21 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "commitizen" -version = "4.13.1" +version = "4.13.2" description = "Python commitizen client tool" authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }] maintainers = [ diff --git a/uv.lock b/uv.lock index e1482cb2dc..885caecfd2 100644 --- a/uv.lock +++ b/uv.lock @@ -195,7 +195,7 @@ wheels = [ [[package]] name = "commitizen" -version = "4.13.1" +version = "4.13.2" source = { editable = "." } dependencies = [ { name = "argcomplete" },