Skip to content

Commit 5b01060

Browse files
Broaden header inputs to mapping types across clients
Co-authored-by: Shri Sukhani <shrisukhani@users.noreply.github.com>
1 parent 491ca1a commit 5b01060

8 files changed

Lines changed: 42 additions & 14 deletions

File tree

hyperbrowser/client/async_client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Dict, Optional
1+
from typing import Mapping, Optional
22

33
from ..config import ClientConfig
44
from ..transport.async_transport import AsyncTransport
@@ -23,7 +23,7 @@ def __init__(
2323
config: Optional[ClientConfig] = None,
2424
api_key: Optional[str] = None,
2525
base_url: Optional[str] = None,
26-
headers: Optional[Dict[str, str]] = None,
26+
headers: Optional[Mapping[str, str]] = None,
2727
timeout: Optional[int] = 30,
2828
):
2929
super().__init__(AsyncTransport, config, api_key, base_url, headers)

hyperbrowser/client/base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import os
2-
from typing import Dict, Optional, Type, Union
2+
from typing import Mapping, Optional, Type, Union
33

44
from hyperbrowser.exceptions import HyperbrowserError
55
from ..config import ClientConfig
@@ -15,7 +15,7 @@ def __init__(
1515
config: Optional[ClientConfig] = None,
1616
api_key: Optional[str] = None,
1717
base_url: Optional[str] = None,
18-
headers: Optional[Dict[str, str]] = None,
18+
headers: Optional[Mapping[str, str]] = None,
1919
):
2020
if config is not None and any(
2121
value is not None for value in (api_key, base_url, headers)

hyperbrowser/client/sync.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Dict, Optional
1+
from typing import Mapping, Optional
22

33
from ..config import ClientConfig
44
from ..transport.sync import SyncTransport
@@ -23,7 +23,7 @@ def __init__(
2323
config: Optional[ClientConfig] = None,
2424
api_key: Optional[str] = None,
2525
base_url: Optional[str] = None,
26-
headers: Optional[Dict[str, str]] = None,
26+
headers: Optional[Mapping[str, str]] = None,
2727
timeout: Optional[int] = 30,
2828
):
2929
super().__init__(SyncTransport, config, api_key, base_url, headers)

hyperbrowser/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class ClientConfig:
1212

1313
api_key: str
1414
base_url: str = "https://api.hyperbrowser.ai"
15-
headers: Optional[Dict[str, str]] = None
15+
headers: Optional[Mapping[str, str]] = None
1616

1717
def __post_init__(self) -> None:
1818
if not isinstance(self.api_key, str):

hyperbrowser/transport/async_transport.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import json
22
import httpx
3-
from typing import Optional
3+
from typing import Mapping, Optional
44

55
from hyperbrowser.exceptions import HyperbrowserError
66
from hyperbrowser.version import __version__
@@ -10,7 +10,7 @@
1010
class AsyncTransport(AsyncTransportStrategy):
1111
"""Asynchronous transport implementation using httpx"""
1212

13-
def __init__(self, api_key: str, headers: Optional[dict] = None):
13+
def __init__(self, api_key: str, headers: Optional[Mapping[str, str]] = None):
1414
merged_headers = {
1515
"x-api-key": api_key,
1616
"User-Agent": f"hyperbrowser-python-sdk/{__version__}",

hyperbrowser/transport/base.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from abc import ABC, abstractmethod
2-
from typing import Generic, Optional, Type, TypeVar, Union
2+
from typing import Generic, Mapping, Optional, Type, TypeVar, Union
33

44
from hyperbrowser.exceptions import HyperbrowserError
55

@@ -37,7 +37,7 @@ class SyncTransportStrategy(ABC):
3737
"""Abstract base class for synchronous transport implementations"""
3838

3939
@abstractmethod
40-
def __init__(self, api_key: str, headers: Optional[dict] = None): ...
40+
def __init__(self, api_key: str, headers: Optional[Mapping[str, str]] = None): ...
4141

4242
@abstractmethod
4343
def close(self) -> None: ...
@@ -63,7 +63,7 @@ class AsyncTransportStrategy(ABC):
6363
"""Abstract base class for asynchronous transport implementations"""
6464

6565
@abstractmethod
66-
def __init__(self, api_key: str, headers: Optional[dict] = None): ...
66+
def __init__(self, api_key: str, headers: Optional[Mapping[str, str]] = None): ...
6767

6868
@abstractmethod
6969
async def close(self) -> None: ...

hyperbrowser/transport/sync.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import json
22
import httpx
3-
from typing import Optional
3+
from typing import Mapping, Optional
44

55
from hyperbrowser.exceptions import HyperbrowserError
66
from hyperbrowser.version import __version__
@@ -10,7 +10,7 @@
1010
class SyncTransport(SyncTransportStrategy):
1111
"""Synchronous transport implementation using httpx"""
1212

13-
def __init__(self, api_key: str, headers: Optional[dict] = None):
13+
def __init__(self, api_key: str, headers: Optional[Mapping[str, str]] = None):
1414
merged_headers = {
1515
"x-api-key": api_key,
1616
"User-Agent": f"hyperbrowser-python-sdk/{__version__}",

tests/test_custom_headers.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import asyncio
2+
from types import MappingProxyType
23

34
import pytest
45

@@ -83,6 +84,33 @@ async def run() -> None:
8384
asyncio.run(run())
8485

8586

87+
def test_sync_client_constructor_accepts_mapping_headers():
88+
source_headers = {"X-Team-Trace": "team-mapping-sync"}
89+
mapping_headers = MappingProxyType(source_headers)
90+
client = Hyperbrowser(api_key="test-key", headers=mapping_headers)
91+
source_headers["X-Team-Trace"] = "mutated"
92+
try:
93+
assert client.transport.client.headers["X-Team-Trace"] == "team-mapping-sync"
94+
finally:
95+
client.close()
96+
97+
98+
def test_async_client_constructor_accepts_mapping_headers():
99+
async def run() -> None:
100+
source_headers = {"X-Team-Trace": "team-mapping-async"}
101+
mapping_headers = MappingProxyType(source_headers)
102+
client = AsyncHyperbrowser(api_key="test-key", headers=mapping_headers)
103+
source_headers["X-Team-Trace"] = "mutated"
104+
try:
105+
assert (
106+
client.transport.client.headers["X-Team-Trace"] == "team-mapping-async"
107+
)
108+
finally:
109+
await client.close()
110+
111+
asyncio.run(run())
112+
113+
86114
def test_client_constructor_rejects_mixed_config_and_direct_args():
87115
with pytest.raises(TypeError, match="Pass either `config`"):
88116
Hyperbrowser(

0 commit comments

Comments
 (0)