from typing import List, Optional

import typer


def main(device: Optional[List[str]] = typer.Option(None)):
    if not device:
        print("No provided devices")
        raise typer.Abort()
    for d in device:
        print(f"Processing device: {d}")


if __name__ == "__main__":
    typer.run(main)

And then it can be used like in:

$ ./my_cli --device 1 --device 2

Take a look at the documentation.

If the only input for the CLI is a list of devices, you can simplify it to using arguments instead. Then it could be used like:

$ ./my_cli 1 2
Answer from ericbn on Stack Overflow
🌐
Typer
typer.tiangolo.com › tutorial › commands › one-or-multiple
One or Multiple Commands - Typer
Commands: create delete // Test the commands $ uv run python main.py create Creating user: Hiro Hamada $ uv run python main.py delete Deleting user: Hiro Hamada · If you want to create a CLI app with one single command but you still want it to be a command/subcommand you can just add a callback: ... import typer app = typer.Typer() @app.command() def create(): print("Creating user: Hiro Hamada") @app.callback() def callback(): pass if __name__ == "__main__": app()
🌐
Typer
typer.tiangolo.com › tutorial › commands
Commands - Typer
Here I'll use CLI application or program to refer to the program you are building in Python with Typer, and command to refer to one of these "subcommands" of your program. Typer allows creating CLI applications with multiple commands/subcommands.
🌐
Typer
typer.tiangolo.com › tutorial › subcommands › nested-subcommands
Nested SubCommands - Typer
Commands: users items lands // Try some users commands $ uv run python main.py users create Camila Creating user: Camila // Now try some items commands $ uv run python main.py items create Sword Creating item: Sword // And now some lands commands for reigns $ uv run python main.py lands reigns conquer Gondor Conquering reign: Gondor // And for towns $ uv run python main.py lands towns found Cartagena Founding town: Cartagena · Here are all the files if you want to review/copy them: ... import typer app = typer.Typer() @app.command() def conquer(name: str): print(f"Conquering reign: {name}") @app.command() def destroy(name: str): print(f"Destroying reign: {name}") if __name__ == "__main__": app()
🌐
Typer
typer.tiangolo.com › tutorial › multiple-values › multiple-options
Multiple CLI Options - Typer
You can declare a CLI option that can be used multiple times, and then get all the values. For example, let's say you want to accept several users in a single execution. For this, use the standard Python list to declare it as a list of str: ... from typing import Annotated import typer app ...
🌐
Typer
typer.tiangolo.com › tutorial › subcommands › single-file
SubCommands in a Single File - Typer
Python 3.10+ import typer app = typer.Typer() items_app = typer.Typer() app.add_typer(items_app, name="items") users_app = typer.Typer() app.add_typer(users_app, name="users") @items_app.command("create") def items_create(item: str): print(f"Creating item: {item}") @items_app.command("delete") def items_delete(item: str): print(f"Deleting item: {item}") @items_app.command("sell") def items_sell(item: str): print(f"Selling item: {item}") @users_app.command("create") def users_create(user_name: str): print(f"Creating user: {user_name}") @users_app.command("delete") def users_delete(user_name: str): print(f"Deleting user: {user_name}") if __name__ == "__main__": app() You can add the commands (subcommands) to each typer.Typer() app later and it will still work.
🌐
Calmcode
calmcode.io › course › typer › commands
Calmcode - typer: Commands
# cli.py import typer app = typer.Typer(name="demo", add_completion=False, help="This is a demo app.") @app.command() def hello_world(name): """Say hello""" print(f"hello {name}!") @app.command() def goodbye_world(name): """Say goodbye""" print(f"goodbye {name}!") if __name__ == "__main__": app() You should now be able to run the following commands; python cli.py --help python cli.py hello-world vincent python cli.py goodbye-world vincent
🌐
Typer
typer.tiangolo.com › tutorial › commands › options
Command CLI Options - Typer
import typer app = typer.Typer() @app.command() def create(username: str): print(f"Creating user: {username}") @app.command() def delete( username: str, force: bool = typer.Option(..., prompt="Are you sure you want to delete the user?"), ): if force: print(f"Deleting user: {username}") else: print("Operation cancelled") @app.command() def delete_all( force: bool = typer.Option( ..., prompt="Are you sure you want to delete ALL users?" ), ): if force: print("Deleting all users") else: print("Operation cancelled") @app.command() def init(): print("Initializing user database") if __name__ == "__main__": app() Here we have multiple commands, with different CLI parameters: ... Doesn't take any CLI parameters. // Check the help python main.py --help Usage: main.py [OPTIONS] COMMAND [ARGS]... Options: --install-completion Install completion for the current shell.
Find elsewhere
🌐
Jacob Kaplan-Moss
jacobian.org › til › common-arguments-with-typer
Common Arguments With Typer - Jacob Kaplan-Moss
I find it mildly easier than Click, but one thing I hadn’t worked out until today was how to implement common options with subcommands. That is, I had code like this:: import typer cli = typer.Typer() @cli.command() def all(pinboard_token: ...
🌐
Typer
typer.tiangolo.com › tutorial › commands › callback
Typer Callback - Typer
When you create an app = typer.Typer() it works as a group of commands. And you can create multiple commands with it.
🌐
GitHub
github.com › fastapi › typer › issues › 369
Typer App with multiple commands: show help when called without a command. · Issue #369 · fastapi/typer
March 8, 2022 - import typer app = typer.Typer() @app.command() def command_1(arg:str): typer.echo(arg) @app.command() def command_2(arg:str): typer.echo(arg) if __name__ == "__main__": app() I would like to know if there's a way to make the "--help" behaviour the default, in case the script is called with no command. ... Can I make this the default behaviour (i.e. python script.py --help should be the same as python script.py).
Author: fastapi
🌐
CloudBytes
cloudbytes.dev › home › snippets › set the default command in python typer cli
Set the default command in Python Typer CLI · CloudBytes/dev
October 1, 2021 - As you can see below, there are no default options and two commands, and Typer doesn’t know which one to use as default.
🌐
Typer
typer.tiangolo.com › tutorial › multiple-values › arguments-with-multiple-values
CLI Arguments with Multiple Values - Typer
CLI arguments can also receive multiple values. You can define the type of a CLI argument using list. ... from pathlib import Path import typer app = typer.Typer() @app.command() def main(files: list[Path], celebration: str): for path in files: if path.is_file(): print(f"This file exists: {path.name}") print(celebration) if __name__ == "__main__": app() And then you can pass it as many CLI arguments of that type as you want: $ uv run python main.py ./index.md ./first-steps.md woohoo!
🌐
Real Python
realpython.com › python-typer-cli
Build a Command-Line To-Do App With Python and Typer – Real Python
July 3, 2026 - In this section, you’ll create a minimal Typer CLI application with support for --help, -v, and --version options. To do so, you’ll use an explicit Typer application. This type of application is suitable for large projects that include multiple commands with several options and arguments.
🌐
Typer
typer.tiangolo.com › tutorial › subcommands › name-and-help
SubCommand Name and Help - Typer
import typer app = typer.Typer() users_app = typer.Typer() app.add_typer(users_app, name="users") @users_app.callback() def users(): """ Manage users in the app. """ @users_app.command() def create(name: str): print(f"Creating user: {name}") if __name__ == "__main__": app() The help text for that command will be the callback function's docstring: Manage users in the app.. ... // Check the main help $ uv run python main.py --help // Notice the help text "Manage users in the app."
🌐
Typer
typer.tiangolo.com › tutorial › subcommands › add-typer
Add Typer - Typer
In fact, items.py and users.py both have commands create and delete. But we need them to be part of the same CLI program. In this case, as with git remote, we can put them together as subcommands in another typer.Typer() CLI program. ... import typer import items import users app = typer.Typer() app.add_typer(users.app, name="users") app.add_typer(items.app, name="items") if __name__ == "__main__": app() ... Import the other Python modules (the files users.py and items.py).
🌐
PyTutorial
pytutorial.com › python-typer-commands-single-vs-multi
PyTutorial | Python Typer Commands: Single vs Multi
August 11, 2025 - ... import typer def greet(name: str): typer.echo(f"Hello, {name}!") if __name__ == "__main__": typer.run(greet) ... Hello, John! This is a basic single-command app. The greet function is the command. Multi-command apps have multiple subcommands.