File and Path Inputs
Handle file system interactions by using pathlib.Path for path-based logic or specialized File* types for direct file-like object interaction.
Validate File System Paths
To receive a path and validate its properties (like existence or permissions) before your command runs, use the standard pathlib.Path type hint combined with typer.Option or typer.Argument validation parameters.
from pathlib import Path
from typing import Annotated
import typer
app = typer.Typer()
@app.command()
def main(
config: Annotated[
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}")
if __name__ == "__main__":
app()
When you use these parameters, Typer internally uses the TyperPath class (a subclass of click.Path) to perform the following checks:
exists: Ensures the path exists on the file system.file_okay: Allows files as valid inputs.dir_okay: Allows directories as valid inputs.writable/readable: Checks for specific file permissions.resolve_path: Automatically converts the input to an absolute path.
Read and Write Text Files
If you need to interact with the file contents directly, use FileText or FileTextWrite. These types provide an open file-like object (io.TextIOWrapper) that yields str data.
Reading Text
Use FileText to open a file for reading. The default mode is "r".
from typing import Annotated
import typer
app = typer.Typer()
@app.command()
def main(config: Annotated[typer.FileText, typer.Option()]):
for line in config:
print(f"Config line: {line}")
if __name__ == "__main__":
app()
Writing Text
Use FileTextWrite to open a file for writing. The default mode is "w".
from typing import Annotated
import typer
app = typer.Typer()
@app.command()
def main(output: Annotated[typer.FileTextWrite, typer.Option()]):
output.write("Some text written by the app\n")
print("Content written to file")
if __name__ == "__main__":
app()
Handle Binary Files
For non-text data like images or compressed files, use FileBinaryRead and FileBinaryWrite. These provide io.BufferedReader or io.BufferedWriter objects that handle bytes.
Reading Binary Data
FileBinaryRead uses mode "rb" by default.
from typing import Annotated
import typer
app = typer.Typer()
@app.command()
def main(file: Annotated[typer.FileBinaryRead, typer.Option()]):
processed_total = 0
for bytes_chunk in file:
processed_total += len(bytes_chunk)
print(f"Processed bytes total: {processed_total}")
if __name__ == "__main__":
app()
Writing Binary Data
FileBinaryWrite uses mode "wb" by default. You must encode strings to bytes before writing.
from typing import Annotated
import typer
app = typer.Typer()
@app.command()
def main(file: Annotated[typer.FileBinaryWrite, typer.Option()]):
# Encode strings to bytes
first_line = "header\n".encode("utf-8")
file.write(first_line)
# Write raw bytes directly
raw_data = b"\x00\x01\x02\x03"
file.write(raw_data)
print("Binary file written")
if __name__ == "__main__":
app()
Advanced File Configuration
You can further customize how files are opened by passing additional parameters to typer.Option or typer.Argument. These parameters are passed directly to the underlying click.File implementation.
mode: Override the default (e.g., usemode="a"for appending).encoding: Specify the text encoding (e.g.,encoding="utf-16").lazy: IfTrue(default), the file is opened only when accessed. IfFalse, it is opened immediately.allow_dash: IfTrue, allows-as an input to representstdinorstdout.
@app.command()
def main(
log: Annotated[
typer.FileText,
typer.Option(mode="a", encoding="utf-8", lazy=False)
]
):
log.write("New log entry\n")
Troubleshooting
Manual Encoding in Binary Mode
When using FileBinaryWrite, attempting to write a str directly will result in a TypeError. Always use .encode() or provide a bytes object:
# Correct
file.write("data".encode("utf-8"))
# Incorrect - will raise TypeError
# file.write("data")
File Handles and Context Managers
Typer manages the lifecycle of these file objects. If lazy=True (the default), the file is opened when your command function starts and is automatically closed when the function finishes. You do not need to use a with statement inside your command for files passed as parameters.
TyperPath and Shell Completion
The TyperPath class used for pathlib.Path validation overrides Click's default shell_complete to return an empty list. This is intentional; it allows Typer's internal completion system to handle path suggestions more accurately across different shells.