Skip to main content

Climax Prompt

Python package for creating cli prompts.

python badge pytest badge ruff badge sphinx badge poetry badge pypi badge git badge gitlab badge codeberg badge github badge mit shield pipeline status shield

This package exports 2 classes that contain the majority of its primary functionality. The Prompt and StringPrompt classes. The main difference between the 2 is that StringPrompts only processes strings while Prompts can process any arbitrary type.

The StringPrompt.exec_string_input_loop and Prompt.exec_input_loop methods of each class create a loop that prompts a user for input and will reprompt until either valid input is inputted or an exception is raised when converting the input string. If valid input is inputted then the inputted string or value (if the string is converted to a non string value) is returned.

Usage Examples

StringPrompt Usage Example

from climax.prompt import StringPrompt, StringPromptInput


# string validator gets passed a tuple containing the formatted and original
# unformatted string input
def is_palindrome(string: StringPromptInput) -> str | None:
    return (
        f'Provided input is not a palindrome: "{string.original}"\n'
        if len(string.formatted) == 0 or string.formatted != string.formatted[::-1]
        else None
    )


palindrome_prompt = StringPrompt(
    "Input a palindrome...\n",
    string_validator=is_palindrome,
    formatter=lambda a_string: a_string.strip().lower(),
    ps1=">>> ",
)

# once validation passes the formatted and original unformatted string input
# gets returned
formatted_palindrome_string_input, original_palindrome_string_input = palindrome_prompt.exec_string_input_loop()

print(f'You inputted the palindrome: "{formatted_palindrome_string_input}"')

Calling the StringPrompt.exec_string_input_loop method will result in the following process:

  1. The StringPrompt.message to be printed to stdout followed by the StringPrompt.ps1 if set.

  2. The string inputted to stdin then gets passed to the StringPrompt.formatter(str).

  3. The formatted and raw unformatted original input string then gets passed to the StringPrompt.string_validator.

  4. If validation passes (the StringPrompt.string_validator returns None) then both the formatted and original unformatted string input gets returned.

  5. If validation fails (the StringPrompt.string_validator returns a string error message) then the string error message gets printed to stderr and the process is repeated.

When the method in the example above is called this will result in the following prompt in the terminal:

Input a palindrome...
>>> slITher                                   # simulated user input
Provided input is not a palindrome: "slITher" # original unformatted string used in error message
Input a palindrome...
>>> levEl                                     # simulated user input
You inputted the palindrome: "level"          # formatted input used for validation and in this message

Prompt Usage Example

from typing import assert_never

from climax.prompt import (
    Prompt,
    PromptResultFailure,
    PromptResultSuccess,
    StringPromptInput,
)


# string validator gets passed a tuple containing the formatted and original
# unformatted string input
def string_is_integer(string: StringPromptInput) -> str | None:
    start_index = 1 if string.formatted.startswith("-") or string.formatted.startswith("+") else 0

    return (
        None
        if string.formatted[start_index:].isdecimal()
        else f'Provided input is not an integer: "{string.formatted}"\n'
    )


# validator gets passed whatever the string input gets converted to
def is_positive_even_integer(integer: int) -> str | None:
    if integer <= 0:
        return f"Integer isn't positive: {integer}\n"

    if integer % 2 != 0:
        return f"Integer isn't even: {integer}\n"

    return None


positive_even_integer_prompt = Prompt[int].create(
    "Input a positive even integer: ",
    int,  # can pass any function/lambda that consumes a string and outputs the specified type
    validator=is_positive_even_integer,
    string_validator=string_is_integer,
    formatter=str.strip,
)

positive_even_integer_prompt_result = positive_even_integer_prompt.exec_input_loop()

# The result is a union of two classes. The type checker narrows the type in
# each case, so `value` is only available on the successful conversion and
# `conversion_exception` is only available on the failed conversion.
match positive_even_integer_prompt_result:
    case PromptResultFailure(
        original_input_string=original_input_string,
        conversion_exception=conversion_exception,
    ):
        print(f'Error converting input to an int: "{original_input_string}"\n\n{repr(conversion_exception)}')
    case PromptResultSuccess(value=value):
        print("You inputted the positive even integer:", value)
    case _:
        assert_never(positive_even_integer_prompt_result)

Calling the Prompt.exec_input_loop method will result in the same process outlined above when StringPrompt.exec_string_input_loop is called, except it performs additional conversion of the string input and validation on that converted string input as outlined below if string input validation passes (otherwise the reprompt process is identical):

  1. The formatted validated string input is passed to the Prompt.converter(str).

  2. If an exception occurs during conversion, then a PromptResultFailure is returned with the original input string and the Exception that was raised.

  3. If conversion succeeds, the converted string input value is then passed to the Prompt.validator.

  4. If validation passes (the Prompt.validator returns None) then a PromptResultSuccess containing the original input string and its converted value is returned.

  5. If validation fails (the Prompt.validator returns a string error message) then the error message gets printed to stderr and the process is repeated.

When the method in the example above is called this will result in the following prompt in the terminal:

Input a positive even integer:    sliTHer          # simulated user input
Provided input is not an integer: "slither"        # formatted string input is used in this message
Input a positive even integer: -34                 # simulated user input
Integer isn't positive: -34
Input a positive even integer: 7                   # simulated user input
Integer isn't even: 7
Input a positive even integer: 22                  # simulated user input
You inputted the positive even integer: 22

Installing Package

This package is available on PyPI and can be installed via any of the standard methods:

# via poetry
poetry add climax-prompt

# via uv
uv add climax-prompt

# via pip
pip install climax-prompt

Repo

The next sections cover details and info about this git repository. If you're just importing this package as a dependency in a python project then it probably doesn't apply to you. If you clone this repo, especially if you intend to make a pr, then they're relevant.

Tooling Tasks

The repo for this project uses ruff and mypy for linting, ruff for formatting, pytest for testing, and sphinx (with numpydoc) for generating API documentation.

All repo tasks can be executed via the make targets listed below:

  • all - Run build.
  • bandit - Scan source code for security issues with bandit.
  • build - Build distributable artifacts.
  • clean - Remove build output and generated docs files.
  • clean-all - Run clean and clean-cache.
  • clean-cache - Remove tool caches and __pycache__ directories.
  • format - Fix lint issues and format code with ruff.
  • lint - Run all lint and type checks.
  • mypy - Type check with mypy.
  • poetry-check - Validates content of pyproject.toml and poetry.lock.
  • readme - Generate source/README.rst by using pandoc to convert the root README.md to rst.
  • ruff-check - Lint with ruff.
  • ruff-format-check - Check formatting with ruff.
  • serve-docs - Build and serve html API docs on 127.0.0.1:8000.
  • serve-tests - Build and serve html test results and coverage reports on 127.0.0.1:8000 and 127.0.0.1:8001. Depends on parallel.
  • setup - Install dependencies and pre-commit hooks. This should be run right after cloning the repo.
  • smoke - Install the built wheel and sdist in clean venvs and run smoke tests. The wheel and sdist artifacts must be built before running this target.
  • test - Run tests with terminal results and coverage report.
  • test-html - Run tests with html results and coverage reports.
  • test-xml - Run test with xml results and coverage reports.

In addition to the make targets listed above, all sphinx make targets are available and valid as well.

Remotes

The primary remote repo for this package is hosted on gitlab, but is also mirrored to codeberg and github.

MIT License

This package is licensed under the MIT license and can be found in LICENSE.txt.

Metadata

Release files for climax-prompt 0.7.80

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

Source distribution (sdist)

Source distribution for climax-prompt 0.7.80
File Size Uploaded Provenance Actions
climax_prompt-0.7.80.tar.gz 12.5 kB Without provenance Details for climax_prompt-0.7.80.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for climax-prompt 0.7.80
File Interpreter ABI Platform Actions
climax_prompt-0.7.80-py3-none-any.whl Python 3 none any Details for climax_prompt-0.7.80-py3-none-any.whl

Total release size: 24.3 kB

Release files / climax_prompt-0.7.80.tar.gz

Download URL climax_prompt-0.7.80.tar.gz
Size 12.5 kB
Tags Source
SHA-256 checksum
How to use checksums
7280a986a3f68620ede837bd88c0e388c011f4514cfb94417bd8b094a3aa68d9
BLAKE2b-256 checksum
How to use checksums
a7a0a9b9726113f5eedfc323b29f45ff371ad93e7c7cd81488de85fbec560962
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / climax_prompt-0.7.80-py3-none-any.whl

Download URL climax_prompt-0.7.80-py3-none-any.whl
Size 11.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cc1226defc930721a6a65b4834351d058c667a8188eacad8238a6cdb41cf8672
BLAKE2b-256 checksum
How to use checksums
8c59cd9305b5140b0dae876e66e11057dcd371994599b84c3c7aa25baf47edcc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.14.8

Security for climax-prompt 0.7.80

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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