Skip to main content

Getting Started with quiCkLI: Your First Command-Line Application

· 3 min read
Sven Patrick Meier
Creator and maintainer of quiCkLI, an educational Python CLI framework

If you have ever wanted to build a small Python command-line tool but found larger frameworks overwhelming, quiCkLI was designed for you. It is a minimal framework that keeps the essentials visible so you can learn - and build - without unnecessary complexity.

In this first article of the Getting Started with quiCkLI series you will build a greeting tool with exactly three concepts: Application, Argument, and Option.

What you will build

$ python hello.py Ada
$ Hello, Ada!

$ python hello.py Ada --uppercase
$ HELLO, ADA!

A single file. No configuration. Under thirty lines of Python.

tip

Application.run() returns the handler result. A real executable wrapper still needs to print that result and decide how to map errors to exit codes.

Prerequisites

Install quickli in a virtual environment:

$ python -m venv .venv
$ source .venv/bin/activate # Windows: .venv\Scripts\activate
$ pip install quickli

You need Python 3.12 or later.

Step 1: Create the application

from quickli import Application

app = Application(
name="hello",
description="Greet a person from the command line.",
)

Application is the root container. It owns command registration and dispatch. You give it a name (used in help output) and a short description.

Step 2: Register a handler

from quickli import Application, Argument, Option

@app.entrypoint(
help_text="Print a greeting.",
arguments=[Argument("name", help_text="Name to greet.")],
options=[
Option(
"uppercase",
short_name="u",
is_flag=True,
help_text="Print the greeting in uppercase.",
),
],
)
def greet(name: str, uppercase: bool = False) -> str:
message = f"Hello, {name}!"
return message.upper() if uppercase else message

@app.entrypoint registers a function as the single handler for this application.

  • Argument("name") declares a required positional value. quickli passes it directly to the name parameter of your function.
  • Option("uppercase", is_flag=True) declares a boolean flag. Present → True, absent → False.

Step 3: Run the application

if __name__ == "__main__":
print(app.run())

Application.run() reads sys.argv[1:] by default and returns the handler result. You decide what to do with it - in this case, you print it.

The full file

from __future__ import annotations

from quickli import Application, Argument, Option


app = Application(
name="hello",
description="Greet a person from the command line.",
)


@app.entrypoint(
help_text="Print a greeting.",
arguments=[Argument("name", help_text="Name to greet.")],
options=[
Option(
"uppercase",
short_name="u",
is_flag=True,
help_text="Print the greeting in uppercase.",
),
],
)
def greet(name: str, uppercase: bool = False) -> str:
message = f"Hello, {name}!"
return message.upper() if uppercase else message


if __name__ == "__main__":
print(app.run())

Try it

$ python hello.py Ada # Hello, Ada!
$ python hello.py Ada -u # HELLO, ADA!
$ python hello.py # (help output)

What you learned

  • Application owns registration and dispatch.
  • @app.entrypoint registers a single handler for the whole application.
  • Argument describes positional, required input.
  • Option with is_flag=True adds a boolean switch.
  • Application.run(argv) returns the result - you control output.

Next in this series

The next article adds file input, validation, and global options by building a small file viewer inspired by the Unix cat command.

📖 Part 2: Reading Files - the quickcat tool →