Skip to content

Commit fc54d05

Browse files
Support pathlib inputs for session file uploads
Co-authored-by: Shri Sukhani <shrisukhani@users.noreply.github.com>
1 parent dbb0e02 commit fc54d05

File tree

3 files changed

+105
-8
lines changed

3 files changed

+105
-8
lines changed

hyperbrowser/client/managers/async_manager/session.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
from typing import List, Optional, Union, IO, overload
1+
import os
2+
from os import PathLike
3+
from typing import IO, List, Optional, Union, overload
24
import warnings
35
from ....models.session import (
46
BasicResponse,
@@ -106,11 +108,11 @@ async def get_downloads_url(self, id: str) -> GetSessionDownloadsUrlResponse:
106108
return GetSessionDownloadsUrlResponse(**response.data)
107109

108110
async def upload_file(
109-
self, id: str, file_input: Union[str, IO]
111+
self, id: str, file_input: Union[str, PathLike[str], IO]
110112
) -> UploadFileResponse:
111113
response = None
112-
if isinstance(file_input, str):
113-
with open(file_input, "rb") as file_obj:
114+
if isinstance(file_input, (str, PathLike)):
115+
with open(os.fspath(file_input), "rb") as file_obj:
114116
files = {"file": file_obj}
115117
response = await self._client.transport.post(
116118
self._client._build_url(f"/session/{id}/uploads"),

hyperbrowser/client/managers/sync_manager/session.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
from typing import List, Optional, Union, IO, overload
1+
import os
2+
from os import PathLike
3+
from typing import IO, List, Optional, Union, overload
24
import warnings
35
from ....models.session import (
46
BasicResponse,
@@ -101,10 +103,12 @@ def get_downloads_url(self, id: str) -> GetSessionDownloadsUrlResponse:
101103
)
102104
return GetSessionDownloadsUrlResponse(**response.data)
103105

104-
def upload_file(self, id: str, file_input: Union[str, IO]) -> UploadFileResponse:
106+
def upload_file(
107+
self, id: str, file_input: Union[str, PathLike[str], IO]
108+
) -> UploadFileResponse:
105109
response = None
106-
if isinstance(file_input, str):
107-
with open(file_input, "rb") as file_obj:
110+
if isinstance(file_input, (str, PathLike)):
111+
with open(os.fspath(file_input), "rb") as file_obj:
108112
files = {"file": file_obj}
109113
response = self._client.transport.post(
110114
self._client._build_url(f"/session/{id}/uploads"),

tests/test_session_upload_file.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import asyncio
2+
from pathlib import Path
3+
4+
from hyperbrowser.client.managers.async_manager.session import (
5+
SessionManager as AsyncSessionManager,
6+
)
7+
from hyperbrowser.client.managers.sync_manager.session import (
8+
SessionManager as SyncSessionManager,
9+
)
10+
11+
12+
class _FakeResponse:
13+
def __init__(self, data):
14+
self.data = data
15+
16+
17+
class _SyncTransport:
18+
def __init__(self):
19+
self.received_file = None
20+
21+
def post(self, url, data=None, files=None):
22+
assert url.endswith("/session/session_123/uploads")
23+
assert files is not None and "file" in files
24+
assert files["file"].closed is False
25+
self.received_file = files["file"]
26+
return _FakeResponse(
27+
{
28+
"message": "ok",
29+
"filePath": "/uploads/file.txt",
30+
"fileName": "file.txt",
31+
"originalName": "file.txt",
32+
}
33+
)
34+
35+
36+
class _AsyncTransport:
37+
def __init__(self):
38+
self.received_file = None
39+
40+
async def post(self, url, data=None, files=None):
41+
assert url.endswith("/session/session_123/uploads")
42+
assert files is not None and "file" in files
43+
assert files["file"].closed is False
44+
self.received_file = files["file"]
45+
return _FakeResponse(
46+
{
47+
"message": "ok",
48+
"filePath": "/uploads/file.txt",
49+
"fileName": "file.txt",
50+
"originalName": "file.txt",
51+
}
52+
)
53+
54+
55+
class _FakeClient:
56+
def __init__(self, transport):
57+
self.transport = transport
58+
59+
def _build_url(self, path: str) -> str:
60+
return f"https://api.hyperbrowser.ai/api{path}"
61+
62+
63+
def _create_upload_file(tmp_path: Path) -> Path:
64+
file_path = tmp_path / "file.txt"
65+
file_path.write_text("content")
66+
return file_path
67+
68+
69+
def test_sync_session_upload_file_accepts_pathlike(tmp_path):
70+
file_path = _create_upload_file(tmp_path)
71+
transport = _SyncTransport()
72+
manager = SyncSessionManager(_FakeClient(transport))
73+
74+
response = manager.upload_file("session_123", file_path)
75+
76+
assert response.file_name == "file.txt"
77+
assert transport.received_file is not None and transport.received_file.closed is True
78+
79+
80+
def test_async_session_upload_file_accepts_pathlike(tmp_path):
81+
file_path = _create_upload_file(tmp_path)
82+
transport = _AsyncTransport()
83+
manager = AsyncSessionManager(_FakeClient(transport))
84+
85+
async def run():
86+
return await manager.upload_file("session_123", file_path)
87+
88+
response = asyncio.run(run())
89+
90+
assert response.file_name == "file.txt"
91+
assert transport.received_file is not None and transport.received_file.closed is True

0 commit comments

Comments
 (0)