Skip to content

Commit 5a043dd

Browse files
Merge pull request #11 from ambicuity/main
feat: Comprehensive Fix - Issues #2, #3, #7, #8, #9, #10
2 parents 661ce37 + 2d1858a commit 5a043dd

14 files changed

Lines changed: 378 additions & 125 deletions

File tree

.github/workflows/ci.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: Python CI
2+
3+
on:
4+
push:
5+
branches: [ "main" ]
6+
pull_request:
7+
branches: [ "main" ]
8+
9+
jobs:
10+
build:
11+
12+
runs-on: ubuntu-latest
13+
strategy:
14+
matrix:
15+
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
16+
17+
steps:
18+
- uses: actions/checkout@v4
19+
- name: Set up Python ${{ matrix.python-version }}
20+
uses: actions/setup-python@v5
21+
with:
22+
python-version: ${{ matrix.python-version }}
23+
- name: Install dependencies
24+
run: |
25+
python -m pip install --upgrade pip
26+
pip install poetry
27+
poetry install
28+
- name: Test with unittest
29+
run: |
30+
poetry run coverage run -m unittest discover tests
31+
- name: Upload coverage reports to Codecov
32+
uses: codecov/codecov-action@v5
33+
with:
34+
token: ${{ secrets.CODECOV_TOKEN }}

.github/workflows/python-ci.yml

Lines changed: 0 additions & 42 deletions
This file was deleted.

README.md

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,47 +8,40 @@ Same as encrypt or lock your PDF via a password.
88
# How to use ?
99

1010
## Clone
11-
```
12-
git clone git@github.com:SUPAIDEAS/passifypdf.git
13-
```
14-
or
15-
```
11+
```bash
1612
git clone https://github.com/SUPAIDEAS/passifypdf.git
1713
```
1814

19-
## Pull Dependencies (install before usage)
15+
## Install Dependencies
16+
Uses [Poetry](https://python-poetry.org/) for dependency management.
2017

21-
```
22-
pip install -r requirements.txt
18+
```bash
19+
cd passifypdf
20+
pip install poetry
21+
poetry install
2322
```
2423

2524
## Usage
25+
Run the CLI tool using `poetry run`:
2626

27-
Run the "main" from IDE or from CLI:
28-
29-
```
30-
if __name__ == "__main__":
31-
...
27+
```bash
28+
poetry run passifypdf --help
3229
```
3330

34-
## Usage via CLI:
35-
36-
```
37-
python encryptpdf.py
31+
Or activate the shell:
32+
```bash
33+
poetry shell
34+
passifypdf --help
3835
```
3936

40-
Sample Run:
41-
```
42-
change dir to "passifypdf/passifypdf",
4337

44-
Then run,
45-
python encryptpdf.py
38+
Sample Run:
39+
```bash
40+
passifypdf -i input.pdf -o protected.pdf -p mySecretPassword
4641

47-
-------------------------Sample output----------------------
48-
$python encryptpdf.py
49-
Congratulations!
50-
PDF file encrypted successfully and saved as 'Sample_PDF_protected.pdf'
51-
$
42+
# -------------------------Sample output----------------------
43+
# Congratulations!
44+
# PDF file encrypted successfully and saved as 'protected.pdf'
5245
```
5346

5447
## Known Issues

docs/CLI_OPTIONS.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# CLI Options
2+
3+
The `passifypdf` tool accepts the following command-line arguments:
4+
5+
| Option | Long Option | Required | Description |
6+
| :--- | :--- | :--- | :--- |
7+
| `-i` | `--input` | Yes | Path to the input PDF file. |
8+
| `-o` | `--output` | Yes | Path to the output (encrypted) PDF file. |
9+
| `-p` | `--passwd` | Yes | Password to use for encryption. **Avoid passing sensitive passwords directly on the command line, as they may be exposed via process listings and shell history.** |
10+
| `-v` | `--version` | No | Show the program's version number and exit. |
11+
| `-h` | `--help` | No | Show the help message and exit. |
12+
13+
## Example Usage
14+
15+
```bash
16+
# Using the installed CLI command (after `poetry install`)
17+
passifypdf -i input.pdf -o protected.pdf -p mySecretPassword
18+
```

passifypdf/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
"""Unit test package for {{ cookiecutter.project_slug }}."""
1+
"""passifypdf - Protect PDF files with a password of your choice."""

passifypdf/cli.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import argparse
22

33

4-
def get_arg_parser():
5-
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"))
4+
def get_arg_parser() -> argparse.ArgumentParser:
5+
arg_parser = argparse.ArgumentParser(
6+
description="Encrypt a PDF file with a password of your choice.",
7+
epilog="For more information, visit: https://github.com/SUPAIDEAS/passifypdf"
8+
)
69
arg_parser.add_argument("-v", "--version", action="version", version="%(prog)s 1.0")
7-
arg_parser.add_argument("-i", "--input", required=True, help="path to the input pdf file")
8-
arg_parser.add_argument("-o", "--output", required=True, help="path to the output encrypted pdf file")
9-
arg_parser.add_argument("-p", "--passwd", required=True, type=str, help="password to encrypt the pdf file")
10+
arg_parser.add_argument("-i", "--input", required=True, help="Path to the input PDF file to be encrypted")
11+
arg_parser.add_argument("-o", "--output", required=True, help="Path where the encrypted PDF file will be saved")
12+
arg_parser.add_argument("-p", "--passwd", required=True, type=str, help="Password to encrypt the PDF file with")
1013
return arg_parser

passifypdf/encryptpdf.py

Lines changed: 57 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,72 @@
1-
from cli import get_arg_parser
1+
"""Core PDF encryption module."""
2+
3+
import sys
4+
from pathlib import Path
5+
from typing import Union
6+
27
from pypdf import PdfReader, PdfWriter
38

9+
from .cli import get_arg_parser
10+
11+
12+
def encrypt_pdf(input_pdf: Union[str, Path], output_pdf: Union[str, Path], password: str) -> None:
13+
"""
14+
Encrypts a PDF file with a password.
415
5-
def encrypt_pdf(input_pdf, output_pdf, password):
6-
reader = PdfReader(input_pdf)
7-
writer = PdfWriter()
16+
Args:
17+
input_pdf (Union[str, Path]): Path to the input PDF file.
18+
output_pdf (Union[str, Path]): Path to the output PDF file.
19+
password (str): Password to encrypt the PDF with.
820
9-
# Add all pages from the reader to the writer
10-
for page_num in range(len(reader.pages)):
11-
writer.add_page(reader.pages[page_num])
21+
Raises:
22+
FileNotFoundError: If the input PDF file does not exist.
23+
IsADirectoryError: If the input path is a directory.
24+
Exception: For other errors during processing.
25+
"""
26+
input_path = Path(input_pdf)
27+
if not input_path.exists():
28+
raise FileNotFoundError(f"Input file '{input_pdf}' not found.")
1229

13-
# Encrypt with a password
14-
writer.encrypt(password)
30+
if not input_path.is_file():
31+
raise IsADirectoryError(f"Input path '{input_pdf}' is not a file.")
1532

16-
# Write the encrypted PDF to a new PDF file passed as param
17-
with open(output_pdf, "wb") as f:
18-
writer.write(f)
33+
try:
34+
reader = PdfReader(input_path)
35+
writer = PdfWriter()
1936

37+
# Add all pages from the reader to the writer
38+
for page in reader.pages:
39+
writer.add_page(page)
2040

21-
def pipeline(any_thing):
22-
return any_thing
41+
# Encrypt with a password
42+
writer.encrypt(password)
2343

44+
# Write the encrypted PDF to a new PDF file passed as param
45+
with open(output_pdf, "wb") as f:
46+
writer.write(f)
2447

25-
def main():
48+
except Exception as e:
49+
raise Exception(f"Failed to encrypt PDF: {e}")
50+
51+
52+
def main() -> int:
53+
"""
54+
Main function to run the CLI.
55+
56+
Returns:
57+
int: Exit code (0 for success, 1 for failure).
58+
"""
2659
arg_parser = get_arg_parser()
2760
args = arg_parser.parse_args()
28-
encrypt_pdf(args.input, args.output, args.passwd)
29-
print(f"Congratulations!\nPDF file encrypted successfully and saved as '{args.output}'")
61+
62+
try:
63+
encrypt_pdf(args.input, args.output, args.passwd)
64+
print(f"Congratulations!\nPDF file encrypted successfully and saved as '{args.output}'")
65+
return 0
66+
except Exception as e:
67+
print(f"Error: {e}", file=sys.stderr)
68+
return 1
3069

3170

3271
if __name__ == "__main__":
33-
main()
72+
sys.exit(main())

0 commit comments

Comments
 (0)