Skip to content

Commit eeedc8c

Browse files
committed
style: lint code
1 parent 12b2e34 commit eeedc8c

5 files changed

Lines changed: 148 additions & 48 deletions

File tree

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Pre Production CI
2+
3+
on:
4+
push:
5+
branches: [master]
6+
pull_request:
7+
branches: [master]
8+
9+
jobs:
10+
lint_code:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Checkout project
15+
uses: actions/checkout@v5
16+
with:
17+
fetch-depth: 0
18+
19+
- name: Download python
20+
uses: actions/setup-python@v6
21+
with:
22+
python-version: '3.13'
23+
24+
- name: Download uv
25+
uses: astral-sh/setup-uv@v6
26+
with:
27+
version: "latest"
28+
29+
- name: Install dependencies
30+
run: uv sync --group dev
31+
32+
- name: Lint code
33+
run: uv run ruff check
34+
35+
- name: Check format
36+
run: uv run ruff format --check

app/api/v1/api_pdf.py

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,20 @@
77
router = APIRouter(prefix="/pdf", tags=["PDF Operations"])
88
file_service = FileService()
99

10-
accepted_quality = {
11-
"extreme": "screen",
12-
"normal": "ebook",
13-
"low": "printer"
14-
}
10+
accepted_quality = {"extreme": "screen", "normal": "ebook", "low": "printer"}
1511

16-
@router.post("/compress", summary="Compress PDF", description="Compress a PDF file with specified quality level")
12+
13+
@router.post(
14+
"/compress",
15+
summary="Compress PDF",
16+
description="Compress a PDF file with specified quality level",
17+
)
1718
async def pdf_compressor(
1819
file: UploadFile = File(..., description="PDF file to compress"),
19-
quality: Literal["extreme", "normal", "low"] = Form(..., description="Compression level: extreme (max compression), normal (balanced), low (min compression)")
20+
quality: Literal["extreme", "normal", "low"] = Form(
21+
...,
22+
description="Compression level: extreme (max compression), normal (balanced), low (min compression)",
23+
),
2024
):
2125
try:
2226
if file.content_type != "application/pdf":
@@ -26,12 +30,14 @@ async def pdf_compressor(
2630
compressed_file: bytes
2731
actual_size = file.size
2832

29-
compressed_file = file_service.compress_pdf_tmp(pdf_bytes, accepted_quality[quality])
33+
compressed_file = file_service.compress_pdf_tmp(
34+
pdf_bytes, accepted_quality[quality]
35+
)
3036

3137
if not compressed_file:
32-
raise HTTPException(500, "Compression did not generate any results")
38+
raise HTTPException(500, "Compression did not generate any results")
3339

34-
file_name = file.filename.removesuffix('.pdf')
40+
file_name = file.filename.removesuffix(".pdf")
3541
compressed_size = len(compressed_file)
3642
reduction = ((actual_size - compressed_size) / actual_size) * 100
3743

@@ -44,44 +50,48 @@ async def pdf_compressor(
4450
"X-Original-Size": str(actual_size),
4551
"X-Compressed-size": str(compressed_size),
4652
"X-Reduction-Percent": f"{reduction:.2f}",
47-
"X-Quality-Level": quality
48-
}
53+
"X-Quality-Level": quality,
54+
},
4955
)
5056
except Exception as e:
5157
raise HTTPException(status_code=500, detail=str(e))
5258

53-
@router.post("/merge", summary="Merge PDFs", description="Merge multiple PDF files into one")
59+
60+
@router.post(
61+
"/merge", summary="Merge PDFs", description="Merge multiple PDF files into one"
62+
)
5463
async def pdf_merge(
55-
files: List[UploadFile] = File(..., description="List of PDF files to merge (minimum 2)"),
64+
files: List[UploadFile] = File(
65+
..., description="List of PDF files to merge (minimum 2)"
66+
),
5667
):
5768
try:
5869
if len(files) < 2:
5970
raise HTTPException(
60-
status_code=400,
61-
detail="At least 2 PDF files are required for merging."
71+
status_code=400, detail="At least 2 PDF files are required for merging."
6272
)
6373

6474
for file in files:
6575
if file.content_type != "application/pdf":
6676
raise HTTPException(
6777
status_code=400,
68-
detail=f"Invalid file type: {file.filename} must be a PDF."
78+
detail=f"Invalid file type: {file.filename} must be a PDF.",
6979
)
7080

7181
bytes_list = [await f.read() for f in files]
7282

7383
merged_file = file_service.merge_pdf(bytes_list)
7484

7585
if not merged_file:
76-
raise HTTPException(500, "Merge did not generate any results")
86+
raise HTTPException(500, "Merge did not generate any results")
7787

7888
return StreamingResponse(
7989
BytesIO(merged_file),
8090
media_type="application/pdf",
8191
headers={
82-
"Content-Disposition": f'attachment; filename="merged.pdf"',
92+
"Content-Disposition": 'attachment; filename="merged.pdf"',
8393
"Content-Length": str(len(merged_file)),
84-
}
94+
},
8595
)
8696
except Exception as e:
8797
raise HTTPException(status_code=500, detail=str(e))

app/services/file_service.py

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44
import tempfile
55
import logging
66

7+
78
class FileService:
89
def compress_pdf_buffer(self, pdf_bytes: bytes, quality: str) -> bytes:
9-
logging.debug(f"Compressing {len(pdf_bytes)} bytes with quality={quality} in buffer")
10+
logging.debug(
11+
f"Compressing {len(pdf_bytes)} bytes with quality={quality} in buffer"
12+
)
1013

1114
gs_cmd = [
1215
"gs",
@@ -24,7 +27,7 @@ def compress_pdf_buffer(self, pdf_bytes: bytes, quality: str) -> bytes:
2427
"-dFILTERTEXTANNOTATIONS=true",
2528
"-dFILTERIMAGEANNOTATIONS=true",
2629
"-sOutputFile=-",
27-
"-"
30+
"-",
2831
]
2932
gs_process: subprocess.Popen = None
3033

@@ -33,12 +36,14 @@ def compress_pdf_buffer(self, pdf_bytes: bytes, quality: str) -> bytes:
3336
gs_cmd,
3437
stdin=subprocess.PIPE,
3538
stdout=subprocess.PIPE,
36-
stderr=subprocess.PIPE
39+
stderr=subprocess.PIPE,
3740
)
3841

3942
gs_output, gs_err = gs_process.communicate(input=pdf_bytes, timeout=60)
4043
if gs_process.returncode != 0:
41-
raise RuntimeError(f"Error to compress file: {gs_err.decode('utf-8', 'ignore')}")
44+
raise RuntimeError(
45+
f"Error to compress file: {gs_err.decode('utf-8', 'ignore')}"
46+
)
4247

4348
if not gs_output:
4449
raise RuntimeError("Compression failed: no output generated")
@@ -53,7 +58,9 @@ def compress_pdf_buffer(self, pdf_bytes: bytes, quality: str) -> bytes:
5358
raise RuntimeError(f"Unexpected error: {str(e)}")
5459

5560
def compress_pdf_tmp(self, pdf_bytes: bytes, quality: str) -> bytes:
56-
logging.debug(f"Compressing {len(pdf_bytes)} bytes with quality={quality} in tmp disk")
61+
logging.debug(
62+
f"Compressing {len(pdf_bytes)} bytes with quality={quality} in tmp disk"
63+
)
5764

5865
input_path = None
5966
output_path = None
@@ -64,10 +71,14 @@ def compress_pdf_tmp(self, pdf_bytes: bytes, quality: str) -> bytes:
6471
input_file.write(pdf_bytes)
6572
input_path = input_file.name
6673

67-
with tempfile.NamedTemporaryFile(suffix="_gs.pdf", delete=False) as output_file:
74+
with tempfile.NamedTemporaryFile(
75+
suffix="_gs.pdf", delete=False
76+
) as output_file:
6877
output_path = output_file.name
6978

70-
with tempfile.NamedTemporaryFile(suffix="_qpdf.pdf", delete=False) as output_compress_file:
79+
with tempfile.NamedTemporaryFile(
80+
suffix="_qpdf.pdf", delete=False
81+
) as output_compress_file:
7182
output_compress_path = output_compress_file.name
7283

7384
gs_cmd = [
@@ -86,18 +97,17 @@ def compress_pdf_tmp(self, pdf_bytes: bytes, quality: str) -> bytes:
8697
"-dFILTERTEXTANNOTATIONS=true",
8798
"-dFILTERIMAGEANNOTATIONS=true",
8899
f"-sOutputFile={output_path}",
89-
input_path
100+
input_path,
90101
]
91102

92103
gs_process = subprocess.run(
93-
gs_cmd,
94-
stdout=subprocess.PIPE,
95-
stderr=subprocess.PIPE,
96-
timeout=60
104+
gs_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=60
97105
)
98106

99107
if gs_process.returncode != 0:
100-
raise RuntimeError(f"Error to compress file: {gs_process.stderr.decode('utf-8', 'ignore')}")
108+
raise RuntimeError(
109+
f"Error to compress file: {gs_process.stderr.decode('utf-8', 'ignore')}"
110+
)
101111

102112
qpdf_cmd = [
103113
"qpdf",
@@ -109,14 +119,13 @@ def compress_pdf_tmp(self, pdf_bytes: bytes, quality: str) -> bytes:
109119
output_compress_path,
110120
]
111121
qpdf_process = subprocess.run(
112-
qpdf_cmd,
113-
stdout=subprocess.PIPE,
114-
stderr=subprocess.PIPE,
115-
timeout=60
122+
qpdf_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=60
116123
)
117124

118125
if qpdf_process.returncode != 0:
119-
raise RuntimeError(f"Error to compress file: {qpdf_process.stderr.decode('utf-8', 'ignore')}")
126+
raise RuntimeError(
127+
f"Error to compress file: {qpdf_process.stderr.decode('utf-8', 'ignore')}"
128+
)
120129

121130
with open(output_compress_path, "rb") as file:
122131
return file.read()
@@ -132,38 +141,44 @@ def compress_pdf_tmp(self, pdf_bytes: bytes, quality: str) -> bytes:
132141
unlink(file_path)
133142
except Exception:
134143
pass
135-
144+
136145
def merge_pdf(self, bytes_list: List[bytes]):
137146
input_paths = []
138147
output_path = None
139148

140149
try:
141150
for i, pdf_bytes in enumerate(bytes_list):
142-
with tempfile.NamedTemporaryFile(suffix=f"_{i}.pdf", delete=False) as input_file:
151+
with tempfile.NamedTemporaryFile(
152+
suffix=f"_{i}.pdf", delete=False
153+
) as input_file:
143154
input_file.write(pdf_bytes)
144155
input_paths.append(input_file.name)
145156

146-
with tempfile.NamedTemporaryFile(suffix="_gs.pdf", delete=False) as output_file:
157+
with tempfile.NamedTemporaryFile(
158+
suffix="_gs.pdf", delete=False
159+
) as output_file:
147160
output_path = output_file.name
148161

149162
qpdf_cmd = [
150163
"qpdf",
151164
"--linearize",
152165
"--empty",
153-
"--pages", *input_paths, "--", output_path,
166+
"--pages",
167+
*input_paths,
168+
"--",
169+
output_path,
154170
"--object-streams=generate",
155171
"--compress-streams=y",
156-
"--recompress-flate"
172+
"--recompress-flate",
157173
]
158174
qpdf_process = subprocess.run(
159-
qpdf_cmd,
160-
stdout=subprocess.PIPE,
161-
stderr=subprocess.PIPE,
162-
timeout=60
175+
qpdf_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=60
163176
)
164177

165178
if qpdf_process.returncode != 0:
166-
raise RuntimeError(f"Error merging PDFs: {qpdf_process.stderr.decode('utf-8', 'ignore')}")
179+
raise RuntimeError(
180+
f"Error merging PDFs: {qpdf_process.stderr.decode('utf-8', 'ignore')}"
181+
)
167182

168183
with open(output_path, "rb") as merged:
169184
return merged.read()

pyproject.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,8 @@ requires-python = ">=3.13"
77
dependencies = [
88
"fastapi[standard]>=0.118.0",
99
]
10+
11+
[dependency-groups]
12+
dev = [
13+
"ruff>=0.13.3",
14+
]

uv.lock

Lines changed: 34 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)