Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/12 implement usethis tool deptry #13

Merged
merged 9 commits into from
Sep 12, 2024
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ on:
jobs:
tests:
runs-on: ${{ matrix.os }}
env:
PYTHONIOENCODING: utf-8
steps:
- name: Checkout code
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
Expand Down
11 changes: 10 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = []
dependencies = [
"rich>=13.8.1",
"typer>=0.12.5",
]

[project.scripts]
usethis = "usethis.__main__:app"

[build-system]
requires = ["hatchling"]
Expand All @@ -15,4 +21,7 @@ dev-dependencies = [
"pytest>=8.3.2",
"pytest-md>=0.2.0",
"pytest-emoji>=0.2.0",
"pydantic>=2.9.1",
"tomlkit>=0.13.2",
"deptry>=0.20.0",
]
5 changes: 3 additions & 2 deletions src/usethis/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
def hello() -> str:
return "Hello from usethis!"
from rich.console import Console

console = Console()
12 changes: 12 additions & 0 deletions src/usethis/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import typer

import usethis.tool

app = typer.Typer(
help=(
"🤖 Automate Python package and project setup tasks that are otherwise "
"performed manually."
)
)
app.add_typer(usethis.tool.app, name="tool")
app(prog_name="usethis")
15 changes: 15 additions & 0 deletions src/usethis/tool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import subprocess

import typer

from usethis import console

app = typer.Typer(help="Add and configure development tools, e.g. linters")


@app.command(
help="Use the deptry linter: avoid missing or superfluous dependency declarations."
)
def deptry() -> None:
console.print("✔ Ensuring deptry is a development dependency", style="green")
subprocess.run(["uv", "add", "--dev", "--quiet", "deptry"], check=True)
2 changes: 0 additions & 2 deletions tests/test_nothing.py

This file was deleted.

82 changes: 82 additions & 0 deletions tests/usethis/test_tool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import os
import subprocess
from contextlib import contextmanager
from pathlib import Path
from typing import Generator

import pytest
import tomlkit
from pydantic import TypeAdapter

from usethis.tool import deptry


@contextmanager
def change_cwd(new_dir: Path) -> Generator[None, None, None]:
"""Change the working directory temporarily."""
old_dir = Path.cwd()
os.chdir(new_dir)
try:
yield
finally:
os.chdir(old_dir)


@pytest.fixture
def uv_init_dir(tmp_path: Path) -> None:
subprocess.run(["uv", "init"], cwd=tmp_path, check=True)
return tmp_path


class TestDeptry:
def test_dependency_added(self, uv_init_dir: Path):
# Act
with change_cwd(uv_init_dir):
deptry()

# Assert
(dev_dep,) = _get_dev_deps(uv_init_dir)
assert dev_dep.startswith("deptry>=")

def test_stdout(self, uv_init_dir: Path, capfd: pytest.CaptureFixture[str]):
# Act
with change_cwd(uv_init_dir):
deptry()

# Assert
out, _ = capfd.readouterr()
assert out == "✔ Ensuring deptry is a development dependency\n"

def test_run_deptry_fail(self, uv_init_dir: Path):
# Arrange
f = uv_init_dir / "bad.py"
f.write_text("import broken_dependency")

# Act
with change_cwd(uv_init_dir):
deptry()

# Assert
with pytest.raises(subprocess.CalledProcessError):
subprocess.run(["deptry", "."], cwd=uv_init_dir, check=True)

def test_run_deptry_pass(self, uv_init_dir: Path):
# Arrange
f = uv_init_dir / "good.py"
f.write_text("import sys")

# Act
with change_cwd(uv_init_dir):
deptry()

# Assert
subprocess.run(["deptry", "."], cwd=uv_init_dir, check=True)

def test_cli(self, uv_init_dir: Path):
subprocess.run(["usethis", "tool", "deptry"], cwd=uv_init_dir, check=True)


def _get_dev_deps(proj_dir: Path) -> list[str]:
pyproject = tomlkit.parse((proj_dir / "pyproject.toml").read_text())
dev_deps = pyproject["tool"]["uv"]["dev-dependencies"]
return TypeAdapter(list[str]).validate_python(dev_deps)
198 changes: 193 additions & 5 deletions uv.lock

Large diffs are not rendered by default.