Skip to content

Commit 3c9b205

Browse files
Merge pull request #8 from datalogics-kam/pdfcloud-5474-implement-delete
PDFCLOUD-5474 Implement the delete function on files
2 parents d2fc602 + 2a3aa61 commit 3c9b205

22 files changed

Lines changed: 1029 additions & 17 deletions

.github/workflows/test-and-publish.yml

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
- name: Install uv
3030
uses: astral-sh/setup-uv@v6
3131
with:
32-
version: 0.8.22
32+
version: 0.9.18
3333
python-version: ${{ matrix.python-version }}
3434
enable-cache: true
3535
cache-suffix: test-and-publish
@@ -39,9 +39,38 @@ jobs:
3939
env:
4040
PDFREST_API_KEY: ${{ secrets.PDFREST_API_KEY }}
4141

42+
examples:
43+
name: Examples (Python ${{ matrix.python-version }})
44+
runs-on: ubuntu-latest
45+
strategy:
46+
fail-fast: false
47+
matrix:
48+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
49+
permissions:
50+
id-token: write
51+
contents: read
52+
packages: write
53+
pull-requests: write
54+
steps:
55+
- uses: actions/checkout@v4
56+
- name: Install uv
57+
uses: astral-sh/setup-uv@v6
58+
with:
59+
version: 0.9.18
60+
python-version: ${{ matrix.python-version }}
61+
enable-cache: true
62+
cache-suffix: test-and-publish
63+
cache-dependency-glob: uv.lock
64+
- name: Run examples with nox
65+
run: uvx nox --python ${{ matrix.python-version }} --session examples
66+
env:
67+
PDFREST_API_KEY: ${{ secrets.PDFREST_API_KEY }}
68+
4269
publish:
4370
name: Publish to CodeArtifact
44-
needs: tests
71+
needs:
72+
- tests
73+
- examples
4574
if: github.event_name == 'release'
4675
runs-on: ubuntu-latest
4776
permissions:
@@ -60,7 +89,7 @@ jobs:
6089
- name: Install uv
6190
uses: astral-sh/setup-uv@v6
6291
with:
63-
version: 0.8.22
92+
version: 0.9.18
6493
enable-cache: true
6594
cache-suffix: pre-commit
6695
cache-dependency-glob: uv.lock
@@ -77,4 +106,4 @@ jobs:
77106
- name: Build distribution artifacts
78107
run: uv build --python 3.11
79108
- name: Publish package to CodeArtifact
80-
run: uv publish --index cit-pypi
109+
run: uv publish --publish-url=https://datalogics-304774597385.d.codeartifact.us-east-2.amazonaws.com/pypi/cit-pypi/ --username __token__

AGENTS.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,11 +138,21 @@
138138
- Write pytest tests: files named `test_*.py`, test functions `test_*`, fixtures
139139
in `conftest.py` where shared.
140140

141+
- Follow ruff’s SIM117 rule: when combining context managers (e.g., a client and
142+
`pytest.RaisesGroup`), use a single `with (...)` statement instead of nesting
143+
them to keep tests idiomatic and lint-clean.
144+
141145
- Cover both client transports in every new test module (unit and live suites):
142146
add distinct test cases (not parameterized branches) that exercise each
143147
assertion through `PdfRestClient` and `AsyncPdfRestClient` so sync/async
144148
behaviour stays independently verifiable.
145149

150+
- When endpoints may raise `PdfRestErrorGroup` (or any future pdfRest-specific
151+
exception groups), assert them with `pytest.RaisesGroup`/`pytest.RaisesExc`,
152+
and use the `check=` hook to confirm the outer group is the expected class so
153+
each inner error is validated individually rather than matching the group
154+
message alone.
155+
146156
- Ensure high-value coverage of public functions and edge cases; document intent
147157
in test docstrings when non-obvious.
148158

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@
33
Python client library for the PDFRest service. The project is managed with
44
[uv](https://docs.astral.sh/uv/) and targets Python 3.9 and newer.
55

6+
## Running examples
7+
8+
```bash
9+
uvx nox -s examples
10+
uv run nox -s run-example -- examples/delete/delete_example.py
11+
```
12+
613
## Getting started
714

815
```bash

TESTING_GUIDELINES.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,10 +222,16 @@ iteration required.
222222
- Local validation failures (`ValidationError`, `ValueError`) that should
223223
prevent HTTP calls.
224224
- Server/transport failures (`PdfRestApiError`, `PdfRestAuthenticationError`,
225-
`PdfRestTimeoutError`, `PdfRestTransportError`).
225+
`PdfRestTimeoutError`, `PdfRestTransportError`, `PdfRestErrorGroup`, etc.).
226226
- When behaviour should short-circuit locally (bad UUIDs, empty query lists,
227227
missing profiles), configure the transport to raise if invoked so the test
228228
proves no HTTP request occurs.
229+
- When endpoints intentionally raise pdfRest-specific `ExceptionGroup`
230+
subclasses (such as `PdfRestErrorGroup` produced by delete failures), capture
231+
them with `pytest.RaisesGroup`/`pytest.RaisesExc`, and use the `check=` hook
232+
to assert the aggregate is the expected group class. This verifies both the
233+
group message and each individual member (`PdfRestDeleteError`, future custom
234+
errors) instead of relying on the aggregate text alone.
229235

230236
## Additional Expectations
231237

examples/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Examples
2+
3+
Each example script includes [PEP 723](https://peps.python.org/pep-0723/)
4+
metadata so `uv` can create a disposable environment and install the script's
5+
dependencies without touching the project-wide virtualenv. Run them directly
6+
with `uv run` instead of relying on `--project` mode:
7+
8+
```bash
9+
# Default (Python 3.11+)
10+
uv run examples/delete/delete_example.py
11+
12+
# Version-specific overrides
13+
uv run --python 3.10 examples/delete/python-3.10/delete_example.py
14+
```
15+
16+
The commands above read `PDFREST_API_KEY` from your environment (you can manage
17+
that via `.env` if desired), upload the checked-in sample assets under
18+
`examples/resources/`, and exercise the async client end-to-end. Use
19+
`uvx nox -s examples` when you want to execute every example across the
20+
supported interpreter matrix.

examples/delete/delete_example.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# /// script
2+
# requires-python = ">=3.11"
3+
# dependencies = ["pdfrest", "python-dotenv"]
4+
# ///
5+
"""Delete files with pdfRest's async client on Python 3.11+.
6+
7+
This sample shows how to:
8+
9+
1. Upload a local resource so we have a file id to delete.
10+
2. Delete that file successfully.
11+
3. Demonstrate how `PdfRestErrorGroup` behaves when we try to delete the same
12+
file again (Python 3.11 also allows `except* PdfRestDeleteError` if you want
13+
to tighten the example even further).
14+
15+
Run with `uv run --project ../.. python delete_example.py`; the script uses the
16+
checked-in `examples/resources/report.pdf` sample so no additional setup is
17+
required.
18+
"""
19+
20+
from __future__ import annotations
21+
22+
import asyncio
23+
from pathlib import Path
24+
25+
from dotenv import load_dotenv
26+
27+
from pdfrest import AsyncPdfRestClient, PdfRestDeleteError
28+
29+
RESOURCE = Path(__file__).resolve().parents[1] / "resources" / "report.pdf"
30+
31+
32+
async def delete_with_except_star() -> None:
33+
load_dotenv()
34+
async with AsyncPdfRestClient() as client:
35+
uploaded = (await client.files.create_from_paths([RESOURCE]))[0]
36+
print(f"Uploaded {uploaded.name} with id={uploaded.id}")
37+
38+
await client.files.delete(uploaded)
39+
print("First deletion succeeded.\n")
40+
41+
print("Attempting to delete the same file again to trigger errors...")
42+
try:
43+
await client.files.delete(uploaded)
44+
except* PdfRestDeleteError as group:
45+
for error in group.exceptions:
46+
print(f"- Cleanup failed for {error.file_id}: {error.detail}")
47+
else: # pragma: no cover - would require server bug
48+
print("Second deletion unexpectedly succeeded.")
49+
50+
51+
if __name__ == "__main__": # pragma: no cover - manual example
52+
asyncio.run(delete_with_except_star())
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# /// script
2+
# requires-python = "==3.10"
3+
# dependencies = ["pdfrest", "exceptiongroup", "python-dotenv"]
4+
# ///
5+
"""Delete files with pdfRest's async client on Python 3.10.
6+
7+
Python 3.10 lacks the built-in `except*` syntax, so this example uses the
8+
`exceptiongroup` backport to catch `PdfRestErrorGroup` and inspect individual
9+
`PdfRestDeleteError` instances when cleanup fails.
10+
11+
Run with `uv run --project ../.. python delete_example.py`; the shared
12+
`examples/resources/report.pdf` sample ships with the repository.
13+
"""
14+
15+
from __future__ import annotations
16+
17+
import asyncio
18+
from pathlib import Path
19+
20+
from dotenv import load_dotenv
21+
from exceptiongroup import BaseExceptionGroup, catch
22+
23+
from pdfrest import AsyncPdfRestClient, PdfRestDeleteError
24+
25+
RESOURCE = Path(__file__).resolve().parents[2] / "resources" / "report.pdf"
26+
27+
28+
def _log_delete_errors(group: BaseExceptionGroup) -> None:
29+
for error in group.exceptions:
30+
print(f"- Cleanup failed for {error.file_id}: {error.detail}")
31+
32+
33+
async def delete_with_exceptiongroup_catch() -> None:
34+
load_dotenv()
35+
async with AsyncPdfRestClient() as client:
36+
uploaded = (await client.files.create_from_paths([RESOURCE]))[0]
37+
print(f"Uploaded {uploaded.name} with id={uploaded.id}")
38+
39+
await client.files.delete(uploaded)
40+
print("First deletion succeeded.\n")
41+
42+
print("Attempting to delete the same file again to trigger errors...")
43+
with catch({PdfRestDeleteError: _log_delete_errors}):
44+
await client.files.delete(uploaded)
45+
46+
47+
if __name__ == "__main__": # pragma: no cover - manual example
48+
asyncio.run(delete_with_exceptiongroup_catch())
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Extend the `ruff.toml` file in the examples directory...
2+
3+
extend = "../../ruff.toml"
4+
target-version = "py310"

examples/resources/report.pdf

25 KB
Binary file not shown.

examples/ruff.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Extend the `pyproject.toml` file in the parent directory...
2+
3+
extend = "../pyproject.toml"
4+
target-version = "py311"

0 commit comments

Comments
 (0)