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, andcb.dir. - First-class enum support via
@cb.enumwith 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
--helpand--versionflags (and optionalhelp/versionsubcommands). - Three built-in rendering styles -
plain,colorful, andfancy- plus aStylebase 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)
| File | Size | Uploaded | |
|---|---|---|---|
| cranberry_cli-0.1.0.tar.gz | 48.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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}
|