Skip to main content

Your all-in-one for beautiful, prod-ready CLIs

Project description

🦄 clypi

PyPI version License PyPI - Python Version PyPI - Downloads Contributors

Type-safe Python CLI prompts with validations, retries, custom messages, etc.

Examples

Check out the examples in ./examples! You can run them locally with:

uv run --all-extras -m examples.cli
uv run --all-extras -m examples.colors
uv run --all-extras -m examples.spinner
uv run --all-extras -m examples.prompts

Docs

Read the API docs for examples and a full API reference.

CLI

Read the docs

# examples/basic_cli.py
from clypi import Command

class Lint(Command):
    files: tuple[str, ...]

    async def run(self):
        print(f"Linting {', '.join(self.files)}")

class MyCli(Command):
    subcommand: Lint | None = None
    verbose: bool = False

    async def run(self):
        print(f"Running the main command with {self.verbose}")

if __name__ == "__main__":
    cli: MyCli = MyCli.parse()
    cli.start()
uv run -m examples.basic_cli lin (Typo)

image

uv run -m examples.basic_cli -h (Main help page)

image

uv run -m examples.basic_cli lint (Subcommand help page)

image

uv run -m examples.basic_cli (Normal run)

image

uv run -m examples.basic_cli lint (Missing args error)

image

❓ Prompting

Read the docs

First, you'll need to import the clypi module:

import clypi

answer = clypi.prompt("Are you going to use clypi?", default=True, parser=bool)

🌈 Colors

Read the docs

# demo.py
import clypi

# Style text
print(clypi.style("This is blue", fg="blue"), "and", clypi.style("this is red", fg="red"))

# Print with colors directly
clypi.print("Some colorful text", fg="green", reverse=True, bold=True, italic=True)

# Store a styler and reuse it
wrong = clypi.styler(fg="red", strikethrough=True)
print("The old version said", wrong("Pluto was a planet"))
print("The old version said", wrong("the Earth was flat"))
uv run -m examples.colors

image

uv run demo.py

image

🌀 Spinners

Read the docs

# demo.py
import asyncio
from clypi import Spinner

async def main():
    async with Spinner("Downloading assets") as s:
        for i in range(1, 6):
            await asyncio.sleep(0.5)
            s.title = f"Downloading assets [{i}/5]"

asyncio.run(main())
uv run -m examples.spinner

uv run demo.py

🐍 Type-checking

This library is fully type-checked. This means that all types will be correctly inferred from the arguments you pass in.

In this example your editor will correctly infer the type:

hours = clypi.prompt(
    "How many hours are there in a year?",
    parser=lambda x: float(x) if x < 24 else timedelta(days=x),
)
reveal_type(hours)  # Type of "res" is "float | timedelta"

Why do I care?

Type checking will help you catch issues way earlier in the development cycle. It will also provide nice autocomplete features in your editor that will make you faster 󱐋.

Integrations

Parsers (v6e, pydantic, etc.)

CLIPy can be integrated with many parsers. The default recommended parser is v6e, which is automatically used if installed in your local environment to parse types more accurately. If you wish you specify any parser (from v6e or elsewhere) manually, you can do so quite easily:

CLI

import v6e
from clypi import Command, config

class MyCli(Command):
    files: list[Path] = config(parser=v6e.path().exists().list())

    async def run(self):
        files = [f.as_posix() for f in self.files]
        print(f"Linting {', '.join(files)}")

if __name__ == "__main__":
    cli: MyCli = MyCli.parse()
    cli.start()

Prompting

import v6e

hours = clypi.prompt(
    "How many hours are there in a year?",
    parser=v6e.float().lte(24).union(v6e.timedelta()),
)
reveal_type(hours)  # Type of "res" is "float | timedelta"

Project details


Release history Release notifications | RSS feed

This version

0.1.6

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

clypi-0.1.6.tar.gz (42.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

clypi-0.1.6-py3-none-any.whl (23.8 kB view details)

Uploaded Python 3

File details

Details for the file clypi-0.1.6.tar.gz.

File metadata

  • Download URL: clypi-0.1.6.tar.gz
  • Upload date:
  • Size: 42.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for clypi-0.1.6.tar.gz
Algorithm Hash digest
SHA256 c28f0886452d827aebf677f152c26aaff5f9b292e361550a3a899a04376373a6
MD5 3b1ed525ce8ef5ecb3ecb85af130d12a
BLAKE2b-256 ea30e626198cc6bcc468394327c45c6ff8186e825c26a8f75a814065d8882b8a

See more details on using hashes here.

Provenance

The following attestation bundles were made for clypi-0.1.6.tar.gz:

Publisher: release.yml on danimelchor/clypi

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file clypi-0.1.6-py3-none-any.whl.

File metadata

  • Download URL: clypi-0.1.6-py3-none-any.whl
  • Upload date:
  • Size: 23.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for clypi-0.1.6-py3-none-any.whl
Algorithm Hash digest
SHA256 53b7c9a78c436cbece8bc473215150a60e21a56721caab27c5e8856fded91dd3
MD5 85d728991e2a963839f3c97c65c1d3af
BLAKE2b-256 4a17db7a9b18abf20569c611cfc43294656c1077a22fca499cf6d0aa148349b5

See more details on using hashes here.

Provenance

The following attestation bundles were made for clypi-0.1.6-py3-none-any.whl:

Publisher: release.yml on danimelchor/clypi

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page