Skip to content

Commit 758d204

Browse files
Comply with B ruff rules (#372)
* Comply with `B` ruff rules * Refactor away shallow badge adding and removal functions Add test for non-markdown README case. * Add test for show sonarqube config case with no key * Add test for readme interface adding badges * Add test for indentation error hint in bitbucket CI yaml
1 parent bb97936 commit 758d204

File tree

20 files changed

+121
-68
lines changed

20 files changed

+121
-68
lines changed

pyproject.toml

+1
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ line-length = 88
8686

8787
lint.select = [
8888
"A",
89+
"B",
8990
"C4",
9091
"D",
9192
"E4",

src/usethis/_core/badge.py

+15-35
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,6 @@ def equivalent_to(self, other: Self) -> bool:
3333
return self.name == other.name
3434

3535

36-
RUFF_BADGE = Badge(
37-
markdown="[![Ruff](<https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json>)](<https://github.com/astral-sh/ruff>)"
38-
)
39-
PRE_COMMIT_BADGE = Badge(
40-
markdown="[![pre-commit](<https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit>)](<https://github.com/pre-commit/pre-commit>)"
41-
)
42-
43-
4436
def get_pypi_badge() -> Badge:
4537
try:
4638
name = get_name()
@@ -56,36 +48,24 @@ def get_pypi_badge() -> Badge:
5648
)
5749

5850

59-
def get_badge_order() -> list[Badge]:
60-
return [
61-
get_pypi_badge(),
62-
RUFF_BADGE,
63-
PRE_COMMIT_BADGE,
64-
]
65-
66-
67-
def add_pypi_badge():
68-
add_badge(get_pypi_badge())
69-
70-
71-
def add_ruff_badge():
72-
add_badge(RUFF_BADGE)
73-
74-
75-
def add_pre_commit_badge():
76-
add_badge(PRE_COMMIT_BADGE)
77-
78-
79-
def remove_pypi_badge():
80-
remove_badge(get_pypi_badge())
51+
def get_ruff_badge() -> Badge:
52+
return Badge(
53+
markdown="[![Ruff](<https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json>)](<https://github.com/astral-sh/ruff>)"
54+
)
8155

8256

83-
def remove_ruff_badge():
84-
remove_badge(RUFF_BADGE)
57+
def get_pre_commit_badge() -> Badge:
58+
return Badge(
59+
markdown="[![pre-commit](<https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit>)](<https://github.com/pre-commit/pre-commit>)"
60+
)
8561

8662

87-
def remove_pre_commit_badge():
88-
remove_badge(PRE_COMMIT_BADGE)
63+
def get_badge_order() -> list[Badge]:
64+
return [
65+
get_pypi_badge(),
66+
get_ruff_badge(),
67+
get_pre_commit_badge(),
68+
]
8969

9070

9171
def add_badge(badge: Badge) -> None:
@@ -95,7 +75,7 @@ def add_badge(badge: Badge) -> None:
9575
path = _get_markdown_readme_path()
9676
except FileNotFoundError as err:
9777
err_print(err)
98-
raise typer.Exit(code=1)
78+
raise typer.Exit(code=1) from None
9979

10080
prerequisites: list[Badge] = []
10181
for _b in get_badge_order():

src/usethis/_core/show.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,4 @@ def show_sonarqube_config() -> None:
2323
print(get_sonar_project_properties())
2424
except UsethisError as err:
2525
err_print(err)
26-
raise typer.Exit(code=1)
26+
raise typer.Exit(code=1) from None

src/usethis/_integrations/github/tags.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def get_github_latest_tag(owner: str, repo: str) -> str:
2828
response.raise_for_status() # Raise an error for HTTP issues
2929
except (requests.exceptions.HTTPError, requests.exceptions.ConnectionError) as err:
3030
msg = f"Failed to fetch tags from GitHub API: {err}"
31-
raise GitHubTagError(msg)
31+
raise GitHubTagError(msg) from None
3232

3333
tags = response.json()
3434

src/usethis/_integrations/pre_commit/hooks.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ def add_repo(repo: LocalRepo | UriRepo) -> None:
7171
hook_idx = _HOOK_ORDER.index(hook_name)
7272
except ValueError:
7373
msg = f"Hook '{hook_name}' not recognized"
74-
raise NotImplementedError(msg)
74+
raise NotImplementedError(msg) from None
7575
precedents = _HOOK_ORDER[:hook_idx]
7676

7777
# Find the last of the precedents in the existing hooks

src/usethis/_integrations/pyproject_toml/core.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def set_pyproject_value(
4141
toml_document=pyproject, id_keys=id_keys, value=value, exists_ok=exists_ok
4242
)
4343
except TOMLValueAlreadySetError as err:
44-
raise PyprojectTOMLValueAlreadySetError(err)
44+
raise PyprojectTOMLValueAlreadySetError(err) from None
4545

4646
PyprojectTOMLManager().commit(pyproject)
4747

@@ -58,7 +58,7 @@ def remove_pyproject_value(
5858
pyproject = remove_toml_value(toml_document=pyproject, id_keys=id_keys)
5959
except TOMLValueMissingError as err:
6060
if not missing_ok:
61-
raise PyprojectTOMLValueMissingError(err)
61+
raise PyprojectTOMLValueMissingError(err) from None
6262
# Otherwise, no changes are needed so skip the write step.
6363
return
6464
PyprojectTOMLManager().commit(pyproject)

src/usethis/_integrations/pyproject_toml/io_.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ def read_file(self) -> None:
9191
self._content = parse(self._path.read_text())
9292
except FileNotFoundError:
9393
msg = "'pyproject.toml' not found in the current directory."
94-
raise PyprojectTOMLNotFoundError(msg)
94+
raise PyprojectTOMLNotFoundError(msg) from None
9595
except TOMLKitError as err:
9696
msg = f"Failed to decode 'pyproject.toml': {err}"
9797
raise PyprojectTOMLDecodeError(msg) from None

src/usethis/_integrations/pyproject_toml/name.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ def get_name() -> str:
1616
name = TypeAdapter(str).validate_python(project_dict["name"])
1717
except KeyError:
1818
msg = "The 'project.name' value is missing from 'pyproject.toml'."
19-
raise PyprojectTOMLProjectNameError(msg)
19+
raise PyprojectTOMLProjectNameError(msg) from None
2020
except ValidationError as err:
2121
msg = (
2222
f"The 'project.name' value in 'pyproject.toml' is not a valid string: {err}"
2323
)
24-
raise PyprojectTOMLProjectNameError(msg)
24+
raise PyprojectTOMLProjectNameError(msg) from None
2525

2626
return name
2727

@@ -33,9 +33,9 @@ def get_description() -> str:
3333
description = TypeAdapter(str).validate_python(project_dict["description"])
3434
except KeyError:
3535
msg = "The 'project.description' value is missing from 'pyproject.toml'."
36-
raise PyprojectTOMLProjectDescriptionError(msg)
36+
raise PyprojectTOMLProjectDescriptionError(msg) from None
3737
except ValidationError as err:
3838
msg = f"The 'project.description' value in 'pyproject.toml' is not a valid string: {err}"
39-
raise PyprojectTOMLProjectDescriptionError(msg)
39+
raise PyprojectTOMLProjectDescriptionError(msg) from None
4040

4141
return description

src/usethis/_integrations/pyproject_toml/project.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ def get_project_dict() -> dict[str, Any]:
2020
project = TypeAdapter(dict).validate_python(pyproject["project"])
2121
except KeyError:
2222
msg = "The 'project' section is missing from 'pyproject.toml'."
23-
raise PyprojectTOMLProjectSectionError(msg)
23+
raise PyprojectTOMLProjectSectionError(msg) from None
2424
except ValidationError as err:
2525
msg = f"The 'project' section in 'pyproject.toml' is not a valid map: {err}"
26-
raise PyprojectTOMLProjectSectionError(msg)
26+
raise PyprojectTOMLProjectSectionError(msg) from None
2727

2828
return project

src/usethis/_integrations/pyproject_toml/requires_python.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@ def get_requires_python() -> SpecifierSet:
1919
)
2020
except KeyError:
2121
msg = "The 'project.requires-python' value is missing from 'pyproject.toml'."
22-
raise MissingRequiresPythonError(msg)
22+
raise MissingRequiresPythonError(msg) from None
2323

2424
return SpecifierSet(requires_python)

src/usethis/_integrations/sonarqube/config.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def get_sonar_project_properties() -> str:
3636
)
3737
except (FileNotFoundError, KeyError):
3838
msg = "Could not find SonarQube project key at 'tool.usethis.sonarqube.project-key' in 'pyproject.toml'."
39-
raise MissingProjectKeyError(msg)
39+
raise MissingProjectKeyError(msg) from None
4040
_validate_project_key(project_key)
4141

4242
try:
@@ -63,7 +63,7 @@ def get_sonar_project_properties() -> str:
6363
)
6464
except (FileNotFoundError, KeyError):
6565
msg = "XML coverage report file path not found at 'tool.coverage.xml.output' in 'pyproject.toml'."
66-
raise CoverageReportConfigNotFoundError(msg)
66+
raise CoverageReportConfigNotFoundError(msg) from None
6767

6868
# No file, so construct the contents
6969
source_dir_str = get_source_dir_str()

src/usethis/_integrations/toml/core.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def remove_toml_value(
100100
d = d[key]
101101
except KeyError:
102102
msg = f"Configuration value '{'.'.join(id_keys)}' is missing."
103-
raise TOMLValueMissingError(msg)
103+
raise TOMLValueMissingError(msg) from None
104104

105105
# Remove the configuration.
106106
d = toml_document

src/usethis/_interface/badge.py

+11-12
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@
22

33
from usethis._config import offline_opt, quiet_opt, usethis_config
44
from usethis._core.badge import (
5-
add_pre_commit_badge,
6-
add_pypi_badge,
7-
add_ruff_badge,
8-
remove_pre_commit_badge,
9-
remove_pypi_badge,
10-
remove_ruff_badge,
5+
add_badge,
6+
get_pre_commit_badge,
7+
get_pypi_badge,
8+
get_ruff_badge,
9+
remove_badge,
1110
)
1211
from usethis._integrations.pyproject_toml.io_ import PyprojectTOMLManager
1312

@@ -29,9 +28,9 @@ def pypi(
2928
PyprojectTOMLManager(),
3029
):
3130
if not remove:
32-
add_pypi_badge()
31+
add_badge(get_pypi_badge())
3332
else:
34-
remove_pypi_badge()
33+
remove_badge(get_pypi_badge())
3534

3635

3736
@app.command(help="Add a badge for the Ruff linter.")
@@ -45,9 +44,9 @@ def ruff(
4544
PyprojectTOMLManager(),
4645
):
4746
if not remove:
48-
add_ruff_badge()
47+
add_badge(get_ruff_badge())
4948
else:
50-
remove_ruff_badge()
49+
remove_badge(get_ruff_badge())
5150

5251

5352
@app.command(help="Add a badge for the pre-commit framework.")
@@ -61,6 +60,6 @@ def pre_commit(
6160
PyprojectTOMLManager(),
6261
):
6362
if not remove:
64-
add_pre_commit_badge()
63+
add_badge(get_pre_commit_badge())
6564
else:
66-
remove_pre_commit_badge()
65+
remove_badge(get_pre_commit_badge())

src/usethis/_interface/ci.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,4 @@ def bitbucket(
2828

2929
if "mapping values are not allowed here" in str(err):
3030
info_print("Hint: You may have incorrect indentation the YAML file.")
31-
raise typer.Exit(code=1)
31+
raise typer.Exit(code=1) from None

src/usethis/_interface/readme.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33
import typer
44

55
from usethis._config import quiet_opt, usethis_config
6-
from usethis._core.badge import add_pre_commit_badge, add_ruff_badge
6+
from usethis._core.badge import (
7+
add_badge,
8+
get_pre_commit_badge,
9+
get_ruff_badge,
10+
)
711
from usethis._core.readme import add_readme
812
from usethis._integrations.pyproject_toml.io_ import PyprojectTOMLManager
913
from usethis._tool import PreCommitTool, RuffTool
@@ -18,7 +22,7 @@ def readme(
1822

1923
if badges:
2024
if RuffTool().is_used():
21-
add_ruff_badge()
25+
add_badge(get_ruff_badge())
2226

2327
if PreCommitTool().is_used():
24-
add_pre_commit_badge()
28+
add_badge(get_pre_commit_badge())

src/usethis/_interface/tool.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ def _run_tool(caller: UseToolFunc, *, remove: bool):
181181
caller(remove=remove)
182182
except UsethisError as err:
183183
err_print(err)
184-
raise typer.Exit(code=1)
184+
raise typer.Exit(code=1) from None
185185

186186

187187
ALL_TOOL_COMMANDS: list[str] = [

tests/usethis/_core/test_core_badge.py

+14
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,27 @@
11
from pathlib import Path
22

33
import pytest
4+
import typer
45

56
from usethis._core.badge import Badge, add_badge, remove_badge
67
from usethis._integrations.pyproject_toml.io_ import PyprojectTOMLManager
78
from usethis._test import change_cwd
89

910

1011
class TestAddBadge:
12+
def test_not_markdown(self, bare_dir: Path):
13+
# Arrange
14+
path = bare_dir / "README.foo"
15+
path.touch()
16+
17+
# Act, Assert
18+
with change_cwd(bare_dir), PyprojectTOMLManager(), pytest.raises(typer.Exit):
19+
add_badge(
20+
Badge(
21+
markdown="![Licence](<https://img.shields.io/badge/licence-mit-green>)",
22+
)
23+
)
24+
1125
def test_empty(self, bare_dir: Path, capfd: pytest.CaptureFixture[str]):
1226
# Arrange
1327
path = bare_dir / "README.md"

tests/usethis/_interface/test_interface_ci.py

+23
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,26 @@ def test_maximal_config(self, tmp_path: Path):
6565
Path(__file__).parent / "maximal_bitbucket_pipelines.yml"
6666
).read_text()
6767
assert (tmp_path / "bitbucket-pipelines.yml").read_text() == expected_yml
68+
69+
def test_incorrect_indentation(self, tmp_path: Path):
70+
# Arrange
71+
(tmp_path / "bitbucket-pipelines.yml").write_text("""\
72+
- path: /
73+
backend:
74+
serviceName: <service_name>
75+
servicePort: <port>
76+
""")
77+
# Use something like pre-commit so we try and modify the file
78+
(tmp_path / ".pre-commit-config.yaml").touch()
79+
80+
# Act
81+
runner = CliRunner()
82+
with change_cwd(tmp_path):
83+
result = runner.invoke(app)
84+
85+
# Assert
86+
assert result.exit_code == 1, result.output
87+
assert "mapping values are not allowed here" in result.output
88+
assert (
89+
"Hint: You may have incorrect indentation the YAML file." in result.output
90+
)

tests/usethis/_interface/test_interface_readme.py

+20
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,23 @@ def test_runs(self, tmp_path: Path):
1515

1616
# Assert
1717
assert result.exit_code == 0, result.output
18+
# Check no badges have been added
19+
assert "ruff" not in (tmp_path / "README.md").read_text()
20+
assert "pre-commit" not in (tmp_path / "README.md").read_text()
21+
22+
def test_badges(self, tmp_path: Path):
23+
# Arrange
24+
(tmp_path / "ruff.toml").touch()
25+
(tmp_path / ".pre-commit-config.yaml").touch()
26+
27+
# Act
28+
runner = CliRunner()
29+
with change_cwd(tmp_path):
30+
result = runner.invoke(app, ["readme", "--badges"])
31+
32+
# Assert
33+
assert result.exit_code == 0, result.output
34+
assert (tmp_path / "README.md").exists()
35+
# and check the badges get created
36+
assert "ruff" in (tmp_path / "README.md").read_text()
37+
assert "pre-commit" in (tmp_path / "README.md").read_text()

tests/usethis/_interface/test_show.py

+12
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,15 @@ def test_runs(self, tmp_path: Path):
4141

4242
# Assert
4343
assert result.exit_code == 0, result.output
44+
45+
def test_missing_key(self, tmp_path: Path):
46+
# Arrange
47+
(tmp_path / "pyproject.toml").touch()
48+
49+
# Act
50+
runner = CliRunner()
51+
with change_cwd(tmp_path):
52+
result = runner.invoke(app, ["sonarqube-config"])
53+
54+
# Assert
55+
assert result.exit_code == 1, result.output

0 commit comments

Comments
 (0)