Skip to content

Commit b6fcd0d

Browse files
[Breaking Change] Add usethis hook command and --hook/--no-hook to usethis init (#1454)
* Initial plan * Add `usethis hook` command and `--hook/--no-hook` to `usethis init` - Create _toolset/hook.py wrapping use_pre_commit() - Create _ui/interface/hook.py CLI interface - Register `usethis hook` command in app.py - Replace --pre-commit/--no-pre-commit with --hook/--no-hook in init - Update .importlinter, docs/cli/overview.md, and README.md - Add test_hook.py tests and update test_init.py Co-authored-by: nathanjmcdougall <18602289+nathanjmcdougall@users.noreply.github.com> Agent-Logs-Url: https://github.com/usethis-python/usethis-python/sessions/5e55d87a-8269-437d-8ff1-abea260f5a4a * Update docs/cli/reference.md: add `usethis hook` section and rename --pre-commit to --hook Co-authored-by: nathanjmcdougall <18602289+nathanjmcdougall@users.noreply.github.com> Agent-Logs-Url: https://github.com/usethis-python/usethis-python/sessions/fdd7c3d5-e514-44f7-a799-5ec619a872f5 --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: nathanjmcdougall <18602289+nathanjmcdougall@users.noreply.github.com> Co-authored-by: Nathan McDougall <nathan.j.mcdougall@gmail.com>
1 parent 93eebf1 commit b6fcd0d

10 files changed

Lines changed: 157 additions & 19 deletions

File tree

.importlinter

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ type = layers
130130
containers =
131131
usethis._ui.interface
132132
layers =
133-
arch | author | badge | browse | ci | doc | docstyle | format_ | init | lint | list | readme | rule | show | spellcheck | status | test | tool | typecheck | version
133+
arch | author | badge | browse | ci | doc | docstyle | format_ | hook | init | lint | list | readme | rule | show | spellcheck | status | test | tool | typecheck | version
134134
exhaustive = true
135135

136136
[importlinter:contract:pipeweld]

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ Additionally, the command line reference documentation can be viewed with `useth
7373
- [`usethis arch`](https://usethis.readthedocs.io/en/stable/cli/reference#usethis-arch) — Add/Configure recommended architecture analysis tools (namely, [Import Linter](https://import-linter.readthedocs.io/en/stable/)).
7474
- [`usethis doc`](https://usethis.readthedocs.io/en/stable/cli/reference#usethis-doc) — Add/Configure recommended documentation tools (namely, [MkDocs](https://www.mkdocs.org/)).
7575
- [`usethis format`](https://usethis.readthedocs.io/en/stable/cli/reference#usethis-format) — Add/Configure recommended formatters (namely, [Ruff](https://docs.astral.sh/ruff/formatter/) and [pyproject-fmt](https://pyproject-fmt.readthedocs.io/en/latest/)).
76+
- [`usethis hook`](https://usethis.readthedocs.io/en/stable/cli/reference#usethis-hook) — Add/Configure a recommended git hook framework (namely, [pre-commit](https://github.com/pre-commit/pre-commit)).
7677
- [`usethis lint`](https://usethis.readthedocs.io/en/stable/cli/reference#usethis-lint) — Add/Configure recommended linters (namely, [Ruff](https://docs.astral.sh/ruff/linter) and [deptry](https://github.com/fpgmaas/deptry)).
7778
- [`usethis spellcheck`](https://usethis.readthedocs.io/en/stable/cli/reference#usethis-spellcheck) — Add/Configure recommended spellcheckers (namely, [codespell](https://github.com/codespell-project/codespell)).
7879
- [`usethis test`](https://usethis.readthedocs.io/en/stable/cli/reference#usethis-test) — Add/Configure a recommended testing framework (namely, [pytest](https://github.com/pytest-dev/pytest) with [Coverage.py](https://github.com/nedbat/coveragepy)).

docs/cli/overview.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
- [`usethis arch`](reference.md#usethis-arch) — Add/Configure recommended architecture analysis tools (namely, [Import Linter](https://import-linter.readthedocs.io/en/stable/)).
1010
- [`usethis doc`](reference.md#usethis-doc) — Add/Configure recommended documentation tools (namely, [MkDocs](https://www.mkdocs.org/)).
1111
- [`usethis format`](reference.md#usethis-format) — Add/Configure recommended formatters (namely, [Ruff](https://docs.astral.sh/ruff/formatter/) and [pyproject-fmt](https://pyproject-fmt.readthedocs.io/en/latest/)).
12+
- [`usethis hook`](reference.md#usethis-hook) — Add/Configure a recommended git hook framework (namely, [pre-commit](https://github.com/pre-commit/pre-commit)).
1213
- [`usethis lint`](reference.md#usethis-lint) — Add/Configure recommended linters (namely, [Ruff](https://docs.astral.sh/ruff/linter) and [deptry](https://github.com/fpgmaas/deptry)).
1314
- [`usethis spellcheck`](reference.md#usethis-spellcheck) — Add/Configure recommended spellcheckers (namely, [codespell](https://github.com/codespell-project/codespell)).
1415
- [`usethis test`](reference.md#usethis-test) — Add/Configure a recommended testing framework (namely, [pytest](https://github.com/pytest-dev/pytest) with [Coverage.py](https://github.com/nedbat/coveragepy)).

docs/cli/reference.md

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Initialize a new Python project with recommended defaults, including:
77
- a `pyproject.toml` file and relevant configuration,
88
- recommended linters, formatters, spellcheckers, type checkers, and test frameworks (all opt-out),
99
- docstring style enforcement (opt-in),
10-
- the pre-commit framework (opt-in),
10+
- a recommended git hook framework (opt-in),
1111
- CI services (opt-in),
1212
- declared & installed dependencies via `uv add`, and
1313
- any other relevant directories or tool-bespoke configuration files.
@@ -21,7 +21,7 @@ Supported options:
2121
- `--spellcheck` to add a recommended spellchecker (default; or `--no-spellcheck` to opt-out)
2222
- `--test` to add a recommended testing framework (default; or `--no-test` to opt-out)
2323
- `--typecheck` to add a recommended type checker (default; or `--no-typecheck` to opt-out)
24-
- `--pre-commit` to add the pre-commit framework for git hooks (but the default is `--no-pre-commit`)
24+
- `--hook` to add a recommended git hook framework (but the default is `--no-hook`)
2525
- `--ci` to add a CI service configuration
2626

2727
Possible values:
@@ -124,6 +124,32 @@ Supported options:
124124

125125
See [`usethis tool`](#usethis-tool) for more information.
126126

127+
## `usethis hook`
128+
129+
Add a recommended git hook framework to the project (namely, [pre-commit](https://github.com/pre-commit/pre-commit)), including:
130+
131+
- declared & installed dependencies via `uv add`,
132+
- relevant configuration, and
133+
- any other relevant directories or tool-bespoke configuration files.
134+
135+
Note if `pyproject.toml` is not present, it will be created, since this is required for declaring dependencies via `uv add`.
136+
137+
Supported options:
138+
139+
- `--remove` to remove the tool instead of adding it
140+
- `--how` to only print how to use the tool, with no other side effects
141+
- `--offline` to disable network access and rely on caches
142+
- `--frozen` to leave the virtual environment and lockfile unchanged
143+
- `--quiet` to suppress output
144+
- `--backend` to specify a package manager backend to use. The default is to auto-detect.
145+
146+
Possible values:
147+
- `auto` to auto-detect the backend (default)
148+
- `uv` to use the [uv](https://docs.astral.sh/uv) package manager
149+
- `none` to not use a package manager backend and display messages for some operations.
150+
151+
See [`usethis tool`](#usethis-tool) for more information.
152+
127153
## `usethis lint`
128154

129155
Add recommended linters to the project (namely, [Ruff](https://docs.astral.sh/ruff/linter) and [deptry](https://github.com/fpgmaas/deptry)), including:

src/usethis/_toolset/hook.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from usethis._core.tool import use_pre_commit
2+
3+
4+
def use_hook_framework(remove: bool = False, how: bool = False):
5+
use_pre_commit(remove=remove, how=how)

src/usethis/_ui/app.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import usethis._ui.interface.doc
1111
import usethis._ui.interface.docstyle
1212
import usethis._ui.interface.format_
13+
import usethis._ui.interface.hook
1314
import usethis._ui.interface.init
1415
import usethis._ui.interface.lint
1516
import usethis._ui.interface.list
@@ -60,6 +61,12 @@
6061
)(
6162
usethis._ui.interface.format_.format_,
6263
)
64+
app.command(
65+
help="Add or configure a recommended git hook framework.",
66+
rich_help_panel=rich_help_panel,
67+
)(
68+
usethis._ui.interface.hook.hook,
69+
)
6370
app.command(
6471
help="Add or configure recommended linters.", rich_help_panel=rich_help_panel
6572
)(

src/usethis/_ui/interface/hook.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import typer
2+
3+
from usethis._config import usethis_config
4+
from usethis._types.backend import BackendEnum
5+
from usethis._ui.options import (
6+
backend_opt,
7+
frozen_opt,
8+
how_opt,
9+
offline_opt,
10+
quiet_opt,
11+
remove_opt,
12+
)
13+
14+
15+
def hook(
16+
remove: bool = remove_opt,
17+
how: bool = how_opt,
18+
offline: bool = offline_opt,
19+
quiet: bool = quiet_opt,
20+
frozen: bool = frozen_opt,
21+
backend: BackendEnum = backend_opt,
22+
) -> None:
23+
"""Add a recommended git hook framework to the project."""
24+
from usethis._config_file import files_manager
25+
from usethis._console import err_print
26+
from usethis._toolset.hook import use_hook_framework
27+
from usethis.errors import UsethisError
28+
29+
assert isinstance(backend, BackendEnum)
30+
31+
with (
32+
usethis_config.set(
33+
offline=offline, quiet=quiet, frozen=frozen, backend=backend
34+
),
35+
files_manager(),
36+
):
37+
try:
38+
use_hook_framework(remove=remove, how=how)
39+
except UsethisError as err:
40+
err_print(err)
41+
raise typer.Exit(code=1) from None

src/usethis/_ui/interface/init.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ def init(
4141
"--typecheck/--no-typecheck",
4242
help="Add a recommended type checker.",
4343
),
44-
pre_commit: bool = typer.Option(
44+
hook: bool = typer.Option(
4545
False,
46-
"--pre-commit/--no-pre-commit",
47-
help="Add the pre-commit framework for git hooks.",
46+
"--hook/--no-hook",
47+
help="Add a recommended git hook framework.",
4848
),
4949
ci: CIServiceEnum | None = typer.Option(
5050
None,
@@ -103,7 +103,7 @@ def init(
103103
spellcheck=spellcheck,
104104
test=test,
105105
typecheck=typecheck,
106-
pre_commit=pre_commit,
106+
hook=hook,
107107
ci=ci,
108108
docstyle=docstyle,
109109
status=status,
@@ -122,7 +122,7 @@ def _init( # noqa: PLR0915
122122
spellcheck: bool,
123123
test: bool,
124124
typecheck: bool,
125-
pre_commit: bool,
125+
hook: bool,
126126
ci: CIServiceEnum | None,
127127
docstyle: DocStyleEnum | None,
128128
status: DevelopmentStatusEnum,
@@ -132,11 +132,11 @@ def _init( # noqa: PLR0915
132132
from usethis._core.docstyle import use_docstyle
133133
from usethis._core.readme import add_readme
134134
from usethis._core.status import use_development_status
135-
from usethis._core.tool import use_pre_commit
136135
from usethis._init import project_init
137136
from usethis._toolset.arch import use_arch_tools
138137
from usethis._toolset.doc import use_doc_frameworks
139138
from usethis._toolset.format_ import use_formatters
139+
from usethis._toolset.hook import use_hook_framework
140140
from usethis._toolset.lint import use_linters
141141
from usethis._toolset.spellcheck import use_spellcheckers
142142
from usethis._toolset.test import use_test_frameworks
@@ -148,11 +148,11 @@ def _init( # noqa: PLR0915
148148
assert isinstance(status, DevelopmentStatusEnum)
149149
use_development_status(status)
150150

151-
if pre_commit:
152-
tick_print("Adding the pre-commit framework.")
151+
if hook:
152+
tick_print("Adding a recommended git hook framework.")
153153
with usethis_config.set(instruct_only=True):
154-
use_pre_commit()
155-
use_pre_commit(how=True)
154+
use_hook_framework()
155+
use_hook_framework(how=True)
156156

157157
if doc:
158158
tick_print("Adding recommended documentation tools.")
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from pathlib import Path
2+
3+
from usethis._config_file import files_manager
4+
from usethis._deps import get_deps_from_group
5+
from usethis._test import CliRunner, change_cwd
6+
from usethis._types.deps import Dependency
7+
from usethis._ui.app import app
8+
9+
10+
class TestHook:
11+
def test_dependencies_added(self, tmp_path: Path):
12+
# Act
13+
runner = CliRunner()
14+
with change_cwd(tmp_path):
15+
result = runner.invoke_safe(app, ["hook"])
16+
17+
# Assert
18+
assert result.exit_code == 0, result.output
19+
with change_cwd(tmp_path), files_manager():
20+
assert Dependency(name="pre-commit") in get_deps_from_group("dev")
21+
22+
def test_how_option(self, tmp_path: Path):
23+
# Act
24+
runner = CliRunner()
25+
with change_cwd(tmp_path):
26+
result = runner.invoke_safe(app, ["hook", "--how"])
27+
28+
# Assert
29+
assert result.exit_code == 0, result.output
30+
assert "pre-commit" in result.output
31+
32+
def test_add_then_remove(self, tmp_path: Path):
33+
# Arrange
34+
runner = CliRunner()
35+
36+
with change_cwd(tmp_path):
37+
# Act: Add hook framework
38+
result = runner.invoke_safe(app, ["hook"])
39+
assert result.exit_code == 0, result.output
40+
41+
# Act: Remove hook framework
42+
result = runner.invoke_safe(app, ["hook", "--remove"])
43+
44+
# Assert
45+
assert result.exit_code == 0, result.output
46+
with change_cwd(tmp_path), files_manager():
47+
assert Dependency(name="pre-commit") not in get_deps_from_group("dev")
48+
49+
def test_none_backend(self, tmp_path: Path):
50+
# Act
51+
runner = CliRunner()
52+
with change_cwd(tmp_path):
53+
result = runner.invoke_safe(app, ["hook", "--backend", "none"])
54+
55+
# Assert
56+
assert result.exit_code == 0, result.output
57+
assert "pre-commit" in result.output

tests/usethis/_ui/interface/test_init.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@
1212

1313
class TestInit:
1414
@pytest.mark.usefixtures("_vary_network_conn")
15-
def test_pre_commit_included(self, tmp_path: Path):
15+
def test_hook_included(self, tmp_path: Path):
1616
# Act
1717
runner = CliRunner()
1818
with change_cwd(tmp_path):
1919
if not usethis_config.offline:
20-
result = runner.invoke_safe(app, ["init", "--pre-commit"])
20+
result = runner.invoke_safe(app, ["init", "--hook"])
2121
else:
22-
result = runner.invoke_safe(app, ["init", "--pre-commit", "--offline"])
22+
result = runner.invoke_safe(app, ["init", "--hook", "--offline"])
2323

2424
# Assert
2525
assert result.exit_code == 0, result.output
@@ -29,7 +29,7 @@ def test_pre_commit_included(self, tmp_path: Path):
2929
"✔ Writing 'README.md'.\n"
3030
"☐ Populate 'README.md' to help users understand the project.\n"
3131
"✔ Setting the development status to '1 - Planning'.\n"
32-
"✔ Adding the pre-commit framework.\n"
32+
"✔ Adding a recommended git hook framework.\n"
3333
"☐ Run 'uv run pre-commit run -a' to run the hooks manually.\n"
3434
"✔ Adding recommended documentation tools.\n"
3535
"☐ Run 'uv run mkdocs build' to build the documentation.\n"
@@ -100,7 +100,7 @@ def test_bitbucket_docstyle_and_status(self, tmp_path: Path):
100100
"bitbucket",
101101
"--docstyle",
102102
"numpy",
103-
"--pre-commit",
103+
"--hook",
104104
"--status",
105105
"production",
106106
],
@@ -114,7 +114,7 @@ def test_bitbucket_docstyle_and_status(self, tmp_path: Path):
114114
"✔ Writing 'README.md'.\n"
115115
"☐ Populate 'README.md' to help users understand the project.\n"
116116
"✔ Setting the development status to '5 - Production/Stable'.\n"
117-
"✔ Adding the pre-commit framework.\n"
117+
"✔ Adding a recommended git hook framework.\n"
118118
"☐ Run 'uv run pre-commit run -a' to run the hooks manually.\n"
119119
"✔ Adding recommended documentation tools.\n"
120120
"☐ Run 'uv run mkdocs build' to build the documentation.\n"

0 commit comments

Comments
 (0)