Skip to content

Commit 1e6e6db

Browse files
Centralize web manager payload building
Co-authored-by: Shri Sukhani <shrisukhani@users.noreply.github.com>
1 parent 1711fe3 commit 1e6e6db

7 files changed

Lines changed: 116 additions & 27 deletions

File tree

‎CONTRIBUTING.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ This runs lint, format checks, compile checks, tests, and package build.
9898
- `tests/test_example_sync_async_parity.py` (sync/async example parity enforcement),
9999
- `tests/test_example_run_instructions.py` (example run-instruction consistency enforcement),
100100
- `tests/test_computer_action_endpoint_helper_usage.py` (computer-action endpoint-normalization helper usage enforcement),
101-
- `tests/test_session_upload_helper_usage.py` (session upload-input normalization helper usage enforcement).
101+
- `tests/test_session_upload_helper_usage.py` (session upload-input normalization helper usage enforcement),
102+
- `tests/test_web_payload_helper_usage.py` (web manager payload-helper usage enforcement).
102103

103104
## Code quality conventions
104105

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

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,8 @@
66
WebSearchParams,
77
WebSearchResponse,
88
)
9-
from ....schema_utils import inject_web_output_schemas
10-
from ...serialization_utils import serialize_model_dump_to_dict
119
from ...response_utils import parse_response_model
10+
from ...web_payload_utils import build_web_fetch_payload, build_web_search_payload
1211

1312

1413
class WebManager:
@@ -18,13 +17,7 @@ def __init__(self, client):
1817
self.crawl = WebCrawlManager(client)
1918

2019
async def fetch(self, params: FetchParams) -> FetchResponse:
21-
payload = serialize_model_dump_to_dict(
22-
params,
23-
error_message="Failed to serialize web fetch params",
24-
)
25-
inject_web_output_schemas(
26-
payload, params.outputs.formats if params.outputs else None
27-
)
20+
payload = build_web_fetch_payload(params)
2821

2922
response = await self._client.transport.post(
3023
self._client._build_url("/web/fetch"),
@@ -37,10 +30,7 @@ async def fetch(self, params: FetchParams) -> FetchResponse:
3730
)
3831

3932
async def search(self, params: WebSearchParams) -> WebSearchResponse:
40-
payload = serialize_model_dump_to_dict(
41-
params,
42-
error_message="Failed to serialize web search params",
43-
)
33+
payload = build_web_search_payload(params)
4434
response = await self._client.transport.post(
4535
self._client._build_url("/web/search"),
4636
data=payload,

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

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,8 @@
66
WebSearchParams,
77
WebSearchResponse,
88
)
9-
from ....schema_utils import inject_web_output_schemas
10-
from ...serialization_utils import serialize_model_dump_to_dict
119
from ...response_utils import parse_response_model
10+
from ...web_payload_utils import build_web_fetch_payload, build_web_search_payload
1211

1312

1413
class WebManager:
@@ -18,13 +17,7 @@ def __init__(self, client):
1817
self.crawl = WebCrawlManager(client)
1918

2019
def fetch(self, params: FetchParams) -> FetchResponse:
21-
payload = serialize_model_dump_to_dict(
22-
params,
23-
error_message="Failed to serialize web fetch params",
24-
)
25-
inject_web_output_schemas(
26-
payload, params.outputs.formats if params.outputs else None
27-
)
20+
payload = build_web_fetch_payload(params)
2821

2922
response = self._client.transport.post(
3023
self._client._build_url("/web/fetch"),
@@ -37,10 +30,7 @@ def fetch(self, params: FetchParams) -> FetchResponse:
3730
)
3831

3932
def search(self, params: WebSearchParams) -> WebSearchResponse:
40-
payload = serialize_model_dump_to_dict(
41-
params,
42-
error_message="Failed to serialize web search params",
43-
)
33+
payload = build_web_search_payload(params)
4434
response = self._client.transport.post(
4535
self._client._build_url("/web/search"),
4636
data=payload,
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from typing import Any, Dict
2+
3+
from hyperbrowser.models import FetchParams, WebSearchParams
4+
5+
from ..schema_utils import inject_web_output_schemas
6+
from .serialization_utils import serialize_model_dump_to_dict
7+
8+
9+
def build_web_fetch_payload(params: FetchParams) -> Dict[str, Any]:
10+
payload = serialize_model_dump_to_dict(
11+
params,
12+
error_message="Failed to serialize web fetch params",
13+
)
14+
inject_web_output_schemas(payload, params.outputs.formats if params.outputs else None)
15+
return payload
16+
17+
18+
def build_web_search_payload(params: WebSearchParams) -> Dict[str, Any]:
19+
return serialize_model_dump_to_dict(
20+
params,
21+
error_message="Failed to serialize web search params",
22+
)

‎tests/test_architecture_marker_usage.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"tests/test_example_run_instructions.py",
3030
"tests/test_computer_action_endpoint_helper_usage.py",
3131
"tests/test_session_upload_helper_usage.py",
32+
"tests/test_web_payload_helper_usage.py",
3233
)
3334

3435

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from pathlib import Path
2+
3+
import pytest
4+
5+
pytestmark = pytest.mark.architecture
6+
7+
8+
WEB_MANAGER_MODULES = (
9+
"hyperbrowser/client/managers/sync_manager/web/__init__.py",
10+
"hyperbrowser/client/managers/async_manager/web/__init__.py",
11+
)
12+
13+
14+
def test_web_managers_use_shared_payload_helpers():
15+
for module_path in WEB_MANAGER_MODULES:
16+
module_text = Path(module_path).read_text(encoding="utf-8")
17+
assert "build_web_fetch_payload(" in module_text
18+
assert "build_web_search_payload(" in module_text
19+
assert "inject_web_output_schemas(" not in module_text
20+
assert "serialize_model_dump_to_dict(" not in module_text

‎tests/test_web_payload_utils.py‎

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import pytest
2+
3+
import hyperbrowser.client.managers.web_payload_utils as web_payload_utils
4+
from hyperbrowser.exceptions import HyperbrowserError
5+
from hyperbrowser.models import FetchOutputOptions, FetchParams, WebSearchParams
6+
7+
8+
def test_build_web_fetch_payload_returns_serialized_payload():
9+
payload = web_payload_utils.build_web_fetch_payload(
10+
FetchParams(url="https://example.com")
11+
)
12+
13+
assert payload["url"] == "https://example.com"
14+
15+
16+
def test_build_web_fetch_payload_invokes_schema_injection(monkeypatch: pytest.MonkeyPatch):
17+
captured = {"payload": None, "formats": None}
18+
19+
def _capture(payload, formats):
20+
captured["payload"] = payload
21+
captured["formats"] = formats
22+
23+
monkeypatch.setattr(web_payload_utils, "inject_web_output_schemas", _capture)
24+
25+
payload = web_payload_utils.build_web_fetch_payload(
26+
FetchParams(
27+
url="https://example.com",
28+
outputs=FetchOutputOptions(formats=["markdown"]),
29+
)
30+
)
31+
32+
assert captured["payload"] is payload
33+
assert captured["formats"] == ["markdown"]
34+
35+
36+
def test_build_web_search_payload_returns_serialized_payload():
37+
payload = web_payload_utils.build_web_search_payload(
38+
WebSearchParams(query="hyperbrowser sdk")
39+
)
40+
41+
assert payload["query"] == "hyperbrowser sdk"
42+
43+
44+
def test_build_web_fetch_payload_wraps_runtime_model_dump_failures():
45+
class _BrokenFetchParams:
46+
outputs = None
47+
48+
def model_dump(self, **kwargs): # noqa: ARG002
49+
raise RuntimeError("boom")
50+
51+
with pytest.raises(HyperbrowserError, match="Failed to serialize web fetch params") as exc_info:
52+
web_payload_utils.build_web_fetch_payload(_BrokenFetchParams()) # type: ignore[arg-type]
53+
54+
assert isinstance(exc_info.value.original_error, RuntimeError)
55+
56+
57+
def test_build_web_search_payload_preserves_hyperbrowser_model_dump_failures():
58+
class _BrokenSearchParams:
59+
def model_dump(self, **kwargs): # noqa: ARG002
60+
raise HyperbrowserError("custom dump failure")
61+
62+
with pytest.raises(HyperbrowserError, match="custom dump failure") as exc_info:
63+
web_payload_utils.build_web_search_payload(_BrokenSearchParams()) # type: ignore[arg-type]
64+
65+
assert exc_info.value.original_error is None

0 commit comments

Comments
 (0)