Skip to content

Commit 73de3c2

Browse files
Refactor URL upload handling in clients
- Replaced `_normalize_url_inputs` utility with `UploadURLs` Pydantic model for validation and normalization. - Simplified input processing in `create_from_urls`. - Updated tests to reflect new URL validation logic and error messages.
1 parent 11cce9e commit 73de3c2

3 files changed

Lines changed: 42 additions & 28 deletions

File tree

src/pdfrest/client.py

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727

2828
__all__ = ("AsyncPdfRestClient", "PdfRestClient")
2929

30+
from .models._internal import UploadURLs
31+
3032
DEFAULT_BASE_URL = "https://api.pdfrest.com"
3133
API_KEY_ENV_VAR = "PDFREST_API_KEY"
3234
API_KEY_HEADER_NAME = "Api-Key"
@@ -195,26 +197,6 @@ def _normalize_path_inputs(
195197
return items
196198

197199

198-
def _normalize_url_inputs(urls: UrlInput) -> list[str]:
199-
if isinstance(urls, Sequence) and not isinstance(urls, (str, bytes, bytearray)):
200-
sequence_urls: Sequence[UrlValue] = urls
201-
items = list(sequence_urls)
202-
else:
203-
single_url: UrlValue = urls
204-
items = [single_url]
205-
if not items:
206-
msg = "At least one URL must be provided."
207-
raise ValueError(msg)
208-
normalized: list[str] = []
209-
for item in items:
210-
parsed = URL(str(item))
211-
if parsed.scheme not in {"http", "https"}:
212-
msg = "URL uploads require http or https scheme."
213-
raise ValueError(msg)
214-
normalized.append(str(parsed))
215-
return normalized
216-
217-
218200
def _resolve_file_id(file_ref: PdfRestFile | str) -> str:
219201
return file_ref.id if isinstance(file_ref, PdfRestFile) else str(file_ref)
220202

@@ -797,11 +779,11 @@ def create_from_paths(self, file_paths: FilePathInput) -> list[PdfRestFile]:
797779
def create_from_urls(self, urls: UrlInput) -> list[PdfRestFile]:
798780
"""Upload one or more files by providing remote URLs."""
799781

800-
normalized_urls = _normalize_url_inputs(urls)
782+
normalized_urls = UploadURLs.model_validate({"url": urls}) # pyright: ignore[reportPrivateUsage]
801783
request = self._client.prepare_request(
802784
"POST",
803785
"/upload",
804-
json_body={"url": normalized_urls},
786+
json_body=normalized_urls.model_dump(mode="json"),
805787
)
806788
payload = self._client.send_request(request)
807789
file_ids = _extract_uploaded_file_ids(payload)
@@ -918,11 +900,11 @@ async def create_from_paths(self, file_paths: FilePathInput) -> list[PdfRestFile
918900
async def create_from_urls(self, urls: UrlInput) -> list[PdfRestFile]:
919901
"""Upload one or more files by providing remote URLs."""
920902

921-
normalized_urls = _normalize_url_inputs(urls)
903+
normalized_urls = UploadURLs.model_validate({"url": urls})
922904
request = self._client.prepare_request(
923905
"POST",
924906
"/upload",
925-
json_body={"url": normalized_urls},
907+
json_body=normalized_urls.model_dump(mode="json"),
926908
)
927909
payload = await self._client.send_request(request)
928910
file_ids = _extract_uploaded_file_ids(payload)

src/pdfrest/models/_internal.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from __future__ import annotations
2+
3+
from typing import Annotated, Any
4+
5+
from pydantic import (
6+
BaseModel,
7+
BeforeValidator,
8+
Field,
9+
HttpUrl,
10+
)
11+
12+
13+
def _ensure_list(value: Any) -> Any:
14+
if value is None:
15+
return None
16+
if not isinstance(value, list):
17+
return [value]
18+
return value
19+
20+
21+
def _list_of_strings(value: list[Any]) -> list[str]:
22+
return [str(e) for e in value]
23+
24+
25+
class UploadURLs(BaseModel):
26+
url: Annotated[
27+
list[HttpUrl] | HttpUrl,
28+
Field(min_length=1),
29+
BeforeValidator(_list_of_strings),
30+
BeforeValidator(_ensure_list),
31+
]

tests/test_files.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -547,9 +547,7 @@ async def test_stream_iter_lines(
547547
async def test_async_files_create_from_urls_invalid_scheme() -> None:
548548
transport = httpx.MockTransport(lambda request: httpx.Response(400))
549549
async with AsyncPdfRestClient(api_key=VALID_API_KEY, transport=transport) as client:
550-
with pytest.raises(
551-
ValueError, match=r"URL uploads require http or https scheme\."
552-
):
550+
with pytest.raises(ValueError, match=r"URL scheme should be 'http' or 'https'"):
553551
await client.files.create_from_urls("ftp://example.com/file.pdf")
554552

555553

@@ -569,7 +567,10 @@ def test_files_create_rejects_empty_input() -> None:
569567
ValueError, match=r"At least one file path must be provided\."
570568
):
571569
client.files.create_from_paths([])
572-
with pytest.raises(ValueError, match=r"At least one URL must be provided\."):
570+
with pytest.raises(
571+
ValueError,
572+
match=r"Value should have at least 1 item after validation, not 0",
573+
):
573574
client.files.create_from_urls([])
574575

575576

0 commit comments

Comments
 (0)