Claude Code for docopt: CLI from Docstring in Python — Claude Skills 360 Blog
Blog / AI / Claude Code for docopt: CLI from Docstring in Python
AI

Claude Code for docopt: CLI from Docstring in Python

Published: March 31, 2028
Read time: 5 min read
By: Claude Skills 360

docopt parses command-line arguments from a usage string in a docstring — no argument parser code needed. pip install docopt-ng. Basic: from docopt import docopt; args = docopt(__doc__) where __doc__ contains Usage: prog <name>. Options: --verbose Verbose output. --port=N Port [default: 8000]. Flags: --help --version. Commands: Usage: prog (start|stop|status). Arguments: <file> positional; [<path>] optional. Repeating: <file>... or FILE .... Multi-cmd: Usage: prog command <arg> matched by args["command"]. Result dict: keys are <arg>, --option, ARGUMENT, command booleans. args["<name>"] → string. args["--port"] → “8000”. args["--verbose"] → True/False. args["start"] → True if start was the command. docopt(doc, version="1.0") handles --version. docopt(doc, argv=["start","--verbose"]). Validation: combine with schema library for type coercion. Multiple occurrences: --verbose... count with args["--verbose"] → int. Optional group: [--host=HOST --port=PORT]. Required group: (--user=USER --pass=PASS). Naval_fate canonical example: commands and args in one docstring. Claude Code generates docopt CLIs, usage-driven tools, and multi-command dispatch scripts.

CLAUDE.md for docopt

## docopt Stack
- Version: docopt-ng >= 0.9 | pip install docopt-ng
- Usage: from docopt import docopt; args = docopt(__doc__) in main()
- Syntax: <arg> positional; [<opt>] optional; --flag boolean; --opt=VAL with value
- Default: --port=N  Port number [default: 8000] — bracket in option description
- Commands: Usage: prog (start|stop|status) — args["start"] is True/False
- Repeating: <file>...  → list; --verbose... → count (int)
- Validate: use schema library for type coercion after docopt parses

docopt CLI Pipeline

# app/cli.py — docopt usage-string driven CLI with commands, options, and validation
"""
Usage:
  cli.py server start [--host=HOST] [--port=PORT] [--workers=N] [--debug]
  cli.py server stop  [--force]
  cli.py server status
  cli.py db migrate   [--target=TARGET]
  cli.py db seed      [--env=ENV]
  cli.py db backup    <output>
  cli.py deploy       <version> [--env=ENV] [--dry-run] [--services=SVCS]
  cli.py config show  [--env=ENV]
  cli.py config set   <key> <value>  [--env=ENV]
  cli.py (-h | --help)
  cli.py --version

Options:
  -h --help          Show this help screen.
  --version          Show version.
  --host=HOST        Bind host [default: 0.0.0.0].
  --port=PORT        Bind port [default: 8000].
  --workers=N        Worker count [default: 4].
  --debug            Enable debug mode.
  --force            Skip confirmation prompts.
  --target=TARGET    Migration target revision [default: head].
  --env=ENV          Target environment [default: development].
  --dry-run          Print actions without executing.
  --services=SVCS    Comma-separated service list [default: all].
"""
from __future__ import annotations

import json
import sys
from pathlib import Path
from typing import Any

from docopt import docopt


# ─────────────────────────────────────────────────────────────────────────────
# 1. Argument helpers
# ─────────────────────────────────────────────────────────────────────────────

def _int(args: dict, key: str) -> int:
    """Coerce a docopt string value to int."""
    v = args.get(key)
    if v is None:
        raise ValueError(f"Missing required option {key}")
    try:
        return int(v)
    except (ValueError, TypeError):
        raise ValueError(f"{key} must be an integer, got {v!r}")


def _bool(args: dict, key: str) -> bool:
    return bool(args.get(key))


def _str(args: dict, key: str, default: str = "") -> str:
    return args.get(key) or default


def _list(args: dict, key: str, sep: str = ",") -> list[str]:
    raw = _str(args, key)
    if raw == "all" or not raw:
        return []
    return [s.strip() for s in raw.split(sep) if s.strip()]


# ─────────────────────────────────────────────────────────────────────────────
# 2. Command handlers
# ─────────────────────────────────────────────────────────────────────────────

def cmd_server_start(args: dict) -> None:
    """Start the development server."""
    host     = _str(args, "--host", "0.0.0.0")
    port     = _int(args, "--port")
    workers  = _int(args, "--workers")
    debug    = _bool(args, "--debug")

    print(f"  Starting server: {host}:{port} (workers={workers}, debug={debug})")
    # In real code: uvicorn.run("app:app", host=host, port=port, ...)


def cmd_server_stop(args: dict) -> None:
    """Stop the server process."""
    force = _bool(args, "--force")
    if not force:
        confirm = input("  Stop server? [y/N] ").strip().lower()
        if confirm != "y":
            print("  Aborted")
            return
    print("  Server stopped")


def cmd_server_status(_: dict) -> None:
    """Show server process status."""
    print("  Server: running (pid=12345)")
    print("  Uptime: 2h 15m")


def cmd_db_migrate(args: dict) -> None:
    """Run database migrations."""
    target = _str(args, "--target", "head")
    print(f"  Running migrations → {target}")


def cmd_db_seed(args: dict) -> None:
    """Seed the database."""
    env = _str(args, "--env", "development")
    print(f"  Seeding {env} database")


def cmd_db_backup(args: dict) -> None:
    """Back up the database to a file."""
    output = _str(args, "<output>")
    print(f"  Backing up to {output}")


def cmd_deploy(args: dict) -> None:
    """Deploy a version to an environment."""
    version  = _str(args, "<version>")
    env      = _str(args, "--env", "development")
    dry_run  = _bool(args, "--dry-run")
    services = _list(args, "--services")

    svc_str = ", ".join(services) if services else "all"
    msg = f"  Deploy {version}{env} (services: {svc_str})"
    if dry_run:
        print(f"  [dry-run] {msg}")
    else:
        print(msg)


def cmd_config_show(args: dict) -> None:
    """Show configuration for an environment."""
    env  = _str(args, "--env", "development")
    conf = {"env": env, "debug": env == "development", "log_level": "INFO"}
    print(json.dumps(conf, indent=2))


def cmd_config_set(args: dict) -> None:
    """Set a configuration key."""
    key   = _str(args, "<key>")
    value = _str(args, "<value>")
    env   = _str(args, "--env", "development")
    print(f"  [{env}] {key} = {value!r}")


# ─────────────────────────────────────────────────────────────────────────────
# 3. Dispatcher
# ─────────────────────────────────────────────────────────────────────────────

def dispatch(args: dict) -> None:
    """Route to the correct handler based on docopt command booleans."""
    if args["server"] and args["start"]:
        cmd_server_start(args)
    elif args["server"] and args["stop"]:
        cmd_server_stop(args)
    elif args["server"] and args["status"]:
        cmd_server_status(args)
    elif args["db"] and args["migrate"]:
        cmd_db_migrate(args)
    elif args["db"] and args["seed"]:
        cmd_db_seed(args)
    elif args["db"] and args["backup"]:
        cmd_db_backup(args)
    elif args["deploy"]:
        cmd_deploy(args)
    elif args["config"] and args["show"]:
        cmd_config_show(args)
    elif args["config"] and args["set"]:
        cmd_config_set(args)
    else:
        print(__doc__)


# ─────────────────────────────────────────────────────────────────────────────
# 4. Schema-based validation helper
# ─────────────────────────────────────────────────────────────────────────────

def validate_args(args: dict) -> dict:
    """
    Optional schema validation after docopt parsing.
    Requires: pip install schema
    """
    try:
        from schema import And, Optional, Or, Schema, Use
        schema = Schema({
            "server":          bool,
            "db":              bool,
            "deploy":          bool,
            "config":          bool,
            "start":           bool,
            "stop":            bool,
            "status":          bool,
            "migrate":         bool,
            "seed":            bool,
            "backup":          bool,
            "show":            bool,
            "set":             bool,
            "--host":          str,
            "--port":          And(Use(int), lambda n: 1 <= n <= 65535),
            "--workers":       And(Use(int), lambda n: n >= 1),
            "--debug":         bool,
            "--force":         bool,
            "--target":        str,
            "--env":           And(str, lambda e: e in ("development", "staging", "production")),
            "--dry-run":       bool,
            "--services":      Or(None, str),
            Optional("<output>"): Or(None, str),
            Optional("<version>"): Or(None, str),
            Optional("<key>"):    Or(None, str),
            Optional("<value>"):  Or(None, str),
            "--help":          bool,
            "--version":       bool,
        })
        return schema.validate(args)
    except ImportError:
        return args


# ─────────────────────────────────────────────────────────────────────────────
# 5. Simple standalone docopt example (single-command tool)
# ─────────────────────────────────────────────────────────────────────────────

SIMPLE_DOC = """
Usage:
  tool.py [options] <files>...

Options:
  -o FILE --output=FILE  Output file [default: stdout].
  -n N    --lines=N      Number of lines [default: 10].
  -v      --verbose      Verbose output.
  -h      --help         Show help.
"""


def run_simple_tool(argv: list[str] | None = None) -> None:
    """
    Standalone example to show how simple tools look.
    Parses <files>... as a list.
    """
    args = docopt(SIMPLE_DOC, argv=argv)
    files     = args["<files>"]
    output    = args["--output"]
    lines     = int(args["--lines"])
    verbose   = args["--verbose"]

    if verbose:
        print(f"  Processing {len(files)} files, output={output}, lines={lines}")

    for f in files:
        path = Path(f)
        if not path.exists():
            print(f"  Warning: {f} not found", file=sys.stderr)
            continue
        content = path.read_text(errors="replace").splitlines()[:lines]
        if output and output != "stdout":
            Path(output).write_text("\n".join(content))
        else:
            for line in content:
                print(line)


# ─────────────────────────────────────────────────────────────────────────────
# Entry point
# ─────────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    args = docopt(__doc__, version="1.0.0")
    try:
        args = validate_args(args)
    except Exception as exc:
        print(f"Argument error: {exc}", file=sys.stderr)
        sys.exit(1)
    dispatch(args)

For the argparse alternative — argparse requires one add_argument() call per parameter with explicit type and help strings scattered across the code; docopt puts the entire CLI contract in one readable usage string that doubles as documentation — the interface is visible at the top of the file without hunting through parser setup code. For the typer / click alternative — click and Typer are better choices for large CLIs where you need sub-command groups, callbacks, error formatting, and plugin architectures; docopt is better for small to medium tools where the usage pattern is clear and you want the CLI defined as a readable string rather than decorator-annotated functions. The Claude Skills 360 bundle includes docopt skill sets covering docopt() from doc usage string, Options section with defaults, positional <arg> and optional [<arg>], repeating <arg>... lists, command booleans (start/stop/status), —flag booleans and —opt=VAL strings, dispatch() routing to handler functions, _int/_bool/_list coercion helpers, schema-based validation, multi-level commands (server start/stop, db migrate/seed), and run_simple_tool() standalone example. Start with the free tier to try docstring CLI code generation.

Keep Reading

AI

Claude Code for email.contentmanager: Python Email Content Accessors

Read and write EmailMessage body content with Python's email.contentmanager module and Claude Code — email contentmanager ContentManager for the class that maps content types to get and set handler functions allowing EmailMessage to support get_content and set_content with type-specific behaviour, email contentmanager raw_data_manager for the ContentManager instance that handles raw bytes and str payloads without any conversion, email contentmanager content_manager for the standard ContentManager instance used by email.policy.default that intelligently handles text plain text html multipart and binary content types, email contentmanager get_content_text for the handler that returns the decoded text payload of a text-star message part as a str, email contentmanager get_content_binary for the handler that returns the raw decoded bytes payload of a non-text message part, email contentmanager get_data_manager for the get-handler lookup used by EmailMessage get_content to find the right reader function for the content type, email contentmanager set_content text for the handler that creates and sets a text part correctly choosing charset and transfer encoding, email contentmanager set_content bytes for the handler that creates and sets a binary part with base64 encoding and optional filename Content-Disposition, email contentmanager EmailMessage get_content for the method that reads the message body using the registered content manager handlers, email contentmanager EmailMessage set_content for the method that sets the message body and MIME headers in one call, email contentmanager EmailMessage make_alternative make_mixed make_related for the methods that convert a simple message into a multipart container, email contentmanager EmailMessage add_attachment for the method that attaches a file or bytes to a multipart message, and email contentmanager integration with email.message and email.policy and email.mime and io for building high-level email readers attachment extractors text body accessors HTML readers and policy-aware MIME construction pipelines.

5 min read Feb 12, 2029
AI

Claude Code for email.charset: Python Email Charset Encoding

Control header and body encoding for international email with Python's email.charset module and Claude Code — email charset Charset for the class that wraps a character set name with the encoding rules for header encoding and body encoding describing how to encode text for that charset in email messages, email charset Charset header_encoding for the attribute specifying whether headers using this charset should use QP quoted-printable encoding BASE64 encoding or no encoding, email charset Charset body_encoding for the attribute specifying the Content-Transfer-Encoding to use for message bodies in this charset such as QP or BASE64, email charset Charset output_codec for the attribute giving the Python codec name used to encode the string to bytes for the wire format, email charset Charset input_codec for the attribute giving the Python codec name used to decode incoming bytes to str, email charset Charset get_output_charset for returning the output charset name, email charset Charset header_encode for encoding a header string using the charset's header_encoding method, email charset Charset body_encode for encoding body content using the charset's body_encoding, email charset Charset convert for converting a string from the input_codec to the output_codec, email charset add_charset for registering a new charset with custom encoding rules in the global charset registry, email charset add_alias for adding an alias name that maps to an existing registered charset, email charset add_codec for registering a codec name mapping for use by the charset machinery, and email charset integration with email.message and email.mime and email.policy and email.encoders for building international email senders non-ASCII header encoders Content-Transfer-Encoding selectors charset-aware message constructors and MIME encoding pipelines.

5 min read Feb 11, 2029
AI

Claude Code for email.utils: Python Email Address and Header Utilities

Parse and format RFC 2822 email addresses and dates with Python's email.utils module and Claude Code — email utils parseaddr for splitting a display-name plus angle-bracket address string into a realname and email address tuple, email utils formataddr for combining a realname and address string into a properly quoted RFC 2822 address with angle brackets, email utils getaddresses for parsing a list of raw address header strings each potentially containing multiple comma-separated addresses into a list of realname address tuples, email utils parsedate for parsing an RFC 2822 date string into a nine-tuple compatible with time.mktime, email utils parsedate_tz for parsing an RFC 2822 date string into a ten-tuple that includes the UTC offset timezone in seconds, email utils parsedate_to_datetime for parsing an RFC 2822 date string into an aware datetime object with timezone, email utils formatdate for formatting a POSIX timestamp or the current time as an RFC 2822 date string with optional usegmt and localtime flags, email utils format_datetime for formatting a datetime object as an RFC 2822 date string, email utils make_msgid for generating a globally unique Message-ID string with optional idstring and domain components, email utils decode_rfc2231 for decoding an RFC 2231 encoded parameter value into a tuple of charset language and value, email utils encode_rfc2231 for encoding a string as an RFC 2231 encoded parameter value, email utils collapse_rfc2231_value for collapsing a decoded RFC 2231 tuple to a Unicode string, and email utils integration with email.message and email.headerregistry and datetime and time for building address parsers date formatters message-id generators header extractors and RFC-compliant email construction utilities.

5 min read Feb 10, 2029

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free