Skip to content

Commit

Permalink
Feature/24 support a quiet flag to silence output (#86)
Browse files Browse the repository at this point in the history
* Add convenience functions for console printing

* Implement quiet flag
Enable "PT"
Bump ruff

* Pass tests, fix bug with removing bitbucket ci
  • Loading branch information
nathanjmcdougall authored Oct 30, 2024
1 parent 49dcb5e commit cbba529
Show file tree
Hide file tree
Showing 18 changed files with 276 additions and 153 deletions.
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ dev = [
"import-linter>=2.1",
"pre-commit>=4.0.1",
"pyproject-fmt>=2.4.3",
"ruff>=0.7.0",
"ruff>=0.7.1",
]
test = [
"coverage[toml]>=7.6.3",
Expand All @@ -72,7 +72,7 @@ source = "vcs"
line-length = 88

src = [ "src" ]
lint.select = [ "C4", "E4", "E7", "E9", "F", "FURB", "I", "PLE", "PLR", "RUF", "SIM", "UP" ]
lint.select = ["C4", "E4", "E7", "E9", "F", "FURB", "I", "PLE", "PLR", "RUF", "SIM", "UP", "PT"]

[tool.pytest.ini_options]
testpaths = [ "tests" ]
Expand All @@ -98,6 +98,7 @@ layers = [
"_interface",
"_tool",
"_integrations",
"_console",
"_utils",
]
containers = [ "usethis" ]
Expand Down
29 changes: 26 additions & 3 deletions src/usethis/_console.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,29 @@
import typer
from collections.abc import Generator
from contextlib import contextmanager

from pydantic import BaseModel
from rich.console import Console

console = Console()
typer_console = Console()


class UsethisConsole(BaseModel):
quiet: bool

def tick_print(self, msg: str) -> None:
if not self.quiet:
typer_console.print(f"✔ {msg}", style="green")

def box_print(self, msg: str) -> None:
if not self.quiet:
typer_console.print(f"☐ {msg}", style="blue")

@contextmanager
def set(self, *, quiet: bool) -> Generator[None, None, None]:
"""Temporarily set the console to quiet mode."""
self.quiet = quiet
yield
self.quiet = False


offline_opt = typer.Option(False, "--offline", help="Disable network access")
console = UsethisConsole(quiet=False)
4 changes: 2 additions & 2 deletions src/usethis/_integrations/bitbucket/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def add_bitbucket_pipeline_config() -> None:
# Early exit; the file already exists
return

console.print("✔ Writing 'bitbucket-pipelines.yml'.", style="green")
console.tick_print("Writing 'bitbucket-pipelines.yml'.")
yaml_contents = _YAML_CONTENTS

(Path.cwd() / "bitbucket-pipelines.yml").write_text(yaml_contents)
Expand All @@ -38,5 +38,5 @@ def remove_bitbucket_pipeline_config() -> None:
# Early exit; the file already doesn't exist
return

console.print("✔ Removing bitbucket-pipelines.yml file", style="green")
console.tick_print("Removing 'bitbucket-pipelines.yml' file.")
(Path.cwd() / "bitbucket-pipelines.yml").unlink()
8 changes: 4 additions & 4 deletions src/usethis/_integrations/pre_commit/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def add_pre_commit_config() -> None:
# Early exit; the file already exists
return

console.print("✔ Writing '.pre-commit-config.yaml'.", style="green")
console.tick_print("Writing '.pre-commit-config.yaml'.")
try:
pkg_version = get_github_latest_tag("abravalheri", "validate-pyproject")
except GitHubTagError:
Expand All @@ -37,12 +37,12 @@ def remove_pre_commit_config() -> None:
# Early exit; the file already doesn't exist
return

console.print("✔ Removing .pre-commit-config.yaml file", style="green")
console.tick_print("Removing .pre-commit-config.yaml file.")
(Path.cwd() / ".pre-commit-config.yaml").unlink()


def install_pre_commit() -> None:
console.print("✔ Ensuring pre-commit hooks are installed.", style="green")
console.tick_print("Ensuring pre-commit hooks are installed.")
subprocess.run(
["uv", "run", "pre-commit", "install"],
check=True,
Expand All @@ -51,7 +51,7 @@ def install_pre_commit() -> None:


def uninstall_pre_commit() -> None:
console.print("✔ Ensuring pre-commit hooks are uninstalled.", style="green")
console.tick_print("Ensuring pre-commit hooks are uninstalled.")
subprocess.run(
["uv", "run", "pre-commit", "uninstall"],
check=True,
Expand Down
10 changes: 4 additions & 6 deletions src/usethis/_integrations/pytest/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ def add_pytest_dir() -> None:
tests_dir = Path.cwd() / "tests"

if not tests_dir.exists():
console.print("✔ Creating '/tests'.", style="green")
console.tick_print("Creating '/tests'.")
tests_dir.mkdir()

if (tests_dir / "conftest.py").exists():
# Early exit; conftest.py already exists
return

console.print("✔ Writing '/tests/conftest.py'.", style="green")
console.tick_print("Writing '/tests/conftest.py'.")
(tests_dir / "conftest.py").write_text(
"collect_ignore_glob = []\npytest_plugins = []\n"
)
Expand All @@ -30,10 +30,8 @@ def remove_pytest_dir() -> None:

if set(tests_dir.iterdir()) <= {tests_dir / "conftest.py"}:
# The only file in the directory is conftest.py
console.print("✔ Removing '/tests'.", style="green")
console.tick_print("Removing '/tests'.")
shutil.rmtree(tests_dir)
else:
console.print(
"☐ Reconfigure the /tests directory to run without pytest", style="blue"
)
console.box_print("Reconfigure the /tests directory to run without pytest.")
# Note we don't actually remove the directory, just explain what needs to be done.
10 changes: 2 additions & 8 deletions src/usethis/_integrations/ruff/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@ def select_ruff_rules(rules: list[str]) -> None:

rules_str = ", ".join([f"'{rule}'" for rule in rules])
s = "" if len(rules) == 1 else "s"
console.print(
f"✔ Enabling ruff rule{s} {rules_str} in 'pyproject.toml'.",
style="green",
)
console.tick_print(f"Enabling ruff rule{s} {rules_str} in 'pyproject.toml'.")

append_config_list(["tool", "ruff", "lint", "select"], rules)

Expand All @@ -33,10 +30,7 @@ def deselect_ruff_rules(rules: list[str]) -> None:

rules_str = ", ".join([f"'{rule}'" for rule in rules])
s = "" if len(rules) == 1 else "s"
console.print(
f"✔ Disabling ruff rule{s} {rules_str} in 'pyproject.toml'.",
style="green",
)
console.tick_print(f"Disabling ruff rule{s} {rules_str} in 'pyproject.toml'.")

remove_from_config_list(["tool", "ruff", "lint", "select"], rules)

Expand Down
8 changes: 2 additions & 6 deletions src/usethis/_integrations/uv/deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,7 @@ def add_deps_to_group(pypi_names: list[str], group: str, *, offline: bool) -> No
# Early exit; the tool is already a dev dependency.
continue

console.print(
f"✔ Adding '{dep}' to the '{group}' dependency group.", style="green"
)
console.tick_print(f"Adding '{dep}' to the '{group}' dependency group.")
if not offline:
subprocess.run(
["uv", "add", "--group", group, "--quiet", dep],
Expand All @@ -68,9 +66,7 @@ def remove_deps_from_group(pypi_names: list[str], group: str, *, offline: bool)
# Early exit; the tool is already not a dependency.
continue

console.print(
f"✔ Removing '{dep}' from the '{group}' dependency group.", style="green"
)
console.tick_print(f"Removing '{dep}' from the '{group}' dependency group.")
if not offline:
subprocess.run(
[
Expand Down
1 change: 1 addition & 0 deletions src/usethis/_interface/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import typer

offline_opt = typer.Option(False, "--offline", help="Disable network access")
quiet_opt = typer.Option(False, "--quiet", help="Suppress output")
12 changes: 9 additions & 3 deletions src/usethis/_interface/browse.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import typer

from usethis._console import console
from usethis._interface import offline_opt, quiet_opt

app = typer.Typer(help="Visit important project-related web pages.")

Expand All @@ -12,13 +13,18 @@ def pypi(
browser: bool = typer.Option(
False, "--browser", help="Open the URL in the default web browser."
),
offline: bool = offline_opt,
quiet: bool = quiet_opt,
) -> None:
_pypi(package=package, browser=browser)
with console.set(quiet=quiet):
_pypi(package=package, browser=browser, offline=offline)


def _pypi(*, package: str, browser: bool = False) -> None:
def _pypi(*, package: str, browser: bool = False, offline: bool = False) -> None:
_ = offline # Already no network access required

url = f"https://pypi.org/project/{package}/"
if browser:
typer.launch(url)
else:
console.print(f"Open URL <{url}>.", style="blue")
console.box_print(f"Open URL <{url}>.")
78 changes: 40 additions & 38 deletions src/usethis/_interface/ci.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from usethis._integrations.pyproject.requires_python import (
get_supported_major_python_versions,
)
from usethis._interface import offline_opt
from usethis._interface import offline_opt, quiet_opt
from usethis._tool import PreCommitTool, PytestTool

app = typer.Typer(help="Add config for Continuous Integration (CI) pipelines.")
Expand All @@ -23,62 +23,64 @@ def bitbucket(
False, "--remove", help="Remove Bitbucket pipelines CI instead of adding it."
),
offline: bool = offline_opt,
quiet: bool = quiet_opt,
) -> None:
_bitbucket(remove=remove, offline=offline)
with console.set(quiet=quiet):
_bitbucket(remove=remove, offline=offline)


def _bitbucket(*, remove: bool = False, offline: bool = False) -> None:
_ = offline # Already offline

config_yaml_path = Path.cwd() / "bitbucket-pipelines.yml"

if config_yaml_path.exists():
if remove:
remove_bitbucket_pipeline_config()
else:
if not remove:
if (Path.cwd() / "bitbucket-pipelines.yml").exists():
# Early exit; the file already exists so we will leave it alone.
return

add_bitbucket_pipeline_config()
add_bitbucket_pipeline_config()

steps = []
if PreCommitTool().is_used():
steps.append(
Step(
name="Run pre-commit hooks",
caches=["uv", "pre-commit"],
script=[
StepRef(name="install-uv"),
"uv run pre-commit run --all-files",
],
)
)
if PytestTool().is_used():
matrix = get_supported_major_python_versions()
for version in matrix:
steps = []
if PreCommitTool().is_used():
steps.append(
Step(
name=f"Run tests with Python 3.{version}",
caches=["uv"],
name="Run pre-commit hooks",
caches=["uv", "pre-commit"],
script=[
StepRef(name="install-uv"),
f"uv run --python 3.{version} pytest",
"uv run pre-commit run --all-files",
],
)
)
if PytestTool().is_used():
matrix = get_supported_major_python_versions()
for version in matrix:
steps.append(
Step(
name=f"Run tests with Python 3.{version}",
caches=["uv"],
script=[
StepRef(name="install-uv"),
f"uv run --python 3.{version} pytest",
],
)
)

if not steps:
# Add a dummy step
steps.append(
Step(
name="Placeholder - add your own steps!",
caches=[],
script=[StepRef(name="install-uv"), "echo 'Hello, world!'"],
if not steps:
# Add a dummy step
steps.append(
Step(
name="Placeholder - add your own steps!",
caches=[],
script=[StepRef(name="install-uv"), "echo 'Hello, world!'"],
)
)
)

console.print("☐ Populate the placeholder step in 'bitbucket-pipelines.yml'.")
console.box_print(
"Populate the placeholder step in 'bitbucket-pipelines.yml'."
)

add_steps(steps, is_parallel=True)
add_steps(steps, is_parallel=True)

console.print("☐ Run your first pipeline on the Bitbucket website.")
console.box_print("Run your first pipeline on the Bitbucket website.")
else:
remove_bitbucket_pipeline_config()
Loading

0 comments on commit cbba529

Please sign in to comment.