diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 44c8499187..01570a526a 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.6 # automatically updated by Commitizen + rev: v4.13.7 # automatically updated by Commitizen hooks: - id: commitizen - id: commitizen-branch diff --git a/CHANGELOG.md b/CHANGELOG.md index 601ab664b0..db8403be20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## v4.13.7 (2026-02-09) + +### Fix + +- **provider**: use encoding settings in config (#1857) + ## v4.13.6 (2026-02-07) ### Fix diff --git a/commitizen/__version__.py b/commitizen/__version__.py index 800ff94d6d..03dab05ffc 100644 --- a/commitizen/__version__.py +++ b/commitizen/__version__.py @@ -1 +1 @@ -__version__ = "4.13.6" +__version__ = "4.13.7" diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py index 182839910d..ab5e671d67 100644 --- a/commitizen/commands/check.py +++ b/commitizen/commands/check.py @@ -2,6 +2,7 @@ import re import sys +from pathlib import Path from typing import TYPE_CHECKING, TypedDict from commitizen import factory, git, out @@ -116,11 +117,10 @@ def _get_commit_message(self) -> str | None: # Get commit message from command line (--message) return self.commit_msg - with open( - self.commit_msg_file, encoding=self.config.settings["encoding"] - ) as commit_file: - # Get commit message from file (--commit-msg-file) - return commit_file.read() + # Get commit message from file (--commit-msg-file) + return Path(self.commit_msg_file).read_text( + encoding=self.config.settings["encoding"] + ) def _get_commits(self) -> list[git.GitCommit]: if (msg := self._get_commit_message()) is not None: diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index e7f337b604..6668c0d65b 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -5,6 +5,7 @@ import shutil import subprocess import tempfile +from pathlib import Path from typing import TYPE_CHECKING, TypedDict import questionary @@ -26,8 +27,6 @@ from commitizen.git import smart_open if TYPE_CHECKING: - from pathlib import Path - from commitizen.config import BaseConfig @@ -61,8 +60,9 @@ def _read_backup_message(self) -> str | None: return None # Read commit message from backup - with self.backup_file_path.open(encoding=self.config.settings["encoding"]) as f: - return f.read().strip() + return self.backup_file_path.read_text( + encoding=self.config.settings["encoding"] + ).strip() def _get_message_by_prompt_commit_questions(self) -> str: # Prompt user for the commit message @@ -112,8 +112,7 @@ def manual_edit(self, message: str) -> str: file_path = file.name argv = [exec_path, file_path] subprocess.call(argv) - with open(file_path) as temp_file: - message = temp_file.read().strip() + message = Path(file_path).read_text().strip() os.unlink(file.name) return message diff --git a/commitizen/config/__init__.py b/commitizen/config/__init__.py index 24a9881bc7..08dc7ffd7e 100644 --- a/commitizen/config/__init__.py +++ b/commitizen/config/__init__.py @@ -28,8 +28,7 @@ def _resolve_config_candidates() -> list[BaseConfig]: def _create_config_from_path(path: Path) -> BaseConfig: - with path.open("rb") as f: - return create_config(data=f.read(), path=path) + return create_config(data=path.read_bytes(), 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 8324bd03a5..28c05aaa52 100644 --- a/commitizen/config/toml_config.py +++ b/commitizen/config/toml_config.py @@ -26,15 +26,13 @@ def __init__(self, *, data: bytes | str, path: Path) -> None: self._parse_setting(data) def contains_commitizen_section(self) -> bool: - with self.path.open("rb") as f: - config_doc = parse(f.read()) + config_doc = parse(self.path.read_bytes()) return config_doc.get("tool", {}).get("commitizen") is not None def init_empty_config_content(self) -> None: config_doc = TOMLDocument() if self.path.is_file(): - with self.path.open("rb") as input_toml_file: - config_doc = parse(input_toml_file.read()) + config_doc = parse(self.path.read_bytes()) if config_doc.get("tool") is None: config_doc["tool"] = table() @@ -46,12 +44,10 @@ def init_empty_config_content(self) -> None: ) def set_key(self, key: str, value: object) -> Self: - with self.path.open("rb") as f: - config_doc = parse(f.read()) + config_doc = parse(self.path.read_bytes()) config_doc["tool"]["commitizen"][key] = value # type: ignore[index] - with self.path.open("wb") as f: - f.write(config_doc.as_string().encode(self._settings["encoding"])) + self.path.write_bytes(config_doc.as_string().encode(self._settings["encoding"])) return self diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py index 6431edec95..31c329595a 100644 --- a/commitizen/cz/conventional_commits/conventional_commits.py +++ b/commitizen/cz/conventional_commits/conventional_commits.py @@ -215,5 +215,4 @@ def schema_pattern(self) -> str: def info(self) -> str: filepath = Path(__file__).parent / "conventional_commits_info.txt" - with filepath.open(encoding=self.config.settings["encoding"]) as f: - return f.read() + return filepath.read_text(encoding=self.config.settings["encoding"]) diff --git a/commitizen/cz/customize/customize.py b/commitizen/cz/customize/customize.py index 0bc31db30a..8fcc63fac3 100644 --- a/commitizen/cz/customize/customize.py +++ b/commitizen/cz/customize/customize.py @@ -1,5 +1,6 @@ from __future__ import annotations +from pathlib import Path from typing import TYPE_CHECKING, Any if TYPE_CHECKING: @@ -68,6 +69,5 @@ def schema(self) -> str: def info(self) -> str: if info_path := self.custom_settings.get("info_path"): - with open(info_path, encoding=self.config.settings["encoding"]) as f: - return f.read() + return Path(info_path).read_text(encoding=self.config.settings["encoding"]) return self.custom_settings.get("info") or "" diff --git a/commitizen/cz/jira/jira.py b/commitizen/cz/jira/jira.py index 78504fbb82..07189d15d3 100644 --- a/commitizen/cz/jira/jira.py +++ b/commitizen/cz/jira/jira.py @@ -74,5 +74,4 @@ def schema_pattern(self) -> str: def info(self) -> str: filepath = Path(__file__).parent / "jira_info.txt" - with filepath.open(encoding=self.config.settings["encoding"]) as f: - return f.read() + return filepath.read_text(encoding=self.config.settings["encoding"]) diff --git a/commitizen/providers/base_provider.py b/commitizen/providers/base_provider.py index 84b745e326..b77d86394c 100644 --- a/commitizen/providers/base_provider.py +++ b/commitizen/providers/base_provider.py @@ -49,6 +49,9 @@ class FileProvider(VersionProvider): def file(self) -> Path: return Path() / self.filename + def _get_encoding(self) -> str: + return self.config.settings["encoding"] + class JsonProvider(FileProvider): """ @@ -58,13 +61,16 @@ class JsonProvider(FileProvider): indent: ClassVar[int] = 2 def get_version(self) -> str: - document = json.loads(self.file.read_text()) + document = json.loads(self.file.read_text(encoding=self._get_encoding())) return self.get(document) def set_version(self, version: str) -> None: - document = json.loads(self.file.read_text()) + document = json.loads(self.file.read_text(encoding=self._get_encoding())) self.set(document, version) - self.file.write_text(json.dumps(document, indent=self.indent) + "\n") + self.file.write_text( + json.dumps(document, indent=self.indent) + "\n", + encoding=self._get_encoding(), + ) def get(self, document: Mapping[str, str]) -> str: return document["version"] @@ -79,13 +85,13 @@ class TomlProvider(FileProvider): """ def get_version(self) -> str: - document = tomlkit.parse(self.file.read_text()) + document = tomlkit.parse(self.file.read_text(encoding=self._get_encoding())) return self.get(document) def set_version(self, version: str) -> None: - document = tomlkit.parse(self.file.read_text()) + document = tomlkit.parse(self.file.read_text(encoding=self._get_encoding())) self.set(document, version) - self.file.write_text(tomlkit.dumps(document)) + self.file.write_text(tomlkit.dumps(document), encoding=self._get_encoding()) def get(self, document: tomlkit.TOMLDocument) -> str: return document["project"]["version"] # type: ignore[index,return-value] diff --git a/commitizen/providers/cargo_provider.py b/commitizen/providers/cargo_provider.py index e7b127c4bd..235d4a110c 100644 --- a/commitizen/providers/cargo_provider.py +++ b/commitizen/providers/cargo_provider.py @@ -43,8 +43,10 @@ def set_version(self, version: str) -> None: self.set_lock_version(version) def set_lock_version(self, version: str) -> None: - cargo_toml_content = parse(self.file.read_text()) - cargo_lock_content = parse(self.lock_file.read_text()) + cargo_toml_content = parse(self.file.read_text(encoding=self._get_encoding())) + cargo_lock_content = parse( + self.lock_file.read_text(encoding=self._get_encoding()) + ) packages = cargo_lock_content["package"] if TYPE_CHECKING: @@ -75,7 +77,9 @@ def set_lock_version(self, version: str) -> None: continue cargo_file = Path(path) / "Cargo.toml" - package_content = parse(cargo_file.read_text()).get("package", {}) + package_content = parse( + cargo_file.read_text(encoding=self._get_encoding()) + ).get("package", {}) if TYPE_CHECKING: assert isinstance(package_content, dict) try: @@ -92,7 +96,9 @@ def set_lock_version(self, version: str) -> None: if package["name"] in members_inheriting: cargo_lock_content["package"][i]["version"] = version # type: ignore[index] - self.lock_file.write_text(dumps(cargo_lock_content)) + self.lock_file.write_text( + dumps(cargo_lock_content), encoding=self._get_encoding() + ) def _try_get_workspace(document: TOMLDocument) -> dict: diff --git a/commitizen/providers/npm_provider.py b/commitizen/providers/npm_provider.py index 597fce4b6e..6794f6a714 100644 --- a/commitizen/providers/npm_provider.py +++ b/commitizen/providers/npm_provider.py @@ -4,13 +4,13 @@ from pathlib import Path from typing import TYPE_CHECKING, Any, ClassVar -from commitizen.providers.base_provider import VersionProvider +from commitizen.providers.base_provider import JsonProvider if TYPE_CHECKING: from collections.abc import Mapping -class NpmProvider(VersionProvider): +class NpmProvider(JsonProvider): """ npm package.json and package-lock.json version management """ @@ -36,29 +36,39 @@ def get_version(self) -> str: """ Get the current version from package.json """ - package_document = json.loads(self.package_file.read_text()) + package_document = json.loads( + self.package_file.read_text(encoding=self._get_encoding()) + ) return self.get_package_version(package_document) def set_version(self, version: str) -> None: package_document = self.set_package_version( - json.loads(self.package_file.read_text()), version + json.loads(self.package_file.read_text(encoding=self._get_encoding())), + version, ) self.package_file.write_text( - json.dumps(package_document, indent=self.indent) + "\n" + json.dumps(package_document, indent=self.indent) + "\n", + encoding=self._get_encoding(), ) if self.lock_file.is_file(): lock_document = self.set_lock_version( - json.loads(self.lock_file.read_text()), version + json.loads(self.lock_file.read_text(encoding=self._get_encoding())), + version, ) self.lock_file.write_text( - json.dumps(lock_document, indent=self.indent) + "\n" + json.dumps(lock_document, indent=self.indent) + "\n", + encoding=self._get_encoding(), ) if self.shrinkwrap_file.is_file(): shrinkwrap_document = self.set_shrinkwrap_version( - json.loads(self.shrinkwrap_file.read_text()), version + json.loads( + self.shrinkwrap_file.read_text(encoding=self._get_encoding()) + ), + version, ) self.shrinkwrap_file.write_text( - json.dumps(shrinkwrap_document, indent=self.indent) + "\n" + json.dumps(shrinkwrap_document, indent=self.indent) + "\n", + encoding=self._get_encoding(), ) def get_package_version(self, document: Mapping[str, str]) -> str: diff --git a/commitizen/providers/uv_provider.py b/commitizen/providers/uv_provider.py index 4f49a29528..0eb19e51a6 100644 --- a/commitizen/providers/uv_provider.py +++ b/commitizen/providers/uv_provider.py @@ -26,15 +26,21 @@ def set_version(self, version: str) -> None: self.set_lock_version(version) def set_lock_version(self, version: str) -> None: - pyproject_toml_content = tomlkit.parse(self.file.read_text()) + pyproject_toml_content = tomlkit.parse( + self.file.read_text(encoding=self._get_encoding()) + ) project_name = pyproject_toml_content["project"]["name"] # type: ignore[index] normalized_project_name = canonicalize_name(str(project_name)) - document = tomlkit.parse(self.lock_file.read_text()) + document = tomlkit.parse( + self.lock_file.read_text(encoding=self._get_encoding()) + ) packages: tomlkit.items.AoT = document["package"] # type: ignore[assignment] for i, package in enumerate(packages): if package["name"] == normalized_project_name: document["package"][i]["version"] = version # type: ignore[index] break - self.lock_file.write_text(tomlkit.dumps(document)) + self.lock_file.write_text( + tomlkit.dumps(document), encoding=self._get_encoding() + ) diff --git a/pyproject.toml b/pyproject.toml index 1d1851deb1..d0d96e9dbf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "commitizen" -version = "4.13.6" +version = "4.13.7" description = "Python commitizen client tool" authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }] maintainers = [ @@ -237,8 +237,9 @@ select = [ ] ignore = ["E501", "D1", "D415"] extend-safe-fixes = [ - "TC", # Move imports inside/outside TYPE_CHECKING blocks - "UP", # Update syntaxes for current Python version recommendations + "TC", # Move imports inside/outside TYPE_CHECKING blocks + "UP", # Update syntaxes for current Python version recommendations + "PT006", # Use tuple in pytest.mark.parametrize ] [tool.ruff.lint.per-file-ignores] diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py index ef950e7ca7..94b40e94e9 100644 --- a/tests/commands/test_bump_command.py +++ b/tests/commands/test_bump_command.py @@ -100,7 +100,8 @@ def test_bump_minor_increment_signed(commit_msg: str, util: UtilFixture): def test_bump_minor_increment_annotated_config_file( commit_msg: str, util: UtilFixture, pyproject: Path ): - pyproject.write_text(pyproject.read_text() + "\nannotated_tag = 1") + with pyproject.open("a", encoding="utf-8") as f: + f.write("\nannotated_tag = 1") util.create_file_and_commit(commit_msg) util.run_cli("bump", "--yes") assert git.tag_exist("0.2.0") is True @@ -117,7 +118,8 @@ def test_bump_minor_increment_signed_config_file( commit_msg: str, util: UtilFixture, tmp_commitizen_project_with_gpg ): tmp_commitizen_cfg_file = tmp_commitizen_project_with_gpg.join("pyproject.toml") - tmp_commitizen_cfg_file.write(f"{tmp_commitizen_cfg_file.read()}\ngpg_sign = 1") + with Path(tmp_commitizen_cfg_file).open("a", encoding="utf-8") as f: + f.write("\ngpg_sign = 1") util.create_file_and_commit(commit_msg) util.run_cli("bump", "--yes") assert git.tag_exist("0.2.0") is True @@ -362,10 +364,8 @@ def test_bump_when_version_inconsistent_in_version_files( tmp_version_file.write("100.999.10000") tmp_commitizen_cfg_file = tmp_commitizen_project.join("pyproject.toml") tmp_version_file_string = str(tmp_version_file).replace("\\", "/") - tmp_commitizen_cfg_file.write( - f"{tmp_commitizen_cfg_file.read()}\n" - f'version_files = ["{tmp_version_file_string}"]' - ) + with Path(tmp_commitizen_cfg_file).open("a", encoding="utf-8") as f: + f.write(f'\nversion_files = ["{tmp_version_file_string}"]') util.create_file_and_commit("feat: new file") @@ -407,10 +407,8 @@ def test_bump_files_only(tmp_commitizen_project, util: UtilFixture): tmp_version_file.write("0.1.0") tmp_commitizen_cfg_file = tmp_commitizen_project.join("pyproject.toml") tmp_version_file_string = str(tmp_version_file).replace("\\", "/") - tmp_commitizen_cfg_file.write( - f"{tmp_commitizen_cfg_file.read()}\n" - f'version_files = ["{tmp_version_file_string}"]' - ) + with Path(tmp_commitizen_cfg_file).open("a", encoding="utf-8") as f: + f.write(f'\nversion_files = ["{tmp_version_file_string}"]') util.create_file_and_commit("feat: new user interface") util.run_cli("bump", "--yes") @@ -422,11 +420,9 @@ def test_bump_files_only(tmp_commitizen_project, util: UtilFixture): assert git.tag_exist("0.3.0") is False - with tmp_version_file.open(encoding="utf-8") as f: - assert "0.3.0" in f.read() + assert "0.3.0" in Path(tmp_version_file).read_text(encoding="utf-8") - with tmp_commitizen_cfg_file.open(encoding="utf-8") as f: - assert "0.3.0" in f.read() + assert "0.3.0" in Path(tmp_commitizen_cfg_file).read_text(encoding="utf-8") def test_bump_local_version(tmp_commitizen_project, util: UtilFixture): @@ -444,8 +440,7 @@ def test_bump_local_version(tmp_commitizen_project, util: UtilFixture): util.run_cli("bump", "--yes", "--local-version") assert git.tag_exist("4.5.1+0.2.0") is True - with tmp_version_file.open(encoding="utf-8") as f: - assert "4.5.1+0.2.0" in f.read() + assert "4.5.1+0.2.0" in Path(tmp_version_file).read_text(encoding="utf-8") @pytest.mark.usefixtures("tmp_commitizen_project") @@ -504,8 +499,7 @@ def test_bump_with_changelog_arg(util: UtilFixture, changelog_path): util.run_cli("bump", "--yes", "--changelog") assert git.tag_exist("0.2.0") is True - with changelog_path.open(encoding="utf-8") as f: - out = f.read() + out = changelog_path.read_text(encoding="utf-8") assert out.startswith("#") assert "0.2.0" in out @@ -519,8 +513,7 @@ def test_bump_with_changelog_config(util: UtilFixture, changelog_path, config_pa util.run_cli("bump", "--yes") assert git.tag_exist("0.2.0") is True - with changelog_path.open(encoding="utf-8") as f: - out = f.read() + out = changelog_path.read_text(encoding="utf-8") assert out.startswith("#") assert "0.2.0" in out @@ -557,8 +550,7 @@ def test_bump_with_changelog_to_stdout_arg( assert "this should appear in stdout" in out assert git.tag_exist("0.2.0") is True - with changelog_path.open(encoding="utf-8") as f: - out = f.read() + out = changelog_path.read_text(encoding="utf-8") assert out.startswith("#") assert "0.2.0" in out @@ -800,8 +792,9 @@ def test_bump_version_with_less_components_in_config( tmp_commitizen_project.join("__version__.py"), tmp_commitizen_project.join("pyproject.toml"), ]: - with version_file.open() as f: - assert expected_version_after_bump in f.read() + assert expected_version_after_bump in Path(version_file).read_text( + encoding="utf-8" + ) @pytest.mark.parametrize("commit_msg", ["feat: new file", "feat(user): new file"]) @@ -812,11 +805,11 @@ def test_bump_with_pre_bump_hooks( post_bump_hook = "scripts/post_bump_hook.sh" tmp_commitizen_cfg_file = tmp_commitizen_project.join("pyproject.toml") - tmp_commitizen_cfg_file.write( - f"{tmp_commitizen_cfg_file.read()}\n" - f'pre_bump_hooks = ["{pre_bump_hook}"]\n' - f'post_bump_hooks = ["{post_bump_hook}"]\n' - ) + with Path(tmp_commitizen_cfg_file).open("a", encoding="utf-8") as f: + f.write( + f'\npre_bump_hooks = ["{pre_bump_hook}"]\n' + f'post_bump_hooks = ["{post_bump_hook}"]\n' + ) run_mock = mocker.Mock() mocker.patch.object(hooks, "run", run_mock) @@ -863,11 +856,11 @@ def test_bump_with_hooks_and_increment( post_bump_hook = "scripts/post_bump_hook.sh" tmp_commitizen_cfg_file = tmp_commitizen_project.join("pyproject.toml") - tmp_commitizen_cfg_file.write( - f"{tmp_commitizen_cfg_file.read()}\n" - f'pre_bump_hooks = ["{pre_bump_hook}"]\n' - f'post_bump_hooks = ["{post_bump_hook}"]\n' - ) + with Path(tmp_commitizen_cfg_file).open("a", encoding="utf-8") as f: + f.write( + f'\npre_bump_hooks = ["{pre_bump_hook}"]\n' + f'post_bump_hooks = ["{post_bump_hook}"]\n' + ) run_mock = mocker.Mock() mocker.patch.object(hooks, "run", run_mock) @@ -913,16 +906,14 @@ def test_bump_command_prerelease_scheme_via_cli( assert git.tag_exist("0.2.0-a0") is True for version_file in [tmp_version_file, tmp_commitizen_cfg_file]: - with version_file.open() as f: - assert "0.2.0-a0" in f.read() + assert "0.2.0-a0" in Path(version_file).read_text(encoding="utf-8") # PRERELEASE BUMP CREATES VERSION WITHOUT PRERELEASE util.run_cli("bump", "--yes") assert git.tag_exist("0.2.0") is True for version_file in [tmp_version_file, tmp_commitizen_cfg_file]: - with version_file.open() as f: - assert "0.2.0" in f.read() + assert "0.2.0" in Path(version_file).read_text(encoding="utf-8") def test_bump_command_prerelease_scheme_via_config( @@ -938,23 +929,20 @@ def test_bump_command_prerelease_scheme_via_config( assert git.tag_exist("0.2.0-a0") is True for version_file in [tmp_version_file, tmp_commitizen_cfg_file]: - with version_file.open() as f: - assert "0.2.0-a0" in f.read() + assert "0.2.0-a0" in Path(version_file).read_text(encoding="utf-8") util.run_cli("bump", "--prerelease", "alpha", "--yes") assert git.tag_exist("0.2.0-a1") is True for version_file in [tmp_version_file, tmp_commitizen_cfg_file]: - with version_file.open() as f: - assert "0.2.0-a1" in f.read() + assert "0.2.0-a1" in Path(version_file).read_text(encoding="utf-8") # PRERELEASE BUMP CREATES VERSION WITHOUT PRERELEASE util.run_cli("bump", "--yes") assert git.tag_exist("0.2.0") is True for version_file in [tmp_version_file, tmp_commitizen_cfg_file]: - with version_file.open() as f: - assert "0.2.0" in f.read() + assert "0.2.0" in Path(version_file).read_text(encoding="utf-8") def test_bump_command_prerelease_scheme_check_old_tags( @@ -970,23 +958,20 @@ def test_bump_command_prerelease_scheme_check_old_tags( assert git.tag_exist("v0.2.0-a0") is True for version_file in [tmp_version_file, tmp_commitizen_cfg_file]: - with version_file.open() as f: - assert "0.2.0-a0" in f.read() + assert "0.2.0-a0" in Path(version_file).read_text(encoding="utf-8") util.run_cli("bump", "--prerelease", "alpha") assert git.tag_exist("v0.2.0-a1") is True for version_file in [tmp_version_file, tmp_commitizen_cfg_file]: - with version_file.open() as f: - assert "0.2.0-a1" in f.read() + assert "0.2.0-a1" in Path(version_file).read_text(encoding="utf-8") # PRERELEASE BUMP CREATES VERSION WITHOUT PRERELEASE util.run_cli("bump") assert git.tag_exist("v0.2.0") is True for version_file in [tmp_version_file, tmp_commitizen_cfg_file]: - with version_file.open() as f: - assert "0.2.0" in f.read() + assert "0.2.0" in Path(version_file).read_text(encoding="utf-8") @pytest.mark.usefixtures("tmp_commitizen_project") @@ -1458,8 +1443,7 @@ def test_changelog_config_flag_merge_prerelease( util.run_cli("bump", "--changelog") - with changelog_path.open() as f: - out = f.read() + out = changelog_path.read_text() file_regression.check(out, extension=".md") @@ -1488,12 +1472,21 @@ def test_changelog_config_flag_merge_prerelease_only_prerelease_present( util.run_cli("bump", "--changelog") - with changelog_path.open() as f: - out = f.read() + out = changelog_path.read_text() file_regression.check(out, extension=".md") +@pytest.mark.usefixtures("tmp_commitizen_project") +def test_bump_deprecate_files_only(util: UtilFixture): + util.create_file_and_commit("feat: new file") + with ( + pytest.warns(DeprecationWarning, match=r".*--files-only.*deprecated"), + pytest.raises(ExpectedExit), + ): + util.run_cli("bump", "--yes", "--files-only") + + @pytest.mark.parametrize( ("prerelease", "merge"), [ @@ -1544,7 +1537,6 @@ def test_changelog_merge_preserves_header( util.create_file_and_commit("feat: new feature right before the bump") util.run_cli("bump", "--changelog") - with changelog_path.open() as f: - out = f.read() + out = changelog_path.read_text() file_regression.check(out, extension=".md") diff --git a/tests/commands/test_changelog_command.py b/tests/commands/test_changelog_command.py index d220aaed40..6fe2289b08 100644 --- a/tests/commands/test_changelog_command.py +++ b/tests/commands/test_changelog_command.py @@ -101,8 +101,7 @@ def test_changelog_from_start( util.run_cli("changelog", "--file-name", changelog_file, "--template", template) - with open(changelog_file, encoding="utf-8") as f: - out = f.read() + out = Path(changelog_file).read_text(encoding="utf-8") file_regression.check(out, extension=changelog_format.ext) @@ -136,8 +135,7 @@ def test_changelog_replacing_unreleased_using_incremental( template, ) - with open(changelog_file, encoding="utf-8") as f: - out = f.read() + out = Path(changelog_file).read_text(encoding="utf-8") file_regression.check(out, extension=changelog_format.ext) @@ -166,8 +164,7 @@ def test_changelog_is_persisted_using_incremental( util.run_cli("changelog", "--incremental") - with changelog_path.open(encoding="utf-8") as f: - out = f.read() + out = changelog_path.read_text(encoding="utf-8") file_regression.check(out, extension=".md") @@ -197,8 +194,7 @@ def test_changelog_incremental_angular_sample( util.run_cli("changelog", "--incremental") - with changelog_path.open(encoding="utf-8") as f: - out = f.read() + out = changelog_path.read_text(encoding="utf-8") file_regression.check(out, extension=".md") @@ -247,8 +243,7 @@ def test_changelog_incremental_keep_a_changelog_sample( util.run_cli("changelog", "--incremental") - with changelog_path.open(encoding="utf-8") as f: - out = f.read() + out = changelog_path.read_text(encoding="utf-8") file_regression.check(out, extension=".md") @@ -419,8 +414,7 @@ def test_changelog_multiple_incremental_do_not_add_new_lines( util.create_file_and_commit(commit_message) util.run_cli("changelog", "--incremental") - with changelog_path.open(encoding="utf-8") as f: - out = f.read() + out = changelog_path.read_text(encoding="utf-8") file_regression.check(out, extension=".md") @@ -437,8 +431,7 @@ def test_changelog_incremental_newline_separates_new_content_from_old( util.create_file_and_commit("feat: add more cat videos") util.run_cli("changelog", "--incremental") - with changelog_path.open(encoding="utf-8") as f: - out = f.read() + out = changelog_path.read_text(encoding="utf-8") file_regression.check(out, extension=".md") @@ -568,8 +561,7 @@ def test_changelog_config_flag_increment( util.run_cli("changelog") - with changelog_path.open(encoding="utf-8") as f: - out = f.read() + out = changelog_path.read_text(encoding="utf-8") assert "this should be persisted using increment" in out file_regression.check(out, extension=".md") @@ -602,8 +594,7 @@ def test_changelog_config_flag_merge_prerelease( util.run_cli("changelog") - with changelog_path.open() as f: - out = f.read() + out = changelog_path.read_text() file_regression.check(out, extension=".md") @@ -653,8 +644,7 @@ def test_changelog_incremental_keep_a_changelog_sample_with_annotated_tag( util.run_cli("changelog", "--incremental") - with changelog_path.open(encoding="utf-8") as f: - out = f.read() + out = changelog_path.read_text(encoding="utf-8") file_regression.check(out, extension=".md") @@ -685,8 +675,7 @@ def test_changelog_incremental_with_release_candidate_version( util.run_cli("changelog", "--incremental") - with changelog_path.open(encoding="utf-8") as f: - out = f.read() + out = changelog_path.read_text(encoding="utf-8") file_regression.check(out, extension=".md") @@ -715,8 +704,7 @@ def test_changelog_incremental_with_prerelease_version_to_prerelease_version( util.run_cli("bump", "--changelog", "--prerelease", to_pre, "--yes") - with changelog_path.open() as f: - out = f.read() + out = changelog_path.read_text() file_regression.check(out, extension=".md") @@ -747,8 +735,7 @@ def test_changelog_release_candidate_version_with_merge_prerelease( util.run_cli("changelog", "--merge-prerelease") - with changelog_path.open() as f: - out = f.read() + out = changelog_path.read_text() file_regression.check(out, extension=".md") @@ -782,8 +769,7 @@ def test_changelog_incremental_with_merge_prerelease( util.run_cli("changelog", "--merge-prerelease", "--incremental") - with changelog_path.open() as f: - out = f.read() + out = changelog_path.read_text() file_regression.check(out, extension=".md") @@ -820,8 +806,7 @@ def test_changelog_from_rev_first_version_from_arg( util.run_cli("bump", "--yes") util.run_cli("changelog", "0.2.0") - with changelog_path.open(encoding="utf-8") as f: - out = f.read() + out = changelog_path.read_text(encoding="utf-8") file_regression.check(out, extension=".md") @@ -848,8 +833,7 @@ def test_changelog_from_rev_latest_version_from_arg( util.run_cli("changelog", "0.3.0") - with changelog_path.open() as f: - out = f.read() + out = changelog_path.read_text() file_regression.check(out, extension=".md") @@ -903,8 +887,7 @@ def test_changelog_multiple_matching_tags( warning = warnings[0] assert "Multiple tags found for version 2.0.0" in str(warning.message) - with changelog_path.open() as f: - out = f.read() + out = changelog_path.read_text() # Ensure only one tag is rendered assert out.count("2.0.0") == 1 @@ -926,8 +909,7 @@ def test_changelog_from_rev_range_default_tag_format( util.run_cli("changelog", "0.3.0") - with changelog_path.open() as f: - out = f.read() + out = changelog_path.read_text() assert "new file" not in out @@ -953,8 +935,7 @@ def test_changelog_from_rev_version_range_including_first_tag( util.run_cli("bump", "--yes") util.run_cli("changelog", "0.2.0..0.3.0") - with changelog_path.open(encoding="utf-8") as f: - out = f.read() + out = changelog_path.read_text(encoding="utf-8") file_regression.check(out, extension=".md") @@ -983,8 +964,7 @@ def test_changelog_from_rev_version_range_from_arg( util.run_cli("bump", "--yes") util.run_cli("changelog", "0.3.0..0.4.0") - with changelog_path.open() as f: - out = f.read() + out = changelog_path.read_text() file_regression.check(out, extension=".md") @@ -1053,8 +1033,7 @@ def test_changelog_from_rev_version_with_big_range_from_arg( util.run_cli("bump", "--yes") # 0.6.0 util.run_cli("changelog", "0.3.0..0.5.0") - with changelog_path.open() as f: - out = f.read() + out = changelog_path.read_text() file_regression.check(out, extension=".md") @@ -1137,8 +1116,7 @@ def test_changelog_with_customized_change_type_order( util.run_cli("bump", "--yes") util.run_cli("changelog", "0.3.0..0.4.0") - with changelog_path.open() as f: - out = f.read() + out = changelog_path.read_text() file_regression.check(out, extension=".md") @@ -1366,8 +1344,7 @@ def test_changelog_only_tag_matching_tag_format_included_prefix( util.run_cli("bump", "--changelog", "--yes") util.create_file_and_commit("feat: another new file") util.run_cli("bump", "--changelog", "--yes") - with changelog_path.open() as f: - out = f.read() + out = changelog_path.read_text() assert out.startswith("## custom0.3.0 (2021-06-11)") assert "## v0.2.0 (2021-06-11)" not in out assert "## 0.2.0 (2021-06-11)" not in out @@ -1387,13 +1364,10 @@ def test_changelog_only_tag_matching_tag_format_included_prefix_sep( util.create_tag("0.2.0") util.create_tag("random0.2.0") util.run_cli("bump", "--changelog", "--yes") - with changelog_path.open() as f: - out = f.read() util.create_file_and_commit("feat: new version another new file") util.create_file_and_commit("feat: new version some new file") util.run_cli("bump", "--changelog") - with changelog_path.open() as f: - out = f.read() + out = changelog_path.read_text() assert out.startswith("## custom-0.3.0") assert "## v0.2.0" not in out assert "## 0.2.0" not in out @@ -1419,8 +1393,7 @@ def test_changelog_only_tag_matching_tag_format_included_suffix( util.create_file_and_commit("feat: another new file") # bump to 0.3.0custom util.run_cli("bump", "--changelog", "--yes") - with changelog_path.open() as f: - out = f.read() + out = changelog_path.read_text() assert out.startswith("## 0.3.0custom (2021-06-11)") assert "## v0.2.0 (2021-06-11)" not in out assert "## 0.2.0 (2021-06-11)" not in out @@ -1443,8 +1416,7 @@ def test_changelog_only_tag_matching_tag_format_included_suffix_sep( util.run_cli("bump", "--changelog", "--yes") util.create_file_and_commit("feat: another new file") util.run_cli("bump", "--changelog", "--yes") - with changelog_path.open() as f: - out = f.read() + out = changelog_path.read_text() assert out.startswith("## 0.3.0-custom (2021-06-11)") assert "## v0.2.0 (2021-06-11)" not in out assert "## 0.2.0 (2021-06-11)" not in out @@ -1475,7 +1447,7 @@ def test_changelog_legacy_tags( util.create_file_and_commit("feat: another new file") util.create_tag("not-0.3.1") util.run_cli("bump", "--changelog", "--yes") - out = changelog_path.open().read() + out = changelog_path.read_text() assert "## v0.3.0" in out assert "## older-0.2.0" in out assert "## oldest-0.1.0" in out @@ -1520,7 +1492,7 @@ def test_changelog_incremental_change_tag_format( util.create_file_and_commit("feat: another new file") util.create_tag("v0.3.0") util.run_cli("changelog", "--incremental") - out = changelog_path.open().read() + out = changelog_path.read_text() assert "## v0.3.0" in out assert "## older-0.2.0" in out assert "## older-0.1.0" in out @@ -1553,8 +1525,7 @@ def test_changelog_ignored_tags( util.create_file_and_commit("feat: another new file") util.create_tag("not-ignored") util.run_cli("bump", "--changelog", "--yes") - with changelog_path.open() as f: - out = f.read() + out = changelog_path.read_text() assert "## ignore-0.1.0" not in out assert "## ignored" not in out assert "## not-ignored" not in out @@ -1717,12 +1688,10 @@ def test_changelog_template_incremental_variable( util.create_file_and_commit("feat(foo): new file") util.run_cli("changelog", "--file-name", target) - with open(target, encoding="utf-8") as f: - out = f.read() + out = Path(target).read_text(encoding="utf-8") file_regression.check(out, extension=".md") util.create_file_and_commit("refactor(bar): another new file") util.run_cli("changelog", "--file-name", target, "--incremental") - with open(target, encoding="utf-8") as f: - out = f.read() + out = Path(target).read_text(encoding="utf-8") file_regression.check(out, extension=".incremental.md") diff --git a/tests/commands/test_check_command.py b/tests/commands/test_check_command.py index 20f0bf87eb..0ea5062ec0 100644 --- a/tests/commands/test_check_command.py +++ b/tests/commands/test_check_command.py @@ -69,9 +69,9 @@ def _build_fake_git_commits(commit_msgs: list[str]) -> list[git.GitCommit]: def test_check_jira_fails(mocker: MockFixture, util: UtilFixture): - mocker.patch( - "commitizen.commands.check.open", - mocker.mock_open(read_data="random message for J-2 #fake_command blah"), + mock_path = mocker.patch("commitizen.commands.check.Path") + mock_path.return_value.read_text.return_value = ( + "random message for J-2 #fake_command blah" ) with pytest.raises(InvalidCommitMessageError) as excinfo: util.run_cli("-n", "cz_jira", "check", "--commit-msg-file", "some_file") @@ -90,10 +90,8 @@ def test_check_jira_fails(mocker: MockFixture, util: UtilFixture): def test_check_jira_command_after_issue( mocker: MockFixture, capsys, util: UtilFixture, commit_msg: str ): - mocker.patch( - "commitizen.commands.check.open", - mocker.mock_open(read_data=commit_msg), - ) + mock_path = mocker.patch("commitizen.commands.check.Path") + mock_path.return_value.read_text.return_value = commit_msg util.run_cli("-n", "cz_jira", "check", "--commit-msg-file", "some_file") out, _ = capsys.readouterr() assert "Commit validation: successful!" in out @@ -102,10 +100,8 @@ def test_check_jira_command_after_issue( def test_check_conventional_commit_succeeds( mocker: MockFixture, capsys, util: UtilFixture ): - mocker.patch( - "commitizen.commands.check.open", - mocker.mock_open(read_data="fix(scope): some commit message"), - ) + mock_path = mocker.patch("commitizen.commands.check.Path") + mock_path.return_value.read_text.return_value = "fix(scope): some commit message" util.run_cli("check", "--commit-msg-file", "some_file") out, _ = capsys.readouterr() assert "Commit validation: successful!" in out @@ -287,17 +283,15 @@ def test_check_command_with_pipe_message_and_failed( def test_check_command_with_comment_in_message_file( mocker: MockFixture, capsys, util: UtilFixture ): - mocker.patch( - "commitizen.commands.check.open", - mocker.mock_open( - read_data="# : (If applied, this commit will...) \n" - "# |<---- Try to Limit to a Max of 50 char ---->|\n" - "ci: add commitizen pre-commit hook\n" - "\n" - "# Explain why this change is being made\n" - "# |<---- Try To Limit Each Line to a Max Of 72 Char ---->|\n" - "This pre-commit hook will check our commits automatically." - ), + mock_path = mocker.patch("commitizen.commands.check.Path") + mock_path.return_value.read_text.return_value = ( + "# : (If applied, this commit will...) \n" + "# |<---- Try to Limit to a Max of 50 char ---->|\n" + "ci: add commitizen pre-commit hook\n" + "\n" + "# Explain why this change is being made\n" + "# |<---- Try To Limit Each Line to a Max Of 72 Char ---->|\n" + "This pre-commit hook will check our commits automatically." ) util.run_cli("check", "--commit-msg-file", "some_file") out, _ = capsys.readouterr() @@ -307,29 +301,25 @@ def test_check_command_with_comment_in_message_file( def test_check_conventional_commit_succeed_with_git_diff( mocker, capsys, util: UtilFixture ): - mocker.patch( - "commitizen.commands.check.open", - mocker.mock_open( - read_data=( - "feat: This is a test commit\n" - "# Please enter the commit message for your changes. Lines starting\n" - "# with '#' will be ignored, and an empty message aborts the commit.\n" - "#\n" - "# On branch ...\n" - "# Changes to be committed:\n" - "# modified: ...\n" - "#\n" - "# ------------------------ >8 ------------------------\n" - "# Do not modify or remove the line above.\n" - "# Everything below it will be ignored.\n" - "diff --git a/... b/...\n" - "index f1234c..1c5678 1234\n" - "--- a/...\n" - "+++ b/...\n" - "@@ -92,3 +92,4 @@ class Command(BaseCommand):\n" - '+ "this is a test"\n' - ) - ), + mock_path = mocker.patch("commitizen.commands.check.Path") + mock_path.return_value.read_text.return_value = ( + "feat: This is a test commit\n" + "# Please enter the commit message for your changes. Lines starting\n" + "# with '#' will be ignored, and an empty message aborts the commit.\n" + "#\n" + "# On branch ...\n" + "# Changes to be committed:\n" + "# modified: ...\n" + "#\n" + "# ------------------------ >8 ------------------------\n" + "# Do not modify or remove the line above.\n" + "# Everything below it will be ignored.\n" + "diff --git a/... b/...\n" + "index f1234c..1c5678 1234\n" + "--- a/...\n" + "+++ b/...\n" + "@@ -92,3 +92,4 @@ class Command(BaseCommand):\n" + '+ "this is a test"\n' ) util.run_cli("check", "--commit-msg-file", "some_file") out, _ = capsys.readouterr() @@ -429,9 +419,9 @@ def use_cz_custom_validator(mocker): def test_check_command_with_custom_validator_succeed( mocker: MockFixture, capsys, util: UtilFixture ): - mocker.patch( - "commitizen.commands.check.open", - mocker.mock_open(read_data="ABC-123: add commitizen pre-commit hook"), + mock_path = mocker.patch("commitizen.commands.check.Path") + mock_path.return_value.read_text.return_value = ( + "ABC-123: add commitizen pre-commit hook" ) util.run_cli( "--name", "cz_custom_validator", "check", "--commit-msg-file", "some_file" @@ -444,11 +434,9 @@ def test_check_command_with_custom_validator_succeed( def test_check_command_with_custom_validator_failed( mocker: MockFixture, util: UtilFixture ): - mocker.patch( - "commitizen.commands.check.open", - mocker.mock_open( - read_data="123-ABC issue id has wrong format and misses colon" - ), + mock_path = mocker.patch("commitizen.commands.check.Path") + mock_path.return_value.read_text.return_value = ( + "123-ABC issue id has wrong format and misses colon" ) with pytest.raises(InvalidCommitMessageError) as excinfo: util.run_cli( diff --git a/tests/commands/test_commit_command.py b/tests/commands/test_commit_command.py index 5ee3545b3b..7e6c04d538 100644 --- a/tests/commands/test_commit_command.py +++ b/tests/commands/test_commit_command.py @@ -49,9 +49,12 @@ def staging_is_clean(mocker: MockFixture, tmp_git_project): @pytest.fixture -def backup_file(tmp_git_project): - with get_backup_file_path().open("w") as backup_file: - backup_file.write("backup commit") +def backup_file(tmp_git_project, monkeypatch): + """Write backup message so Commit finds it when run from tmp_git_project.""" + with tmp_git_project.as_cwd(): + path = get_backup_file_path() + path.write_text("backup commit", encoding="utf-8") + monkeypatch.chdir(tmp_git_project) @pytest.mark.usefixtures("staging_is_clean", "commit_mock", "prompt_mock_feat") diff --git a/tests/commands/test_init_command.py b/tests/commands/test_init_command.py index 8675f7f9f3..7d1f2f2bed 100644 --- a/tests/commands/test_init_command.py +++ b/tests/commands/test_init_command.py @@ -79,8 +79,7 @@ def test_init_without_setup_pre_commit_hook( with tmpdir.as_cwd(): commands.Init(config)() - with open("pyproject.toml", encoding="utf-8") as toml_file: - config_data = toml_file.read() + config_data = Path("pyproject.toml").read_text(encoding="utf-8") assert config_data == expected_config assert not Path(pre_commit_config_filename).exists() @@ -156,22 +155,22 @@ def check_cz_config(config_filepath: str): """ Check the content of commitizen config is as expected """ - with open(config_filepath) as file: - if "json" in config_filepath: - assert json.load(file) == EXPECTED_DICT_CONFIG - elif "yaml" in config_filepath: - assert yaml.load(file, Loader=yaml.FullLoader) == EXPECTED_DICT_CONFIG - else: - config_data = file.read() - assert config_data == expected_config + content = Path(config_filepath).read_text(encoding="utf-8") + if "json" in config_filepath: + assert json.loads(content) == EXPECTED_DICT_CONFIG + elif "yaml" in config_filepath: + assert yaml.load(content, Loader=yaml.FullLoader) == EXPECTED_DICT_CONFIG + else: + assert content == expected_config def check_pre_commit_config(expected: list[dict[str, Any]]): """ Check the content of pre-commit config is as expected """ - with open(pre_commit_config_filename) as pre_commit_file: - pre_commit_config_data = yaml.safe_load(pre_commit_file.read()) + pre_commit_config_data = yaml.safe_load( + Path(pre_commit_config_filename).read_text(encoding="utf-8") + ) assert pre_commit_config_data == {"repos": expected} @@ -295,8 +294,9 @@ def test_init_with_confirmed_tag_format( with tmpdir.as_cwd(): commands.Init(config)() - with open("pyproject.toml", encoding="utf-8") as toml_file: - assert 'tag_format = "v$version"' in toml_file.read() + assert 'tag_format = "v$version"' in Path("pyproject.toml").read_text( + encoding="utf-8" + ) def test_init_with_no_existing_tags(config: BaseConfig, mocker: MockFixture, tmpdir): @@ -317,8 +317,7 @@ def test_init_with_no_existing_tags(config: BaseConfig, mocker: MockFixture, tmp with tmpdir.as_cwd(): commands.Init(config)() - with open("pyproject.toml", encoding="utf-8") as toml_file: - assert 'version = "0.0.1"' in toml_file.read() + assert 'version = "0.0.1"' in Path("pyproject.toml").read_text(encoding="utf-8") def test_init_with_no_existing_latest_tag( @@ -340,8 +339,7 @@ def test_init_with_no_existing_latest_tag( with tmpdir.as_cwd(): commands.Init(config)() - with open("pyproject.toml", encoding="utf-8") as toml_file: - assert 'version = "0.0.1"' in toml_file.read() + assert 'version = "0.0.1"' in Path("pyproject.toml").read_text(encoding="utf-8") def test_init_with_existing_tags(config: BaseConfig, mocker: MockFixture, tmpdir): @@ -364,8 +362,7 @@ def test_init_with_existing_tags(config: BaseConfig, mocker: MockFixture, tmpdir with tmpdir.as_cwd(): commands.Init(config)() - with open("pyproject.toml", encoding="utf-8") as toml_file: - assert 'version = "1.0.0"' in toml_file.read() + assert 'version = "1.0.0"' in Path("pyproject.toml").read_text(encoding="utf-8") def test_init_with_valid_tag_selection(config: BaseConfig, mocker: MockFixture, tmpdir): @@ -393,10 +390,9 @@ def test_init_with_valid_tag_selection(config: BaseConfig, mocker: MockFixture, with tmpdir.as_cwd(): commands.Init(config)() - with open("pyproject.toml", encoding="utf-8") as toml_file: - content = toml_file.read() - assert 'version = "0.9.0"' in content - assert 'version_scheme = "semver"' in content + content = Path("pyproject.toml").read_text(encoding="utf-8") + assert 'version = "0.9.0"' in content + assert 'version_scheme = "semver"' in content def test_init_configuration_settings(tmpdir, mocker: MockFixture, config: BaseConfig): @@ -417,8 +413,7 @@ def test_init_configuration_settings(tmpdir, mocker: MockFixture, config: BaseCo with tmpdir.as_cwd(): commands.Init(config)() - with open("pyproject.toml", encoding="utf-8") as toml_file: - config_data = toml_file.read() + config_data = Path("pyproject.toml").read_text(encoding="utf-8") # Verify all expected settings are present assert 'name = "cz_conventional_commits"' in config_data @@ -449,8 +444,7 @@ def test_init_configuration_with_version_provider( with tmpdir.as_cwd(): commands.Init(config)() - with open("pyproject.toml", encoding="utf-8") as toml_file: - config_data = toml_file.read() + config_data = Path("pyproject.toml").read_text(encoding="utf-8") # Verify version provider is set instead of version assert 'name = "cz_conventional_commits"' in config_data diff --git a/tests/conftest.py b/tests/conftest.py index 20daa13e1e..5c13423169 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -113,10 +113,8 @@ def _initial( tmp_version_file.write(version) tmp_commitizen_cfg_file = tmp_git_project.join("pyproject.toml") tmp_version_file_string = str(tmp_version_file).replace("\\", "/") - tmp_commitizen_cfg_file.write( - f"{tmp_commitizen_cfg_file.read()}\n" - f'version_files = ["{tmp_version_file_string}"]\n' - ) + with open(tmp_commitizen_cfg_file, "a", encoding="utf-8") as f: + f.write(f'\nversion_files = ["{tmp_version_file_string}"]\n') if config_extra: tmp_commitizen_cfg_file.write(config_extra, mode="a") util.create_file_and_commit(initial_commit) @@ -309,7 +307,8 @@ def changelog_format( if "tmp_commitizen_project" in request.fixturenames: tmp_commitizen_project = request.getfixturevalue("tmp_commitizen_project") pyproject = tmp_commitizen_project / "pyproject.toml" - pyproject.write(f'{pyproject.read()}\nchangelog_format = "{format}"\n') + with pyproject.open("a", encoding="utf-8") as f: + f.write(f'\nchangelog_format = "{format}"\n') return get_changelog_format(config) diff --git a/tests/data/encoding_test_composer.json b/tests/data/encoding_test_composer.json new file mode 100644 index 0000000000..2cbf2e70cc --- /dev/null +++ b/tests/data/encoding_test_composer.json @@ -0,0 +1,6 @@ +{ + "name": "encoding-test-composer", + "description": "Тест описания для проверки кодировки", + "version": "0.1.0" +} + diff --git a/tests/data/encoding_test_pyproject.toml b/tests/data/encoding_test_pyproject.toml new file mode 100644 index 0000000000..6e47e88ecd --- /dev/null +++ b/tests/data/encoding_test_pyproject.toml @@ -0,0 +1,36 @@ +[project] +name = "pythonproject-test" +version = "0.4.1" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.12" +dependencies = [] + + +[tool.commitizen] +name = "cz_customize" +tag_format = "v$version" +version_scheme = "pep440" +version_provider = "uv" +update_changelog_on_bump = true +changelog_start_rev = "v1.1.0" + +[tool.commitizen.customize] +message_template = "{{ change_type }}{% if scope != 'none' %}({{ scope }}){% endif %}: {{ message }}" +commit_parser = '^(?Pfeat|fix|refactor|test|perf|misc):\s(?P.*)' +schema_pattern = '(feat|fix|refactor|test|perf|misc)(\((api|core)\))?:\s(.{3,})' +bump_pattern = "^(feat|fix|refactor|test|perf|misc)" +change_type_map = { "feat" = "Новое", "fix" = "Исправление" } + +[[tool.commitizen.customize.questions]] +name = "change_type" +type = "list" +message = "Выберите тип изменений" +choices = [ + { value = "feat", name = "feat: Новая функциональность" }, + { value = "fix", name = "fix: Исправление" }, + { value = "refactor", name = "refactor: Рефакторинг" }, + { value = "test", name = "test: Изменение авто-тестов" }, + { value = "perf", name = "perf: Оптимизации" }, + { value = "misc", name = "misc: Другое" }, +] \ No newline at end of file diff --git a/tests/providers/test_base_provider.py b/tests/providers/test_base_provider.py index 782a8ba89e..4129fa8c22 100644 --- a/tests/providers/test_base_provider.py +++ b/tests/providers/test_base_provider.py @@ -7,8 +7,13 @@ from commitizen.exceptions import VersionProviderUnknown from commitizen.providers import get_provider from commitizen.providers.commitizen_provider import CommitizenProvider +from commitizen.providers.composer_provider import ComposerProvider +from commitizen.providers.pep621_provider import Pep621Provider +from commitizen.providers.uv_provider import UvProvider if TYPE_CHECKING: + from pathlib import Path + from commitizen.config.base_config import BaseConfig @@ -22,3 +27,98 @@ def test_raise_for_unknown_provider(config: BaseConfig): config.settings["version_provider"] = "unknown" with pytest.raises(VersionProviderUnknown): get_provider(config) + + +@pytest.mark.parametrize("encoding", ["utf-8", "latin-1"]) +def test_file_provider_get_encoding(config: BaseConfig, encoding: str): + """_get_encoding should return the configured encoding.""" + config.settings["encoding"] = encoding + provider = ComposerProvider(config) + assert provider._get_encoding() == encoding + + +def test_json_provider_uses_encoding_with_encoding_fixture( + config: BaseConfig, + chdir: Path, + data_dir: Path, +): + """JsonProvider should correctly read a JSON file with non-ASCII content.""" + source = data_dir / "encoding_test_composer.json" + target = chdir / "composer.json" + target.write_text(source.read_text(encoding="utf-8"), encoding="utf-8") + + config.settings["encoding"] = "utf-8" + config.settings["version_provider"] = "composer" + + provider = get_provider(config) + assert isinstance(provider, ComposerProvider) + assert provider.get_version() == "0.1.0" + + +def test_toml_provider_uses_encoding_with_encoding_fixture( + config: BaseConfig, + chdir: Path, + data_dir: Path, +): + """TomlProvider should correctly read a TOML file with non-ASCII content.""" + source = data_dir / "encoding_test_pyproject.toml" + target = chdir / "pyproject.toml" + target.write_text(source.read_text(encoding="utf-8"), encoding="utf-8") + + config.settings["encoding"] = "utf-8" + config.settings["version_provider"] = "uv" + + provider = get_provider(config) + assert isinstance(provider, UvProvider) + assert provider.get_version() == "0.4.1" + + +def test_json_provider_handles_various_unicode_characters( + config: BaseConfig, + chdir: Path, +): + """JsonProvider should handle a wide range of Unicode characters.""" + config.settings["encoding"] = "utf-8" + config.settings["version_provider"] = "composer" + + filename = ComposerProvider.filename + file = chdir / filename + file.write_text( + ( + "{\n" + ' "name": "多言語-имя-árbol",\n' + ' "description": "Emoji 😀 – 漢字 – العربية",\n' + ' "version": "0.1.0"\n' + "}\n" + ), + encoding="utf-8", + ) + + provider = get_provider(config) + assert isinstance(provider, ComposerProvider) + assert provider.get_version() == "0.1.0" + + +def test_toml_provider_handles_various_unicode_characters( + config: BaseConfig, + chdir: Path, +): + """TomlProvider should handle a wide range of Unicode characters.""" + config.settings["encoding"] = "utf-8" + config.settings["version_provider"] = "pep621" + + filename = Pep621Provider.filename + file = chdir / filename + file.write_text( + ( + "[project]\n" + 'name = "多言語-имя-árbol"\n' + 'description = "Emoji 😀 – 漢字 – العربية"\n' + 'version = "0.1.0"\n' + ), + encoding="utf-8", + ) + + provider = get_provider(config) + assert isinstance(provider, Pep621Provider) + assert provider.get_version() == "0.1.0" diff --git a/tests/providers/test_npm_provider.py b/tests/providers/test_npm_provider.py index 4de171a927..429b46fac9 100644 --- a/tests/providers/test_npm_provider.py +++ b/tests/providers/test_npm_provider.py @@ -63,6 +63,28 @@ } """ +NPM_PACKAGE_JSON_LATIN1 = """\ +{ + "name": "calf\u00e9-n\u00famero", + "version": "0.1.0" +} +""" + +NPM_LOCKFILE_JSON_LATIN1 = """\ +{ + "name": "calf\u00e9-n\u00famero", + "version": "0.1.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "calf\u00e9-n\u00famero", + "version": "0.1.0" + } + } +} +""" + @pytest.mark.parametrize( ("pkg_shrinkwrap_content", "pkg_shrinkwrap_expected"), @@ -100,3 +122,37 @@ def test_npm_provider( assert pkg_lock.read_text() == dedent(pkg_lock_expected) if pkg_shrinkwrap_content: assert pkg_shrinkwrap.read_text() == dedent(pkg_shrinkwrap_expected) + + +def test_npm_provider_respects_configured_encoding_for_all_files( + config: BaseConfig, + chdir: Path, +): + """NpmProvider should use the configured encoding for all files it touches.""" + config.settings["encoding"] = "latin-1" + config.settings["version_provider"] = "npm" + + pkg = chdir / NpmProvider.package_filename + pkg_lock = chdir / NpmProvider.lock_filename + pkg_shrinkwrap = chdir / NpmProvider.shrinkwrap_filename + + # Write initial contents using latin-1 encoding + pkg.write_text(dedent(NPM_PACKAGE_JSON_LATIN1), encoding="latin-1") + pkg_lock.write_text(dedent(NPM_LOCKFILE_JSON_LATIN1), encoding="latin-1") + pkg_shrinkwrap.write_text(dedent(NPM_LOCKFILE_JSON_LATIN1), encoding="latin-1") + + provider = get_provider(config) + assert isinstance(provider, NpmProvider) + assert provider.get_version() == "0.1.0" + + provider.set_version("42.1") + + # Verify that the files can be read back using the configured encoding + pkg_text = pkg.read_text(encoding="latin-1") + pkg_lock_text = pkg_lock.read_text(encoding="latin-1") + pkg_shrinkwrap_text = pkg_shrinkwrap.read_text(encoding="latin-1") + + # Version was updated everywhere + assert '"version": "42.1"' in pkg_text + assert '"version": "42.1"' in pkg_lock_text + assert '"version": "42.1"' in pkg_shrinkwrap_text diff --git a/tests/test_bump_update_version_in_files.py b/tests/test_bump_update_version_in_files.py index b953937f40..80823a4e1d 100644 --- a/tests/test_bump_update_version_in_files.py +++ b/tests/test_bump_update_version_in_files.py @@ -111,8 +111,7 @@ def test_update_version_in_files(version_files, file_regression): file_contents = "" for filepath in version_files: - with open(filepath, encoding="utf-8") as f: - file_contents += f.read() + file_contents += Path(filepath).read_text(encoding="utf-8") file_regression.check(file_contents, extension=".txt") @@ -125,8 +124,9 @@ def test_partial_update_of_file(version_repeated_file, file_regression): bump.update_version_in_files( old_version, new_version, [location], check_consistency=False, encoding="utf-8" ) - with version_repeated_file.open(encoding="utf-8") as f: - file_regression.check(f.read(), extension=".json") + file_regression.check( + version_repeated_file.read_text(encoding="utf-8"), extension=".json" + ) def test_random_location(random_location_version_file, file_regression): @@ -137,8 +137,9 @@ def test_random_location(random_location_version_file, file_regression): bump.update_version_in_files( old_version, new_version, [location], check_consistency=False, encoding="utf-8" ) - with random_location_version_file.open(encoding="utf-8") as f: - file_regression.check(f.read(), extension=".lock") + file_regression.check( + random_location_version_file.read_text(encoding="utf-8"), extension=".lock" + ) def test_duplicates_are_change_with_no_regex( @@ -151,8 +152,9 @@ def test_duplicates_are_change_with_no_regex( bump.update_version_in_files( old_version, new_version, [location], check_consistency=False, encoding="utf-8" ) - with random_location_version_file.open(encoding="utf-8") as f: - file_regression.check(f.read(), extension=".lock") + file_regression.check( + random_location_version_file.read_text(encoding="utf-8"), extension=".lock" + ) def test_version_bump_increase_string_length( @@ -165,8 +167,10 @@ def test_version_bump_increase_string_length( bump.update_version_in_files( old_version, new_version, [location], check_consistency=False, encoding="utf-8" ) - with open(multiple_versions_increase_string, encoding="utf-8") as f: - file_regression.check(f.read(), extension=".txt") + file_regression.check( + Path(multiple_versions_increase_string).read_text(encoding="utf-8"), + extension=".txt", + ) def test_version_bump_reduce_string_length( @@ -179,8 +183,10 @@ def test_version_bump_reduce_string_length( bump.update_version_in_files( old_version, new_version, [location], check_consistency=False, encoding="utf-8" ) - with open(multiple_versions_reduce_string, encoding="utf-8") as f: - file_regression.check(f.read(), extension=".txt") + file_regression.check( + Path(multiple_versions_reduce_string).read_text(encoding="utf-8"), + extension=".txt", + ) def test_file_version_inconsistent_error( @@ -220,8 +226,10 @@ def test_multiple_versions_to_bump( bump.update_version_in_files( old_version, new_version, [location], check_consistency=False, encoding="utf-8" ) - with multiple_versions_to_update_poetry_lock.open(encoding="utf-8") as f: - file_regression.check(f.read(), extension=".toml") + file_regression.check( + multiple_versions_to_update_poetry_lock.read_text(encoding="utf-8"), + extension=".toml", + ) def test_update_version_in_globbed_files(commitizen_config_file, file_regression): diff --git a/tests/test_changelog.py b/tests/test_changelog.py index 31a0e11b0f..11c3a60446 100644 --- a/tests/test_changelog.py +++ b/tests/test_changelog.py @@ -579,8 +579,7 @@ def tags() -> list[git.GitTag]: @pytest.fixture def changelog_content(data_dir: Path) -> str: changelog = data_dir / "CHANGELOG_FOR_TEST.md" - with changelog.open(encoding="utf-8") as f: - return f.read() + return changelog.read_text(encoding="utf-8") def test_get_commit_tag_is_a_version(gitcommits, tags): diff --git a/tests/test_conf.py b/tests/test_conf.py index fd4d020948..3b79376031 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -391,8 +391,7 @@ def test_init_empty_config_content(self, tmpdir, config_file): toml_config = TomlConfig(data="", path=path) toml_config.init_empty_config_content() - with path.open(encoding="utf-8") as toml_file: - assert toml_file.read() == "[tool.commitizen]\n" + assert Path(path).read_text(encoding="utf-8") == "[tool.commitizen]\n" def test_init_empty_config_content_with_existing_content(self, tmpdir, config_file): existing_content = "[tool.black]\nline-length = 88\n" @@ -402,8 +401,10 @@ def test_init_empty_config_content_with_existing_content(self, tmpdir, config_fi toml_config = TomlConfig(data="", path=path) toml_config.init_empty_config_content() - with path.open(encoding="utf-8") as toml_file: - assert toml_file.read() == existing_content + "\n[tool.commitizen]\n" + assert ( + Path(path).read_text(encoding="utf-8") + == existing_content + "\n[tool.commitizen]\n" + ) def test_init_with_invalid_config_content(self, tmpdir, config_file): existing_content = "invalid toml content" diff --git a/tests/test_deprecated.py b/tests/test_deprecated.py index 6e695c300c..ebaae82c37 100644 --- a/tests/test_deprecated.py +++ b/tests/test_deprecated.py @@ -3,33 +3,33 @@ from commitizen import changelog_formats, defaults -def test_getattr_deprecated_vars(): +@pytest.mark.parametrize( + ("deprecated_var_getter", "replacement"), + [ + (lambda: defaults.bump_pattern, defaults.BUMP_PATTERN), + (lambda: defaults.bump_map, defaults.BUMP_MAP), + ( + lambda: defaults.bump_map_major_version_zero, + defaults.BUMP_MAP_MAJOR_VERSION_ZERO, + ), + (lambda: defaults.bump_message, defaults.BUMP_MESSAGE), + (lambda: defaults.change_type_order, defaults.CHANGE_TYPE_ORDER), + (lambda: defaults.encoding, defaults.ENCODING), + (lambda: defaults.name, defaults.DEFAULT_SETTINGS["name"]), + ( + lambda: changelog_formats.guess_changelog_format, + changelog_formats._guess_changelog_format, + ), + ], +) +def test_getattr_deprecated_vars(deprecated_var_getter, replacement): # Test each deprecated variable with pytest.warns(DeprecationWarning, match="is deprecated and will be removed"): - assert defaults.bump_pattern == defaults.BUMP_PATTERN - with pytest.warns(DeprecationWarning, match="is deprecated and will be removed"): - assert defaults.bump_map == defaults.BUMP_MAP - with pytest.warns(DeprecationWarning, match="is deprecated and will be removed"): - assert ( - defaults.bump_map_major_version_zero == defaults.BUMP_MAP_MAJOR_VERSION_ZERO - ) - with pytest.warns(DeprecationWarning, match="is deprecated and will be removed"): - assert defaults.bump_message == defaults.BUMP_MESSAGE - with pytest.warns(DeprecationWarning, match="is deprecated and will be removed"): - assert defaults.change_type_order == defaults.CHANGE_TYPE_ORDER - with pytest.warns(DeprecationWarning, match="is deprecated and will be removed"): - assert defaults.encoding == defaults.ENCODING - with pytest.warns(DeprecationWarning, match="is deprecated and will be removed"): - assert defaults.name == defaults.DEFAULT_SETTINGS["name"] - with pytest.warns(DeprecationWarning, match="is deprecated and will be removed"): - assert ( - changelog_formats._guess_changelog_format - == changelog_formats.guess_changelog_format - ) + val = deprecated_var_getter() + assert val == replacement def test_getattr_non_existent(): # Test non-existent attribute - with pytest.raises(AttributeError) as exc_info: + with pytest.raises(AttributeError, match="is not an attribute of"): _ = defaults.non_existent_attribute - assert "is not an attribute of" in str(exc_info.value) diff --git a/uv.lock b/uv.lock index 7198658490..c2bfe993d6 100644 --- a/uv.lock +++ b/uv.lock @@ -195,7 +195,7 @@ wheels = [ [[package]] name = "commitizen" -version = "4.13.6" +version = "4.13.7" source = { editable = "." } dependencies = [ { name = "argcomplete" },