mcpField
B
building-python-clis

building-python-clis

github.com/wdm0006Development109184 installsUpdated 2d ago
Repository

About building-python-clis

A development skill for building and testing Python command-line tools. It covers framework choices, entry points, common CLI patterns, shell completion, and testing with Click's CliRunner.

Key features

  1. 01Click, Typer, and argparse guidance
  2. 02pyproject.toml entry point setup
  3. 03File input and output patterns
  4. 04Progress bar usage
  5. 05CliRunner testing and shell completion

Use cases

  1. 01Choosing a Python CLI framework
  2. 02Adding a CLI entry point to a package
  3. 03Testing commands and their output
  4. 04Generating shell completion scripts

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/wdm0006/python-skills --skill building-python-clis

After installing, type/building-python-clisin Claude Code to confirm it loaded.

Bundled filesCLICK_PATTERNS.mdTYPER_GUIDE.md

Details

From the project README

Python CLI Development

Framework Selection

Click (Recommended): Mature, extensive features Typer: Modern, type-hint focused argparse: Zero dependencies, standard library

Click Quick Start

import click

@click.group()
@click.version_option(version='1.0.0')
def cli():
    """My CLI tool."""
    pass

@cli.command()
@click.argument('input_file', type=click.Path(exists=True))
@click.option('--output', '-o', default='-', help='Output file')
@click.option('--verbose', '-v', is_flag=True)
def process(input_file, output, verbose):
    """Process an input file."""
    if verbose:
        click.echo(f"Processing {input_file}")
    # ...

if __name__ == '__main__':
    cli()

Entry Point (pyproject.toml)

[project.scripts]
mycli = "my_package.cli:cli"

[project.optional-dependencies]
cli = ["click>=8.0"]

Common Patterns

# File I/O with stdin/stdout support
@click.argument('input', type=click.File('r'), default='-')
@click.argument('output', type=click.File('w'), default='-')

# Progress bar
with click.progressbar(items, label='Processing') as bar:
    for item in bar:
        process(item)

# Colored output
click.secho("Success!", fg='green', bold=True)
click.secho("Error!", fg='red', err=True)

# Error handling
if not valid:
    raise click.BadParameter(f'Invalid value: {value}')

Testing with CliRunner

from click.testing import CliRunner
from mypackage.cli import cli

def test_process():
    runner = CliRunner()
    result = runner.invoke(cli, ['process', 'input.txt'])
    assert result.exit_code == 0
    assert 'expected output' in result.output

def test_stdin():
    runner = CliRunner()
    result = runner.invoke(cli, ['process', '-'], input='test data\n')
    assert result.exit_code == 0

Shell Completion

# Generate completion scripts
_MYCLI_COMPLETE=bash_source mycli > ~/.mycli-complete.bash
_MYCLI_COMPLETE=zsh_source mycli > ~/.mycli-complete.zsh

For detailed patterns, see:

CLI Checklist

Setup:
- [ ] Entry point in pyproject.toml
- [ ] --help works for all commands
- [ ] --version displays version

UX:
- [ ] Errors go to stderr with non-zero exit
- [ ] Helpful error messages
- [ ] stdin/stdout support where appropriate

Testing:
- [ ] Tests for all commands
- [ ] Test error cases
- [ ] Test stdin processing

Learn More

This skill is based on the Guide to Developing High-Quality Python Libraries by Will McGinnis. See these posts for related coverage:

Comments

Markdown supported