Skip to content

API Reference

This page is auto-generated from Python docstrings.

pup_clean

pup-clean package.

__main__

Run as a module.

base

Base package.

types

Typed records.

CleanupTarget dataclass

A known disposable repository target.

Source code in src/pup_clean/base/types.py
19
20
21
22
23
24
@dataclass(frozen=True)
class CleanupTarget:
    """A known disposable repository target."""

    path: Path
    kind: CleanupKind

cli

Command-line interface.

This module parses arguments and dispatches repository cleanup behavior.

Commands: uv run pup-clean uv run pup-clean --delete

Equivalent uvx usage after release: uvx pup-clean uvx pup-clean@latest uvx pup-clean --delete

build_parser

build_parser() -> argparse.ArgumentParser

Build the argument parser.

Source code in src/pup_clean/cli.py
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
def build_parser() -> argparse.ArgumentParser:
    """Build the argument parser."""
    parser = argparse.ArgumentParser(
        prog="pup-clean",
        description=(
            "Identify and remove known generated and disposable repository artifacts."
        ),
    )

    parser.add_argument(
        "--root",
        type=Path,
        default=None,
        help=(
            "Repository root to clean. Defaults to the nearest parent "
            "directory containing .git, or the current directory."
        ),
    )

    parser.add_argument(
        "--delete",
        action="store_true",
        help=(
            "Delete detected cleanup targets. Without this flag, "
            "pup-clean performs a dry run only."
        ),
    )

    parser.add_argument(
        "paths",
        nargs="*",
        type=Path,
        help=(
            "Optional repository-relative cleanup targets. "
            "When provided, operate only on detected targets matching these paths."
        ),
    )

    return parser

main

main(argv: Sequence[str] | None = None) -> int

Run the command-line interface.

Parameters:

Name Type Description Default
argv Sequence[str] | None

Optional command-line arguments. If None, uses sys.argv.

None

Returns:

Type Description
int

Exit code from the clean command.

Source code in src/pup_clean/cli.py
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
def main(argv: Sequence[str] | None = None) -> int:
    """Run the command-line interface.

    Args:
        argv: Optional command-line arguments. If None, uses sys.argv.

    Returns:
        Exit code from the clean command.
    """
    parser = build_parser()
    args = parser.parse_args(argv)

    return clean.run(
        root=args.root,
        delete=args.delete,
        paths=tuple(args.paths),
    )

commands

Command modules.

Each command module exposes a stable run(...) -> int entry point.

The CLI parser lives in pup_clean.cli. Behavior lives here.

clean

Clean known disposable and source-generated repository artifacts.

run
run(
    *,
    root: Path | None = None,
    delete: bool = False,
    paths: tuple[Path, ...] = (),
) -> int

Preview or remove known disposable and generated repository artifacts.

Parameters:

Name Type Description Default
root Path | None

Repository root. If None, detect the current repository root.

None
delete bool

Whether to delete detected cleanup targets. False means dry-run only.

False
paths tuple[Path, ...]

Optional repository-relative cleanup targets. When provided, operate only on detected targets matching these paths.

()

Returns:

Type Description
int

Process exit code.

Source code in src/pup_clean/commands/clean.py
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
def run(
    *,
    root: Path | None = None,
    delete: bool = False,
    paths: tuple[Path, ...] = (),
) -> int:
    """Preview or remove known disposable and generated repository artifacts.

    Args:
        root: Repository root. If None, detect the current repository root.
        delete: Whether to delete detected cleanup targets.
            False means dry-run only.
        paths: Optional repository-relative cleanup targets.
            When provided, operate only on detected targets matching these paths.

    Returns:
        Process exit code.
    """
    repository = detect_repository(root)
    targets = _detect_cleanup_targets(repository.root)

    if paths:
        selected_paths = set(paths)
        targets = tuple(target for target in targets if target.path in selected_paths)

    if delete:
        _delete_cleanup_targets(repository.root, targets)

    print_cleanup_plan(
        repository,
        targets,
        delete=delete,
    )

    return 0

delete_terminal

Terminal reporting.

print_cleanup_plan

print_cleanup_plan(
    context: RepositoryContext,
    targets: Sequence[CleanupTarget],
    *,
    delete: bool,
) -> None

Print the repository cleanup plan.

Parameters:

Name Type Description Default
context RepositoryContext

The repository context.

required
targets Sequence[CleanupTarget]

The list of cleanup targets.

required
delete bool

Whether to actually delete the targets or just perform a dry run.

required

Returns:

Type Description
None

None

Source code in src/pup_clean/delete_terminal.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
def print_cleanup_plan(
    context: RepositoryContext,
    targets: Sequence[CleanupTarget],
    *,
    delete: bool,
) -> None:
    """Print the repository cleanup plan.

    Args:
        context (RepositoryContext): The repository context.
        targets (Sequence[CleanupTarget]): The list of cleanup targets.
        delete (bool): Whether to actually delete the targets or just perform a dry run.

    Returns:
        None
    """
    mode = "DELETE" if delete else "DRY RUN"

    print(f"[pup-clean] {mode}")
    print(f"[pup-clean] repo: {context.repo_name}")
    print(f"[pup-clean] root: {context.root}")
    print()

    if not targets:
        print("[pup-clean] no cleanup targets found")
        return

    label = "DELETED" if delete else "WOULD DELETE"

    for target in targets:
        suffix = "/" if target.kind == "directory" else ""
        print(f"{label:13} {target.path.as_posix()}{suffix}")

    print()
    print(f"[pup-clean] summary: {len(targets)} cleanup target(s)")

    if not delete:
        print("[pup-clean] nothing deleted; rerun with --delete to apply cleanup")

inspect_generated

Discover generated artifacts by inspecting Python source code.

This module statically inspects Python source files under src/ and finds paths passed to known data-writing, database-creation, and image-writing operations.

The purpose is to identify artifacts created by running a project so that pup-clean can remove generated outputs.

Only statically resolvable generated file paths are returned. Containing directories are not cleanup targets.

discover_generated_paths

discover_generated_paths(root: Path) -> tuple[Path, ...]

Discover generated data artifacts referenced by project source code.

Python files under src/ are parsed using the AST. Known data-writing operations are inspected and statically resolvable output paths are returned.

A path is returned only when it can be resolved from source code with sufficient confidence. Dynamic or ambiguous paths are ignored.

Parameters:

Name Type Description Default
root Path

Repository root.

required

Returns:

Type Description
tuple[Path, ...]

Repository-relative paths for discovered generated artifacts.

Source code in src/pup_clean/inspect_generated.py
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
def discover_generated_paths(root: Path) -> tuple[Path, ...]:
    """Discover generated data artifacts referenced by project source code.

    Python files under ``src/`` are parsed using the AST. Known data-writing
    operations are inspected and statically resolvable output paths are
    returned.

    A path is returned only when it can be resolved from source code with
    sufficient confidence. Dynamic or ambiguous paths are ignored.

    Args:
        root: Repository root.

    Returns:
        Repository-relative paths for discovered generated artifacts.
    """
    src_root = root / "src"

    if not src_root.is_dir():
        return ()

    discovered: set[Path] = set()

    for source_path in sorted(src_root.rglob("*.py")):
        discovered.update(
            _discover_generated_paths_in_file(
                root=root,
                source_path=source_path,
            )
        )

    return tuple(sorted(discovered, key=lambda path: path.as_posix()))