Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,8 @@ ALWAYS check whether an existing function already covers your use case before im
- `get_project_name_from_dir()` (`usethis._file.dir`) — Derive a valid project name from the current directory name.
- `deep_merge()` (`usethis._file.merge`) — Recursively merge source into target in place, returning target.
- `print_keys()` (`usethis._file.print_`) — Convert a list of keys to a string.
- `get_project_deps()` (`usethis._file.pyproject_toml.deps`) — Get all project dependencies from [project.dependencies].
- `get_dep_groups()` (`usethis._file.pyproject_toml.deps`) — Get all dependency groups from [dependency-groups].
- `get_name()` (`usethis._file.pyproject_toml.name`) — Get the project name from pyproject.toml.
- `get_description()` (`usethis._file.pyproject_toml.name`) — Get the project description from pyproject.toml.
- `get_project_dict()` (`usethis._file.pyproject_toml.project`) — Get the contents of the [project] section from pyproject.toml.
Expand Down
2 changes: 2 additions & 0 deletions docs/functions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@
- `get_project_name_from_dir()` (`usethis._file.dir`) — Derive a valid project name from the current directory name.
- `deep_merge()` (`usethis._file.merge`) — Recursively merge source into target in place, returning target.
- `print_keys()` (`usethis._file.print_`) — Convert a list of keys to a string.
- `get_project_deps()` (`usethis._file.pyproject_toml.deps`) — Get all project dependencies from [project.dependencies].
- `get_dep_groups()` (`usethis._file.pyproject_toml.deps`) — Get all dependency groups from [dependency-groups].
- `get_name()` (`usethis._file.pyproject_toml.name`) — Get the project name from pyproject.toml.
- `get_description()` (`usethis._file.pyproject_toml.name`) — Get the project description from pyproject.toml.
- `get_project_dict()` (`usethis._file.pyproject_toml.project`) — Get the contents of the [project] section from pyproject.toml.
Expand Down
12 changes: 6 additions & 6 deletions src/usethis/_console.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def tick_print(msg: str | Exception) -> None:
or usethis_config.instruct_only
):
icon = _get_icon("tick")
console.print(f"{icon} {msg}", style="green")
console.print(f"{icon} {msg}", style="green", soft_wrap=True)


def instruct_print(msg: str | Exception) -> None:
Expand All @@ -67,7 +67,7 @@ def instruct_print(msg: str | Exception) -> None:

if not (usethis_config.quiet or usethis_config.alert_only):
icon = _get_icon("instruct")
console.print(f"{icon} {msg}", style="red")
console.print(f"{icon} {msg}", style="red", soft_wrap=True)


def how_print(msg: str | Exception) -> None:
Expand All @@ -80,7 +80,7 @@ def how_print(msg: str | Exception) -> None:
or usethis_config.instruct_only
):
icon = _get_icon("how")
console.print(f"{icon} {msg}", style="red")
console.print(f"{icon} {msg}", style="red", soft_wrap=True)


def info_print(msg: str | Exception, temporary: bool = False) -> None:
Expand All @@ -97,7 +97,7 @@ def info_print(msg: str | Exception, temporary: bool = False) -> None:
end = "\r"
else:
end = "\n"
console.print(f"{icon} {msg}", style="blue", end=end)
console.print(f"{icon} {msg}", style="blue", end=end, soft_wrap=True)


def err_print(msg: str | Exception) -> None:
Expand All @@ -106,7 +106,7 @@ def err_print(msg: str | Exception) -> None:

if not usethis_config.quiet:
icon = _get_icon("error")
err_console.print(f"{icon} {msg}", style="red")
err_console.print(f"{icon} {msg}", style="red", soft_wrap=True)


def warn_print(msg: str | Exception) -> None:
Expand All @@ -120,7 +120,7 @@ def warn_print(msg: str | Exception) -> None:
def _cached_warn_print(msg: str) -> None:
if not usethis_config.quiet:
icon = _get_icon("warning")
console.print(f"{icon} {msg}", style="yellow")
console.print(f"{icon} {msg}", style="yellow", soft_wrap=True)


# Icon fallback system for terminals with varying Unicode support
Expand Down
62 changes: 31 additions & 31 deletions tests/usethis/_core/test_core_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,12 @@ def test_pre_commit_integration(
assert not err
# Note: Since deps are now added (issue #1020), the message is "uv run codespell"
# not "pre-commit run -acodespell" because get_install_method() returns "devdep"
assert out.replace("\n", "") == (
"✔ Adding dependency 'codespell' to the 'dev' group in 'pyproject.toml'."
"☐ Install the dependency 'codespell'."
"✔ Adding hook 'codespell' to '.pre-commit-config.yaml'."
"✔ Adding Codespell config to 'pyproject.toml'."
"☐ Run 'uv run codespell' to run the Codespell spellchecker."
assert out == (
"✔ Adding dependency 'codespell' to the 'dev' group in 'pyproject.toml'.\n"
"☐ Install the dependency 'codespell'.\n"
"✔ Adding hook 'codespell' to '.pre-commit-config.yaml'.\n"
"✔ Adding Codespell config to 'pyproject.toml'.\n"
"☐ Run 'uv run codespell' to run the Codespell spellchecker.\n"
)

@pytest.mark.usefixtures("_vary_network_conn")
Expand Down Expand Up @@ -2058,11 +2058,11 @@ def test_requirements_txt_used(

# Assert
out, _ = capfd.readouterr()
assert out.replace("\n", "") == (
"☐ Run 'uv run --with pre-commit pre-commit uninstall' to deregister pre-commit."
"✔ Removing '.pre-commit-config.yaml'."
"✔ Removing dependency 'pre-commit' from the 'dev' group in 'pyproject.toml'."
"☐ Run 'uv export -o=requirements.txt' to write 'requirements.txt'."
assert out == (
"☐ Run 'uv run --with pre-commit pre-commit uninstall' to deregister pre-commit.\n"
"✔ Removing '.pre-commit-config.yaml'.\n"
"✔ Removing dependency 'pre-commit' from the 'dev' group in 'pyproject.toml'.\n"
"☐ Run 'uv export -o=requirements.txt' to write 'requirements.txt'.\n"
)

@pytest.mark.usefixtures("_vary_network_conn")
Expand Down Expand Up @@ -2319,12 +2319,12 @@ def test_pre_commit_integration(
# Check output
out, err = capfd.readouterr()
assert not err
assert out.replace("\n", "") == (
"✔ Adding dependency 'pyproject-fmt' to the 'dev' group in 'pyproject.toml'."
"☐ Install the dependency 'pyproject-fmt'."
"✔ Adding hook 'pyproject-fmt' to '.pre-commit-config.yaml'."
"✔ Adding pyproject-fmt config to 'pyproject.toml'."
"☐ Run 'uv run pyproject-fmt pyproject.toml' to run pyproject-fmt."
assert out == (
"✔ Adding dependency 'pyproject-fmt' to the 'dev' group in 'pyproject.toml'.\n"
"☐ Install the dependency 'pyproject-fmt'.\n"
"✔ Adding hook 'pyproject-fmt' to '.pre-commit-config.yaml'.\n"
"✔ Adding pyproject-fmt config to 'pyproject.toml'.\n"
"☐ Run 'uv run pyproject-fmt pyproject.toml' to run pyproject-fmt.\n"
)

class TestRemove:
Expand Down Expand Up @@ -2875,9 +2875,9 @@ def test_start_from_nothing_uv_backend(
assert not (tmp_path / "uv.lock").exists()
out, err = capfd.readouterr()
assert not err
assert out.replace("\n", "") == (
"✔ Writing 'requirements.txt'."
"☐ Run 'uv export -o=requirements.txt' to write 'requirements.txt'."
assert out == (
"✔ Writing 'requirements.txt'.\n"
"☐ Run 'uv export -o=requirements.txt' to write 'requirements.txt'.\n"
)
content = (tmp_path / "requirements.txt").read_text()
assert content == "-e .\n"
Expand Down Expand Up @@ -2917,10 +2917,10 @@ def test_start_from_uv_init(
assert (uv_init_dir / "requirements.txt").exists()
out, err = capfd.readouterr()
assert not err
assert out.replace("\n", "") == (
"✔ Writing 'uv.lock'."
"✔ Writing 'requirements.txt'."
"☐ Run 'uv export -o=requirements.txt' to write 'requirements.txt'."
assert out == (
"✔ Writing 'uv.lock'.\n"
"✔ Writing 'requirements.txt'.\n"
"☐ Run 'uv export -o=requirements.txt' to write 'requirements.txt'.\n"
)

def test_start_from_uv_locked(
Expand All @@ -2941,9 +2941,9 @@ def test_start_from_uv_locked(
assert (uv_init_dir / "requirements.txt").exists()
out, err = capfd.readouterr()
assert not err
assert out.replace("\n", "") == (
"✔ Writing 'requirements.txt'."
"☐ Run 'uv export -o=requirements.txt' to write 'requirements.txt'."
assert out == (
"✔ Writing 'requirements.txt'.\n"
"☐ Run 'uv export -o=requirements.txt' to write 'requirements.txt'.\n"
)

@pytest.mark.usefixtures("_vary_network_conn")
Expand Down Expand Up @@ -3024,10 +3024,10 @@ def test_none_backend(

out, err = capfd.readouterr()
assert not err
assert out.replace("\n", "") == (
"ℹ Generating 'requirements.txt' with un-pinned, abstract dependencies." # noqa: RUF001
"ℹ Consider installing 'uv' for pinned, cross-platform, full requirements files." # noqa: RUF001
"✔ Writing 'requirements.txt'."
assert out == (
"ℹ Generating 'requirements.txt' with un-pinned, abstract dependencies.\n" # noqa: RUF001
"ℹ Consider installing 'uv' for pinned, cross-platform, full requirements files.\n" # noqa: RUF001
"✔ Writing 'requirements.txt'.\n"
)

class TestRemove:
Expand Down
8 changes: 5 additions & 3 deletions tests/usethis/_tool/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,9 +411,11 @@ def test_syntax_errors_in_pyproject_toml(
assert not result
out, err = capsys.readouterr()
assert not err
assert out.replace("\n", " ").replace(" ", " ") == (
r"⚠ Failed to decode 'pyproject.toml': Unexpected character: '\n' at line 1 col 13 "
"⚠ Assuming 'pyproject.toml' contains no evidence of my_tool being used. "
assert out == (
"⚠ Failed to decode 'pyproject.toml':\n"
r"Unexpected character: '\n' at line 1 col 13"
"\n"
"⚠ Assuming 'pyproject.toml' contains no evidence of my_tool being used.\n"
)

def test_syntax_errors_in_setup_cfg(
Expand Down
8 changes: 4 additions & 4 deletions tests/usethis/_ui/interface/test_lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def test_none_backend_no_pyproject_toml(self, tmp_path: Path):
# Assert
assert result.exit_code == 0, result.output
assert (tmp_path / "pyproject.toml").exists() # from deptry config
assert result.output.replace("\n", "") == (
assert result.output == (
"☐ Add the dev dependency 'deptry'.\n"
"✔ Writing 'pyproject.toml'.\n"
"✔ Adding deptry config to 'pyproject.toml'.\n"
Expand All @@ -44,7 +44,7 @@ def test_none_backend_no_pyproject_toml(self, tmp_path: Path):
"✔ Selecting Ruff rules 'A', 'C4', 'E4', 'E7', 'E9', 'F', 'FLY', 'FURB', 'I', 'PLE', 'PLR', 'RUF', 'SIM', 'UP' in 'pyproject.toml'.\n"
"✔ Ignoring Ruff rules 'PLR2004', 'SIM108' in 'pyproject.toml'.\n"
"☐ Run 'ruff check --fix' to run the Ruff linter with autofixes.\n"
).replace("\n", "")
)

def test_none_backend_pyproject_toml(self, tmp_path: Path):
# Arrange
Expand All @@ -58,7 +58,7 @@ def test_none_backend_pyproject_toml(self, tmp_path: Path):
# Assert
assert result.exit_code == 0, result.output
assert (tmp_path / "pyproject.toml").exists()
assert result.output.replace("\n", "") == (
assert result.output == (
"☐ Add the dev dependency 'deptry'.\n"
"✔ Adding deptry config to 'pyproject.toml'.\n"
"☐ Run 'deptry .' to run deptry.\n"
Expand All @@ -67,4 +67,4 @@ def test_none_backend_pyproject_toml(self, tmp_path: Path):
"✔ Selecting Ruff rules 'A', 'C4', 'E4', 'E7', 'E9', 'F', 'FLY', 'FURB', 'I', 'PLE', 'PLR', 'RUF', 'SIM', 'UP' in 'pyproject.toml'.\n"
"✔ Ignoring Ruff rules 'PLR2004', 'SIM108' in 'pyproject.toml'.\n"
"☐ Run 'ruff check --fix' to run the Ruff linter with autofixes.\n"
).replace("\n", "")
)
8 changes: 4 additions & 4 deletions tests/usethis/_ui/interface/test_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def test_none_backend_no_pyproject_toml(self, tmp_path: Path):
# Assert
assert result.exit_code == 0, result.output
assert not (tmp_path / "pyproject.toml").exists()
assert result.output.replace("\n", "") == (
assert result.output == (
"☐ Add the dev dependency 'ruff'.\n"
"✔ Writing 'ruff.toml'.\n"
"✔ Adding Ruff config to 'ruff.toml'.\n"
Expand All @@ -104,7 +104,7 @@ def test_none_backend_no_pyproject_toml(self, tmp_path: Path):
"☐ Run 'ruff check --fix' to run the Ruff linter with autofixes.\n"
"☐ Run 'ruff format' to run the Ruff formatter.\n"
"✔ Selecting Ruff rule 'FAKE' in 'ruff.toml'.\n"
).replace("\n", "")
)

def test_none_backend_pyproject_toml(self, tmp_path: Path):
# Arrange
Expand All @@ -117,12 +117,12 @@ def test_none_backend_pyproject_toml(self, tmp_path: Path):

# Assert
assert result.exit_code == 0, result.output
assert result.output.replace("\n", "") == (
assert result.output == (
"☐ Add the dev dependency 'ruff'.\n"
"✔ Adding Ruff config to 'pyproject.toml'.\n"
"✔ Selecting Ruff rules 'A', 'C4', 'E4', 'E7', 'E9', 'F', 'FLY', 'FURB', 'I', 'PLE', 'PLR', 'RUF', 'SIM', 'UP' in 'pyproject.toml'.\n"
"✔ Ignoring Ruff rules 'PLR2004', 'SIM108' in 'pyproject.toml'.\n"
"☐ Run 'ruff check --fix' to run the Ruff linter with autofixes.\n"
"☐ Run 'ruff format' to run the Ruff formatter.\n"
"✔ Selecting Ruff rule 'FAKE' in 'pyproject.toml'.\n"
).replace("\n", "")
)
57 changes: 57 additions & 0 deletions tests/usethis/test_console.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,17 @@ def test_out(self, capfd: pytest.CaptureFixture[str]) -> None:
out, _ = capfd.readouterr()
assert out == "✔ Hello\n"

def test_no_line_wrapping(self, capfd: pytest.CaptureFixture[str]) -> None:
# Arrange
long_msg = "A" * 200

# Act
tick_print(long_msg)

# Assert
out, _ = capfd.readouterr()
assert out == f"✔ {long_msg}\n"

def test_alert_only_suppresses(self, capfd: pytest.CaptureFixture[str]) -> None:
# Act
with usethis_config.set(alert_only=True):
Expand Down Expand Up @@ -98,6 +109,17 @@ def test_out(self, capfd: pytest.CaptureFixture[str]) -> None:
out, _ = capfd.readouterr()
assert out == "☐ Hello\n"

def test_no_line_wrapping(self, capfd: pytest.CaptureFixture[str]) -> None:
# Arrange
long_msg = "A" * 200

# Act
instruct_print(long_msg)

# Assert
out, _ = capfd.readouterr()
assert out == f"☐ {long_msg}\n"

def test_instruct_only_does_not_suppress(
self, capfd: pytest.CaptureFixture[str]
) -> None:
Expand Down Expand Up @@ -138,6 +160,17 @@ def test_out(self, capfd: pytest.CaptureFixture[str]) -> None:
out, _ = capfd.readouterr()
assert out == "ℹ Hello\n" # noqa: RUF001

def test_no_line_wrapping(self, capfd: pytest.CaptureFixture[str]) -> None:
# Arrange
long_msg = "A" * 200

# Act
info_print(long_msg)

# Assert
out, _ = capfd.readouterr()
assert out == f"ℹ {long_msg}\n" # noqa: RUF001


class TestErrPrint:
def test_out(self, capfd: pytest.CaptureFixture[str]) -> None:
Expand All @@ -149,6 +182,18 @@ def test_out(self, capfd: pytest.CaptureFixture[str]) -> None:
assert not out
assert err == "✗ Hello\n"

def test_no_line_wrapping(self, capfd: pytest.CaptureFixture[str]) -> None:
# Arrange
long_msg = "A" * 200

# Act
err_print(long_msg)

# Assert
out, err = capfd.readouterr()
assert not out
assert err == f"✗ {long_msg}\n"

def test_alert_only_doesnt_suppress(
self, capfd: pytest.CaptureFixture[str]
) -> None:
Expand All @@ -172,6 +217,18 @@ def test_out(self, capfd: pytest.CaptureFixture[str]) -> None:
assert not err
assert out == "⚠ Hello\n"

def test_no_line_wrapping(self, capfd: pytest.CaptureFixture[str]) -> None:
# Arrange
long_msg = "A" * 200

# Act
warn_print(long_msg)

# Assert
out, err = capfd.readouterr()
assert not err
assert out == f"⚠ {long_msg}\n"

def test_alert_only_doesnt_suppress(
self, capfd: pytest.CaptureFixture[str]
) -> None:
Expand Down
Loading