Help

A help screen is standard for every CLI application. Cyclopts by-default adds --help and -h flags to the application:

$ my-application --help
Usage: my-application COMMAND

My application short description.

╭─ Commands ─────────────────────────────────────────────────────────╮
│ foo        Foo help string.                                        │
│ bar        Bar help string.                                        │
│ --help -h  Display this message and exit.                          │
│ --version  Display application version.                            │
╰────────────────────────────────────────────────────────────────────╯

Cyclopts derives the components of the help string from a variety of sources. The source resolution order is as follows (as applicable):

  1. The help field in the @app.command decorator.

    app = cyclopts.App()
    
    
    @app.command(help="This is the highest precedence help-string for 'bar'.")
    def bar():
        pass
    

    When registering an App object, supplying help via the @app.command decorator is forbidden to reduce ambiguity and will raise a ValueError. See (2).

  2. Via App.help.

    app = cyclopts.App(help="This help string has highest precedence at the app-level.")
    
    sub_app = cyclopts.App(help="This is the help string for the 'foo' subcommand.")
    app.command(sub_app, name="foo")
    app.command(sub_app, name="foo", help="This is illegal and raises a ValueError.")
    
  3. The __doc__ docstring of the registered @app.default command. Cyclopts parses the docstring to populate short-descriptions and long-descriptions at the command-level, as well as at the parameter-level.

    app = cyclopts.App()
    app.command(cyclopts.App(), name="foo")
    
    
    @app.default
    def bar(val1: str):
        """This is the primary application docstring.
    
        Parameters
        ----------
        val1: str
            This will be parsed for val1 help-string.
        """
    
    
    @app["foo"].default  # You can access sub-apps like a dictionary.
    def foo_handler():
        """This will be shown for the "foo" command."""
    

    Note

    Docstrings should always use the Python variable name from the function signature.

    @app.default
    def main(internal_name: Annotated[str, Parameter(name="external-name")]):
        """Command description.
    
        Parameters
        ----------
        internal_name:            # Use the Python variable name
            Help text here.
        """
    

    This follows standard Python documentation conventions; the parameter will still appear as --external-name on the CLI.

  4. This resolution order, but of the Meta App.

    app = cyclopts.App()
    
    
    @app.meta.default
    def bar():
        """This is the primary application docstring."""
    

Markup Format

While the standard markup language for docstrings in Python is reStructuredText (see PEP-0287), Cyclopts defaults to Markdown for better readability and simplicity. Cyclopts mostly respects PEP-0257, but has some slight differences for developer ergonomics:

  1. The "summary line" (AKA short-description) may actually be multiple lines. Cyclopts will unwrap the first block of text and interpret it as the short description. The first block of text ends at the first double-newline (i.e. a single blank line) is reached.

    def my_command():
        """
        This entire sentence
        is part of the short description and will
        have all the newlines removed.
    
        This is the beginning of the long description.
        """
    
  2. If a docstring is provided with a long description, it must also have a short description.

By default, Cyclopts parses docstring descriptions as markdown and renders it appropriately. To change the markup format, set the App.help_format field accordingly. The different options are described below.

Subapps inherit their parent's App.help_format unless explicitly overridden. I.e. you only need to set App.help_format in your main root application for all docstrings to be parsed appropriately.

PlainText

Do not perform any additional parsing, display supplied text as-is.

from cyclopts import App

app = App(help_format="plaintext")

@app.default
def default():
    """My application summary.

    This is a pretty standard docstring; if there's a really long sentence
    I should probably wrap it because people don't like code that is more
    than 80 columns long.

    In this new paragraph, I would like to discuss the benefits of relaxing 80 cols to 120 cols.
    More text in this paragraph.

    Some new paragraph.
    """

app()
Usage: default COMMAND

My application summary.

This is a pretty standard docstring; if there's a really long
sentence
I should probably wrap it because people don't like code that is
more
than 80 columns long.

In this new paragraph, I would like to discuss the benefits of
relaxing 80 cols to 120 cols.
More text in this paragraph.

Some new paragraph.

╭─ Commands ─────────────────────────────────────────────────────╮
│ --help,-h  Display this message and exit.                      │
│ --version  Display application version.                        │
╰────────────────────────────────────────────────────────────────╯

Most noteworthy, is no additional text reflow is performed; newlines are presented as-is.

Rich

Displays text as Rich Markup.

Note

Newlines are interpreted literally.

from cyclopts import App

app = App(help_format="rich")

@app.default
def default():
   """Rich can display colors like [red]red[/red] easily.

   However, I cannot be bothered to figure out how to show that in documentation.
   """

app()
Usage: default COMMAND

Rich can display colors like red easily.

╭─ Commands ───────────────────────────────────────────────────────╮
│ --help -h  Display this message and exit.                        │
│ --version  Display application version.                          │
╰──────────────────────────────────────────────────────────────────╯

ReStructuredText

ReStructuredText can be enabled by setting help_format to "restructuredtext" or "rst".

app = App(help_format="restructuredtext")  # or "rst"

@app.default
def default():
    """My application summary.

    We can do RST things like have **bold text**.
    More words in this paragraph.

    This is a new paragraph with some bulletpoints below:

    * bullet point 1.
    * bullet point 2.
    """

app()

Resulting help:

Usage: default COMMAND

My application summary.

We can do RST things like have bold text. More words in this
paragraph.

This is a new paragraph with some bulletpoints below:

1. bullet point 1.
2. bullet point 2.

╭─ Commands ──────────────────────────────────────────────────────────╮
│ --help -h  Display this message and exit.                           │
│ --version  Display application version.                             │
╰─────────────────────────────────────────────────────────────────────╯

Under most circumstances, plaintext (without any additional markup) looks prettier and reflows better when interpreted as restructuredtext (or markdown, for that matter).

Markdown

Markdown is the default parsing behavior of Cyclopts, so help_format won't need to be explicitly set. It's another popular markup language that Cyclopts can render.

app = App(help_format="markdown")  # or "md"
# or don't supply help_format at all; markdown is default.


@app.default
def default():
    """My application summary.

    We can do markdown things like have **bold text**.
    [Hyperlinks work as well.](https://cyclopts.readthedocs.io)
    """

Resulting help:

Usage: default COMMAND

My application summary.

We can do markdown things like have bold text. Hyperlinks work as well.

╭─ Commands ──────────────────────────────────────────────────────────╮
│ --help -h  Display this message and exit.                           │
│ --version  Display application version.                             │
╰─────────────────────────────────────────────────────────────────────╯

Help Flags

The default --help flags can be changed to different name(s) via the help_flags parameter.

app = cyclopts.App(help_flags="--show-help")
app = cyclopts.App(help_flags=["--send-help", "--send-help-plz", "-h"])

To disable the help-page entirely, set help_flags to an empty string or iterable.

app = cyclopts.App(help_flags="")
app = cyclopts.App(help_flags=[])

Help Prologue and Epilogue

An epilogue is text displayed at the end of the help screen, after all command and parameter panels. A prologue is text display at the beginning of the help screen, before the usage section. This is commonly used for version information, support contact details, or additional notes.

The epilogue is set via the App.help_epilogue attribute:

from cyclopts import App

app = App(
    name="myapp",
    help="My application description.",
    help_epilogue="Support: support@example.com"
)

@app.default
def main():
    """Main command."""
    pass

app()
$ myapp --help
Usage: myapp [ARGS]

My application description.

╭─ Commands ────────────────────────────────────────────────────────────╮
│ --help -h  Display this message and exit.                             │
│ --version  Display application version.                               │
╰───────────────────────────────────────────────────────────────────────╯

Support: support@example.com

The prologue is set via the App.help_prologue attribute:

from cyclopts import App

app = App(
    name="myapp",
    help="My application help.",
    help_prologue=f"myapp, v1.0.0 (http://example.myapp.com)"
)
app()
$ my-script --help
myapp, v1.0.0 (http://example.myapp.com)

Usage: myapp COMMAND

My application help.

╭─ Commands ────────────────────────────────────────────────────────────╮
│ --help -h  Display this message and exit.                             │
│ --version  Display application version.                               │
╰───────────────────────────────────────────────────────────────────────╯

Like App.help_format, epilogues and prologues inherit from parent to child apps. This allows you to set a single epilogue or prologue that applies across your entire application:

parent = App(
    name="myapp",
    help_epilogue="Version 1.0.0 | support@example.com"
)

# Child inherits parent's epilogue
child = App(name="process", help="Process data files.")
parent.command(child)

# Another child overrides with its own epilogue
admin = App(
    name="admin",
    help="Admin commands.",
    help_epilogue="Admin Tools v2.0 | USE WITH CAUTION"
)
parent.command(admin)

parent()
$ myapp process --help
Usage: myapp process

Process data files.

Version 1.0.0 | support@example.com    # Inherited from parent

$ myapp admin --help
Usage: myapp admin

Admin commands.

Admin Tools v2.0 | USE WITH CAUTION    # Overridden by child

To disable the epilogue or prologue for a specific subcommand, set it to an empty string:

no_epilogue = App(name="internal", help_epilogue="")
parent.command(no_epilogue)
no_prologue = App(name="internal", help_prologue="")
parent.command(no_prologue)

Help Customization

For advanced customization of help screen appearance, including custom formatters, styled panels, and dynamic column layouts, see Help Customization.