Skip to main content

cranberry

cranberry is a Python library for building expressive, decorator-driven command-line interfaces. It provides a clean API for defining subcommands, typed arguments, global options, enums, and rich help output - all from plain Python classes and functions.


Features

  • Define subcommands as plain Python classes decorated with @cb.command.
  • Declare typed arguments, options, flags, file and directory inputs with cb.arg, cb.option, cb.flag, cb.file, and cb.dir.
  • First-class enum support via @cb.enum with strict or permissive validation.
  • Global options that are parsed before subcommand dispatch and accessible directly on the context.
  • Nested subcommand trees with automatic chain resolution.
  • Built-in --help and --version flags (and optional help / version subcommands).
  • Three built-in rendering styles - plain, colorful, and fancy - plus a Style base class for custom themes.
  • Validation callbacks, file existence checks, and per-argument type coercion.
  • Mixin-based field inheritance for sharing option groups across commands.

Installation

pip install cranberry-cli

# Installing from source
pip install git+https://github.com/chriso345/cranberry

Usage

The following example defines a small CLI with two subcommands and a global flag.

import cranberry as cb


@cb.enum(strict=True)
class Format:
    JSON = "json"
    CSV = "csv"
    TEXT = "text"


@cb.command("export")
@cb.description("Export data in a chosen format.")
class ExportCommand(cb.Fields):
    output: str = cb.option("-o", "--output", help="Destination file path")
    fmt: Format = cb.option(
        "-f", "--format", help="Output format", type=Format, default="json"
    )
    quiet: bool = cb.flag("-q", "--quiet", help="Suppress progress output")


@cb.command("validate")
@cb.description("Validate an input file.")
class ValidateCommand(cb.Fields):
    path: str = cb.file("-p", "--path", help="File to validate", exists=True)


@cb.globals()
class GlobalOptions(cb.Fields):
    verbose: bool = cb.flag("-v", "--verbose", help="Enable verbose logging")


@cb.app("myapp")
@cb.description("A sample CLI built with cranberry.")
@cb.version("1.0.0")
@cb.help(subcommand=True)
@cb.subcommand(ExportCommand, ValidateCommand)
@cb.style("colorful")
@cb.footer("Run 'myapp help' for usage details.")
def main():
    ctx = cb.parse_args(globals_cls=GlobalOptions)

    match ctx.command:
        case ExportCommand() as cmd:
            if not cmd.quiet:
                print(f"Exporting to {cmd.output} as {cmd.fmt}...")
        case ValidateCommand() as cmd:
            print(f"Validating {cmd.path}...")
        case None:
            print("No subcommand given. Try --help.")

    if ctx.globals.verbose:
        print("[verbose mode enabled]")


if __name__ == "__main__":
    main()

Running myapp --help produces:

A sample CLI built with cranberry.

Usage: myapp [OPTIONS] <COMMAND>

Commands:
  export    Export data in a chosen format.
  validate  Validate an input file.
  help      Display this help message and exit.

Flags:
  -h, --help     Display this help message and exit.
  -V, --version  Display version information and exit.

Global Options:
  -v, --verbose  Enable verbose logging

Run 'myapp help' for usage details.

Enums

@cb.enum turns a plain class into a validated string enum. In strict mode, only declared values are accepted; non-strict mode allows any unknown value through.

@cb.enum(strict=True)
class Color:
    RED = "red"
    GREEN = "green"
    BLUE = "blue"


@cb.command("paint")
class PaintCommand:
    color: Color = cb.option("-c", "--color", type=Color, help="Color to apply")

Enum instances compare equal to their string values, so cmd.color == "red" works as expected.


Global Options

Mark a class with @cb.globals() to declare options that are parsed before subcommand dispatch and attached directly to the ParseContext.

@cb.globals()
class Globals:
    config: str = cb.option("-c", "--config", help="Path to config file")

# Accessible as ctx.config after parse_args()

Field Inheritance

Share common fields across commands using plain Python inheritance or mixin classes.

class PaginationMixin:
    page: int = cb.option("-p", "--page", type=int, default=1, help="Page number")
    limit: int = cb.option(
        "-l", "--limit", type=int, default=20, help="Results per page"
    )


@cb.command("list-users")
class ListUsersCommand(PaginationMixin):
    filter: str = cb.option("-f", "--filter", help="Filter string")

Styles

Three built-in styles control the appearance of help output:

Style Description
plain No ANSI codes; safe for all terminals.
colorful Tasteful color accents using true-color ANSI.
fancy Bolder palette with underlined headings.

A custom style can be created by subclassing cb.Style and overriding individual methods.

from cranberry import Style


class MyStyle(Style):
    def heading(self, text: str) -> str:
        return f">>> {text}"


@cb.app("myapp")
@cb.style(MyStyle)
def main(): ...

License

Licensed under the MIT License. See the LICENSE file for more details.

Metadata

Release files for cranberry-cli 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cranberry-cli 0.1.0
File Size Uploaded
cranberry_cli-0.1.0.tar.gz 48.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for cranberry-cli 0.1.0
File Interpreter ABI Platform
cranberry_cli-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 71.3 kB

Release files / cranberry_cli-0.1.0.tar.gz

Download URL cranberry_cli-0.1.0.tar.gz
Size 48.6 kB
Tags Source
SHA-256 checksum
How to use checksums
68268bc20bc20913f6851dce95be6a05e067eb0a89b003bc3401efc8018f39d9
BLAKE2b-256 checksum
How to use checksums
2539c026f9827eb2dc640294f5bd53c4e48b7f9a13814b4ec3b38a47473fcfc1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.16 {"installer":{"name":"uv","version":"0.12.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / cranberry_cli-0.1.0-py3-none-any.whl

Download URL cranberry_cli-0.1.0-py3-none-any.whl
Size 22.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
739b7429da4d6a39e6e79317906fe2f6f1aa0d0ec676ad78d9bbbeb524c74053
BLAKE2b-256 checksum
How to use checksums
362252b01008948ed653505e4440cbbe75f8907dd8ce6b4904be3e362787f101
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.16 {"installer":{"name":"uv","version":"0.12.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page