Skip to content

Commit 2fad99a

Browse files
250 add frozen flag (#254)
* Add frozen attribute to UsethisConfig class * Modify call_uv_subprocess to use frozen * Updated CLI command * Test_Case Updated * Added UTF-8 to support Windows CLI * Tweak stdout fix and apply pre-commits --------- Co-authored-by: Nathan McDougall <nmcdougall@tonkintaylor.co.nz>
1 parent 2bce03f commit 2fad99a

5 files changed

Lines changed: 77 additions & 18 deletions

File tree

src/usethis/_config.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,43 @@ class UsethisConfig(BaseModel):
1010

1111
offline: bool
1212
quiet: bool
13+
frozen: bool = False
1314

1415
@contextmanager
1516
def set(
16-
self, *, offline: bool | None = None, quiet: bool | None = None
17+
self,
18+
*,
19+
offline: bool | None = None,
20+
quiet: bool | None = None,
21+
frozen: bool | None = None,
1722
) -> Generator[None, None, None]:
1823
"""Temporarily change command options."""
1924
old_offline = self.offline
2025
old_quiet = self.quiet
26+
old_frozen = self.frozen
2127

2228
if offline is None:
2329
offline = old_offline
2430
if quiet is None:
2531
quiet = old_quiet
32+
if frozen is None:
33+
frozen = old_frozen
2634

2735
self.offline = offline
2836
self.quiet = quiet
37+
self.frozen = frozen
2938
yield
3039
self.offline = old_offline
3140
self.quiet = old_quiet
41+
self.frozen = old_frozen
3242

3343

3444
_OFFLINE_DEFAULT = False
3545
_QUIET_DEFAULT = False
3646

37-
usethis_config = UsethisConfig(offline=_OFFLINE_DEFAULT, quiet=_QUIET_DEFAULT)
47+
usethis_config = UsethisConfig(
48+
offline=_OFFLINE_DEFAULT, quiet=_QUIET_DEFAULT, frozen=False
49+
)
3850

3951
offline_opt = typer.Option(_OFFLINE_DEFAULT, "--offline", help="Disable network access")
4052
quiet_opt = typer.Option(_QUIET_DEFAULT, "--quiet", help="Suppress output")

src/usethis/_console.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
1+
import codecs
2+
import sys
3+
14
from rich.console import Console
25

36
from usethis._config import usethis_config
47

8+
# Unicode support - but we need to be able to write bytes
9+
sys.stdout = codecs.getwriter("utf-8")(sys.stdout.buffer)
10+
511
console = Console()
612

713

814
def tick_print(msg: str | Exception) -> None:
915
msg = str(msg)
1016

1117
if not usethis_config.quiet:
12-
console.print(f"✔ {msg}", style="green")
18+
console.print(
19+
f"{'✔'.encode('utf-8', 'ignore').decode('utf-8')} {msg}", style="green"
20+
)
1321

1422

1523
def box_print(msg: str | Exception) -> None:

src/usethis/_integrations/uv/call.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from usethis._config import usethis_config
12
from usethis._integrations.pyproject.io_ import read_pyproject_toml_from_path
23
from usethis._integrations.uv.errors import UVSubprocessFailedError
34
from usethis._subprocess import SubprocessFailedError, call_subprocess
@@ -10,7 +11,10 @@ def call_uv_subprocess(args: list[str]) -> str:
1011
UVSubprocessFailedError: If the subprocess fails.
1112
"""
1213
read_pyproject_toml_from_path.cache_clear()
14+
new_args = ["uv", *args]
15+
if usethis_config.frozen:
16+
new_args.append("--frozen")
1317
try:
14-
return call_subprocess(["uv", *args])
18+
return call_subprocess(new_args)
1519
except SubprocessFailedError as err:
1620
raise UVSubprocessFailedError(err) from None

src/usethis/_interface/tool.py

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,50 +22,67 @@
2222
False, "--remove", help="Remove the tool instead of adding it."
2323
)
2424

25+
frozen_opt = typer.Option(False, "--frozen", help="Use the frozen dependencies.")
26+
2527

2628
@app.command(help="Use the coverage code coverage measurement tool.")
2729
def coverage(
28-
remove: bool = remove_opt, offline: bool = offline_opt, quiet: bool = quiet_opt
30+
remove: bool = remove_opt,
31+
offline: bool = offline_opt,
32+
quiet: bool = quiet_opt,
33+
frozen: bool = frozen_opt,
2934
) -> None:
30-
with usethis_config.set(offline=offline, quiet=quiet):
35+
with usethis_config.set(offline=offline, quiet=quiet, frozen=frozen):
3136
_run_tool(use_coverage, remove=remove)
3237

3338

3439
@app.command(
3540
help="Use the deptry linter: avoid missing or superfluous dependency declarations."
3641
)
3742
def deptry(
38-
remove: bool = remove_opt, offline: bool = offline_opt, quiet: bool = quiet_opt
43+
remove: bool = remove_opt,
44+
offline: bool = offline_opt,
45+
quiet: bool = quiet_opt,
46+
frozen: bool = frozen_opt,
3947
) -> None:
40-
with usethis_config.set(offline=offline, quiet=quiet):
48+
with usethis_config.set(offline=offline, quiet=quiet, frozen=frozen):
4149
_run_tool(use_deptry, remove=remove)
4250

4351

4452
@app.command(
4553
help="Use the pre-commit framework to manage and maintain pre-commit hooks."
4654
)
4755
def pre_commit(
48-
remove: bool = remove_opt, offline: bool = offline_opt, quiet: bool = quiet_opt
56+
remove: bool = remove_opt,
57+
offline: bool = offline_opt,
58+
quiet: bool = quiet_opt,
59+
frozen: bool = frozen_opt,
4960
) -> None:
50-
with usethis_config.set(offline=offline, quiet=quiet):
61+
with usethis_config.set(offline=offline, quiet=quiet, frozen=frozen):
5162
_run_tool(use_pre_commit, remove=remove)
5263

5364

5465
@app.command(
5566
help="Use the pyproject-fmt linter: opinionated formatting of 'pyproject.toml' files."
5667
)
5768
def pyproject_fmt(
58-
remove: bool = remove_opt, offline: bool = offline_opt, quiet: bool = quiet_opt
69+
remove: bool = remove_opt,
70+
offline: bool = offline_opt,
71+
quiet: bool = quiet_opt,
72+
frozen: bool = frozen_opt,
5973
) -> None:
60-
with usethis_config.set(offline=offline, quiet=quiet):
74+
with usethis_config.set(offline=offline, quiet=quiet, frozen=frozen):
6175
_run_tool(use_pyproject_fmt, remove=remove)
6276

6377

6478
@app.command(help="Use the pytest testing framework.")
6579
def pytest(
66-
remove: bool = remove_opt, offline: bool = offline_opt, quiet: bool = quiet_opt
80+
remove: bool = remove_opt,
81+
offline: bool = offline_opt,
82+
quiet: bool = quiet_opt,
83+
frozen: bool = frozen_opt,
6784
) -> None:
68-
with usethis_config.set(offline=offline, quiet=quiet):
85+
with usethis_config.set(offline=offline, quiet=quiet, frozen=frozen):
6986
_run_tool(use_pytest, remove=remove)
7087

7188

@@ -74,17 +91,23 @@ def pytest(
7491
help="Use a requirements.txt file exported from the uv lockfile.",
7592
)
7693
def requirements_txt(
77-
remove: bool = remove_opt, offline: bool = offline_opt, quiet: bool = quiet_opt
94+
remove: bool = remove_opt,
95+
offline: bool = offline_opt,
96+
quiet: bool = quiet_opt,
97+
frozen: bool = frozen_opt,
7898
) -> None:
79-
with usethis_config.set(offline=offline, quiet=quiet):
99+
with usethis_config.set(offline=offline, quiet=quiet, frozen=frozen):
80100
_run_tool(use_requirements_txt, remove=remove)
81101

82102

83103
@app.command(help="Use Ruff: an extremely fast Python linter and code formatter.")
84104
def ruff(
85-
remove: bool = remove_opt, offline: bool = offline_opt, quiet: bool = quiet_opt
105+
remove: bool = remove_opt,
106+
offline: bool = offline_opt,
107+
quiet: bool = quiet_opt,
108+
frozen: bool = frozen_opt,
86109
) -> None:
87-
with usethis_config.set(offline=offline, quiet=quiet):
110+
with usethis_config.set(offline=offline, quiet=quiet, frozen=frozen):
88111
_run_tool(use_ruff, remove=remove)
89112

90113

tests/usethis/_interface/test_tool.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,18 @@ def test_cli(self, uv_init_dir: Path):
1818
else:
1919
call_subprocess(["usethis", "tool", "deptry", "--offline"])
2020

21+
@pytest.mark.usefixtures("_vary_network_conn")
22+
def test_cli_frozen(self, uv_init_dir: Path):
23+
with change_cwd(uv_init_dir):
24+
call_subprocess(["usethis", "tool", "deptry", "--frozen"])
25+
assert not (uv_init_dir / ".venv").exists()
26+
27+
@pytest.mark.usefixtures("_vary_network_conn")
28+
def test_cli_not_frozen(self, uv_init_dir: Path):
29+
with change_cwd(uv_init_dir):
30+
call_subprocess(["usethis", "tool", "deptry"])
31+
assert (uv_init_dir / ".venv").exists()
32+
2133

2234
class TestPreCommit:
2335
@pytest.mark.usefixtures("_vary_network_conn")

0 commit comments

Comments
 (0)