Handling File Inputs and Outputs
To handle file inputs and outputs in your CLI applications, use the specialized file type hints provided in typer.models. These classes automatically manage opening and closing files using Click's underlying File type, allowing you to work directly with file-like objects.
Read Text Files
Use typer.FileText to receive an open file-like object for reading text. By default, it opens files in "r" mode and returns str data.
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()
Write Text Files
Use typer.FileTextWrite to open a file for writing text. This class defaults to "w" mode.
from typing import Annotated
import typer
app = typer.Typer()
@app.command()
def main(output: Annotated[typer.FileTextWrite, typer.Option()]):
output.write("Writing results to file\n")
print("Results written successfully")
if __name__ == "__main__":
app()
Read Binary Files
Use typer.FileBinaryRead when you need to process binary data, such as images or compressed files. It opens files in "rb" mode and returns bytes.
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 {processed_total} bytes")
if __name__ == "__main__":
app()
Write Binary Files
Use typer.FileBinaryWrite to write binary data. This class defaults to "wb" mode. Note that 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()]):
# Strings must be encoded to bytes
content = "binary content\n".encode("utf-8")
file.write(content)
# Or write raw bytes directly
raw_data = b"\x00\x01\x02\x03"
file.write(raw_data)
print("Binary file written")
if __name__ == "__main__":
app()
Append to Files
You can override the default file mode by passing the mode parameter to typer.Option or typer.Argument. This is useful for appending to existing files.
from typing import Annotated
import typer
app = typer.Typer()
@app.command()
def main(log: Annotated[typer.FileText, typer.Option(mode="a")]):
log.write("New log entry\n")
print("Appended to log")
if __name__ == "__main__":
app()
Advanced File Configuration
The typer.Option and typer.Argument functions support several parameters for fine-tuning file handling:
encoding: Specify the text encoding (e.g.,"utf-8").lazy: IfTrue, the file is only opened when you first access it.atomic: IfTrue, writes are performed to a temporary file and moved to the destination only after the file is closed successfully.
from typing import Annotated
import typer
app = typer.Typer()
@app.command()
def main(
output: Annotated[
typer.FileTextWrite,
typer.Option(encoding="utf-16", atomic=True, lazy=True)
]
):
output.write("This write is atomic and uses UTF-16 encoding.")
if __name__ == "__main__":
app()
Troubleshooting
Writing Strings to Binary Files
If you use FileBinaryWrite, attempting to write a str directly will result in a TypeError. You must always encode your strings:
# Incorrect
# file.write("some text")
# Correct
file.write("some text".encode("utf-8"))
File Closing
Typer relies on Click to manage the lifecycle of the file. Files are typically closed automatically when the command finishes execution. If you use lazy=True, the file won't be opened until you interact with the object in your code.