Why Interfacy?

Interfacy exists for code that already has a good Python interface and only needs a command-line interface around it.

A command is usually already hiding in your code:

def resize(path: str, width: int, height: int, overwrite: bool = False) -> None:
    """Resize an image."""

The function name, parameter names, annotations, defaults, and docstring already say most of what a CLI parser needs to know. Interfacy uses that information directly instead of asking you to duplicate it in another parser definition.

What you keep

The callable remains an ordinary callable.

def greet(name: str, excited: bool = False) -> str:
    """Build a greeting."""
    suffix = "!" if excited else "."
    return f"Hello, {name}{suffix}"

Expose the callable as a CLI:

from interfacy import Interfacy

Interfacy(print_result=True).run(greet)
$ python app.py Ada --excited
Hello, Ada!

It is still a normal Python function, so tests can call it directly:

def test_greet() -> None:
    assert greet("Ada", excited=True) == "Hello, Ada!"

What Interfacy adds

Interfacy provides the CLI boundary around those objects:

  • parser construction

  • command routing

  • type conversion

  • nested subcommands

  • generated help

  • stdin routing

  • tab completion metadata

  • configurable output style

  • plugin hooks

The Python object remains the source of truth for command shape and help text.

Argparse-compatible parser

Interfacy also includes an ArgumentParser wrapper for manually shaped parser trees.

It follows the regular argparse workflow: create a parser, call add_argument(), and parse arguments. The wrapper adds Interfacy help rendering, layout and color configuration, custom help flags, and nested destination handling.

See Argparse Wrapper.