Skip to content

Commit 6e06ea8

Browse files
Unify manager model parsing with shared response utility
Co-authored-by: Shri Sukhani <shrisukhani@users.noreply.github.com>
1 parent b12d5b1 commit 6e06ea8

11 files changed

Lines changed: 381 additions & 38 deletions

File tree

hyperbrowser/client/managers/async_manager/computer_action.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from pydantic import BaseModel
22
from typing import Union, List, Optional
33
from hyperbrowser.exceptions import HyperbrowserError
4+
from ..response_utils import parse_response_model
45
from hyperbrowser.models import (
56
SessionDetail,
67
ComputerActionParams,
@@ -46,7 +47,11 @@ async def _execute_request(
4647
session.computer_action_endpoint,
4748
data=payload,
4849
)
49-
return ComputerActionResponse(**response.data)
50+
return parse_response_model(
51+
response.data,
52+
model=ComputerActionResponse,
53+
operation_name="computer action",
54+
)
5055

5156
async def click(
5257
self,

hyperbrowser/client/managers/async_manager/profile.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
ProfileResponse,
99
)
1010
from hyperbrowser.models.session import BasicResponse
11+
from ..response_utils import parse_response_model
1112

1213

1314
class ProfileManager:
@@ -25,19 +26,31 @@ async def create(
2526
else params.model_dump(exclude_none=True, by_alias=True)
2627
),
2728
)
28-
return CreateProfileResponse(**response.data)
29+
return parse_response_model(
30+
response.data,
31+
model=CreateProfileResponse,
32+
operation_name="create profile",
33+
)
2934

3035
async def get(self, id: str) -> ProfileResponse:
3136
response = await self._client.transport.get(
3237
self._client._build_url(f"/profile/{id}"),
3338
)
34-
return ProfileResponse(**response.data)
39+
return parse_response_model(
40+
response.data,
41+
model=ProfileResponse,
42+
operation_name="get profile",
43+
)
3544

3645
async def delete(self, id: str) -> BasicResponse:
3746
response = await self._client.transport.delete(
3847
self._client._build_url(f"/profile/{id}"),
3948
)
40-
return BasicResponse(**response.data)
49+
return parse_response_model(
50+
response.data,
51+
model=BasicResponse,
52+
operation_name="delete profile",
53+
)
4154

4255
async def list(
4356
self, params: Optional[ProfileListParams] = None
@@ -47,4 +60,8 @@ async def list(
4760
self._client._build_url("/profiles"),
4861
params=params_obj.model_dump(exclude_none=True, by_alias=True),
4962
)
50-
return ProfileListResponse(**response.data)
63+
return parse_response_model(
64+
response.data,
65+
model=ProfileListResponse,
66+
operation_name="list profiles",
67+
)

hyperbrowser/client/managers/async_manager/team.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from hyperbrowser.models import TeamCreditInfo
2+
from ..response_utils import parse_response_model
23

34

45
class TeamManager:
@@ -9,4 +10,8 @@ async def get_credit_info(self) -> TeamCreditInfo:
910
response = await self._client.transport.get(
1011
self._client._build_url("/team/credit-info")
1112
)
12-
return TeamCreditInfo(**response.data)
13+
return parse_response_model(
14+
response.data,
15+
model=TeamCreditInfo,
16+
operation_name="team credit info",
17+
)

hyperbrowser/client/managers/async_manager/web/__init__.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
WebSearchResponse,
88
)
99
from ....schema_utils import inject_web_output_schemas
10+
from ...response_utils import parse_response_model
1011

1112

1213
class WebManager:
@@ -25,11 +26,19 @@ async def fetch(self, params: FetchParams) -> FetchResponse:
2526
self._client._build_url("/web/fetch"),
2627
data=payload,
2728
)
28-
return FetchResponse(**response.data)
29+
return parse_response_model(
30+
response.data,
31+
model=FetchResponse,
32+
operation_name="web fetch",
33+
)
2934

3035
async def search(self, params: WebSearchParams) -> WebSearchResponse:
3136
response = await self._client.transport.post(
3237
self._client._build_url("/web/search"),
3338
data=params.model_dump(exclude_none=True, by_alias=True),
3439
)
35-
return WebSearchResponse(**response.data)
40+
return parse_response_model(
41+
response.data,
42+
model=WebSearchResponse,
43+
operation_name="web search",
44+
)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from collections.abc import Mapping
2+
from typing import Any, Type, TypeVar
3+
4+
from hyperbrowser.exceptions import HyperbrowserError
5+
6+
T = TypeVar("T")
7+
8+
9+
def parse_response_model(
10+
response_data: Any,
11+
*,
12+
model: Type[T],
13+
operation_name: str,
14+
) -> T:
15+
if not isinstance(operation_name, str) or not operation_name.strip():
16+
raise HyperbrowserError("operation_name must be a non-empty string")
17+
if not isinstance(response_data, Mapping):
18+
raise HyperbrowserError(f"Expected {operation_name} response to be an object")
19+
try:
20+
response_payload = dict(response_data)
21+
except HyperbrowserError:
22+
raise
23+
except Exception as exc:
24+
raise HyperbrowserError(
25+
f"Failed to read {operation_name} response data",
26+
original_error=exc,
27+
) from exc
28+
try:
29+
return model(**response_payload)
30+
except HyperbrowserError:
31+
raise
32+
except Exception as exc:
33+
raise HyperbrowserError(
34+
f"Failed to parse {operation_name} response",
35+
original_error=exc,
36+
) from exc

hyperbrowser/client/managers/session_utils.py

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from hyperbrowser.exceptions import HyperbrowserError
55
from hyperbrowser.models.session import SessionRecording
6+
from .response_utils import parse_response_model
67

78
T = TypeVar("T")
89

@@ -13,28 +14,11 @@ def parse_session_response_model(
1314
model: Type[T],
1415
operation_name: str,
1516
) -> T:
16-
if not isinstance(operation_name, str) or not operation_name.strip():
17-
raise HyperbrowserError("operation_name must be a non-empty string")
18-
if not isinstance(response_data, Mapping):
19-
raise HyperbrowserError(f"Expected {operation_name} response to be an object")
20-
try:
21-
response_payload = dict(response_data)
22-
except HyperbrowserError:
23-
raise
24-
except Exception as exc:
25-
raise HyperbrowserError(
26-
f"Failed to read {operation_name} response data",
27-
original_error=exc,
28-
) from exc
29-
try:
30-
return model(**response_payload)
31-
except HyperbrowserError:
32-
raise
33-
except Exception as exc:
34-
raise HyperbrowserError(
35-
f"Failed to parse {operation_name} response",
36-
original_error=exc,
37-
) from exc
17+
return parse_response_model(
18+
response_data,
19+
model=model,
20+
operation_name=operation_name,
21+
)
3822

3923

4024
def parse_session_recordings_response_data(response_data: Any) -> List[SessionRecording]:

hyperbrowser/client/managers/sync_manager/computer_action.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from pydantic import BaseModel
22
from typing import Union, List, Optional
33
from hyperbrowser.exceptions import HyperbrowserError
4+
from ..response_utils import parse_response_model
45
from hyperbrowser.models import (
56
SessionDetail,
67
ComputerActionParams,
@@ -46,7 +47,11 @@ def _execute_request(
4647
session.computer_action_endpoint,
4748
data=payload,
4849
)
49-
return ComputerActionResponse(**response.data)
50+
return parse_response_model(
51+
response.data,
52+
model=ComputerActionResponse,
53+
operation_name="computer action",
54+
)
5055

5156
def click(
5257
self,

hyperbrowser/client/managers/sync_manager/profile.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
ProfileResponse,
99
)
1010
from hyperbrowser.models.session import BasicResponse
11+
from ..response_utils import parse_response_model
1112

1213

1314
class ProfileManager:
@@ -25,24 +26,40 @@ def create(
2526
else params.model_dump(exclude_none=True, by_alias=True)
2627
),
2728
)
28-
return CreateProfileResponse(**response.data)
29+
return parse_response_model(
30+
response.data,
31+
model=CreateProfileResponse,
32+
operation_name="create profile",
33+
)
2934

3035
def get(self, id: str) -> ProfileResponse:
3136
response = self._client.transport.get(
3237
self._client._build_url(f"/profile/{id}"),
3338
)
34-
return ProfileResponse(**response.data)
39+
return parse_response_model(
40+
response.data,
41+
model=ProfileResponse,
42+
operation_name="get profile",
43+
)
3544

3645
def delete(self, id: str) -> BasicResponse:
3746
response = self._client.transport.delete(
3847
self._client._build_url(f"/profile/{id}"),
3948
)
40-
return BasicResponse(**response.data)
49+
return parse_response_model(
50+
response.data,
51+
model=BasicResponse,
52+
operation_name="delete profile",
53+
)
4154

4255
def list(self, params: Optional[ProfileListParams] = None) -> ProfileListResponse:
4356
params_obj = params or ProfileListParams()
4457
response = self._client.transport.get(
4558
self._client._build_url("/profiles"),
4659
params=params_obj.model_dump(exclude_none=True, by_alias=True),
4760
)
48-
return ProfileListResponse(**response.data)
61+
return parse_response_model(
62+
response.data,
63+
model=ProfileListResponse,
64+
operation_name="list profiles",
65+
)

hyperbrowser/client/managers/sync_manager/team.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from hyperbrowser.models import TeamCreditInfo
2+
from ..response_utils import parse_response_model
23

34

45
class TeamManager:
@@ -9,4 +10,8 @@ def get_credit_info(self) -> TeamCreditInfo:
910
response = self._client.transport.get(
1011
self._client._build_url("/team/credit-info")
1112
)
12-
return TeamCreditInfo(**response.data)
13+
return parse_response_model(
14+
response.data,
15+
model=TeamCreditInfo,
16+
operation_name="team credit info",
17+
)

hyperbrowser/client/managers/sync_manager/web/__init__.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
WebSearchResponse,
88
)
99
from ....schema_utils import inject_web_output_schemas
10+
from ...response_utils import parse_response_model
1011

1112

1213
class WebManager:
@@ -25,11 +26,19 @@ def fetch(self, params: FetchParams) -> FetchResponse:
2526
self._client._build_url("/web/fetch"),
2627
data=payload,
2728
)
28-
return FetchResponse(**response.data)
29+
return parse_response_model(
30+
response.data,
31+
model=FetchResponse,
32+
operation_name="web fetch",
33+
)
2934

3035
def search(self, params: WebSearchParams) -> WebSearchResponse:
3136
response = self._client.transport.post(
3237
self._client._build_url("/web/search"),
3338
data=params.model_dump(exclude_none=True, by_alias=True),
3439
)
35-
return WebSearchResponse(**response.data)
40+
return parse_response_model(
41+
response.data,
42+
model=WebSearchResponse,
43+
operation_name="web search",
44+
)

0 commit comments

Comments
 (0)