Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit e03b1f0

Browse files
refactor(Init): make project_info a module and remove self.project_info
1 parent a472bea commit e03b1f0

File tree

4 files changed

+146
-87
lines changed

4 files changed

+146
-87
lines changed

‎commitizen/commands/init.py‎

Lines changed: 7 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
from __future__ import annotations
22

3-
import os
4-
import shutil
3+
from pathlib import Path
54
from typing import Any, NamedTuple
65

76
import questionary
87
import yaml
98

10-
from commitizen import cmd, factory, out
9+
from commitizen import cmd, factory, out, project_info
1110
from commitizen.__version__ import __version__
1211
from commitizen.config import BaseConfig, JsonConfig, TomlConfig, YAMLConfig
1312
from commitizen.cz import registry
@@ -65,65 +64,13 @@ def title(self) -> str:
6564
)
6665

6766

68-
class ProjectInfo:
69-
"""Discover information about the current folder."""
70-
71-
@property
72-
def has_pyproject(self) -> bool:
73-
return os.path.isfile("pyproject.toml")
74-
75-
@property
76-
def has_uv_lock(self) -> bool:
77-
return os.path.isfile("uv.lock")
78-
79-
@property
80-
def has_setup(self) -> bool:
81-
return os.path.isfile("setup.py")
82-
83-
@property
84-
def has_pre_commit_config(self) -> bool:
85-
return os.path.isfile(".pre-commit-config.yaml")
86-
87-
@property
88-
def is_python_uv(self) -> bool:
89-
return self.has_pyproject and self.has_uv_lock
90-
91-
@property
92-
def is_python_poetry(self) -> bool:
93-
if not self.has_pyproject:
94-
return False
95-
with open("pyproject.toml") as f:
96-
return "[tool.poetry]" in f.read()
97-
98-
@property
99-
def is_python(self) -> bool:
100-
return self.has_pyproject or self.has_setup
101-
102-
@property
103-
def is_rust_cargo(self) -> bool:
104-
return os.path.isfile("Cargo.toml")
105-
106-
@property
107-
def is_npm_package(self) -> bool:
108-
return os.path.isfile("package.json")
109-
110-
@property
111-
def is_php_composer(self) -> bool:
112-
return os.path.isfile("composer.json")
113-
114-
@property
115-
def is_pre_commit_installed(self) -> bool:
116-
return bool(shutil.which("pre-commit"))
117-
118-
11967
class Init:
12068
_PRE_COMMIT_CONFIG_PATH = ".pre-commit-config.yaml"
12169

12270
def __init__(self, config: BaseConfig, *args: object) -> None:
12371
self.config: BaseConfig = config
12472
self.encoding = config.settings["encoding"]
12573
self.cz = factory.committer_factory(self.config)
126-
self.project_info = ProjectInfo()
12774

12875
def __call__(self) -> None:
12976
if self.config.path:
@@ -167,7 +114,7 @@ def __call__(self) -> None:
167114
) as config_file:
168115
yaml.safe_dump(config_data, stream=config_file)
169116

170-
if not self.project_info.is_pre_commit_installed:
117+
if not project_info.is_pre_commit_installed():
171118
raise InitFailedError(
172119
"Failed to install pre-commit hook.\n"
173120
"pre-commit is not installed in current environment."
@@ -215,14 +162,10 @@ def __call__(self) -> None:
215162
out.success("Configuration complete 🚀")
216163

217164
def _ask_config_path(self) -> str:
218-
default_path = (
219-
"pyproject.toml" if self.project_info.has_pyproject else ".cz.toml"
220-
)
221-
222165
name: str = questionary.select(
223166
"Please choose a supported config file: ",
224167
choices=CONFIG_FILES,
225-
default=default_path,
168+
default=project_info.get_default_config_filename(),
226169
style=self.cz.style,
227170
).unsafe_ask()
228171
return name
@@ -287,37 +230,17 @@ def _ask_version_provider(self) -> str:
287230
"Choose the source of the version:",
288231
choices=_VERSION_PROVIDER_CHOICES,
289232
style=self.cz.style,
290-
default=self._default_version_provider,
233+
default=project_info.get_default_version_provider(),
291234
).unsafe_ask()
292235
return version_provider
293236

294-
@property
295-
def _default_version_provider(self) -> str:
296-
if self.project_info.is_python:
297-
if self.project_info.is_python_poetry:
298-
return "poetry"
299-
if self.project_info.is_python_uv:
300-
return "uv"
301-
return "pep621"
302-
303-
if self.project_info.is_rust_cargo:
304-
return "cargo"
305-
if self.project_info.is_npm_package:
306-
return "npm"
307-
if self.project_info.is_php_composer:
308-
return "composer"
309-
310-
return "commitizen"
311-
312237
def _ask_version_scheme(self) -> str:
313238
"""Ask for setting: version_scheme"""
314-
default_scheme = "pep440" if self.project_info.is_python else "semver"
315-
316239
scheme: str = questionary.select(
317240
"Choose version scheme: ",
318241
choices=KNOWN_SCHEMES,
319242
style=self.cz.style,
320-
default=default_scheme,
243+
default=project_info.get_default_version_scheme(),
321244
).unsafe_ask()
322245
return scheme
323246

@@ -351,8 +274,7 @@ def _get_config_data(self) -> dict[str, Any]:
351274
],
352275
}
353276

354-
if not self.project_info.has_pre_commit_config:
355-
# .pre-commit-config.yaml does not exist
277+
if not Path(".pre-commit-config.yaml").is_file():
356278
return {"repos": [CZ_HOOK_CONFIG]}
357279

358280
with open(self._PRE_COMMIT_CONFIG_PATH, encoding=self.encoding) as config_file:

‎commitizen/project_info.py‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
"""Resolves project information about the current working directory."""
2+
3+
import shutil
4+
from pathlib import Path
5+
from typing import Literal
6+
7+
8+
def is_pre_commit_installed() -> bool:
9+
return bool(shutil.which("pre-commit"))
10+
11+
12+
def get_default_version_provider() -> Literal[
13+
"commitizen", "cargo", "composer", "npm", "pep621", "poetry", "uv"
14+
]:
15+
pyproject_path = Path("pyproject.toml")
16+
if pyproject_path.is_file():
17+
if "[tool.poetry]" in pyproject_path.read_text():
18+
return "poetry"
19+
if Path("uv.lock").is_file():
20+
return "uv"
21+
return "pep621"
22+
23+
if Path("setup.py").is_file():
24+
return "pep621"
25+
26+
if Path("Cargo.toml").is_file():
27+
return "cargo"
28+
29+
if Path("package.json").is_file():
30+
return "npm"
31+
32+
if Path("composer.json").is_file():
33+
return "composer"
34+
35+
return "commitizen"
36+
37+
38+
def get_default_config_filename() -> Literal["pyproject.toml", ".cz.toml"]:
39+
return "pyproject.toml" if Path("pyproject.toml").is_file() else ".cz.toml"
40+
41+
42+
def get_default_version_scheme() -> Literal["pep440", "semver"]:
43+
return (
44+
"pep440"
45+
if Path("pyproject.toml").is_file() or Path("setup.py").is_file()
46+
else "semver"
47+
)

‎tests/commands/test_init_command.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ def test_init_without_choosing_tag(config: BaseConfig, mocker: MockFixture, tmpd
121121
def pre_commit_installed(mocker: MockFixture):
122122
# Assume the `pre-commit` is installed
123123
mocker.patch(
124-
"commitizen.commands.init.ProjectInfo.is_pre_commit_installed",
124+
"commitizen.project_info.is_pre_commit_installed",
125125
return_value=True,
126126
)
127127
# And installation success (i.e. no exception raised)
@@ -231,7 +231,7 @@ def test_pre_commit_not_installed(
231231
):
232232
# Assume `pre-commit` is not installed
233233
mocker.patch(
234-
"commitizen.commands.init.ProjectInfo.is_pre_commit_installed",
234+
"commitizen.project_info.is_pre_commit_installed",
235235
return_value=False,
236236
)
237237
with tmpdir.as_cwd():

‎tests/test_project_info.py‎

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
"""Tests for project_info module."""
2+
3+
from __future__ import annotations
4+
5+
from pathlib import Path
6+
7+
import pytest
8+
9+
from commitizen import project_info
10+
11+
12+
def _create_project_files(files: dict[str, str | None]) -> None:
13+
for file_path, content in files.items():
14+
path = Path(file_path)
15+
if content is None:
16+
path.touch()
17+
else:
18+
path.write_text(content)
19+
20+
21+
@pytest.mark.parametrize(
22+
"which_return, expected",
23+
[
24+
("/usr/local/bin/pre-commit", True),
25+
(None, False),
26+
("", False),
27+
],
28+
)
29+
def test_is_pre_commit_installed(mocker, which_return, expected):
30+
mocker.patch("shutil.which", return_value=which_return)
31+
assert project_info.is_pre_commit_installed() is expected
32+
33+
34+
@pytest.mark.parametrize(
35+
"files, expected",
36+
[
37+
(
38+
{"pyproject.toml": '[tool.poetry]\nname = "test"\nversion = "0.1.0"'},
39+
"poetry",
40+
),
41+
({"pyproject.toml": "", "uv.lock": ""}, "uv"),
42+
(
43+
{"pyproject.toml": '[tool.commitizen]\nversion = "0.1.0"'},
44+
"pep621",
45+
),
46+
({"setup.py": ""}, "pep621"),
47+
({"Cargo.toml": ""}, "cargo"),
48+
({"package.json": ""}, "npm"),
49+
({"composer.json": ""}, "composer"),
50+
({}, "commitizen"),
51+
(
52+
{
53+
"pyproject.toml": "",
54+
"Cargo.toml": "",
55+
"package.json": "",
56+
"composer.json": "",
57+
},
58+
"pep621",
59+
),
60+
],
61+
)
62+
def test_get_default_version_provider(chdir, files, expected):
63+
_create_project_files(files)
64+
assert project_info.get_default_version_provider() == expected
65+
66+
67+
@pytest.mark.parametrize(
68+
"files, expected",
69+
[
70+
({"pyproject.toml": ""}, "pyproject.toml"),
71+
({}, ".cz.toml"),
72+
],
73+
)
74+
def test_get_default_config_filename(chdir, files, expected):
75+
_create_project_files(files)
76+
assert project_info.get_default_config_filename() == expected
77+
78+
79+
@pytest.mark.parametrize(
80+
"files, expected",
81+
[
82+
({"pyproject.toml": ""}, "pep440"),
83+
({"setup.py": ""}, "pep440"),
84+
({"package.json": ""}, "semver"),
85+
({}, "semver"),
86+
],
87+
)
88+
def test_get_default_version_scheme(chdir, files, expected):
89+
_create_project_files(files)
90+
assert project_info.get_default_version_scheme() == expected

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /