-
Notifications
You must be signed in to change notification settings - Fork 1
feat: migrate CLI from argparse to typer #49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,102 @@ | ||
| import argparse | ||
|
|
||
|
|
||
| def get_arg_parser() -> argparse.ArgumentParser: | ||
| arg_parser = argparse.ArgumentParser( | ||
| description="Encrypt a PDF file with a password of your choice.", | ||
| epilog="For more information, visit: https://github.com/SUPAIDEAS/passifypdf" | ||
| ) | ||
| arg_parser.add_argument("-v", "--version", action="version", version="%(prog)s 1.0") | ||
| arg_parser.add_argument("-i", "--input", required=True, help="Path to the input PDF file to be encrypted") | ||
| arg_parser.add_argument("-o", "--output", required=True, help="Path where the encrypted PDF file will be saved") | ||
| arg_parser.add_argument("-p", "--passwd", required=True, type=str, help="Password to encrypt the PDF file with") | ||
| return arg_parser | ||
| """CLI module using Typer for the passifypdf tool.""" | ||
|
|
||
| import logging | ||
| from importlib.metadata import PackageNotFoundError, version | ||
| from pathlib import Path | ||
|
|
||
| import typer | ||
| from typing_extensions import Annotated | ||
|
|
||
| from passifypdf.encryptpdf import encrypt_pdf | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
| try: | ||
| __version__ = version("passifypdf") | ||
| except PackageNotFoundError: | ||
| __version__ = "unknown" | ||
|
|
||
| app = typer.Typer( | ||
| name="passifypdf", | ||
| help="Encrypt a PDF file with a password of your choice.", | ||
| epilog="For more information, visit: https://github.com/SUPAIDEAS/passifypdf", | ||
| add_completion=False, | ||
| ) | ||
|
|
||
|
|
||
| def version_callback(value: bool) -> None: | ||
| """Print the version and exit.""" | ||
| if value: | ||
| typer.echo(f"passifypdf {__version__}") | ||
| raise typer.Exit() | ||
|
|
||
|
|
||
| @app.command() | ||
| def encrypt( | ||
| input: Annotated[ | ||
| Path, | ||
| typer.Option( | ||
| "--input", "-i", | ||
| help="Path to the input PDF file to be encrypted.", | ||
| exists=True, | ||
| file_okay=True, | ||
| dir_okay=False, | ||
| readable=True, | ||
| ), | ||
| ], | ||
| output: Annotated[ | ||
| Path, | ||
| typer.Option( | ||
| "--output", "-o", | ||
| help="Path where the encrypted PDF file will be saved.", | ||
| ), | ||
| ], | ||
| passwd: Annotated[ | ||
| str, | ||
| typer.Option( | ||
| "--passwd", "-p", | ||
| help="Password to encrypt the PDF file with.", | ||
| ), | ||
| ], | ||
| force: Annotated[ | ||
| bool, | ||
| typer.Option( | ||
| "--force", "-f", | ||
| help="Overwrite the output file if it already exists without prompting.", | ||
| ), | ||
| ] = False, | ||
| version: Annotated[ | ||
| bool, | ||
| typer.Option( | ||
| "--version", "-v", | ||
| help="Show the version and exit.", | ||
| callback=version_callback, | ||
| is_eager=True, | ||
| ), | ||
| ] = False, | ||
| ) -> None: | ||
| """Encrypt a PDF file with a password of your choice.""" | ||
|
|
||
| if output.exists() and not force: | ||
| overwrite = typer.confirm( | ||
| f"File '{output}' already exists. Overwrite?", | ||
| default=False, | ||
| ) | ||
| if not overwrite: | ||
| logger.info("Operation cancelled.") | ||
| raise typer.Exit() | ||
|
|
||
| try: | ||
| encrypt_pdf(input, output, passwd) | ||
| logger.info( | ||
| "Congratulations!\nPDF file encrypted successfully and saved as '%s'", | ||
| output, | ||
| ) | ||
| except Exception as e: | ||
| logger.error("Error: %s", e) | ||
| raise typer.Exit(code=1) | ||
|
Comment on lines
+91
to
+97
|
||
|
|
||
|
|
||
| def get_typer_app() -> typer.Typer: | ||
| """Return the configured Typer application instance.""" | ||
| return app | ||
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
| @@ -0,0 +1,186 @@ | ||||
| """Unit tests for the Typer-based CLI module.""" | ||||
|
|
||||
| from pathlib import Path | ||||
| from typing import Generator | ||||
|
||||
| from typing import Generator |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using
@app.command()creates a subcommand, which means users might need to runpassifypdf encrypt --input ...instead ofpassifypdf --input .... This breaks backward compatibility with the documented usage in README.md (line 40) which shows direct option usage without a subcommand. To maintain backward compatibility and provide a simpler CLI, use@app.callback()instead of@app.command(), or useapp.command(name="")to make it the default command that doesn't require typing "encrypt".