Data Types and Context
Typer leverages Python's type hinting system to handle complex data types like file system paths and open file streams. It also provides a mechanism to access the underlying execution state through a context object, allowing for advanced CLI behaviors such as subcommand inspection and handling unknown arguments.
Path Handling
For interacting with the file system, Typer encourages the use of the standard library's pathlib.Path. When a parameter is hinted as Path, Typer automatically converts the string input from the command line into a Path object.
Beyond simple conversion, Typer provides extensive validation through typer.Option and typer.Argument. These parameters allow you to enforce constraints on the path before your command function is even executed.
Path Validation
The following validation parameters are available in typer.Option and typer.Argument (defined in typer/models.py and exposed in typer/params.py):
exists: IfTrue, the path must exist.file_okay: IfFalse, the path cannot be a file.dir_okay: IfFalse, the path cannot be a directory.writable: IfTrue, the path must be writable.readable: IfTrue, the path must be readable.resolve_path: IfTrue, the path will be resolved to its absolute form.
Example from docs_src/parameter_types/path/tutorial002_py310.py:
from pathlib import Path
import typer
app = typer.Typer()
@app.command()
def main(
config: Path = typer.Option(
...,
exists=True,
file_okay=True,
dir_okay=False,
writable=False,
readable=True,
resolve_path=True,
),
):
text = config.read_text()
print(f"Config file contents: {text}")
Internally, Typer uses typer.models.TyperPath, which inherits from click.Path. It overrides shell_complete to return an empty list, allowing Typer's own autocompletion logic to handle path suggestions.
File Objects
Typer provides specialized types for handling open file handles directly. These types inherit from standard library IO classes and ensure that files are opened with the correct modes and encodings.
Available File Types
Defined in typer/models.py, these classes simplify file operations:
FileText: Inherits fromio.TextIOWrapper. Default mode is"r".FileTextWrite: Inherits fromFileText. Default mode is"w".FileBinaryRead: Inherits fromio.BufferedReader. Default mode is"rb".FileBinaryWrite: Inherits fromio.BufferedWriter. Default mode is"wb".
Automatic Lifecycle Management
When you use these types, Typer (via Click) handles the opening of the file. If the user provides - as the filename, Typer automatically maps it to standard input or standard output depending on the mode.
Example from docs_src/parameter_types/file/tutorial001_py310.py:
import typer
app = typer.Typer()
@app.command()
def main(config: typer.FileText = typer.Option(...)):
for line in config:
print(f"Config line: {line}")
if __name__ == "__main__":
app()
You can further customize the file handling using parameters in typer.Option or typer.Argument such as mode, encoding, errors, and lazy.
Execution Context
The typer.Context class (a subclass of click.Context) provides access to the state of the current CLI execution. This is useful for accessing shared data, checking which subcommand was invoked, or handling extra arguments.
To access the context, simply declare a parameter with the type typer.Context in your command or callback function.
Subcommand Inspection
In a callback, you can use ctx.invoked_subcommand to determine which command is about to be run. This is demonstrated in docs_src/commands/context/tutorial001_py310.py:
import typer
app = typer.Typer()
@app.callback()
def main(ctx: typer.Context):
"""
Manage users in the awesome CLI app.
"""
print(f"About to execute command: {ctx.invoked_subcommand}")
Handling Extra Arguments
If you need to accept arbitrary arguments that aren't explicitly defined as parameters, you can configure the command to allow extra arguments and then access them via ctx.args.
Example from docs_src/commands/context/tutorial004_py310.py:
import typer
app = typer.Typer()
@app.command(
context_settings={"allow_extra_args": True, "ignore_unknown_options": True}
)
def main(ctx: typer.Context):
for extra_arg in ctx.args:
print(f"Got extra arg: {extra_arg}")
The context_settings dictionary allows you to pass configuration directly to the underlying Click implementation, such as help_option_names or ignore_unknown_options.
Callback Parameters
When writing a parameter callback, you might need information about the parameter itself (e.g., its name or help text). Typer provides typer.CallbackParam (inheriting from click.Parameter) for this purpose.
Example from docs_src/options/callback/tutorial004_py310.py:
import typer
def name_callback(ctx: typer.Context, param: typer.CallbackParam, value: str):
if ctx.resilient_parsing:
return
print(f"Validating param: {param.name}")
if value != "Camila":
raise typer.BadParameter("Only Camila is allowed")
return value
@app.command()
def main(name: str | None = typer.Option(default=None, callback=name_callback)):
print(f"Hello {name}")
By declaring param: typer.CallbackParam, you gain access to the Click metadata for that specific option or argument within the validation logic.