Skip to content

Commit 622f844

Browse files
committed
refactor: clean up python browser routing diff
Move the handwritten routing helpers out of the old browser_scoped package, delete the unused browser session clone helper, warm the async browser list cache, and drop the generated type churn from the branch. Made-with: Cursor
1 parent 3ae9dab commit 622f844

14 files changed

Lines changed: 69 additions & 171 deletions

src/kernel/_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
SyncAPIClient,
3030
AsyncAPIClient,
3131
)
32-
from .lib.browser_scoped.routing import (
32+
from .lib.browser_routing.routing import (
3333
BrowserRouteCache,
3434
BrowserRoutingConfig,
3535
rewrite_direct_vm_options,
File renamed without changes.

src/kernel/lib/browser_scoped/raw_http.py renamed to src/kernel/lib/browser_routing/raw_http.py

Lines changed: 36 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
from __future__ import annotations
22

3+
from collections.abc import AsyncIterator, Iterable, Iterator
34
from contextlib import asynccontextmanager, contextmanager
45
from typing import IO, Any, Mapping, cast
5-
from collections.abc import AsyncIterator, Iterable, Iterator
66

77
import httpx
88

99
from ..._models import FinalRequestOptions
10-
from ..._types import Body, BinaryTypes, NotGiven, Timeout, not_given
10+
from ..._types import BinaryTypes, Body, NotGiven, Timeout, not_given
1111
from .routing import BrowserRoute
1212
from .util import sanitize_curl_raw_params
1313

@@ -26,18 +26,17 @@ def request_via_browser_route(
2626
) -> httpx.Response:
2727
if json is not None and content is not None:
2828
raise TypeError("Passing both `json` and `content` is not supported")
29-
q: dict[str, object] = {**sanitize_curl_raw_params(params), "url": url}
30-
q["jwt"] = route.jwt
31-
opts = FinalRequestOptions.construct(
29+
query: dict[str, object] = {**sanitize_curl_raw_params(params), "url": url, "jwt": route.jwt}
30+
options = FinalRequestOptions.construct(
3231
method=method.upper(),
3332
url=route.base_url.rstrip("/") + "/curl/raw",
34-
params=q,
33+
params=query,
3534
headers=headers or {},
3635
content=_normalize_binary_content(content),
3736
json_data=json,
3837
timeout=timeout,
3938
)
40-
return cast(httpx.Response, parent.request(httpx.Response, opts))
39+
return cast(httpx.Response, parent.request(httpx.Response, options))
4140

4241

4342
@contextmanager
@@ -52,25 +51,25 @@ def stream_via_browser_route(
5251
params: Mapping[str, object] | None = None,
5352
timeout: float | Timeout | None | NotGiven = not_given,
5453
) -> Iterator[httpx.Response]:
55-
q: dict[str, Any] = sanitize_curl_raw_params(params)
56-
q["jwt"] = route.jwt
57-
q["url"] = url
58-
h = {k: v for k, v in parent.default_headers.items() if isinstance(v, str)}
54+
query: dict[str, Any] = sanitize_curl_raw_params(params)
55+
query["jwt"] = route.jwt
56+
query["url"] = url
57+
request_headers = {k: v for k, v in parent.default_headers.items() if isinstance(v, str)}
5958
if content is None:
60-
h.pop("Content-Type", None)
59+
request_headers.pop("Content-Type", None)
6160
if headers:
62-
h.update(headers)
63-
h.pop("Authorization", None)
64-
eff_timeout = parent.timeout if isinstance(timeout, NotGiven) else timeout
61+
request_headers.update(headers)
62+
request_headers.pop("Authorization", None)
63+
effective_timeout = parent.timeout if isinstance(timeout, NotGiven) else timeout
6564
with parent._client.stream(
6665
method.upper(),
6766
route.base_url.rstrip("/") + "/curl/raw",
68-
params=q,
69-
headers=h,
67+
params=query,
68+
headers=request_headers,
7069
content=_normalize_binary_content(content),
71-
timeout=_normalize_timeout(eff_timeout),
72-
) as resp:
73-
yield resp
70+
timeout=_normalize_timeout(effective_timeout),
71+
) as response:
72+
yield response
7473

7574

7675
async def async_request_via_browser_route(
@@ -87,18 +86,17 @@ async def async_request_via_browser_route(
8786
) -> httpx.Response:
8887
if json is not None and content is not None:
8988
raise TypeError("Passing both `json` and `content` is not supported")
90-
q: dict[str, object] = {**sanitize_curl_raw_params(params), "url": url}
91-
q["jwt"] = route.jwt
92-
opts = FinalRequestOptions.construct(
89+
query: dict[str, object] = {**sanitize_curl_raw_params(params), "url": url, "jwt": route.jwt}
90+
options = FinalRequestOptions.construct(
9391
method=method.upper(),
9492
url=route.base_url.rstrip("/") + "/curl/raw",
95-
params=q,
93+
params=query,
9694
headers=headers or {},
9795
content=_normalize_binary_content(content),
9896
json_data=json,
9997
timeout=timeout,
10098
)
101-
return cast(httpx.Response, await parent.request(httpx.Response, opts))
99+
return cast(httpx.Response, await parent.request(httpx.Response, options))
102100

103101

104102
@asynccontextmanager
@@ -113,25 +111,25 @@ async def async_stream_via_browser_route(
113111
params: Mapping[str, object] | None = None,
114112
timeout: float | Timeout | None | NotGiven = not_given,
115113
) -> AsyncIterator[httpx.Response]:
116-
q: dict[str, Any] = sanitize_curl_raw_params(params)
117-
q["jwt"] = route.jwt
118-
q["url"] = url
119-
h = {k: v for k, v in parent.default_headers.items() if isinstance(v, str)}
114+
query: dict[str, Any] = sanitize_curl_raw_params(params)
115+
query["jwt"] = route.jwt
116+
query["url"] = url
117+
request_headers = {k: v for k, v in parent.default_headers.items() if isinstance(v, str)}
120118
if content is None:
121-
h.pop("Content-Type", None)
119+
request_headers.pop("Content-Type", None)
122120
if headers:
123-
h.update(headers)
124-
h.pop("Authorization", None)
125-
eff_timeout = parent.timeout if isinstance(timeout, NotGiven) else timeout
121+
request_headers.update(headers)
122+
request_headers.pop("Authorization", None)
123+
effective_timeout = parent.timeout if isinstance(timeout, NotGiven) else timeout
126124
async with parent._client.stream(
127125
method.upper(),
128126
route.base_url.rstrip("/") + "/curl/raw",
129-
params=q,
130-
headers=h,
127+
params=query,
128+
headers=request_headers,
131129
content=_normalize_binary_content(content),
132-
timeout=_normalize_timeout(eff_timeout),
133-
) as resp:
134-
yield resp
130+
timeout=_normalize_timeout(effective_timeout),
131+
) as response:
132+
yield response
135133

136134

137135
def _normalize_timeout(timeout: float | Timeout | None | NotGiven) -> float | Timeout | None:
Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from ..._compat import model_copy
99
from ..._models import FinalRequestOptions
10-
from .util import base_url_from_browser_like, jwt_from_cdp_ws_url, cdp_ws_url_from_browser_like, session_id_from_browser_like
10+
from .util import base_url_from_browser_like, cdp_ws_url_from_browser_like, jwt_from_cdp_ws_url, session_id_from_browser_like
1111

1212

1313
@dataclass
@@ -118,5 +118,3 @@ def match_direct_vm_path(path: str) -> tuple[str, str, str] | None:
118118
suffix = "/" + "/".join(parts[index + 3 :])
119119
return session_id, subresource, suffix
120120
return None
121-
122-
Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -28,33 +28,31 @@ def session_id_from_browser_like(browser: Any) -> str:
2828
return sid
2929
if isinstance(browser, Mapping):
3030
mapping = cast(Mapping[str, object], browser)
31-
m = mapping.get("session_id")
32-
if isinstance(m, str) and m:
33-
return m
31+
value = mapping.get("session_id")
32+
if isinstance(value, str) and value:
33+
return value
3434
raise TypeError("browser object must have a non-empty session_id")
3535

3636

3737
def base_url_from_browser_like(browser: Any) -> str | None:
38-
bu = getattr(browser, "base_url", None)
39-
if isinstance(bu, str) and bu.strip():
40-
return bu.strip().rstrip("/") + "/"
38+
base_url = getattr(browser, "base_url", None)
39+
if isinstance(base_url, str) and base_url.strip():
40+
return base_url.strip().rstrip("/") + "/"
4141
if isinstance(browser, Mapping):
4242
mapping = cast(Mapping[str, object], browser)
43-
raw = mapping.get("base_url")
44-
if isinstance(raw, str) and raw.strip():
45-
return raw.strip().rstrip("/") + "/"
43+
value = mapping.get("base_url")
44+
if isinstance(value, str) and value.strip():
45+
return value.strip().rstrip("/") + "/"
4646
return None
4747

4848

4949
def cdp_ws_url_from_browser_like(browser: Any) -> str:
50-
u = getattr(browser, "cdp_ws_url", None)
51-
if isinstance(u, str) and u:
52-
return u
50+
cdp_ws_url = getattr(browser, "cdp_ws_url", None)
51+
if isinstance(cdp_ws_url, str) and cdp_ws_url:
52+
return cdp_ws_url
5353
if isinstance(browser, Mapping):
5454
mapping = cast(Mapping[str, object], browser)
55-
m = mapping.get("cdp_ws_url")
56-
if isinstance(m, str) and m:
57-
return m
55+
value = mapping.get("cdp_ws_url")
56+
if isinstance(value, str) and value:
57+
return value
5858
raise TypeError("browser object must have a non-empty cdp_ws_url")
59-
60-

src/kernel/lib/browser_scoped/browser_session_kernel.py

Lines changed: 0 additions & 101 deletions
This file was deleted.

src/kernel/resources/browsers/browsers.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,13 @@
8888
from ...types.shared_params.browser_profile import BrowserProfile
8989
from ...types.shared_params.browser_viewport import BrowserViewport
9090
from ...types.shared_params.browser_extension import BrowserExtension
91-
from ...lib.browser_scoped.raw_http import (
91+
from ...lib.browser_routing.raw_http import (
9292
async_request_via_browser_route,
9393
async_stream_via_browser_route,
9494
request_via_browser_route,
9595
stream_via_browser_route,
9696
)
97-
from ...lib.browser_scoped.routing import browser_route_from_browser
97+
from ...lib.browser_routing.routing import browser_route_from_browser
9898

9999
__all__ = ["BrowsersResource", "AsyncBrowsersResource"]
100100

@@ -978,7 +978,7 @@ def list(
978978
979979
timeout: Override the client-level default timeout for this request, in seconds
980980
"""
981-
return self._get_api_list(
981+
page = self._get_api_list(
982982
"/browsers",
983983
page=AsyncOffsetPagination[BrowserListResponse],
984984
options=make_request_options(
@@ -999,6 +999,11 @@ def list(
999999
),
10001000
model=BrowserListResponse,
10011001
)
1002+
for item in page.items:
1003+
route = browser_route_from_browser(item)
1004+
if route is not None:
1005+
self._client.browser_route_cache.set(route)
1006+
return page
10021007

10031008
@typing_extensions.deprecated("deprecated")
10041009
async def delete(

src/kernel/types/browser_create_response.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class BrowserCreateResponse(BaseModel):
3636
"""Websocket URL for WebDriver BiDi connections to the browser session"""
3737

3838
base_url: Optional[str] = None
39-
"""HTTP base URL for this browser session (browser VM / session proxy)."""
39+
"""Metro-API HTTP base URL for this browser session."""
4040

4141
browser_live_view_url: Optional[str] = None
4242
"""Remote URL for live viewing the browser session.

src/kernel/types/browser_list_response.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class BrowserListResponse(BaseModel):
3636
"""Websocket URL for WebDriver BiDi connections to the browser session"""
3737

3838
base_url: Optional[str] = None
39-
"""HTTP base URL for this browser session (browser VM / session proxy)."""
39+
"""Metro-API HTTP base URL for this browser session."""
4040

4141
browser_live_view_url: Optional[str] = None
4242
"""Remote URL for live viewing the browser session.

src/kernel/types/browser_pool_acquire_response.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class BrowserPoolAcquireResponse(BaseModel):
3636
"""Websocket URL for WebDriver BiDi connections to the browser session"""
3737

3838
base_url: Optional[str] = None
39-
"""HTTP base URL for this browser session (browser VM / session proxy)."""
39+
"""Metro-API HTTP base URL for this browser session."""
4040

4141
browser_live_view_url: Optional[str] = None
4242
"""Remote URL for live viewing the browser session.

0 commit comments

Comments
 (0)