Skip to content

Commit db10193

Browse files
Read HYPERBROWSER_HEADERS in default client construction
Co-authored-by: Shri Sukhani <shrisukhani@users.noreply.github.com>
1 parent 615ea3a commit db10193

File tree

3 files changed

+62
-22
lines changed

3 files changed

+62
-22
lines changed

hyperbrowser/client/base.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ def __init__(
2525
)
2626

2727
if config is None:
28+
resolved_headers = (
29+
headers
30+
if headers is not None
31+
else ClientConfig.parse_headers_from_env(
32+
os.environ.get("HYPERBROWSER_HEADERS")
33+
)
34+
)
2835
config = ClientConfig(
2936
api_key=(
3037
api_key
@@ -38,7 +45,7 @@ def __init__(
3845
"HYPERBROWSER_BASE_URL", "https://api.hyperbrowser.ai"
3946
)
4047
),
41-
headers=headers,
48+
headers=resolved_headers,
4249
)
4350

4451
if not config.api_key:

hyperbrowser/config.py

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -50,25 +50,28 @@ def from_env(cls) -> "ClientConfig":
5050
base_url = os.environ.get(
5151
"HYPERBROWSER_BASE_URL", "https://api.hyperbrowser.ai"
5252
)
53-
headers = None
54-
raw_headers = os.environ.get("HYPERBROWSER_HEADERS")
55-
if raw_headers is not None and raw_headers.strip():
56-
try:
57-
parsed_headers = json.loads(raw_headers)
58-
except json.JSONDecodeError as exc:
59-
raise HyperbrowserError(
60-
"HYPERBROWSER_HEADERS must be valid JSON object"
61-
) from exc
62-
if not isinstance(parsed_headers, dict):
63-
raise HyperbrowserError(
64-
"HYPERBROWSER_HEADERS must be a JSON object of string pairs"
65-
)
66-
if any(
67-
not isinstance(key, str) or not isinstance(value, str)
68-
for key, value in parsed_headers.items()
69-
):
70-
raise HyperbrowserError(
71-
"HYPERBROWSER_HEADERS must be a JSON object of string pairs"
72-
)
73-
headers = parsed_headers
53+
headers = cls.parse_headers_from_env(os.environ.get("HYPERBROWSER_HEADERS"))
7454
return cls(api_key=api_key, base_url=base_url, headers=headers)
55+
56+
@staticmethod
57+
def parse_headers_from_env(raw_headers: Optional[str]) -> Optional[Dict[str, str]]:
58+
if raw_headers is None or not raw_headers.strip():
59+
return None
60+
try:
61+
parsed_headers = json.loads(raw_headers)
62+
except json.JSONDecodeError as exc:
63+
raise HyperbrowserError(
64+
"HYPERBROWSER_HEADERS must be valid JSON object"
65+
) from exc
66+
if not isinstance(parsed_headers, dict):
67+
raise HyperbrowserError(
68+
"HYPERBROWSER_HEADERS must be a JSON object of string pairs"
69+
)
70+
if any(
71+
not isinstance(key, str) or not isinstance(value, str)
72+
for key, value in parsed_headers.items()
73+
):
74+
raise HyperbrowserError(
75+
"HYPERBROWSER_HEADERS must be a JSON object of string pairs"
76+
)
77+
return parsed_headers

tests/test_custom_headers.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from hyperbrowser import AsyncHyperbrowser, Hyperbrowser
77
from hyperbrowser.config import ClientConfig
8+
from hyperbrowser.exceptions import HyperbrowserError
89
from hyperbrowser.transport.async_transport import AsyncTransport
910
from hyperbrowser.transport.sync import SyncTransport
1011

@@ -111,6 +112,35 @@ async def run() -> None:
111112
asyncio.run(run())
112113

113114

115+
def test_sync_client_constructor_reads_headers_from_environment(monkeypatch):
116+
monkeypatch.setenv("HYPERBROWSER_HEADERS", '{"X-Team-Trace":"env-sync"}')
117+
client = Hyperbrowser(api_key="test-key")
118+
try:
119+
assert client.transport.client.headers["X-Team-Trace"] == "env-sync"
120+
finally:
121+
client.close()
122+
123+
124+
def test_async_client_constructor_reads_headers_from_environment(monkeypatch):
125+
monkeypatch.setenv("HYPERBROWSER_HEADERS", '{"X-Team-Trace":"env-async"}')
126+
127+
async def run() -> None:
128+
client = AsyncHyperbrowser(api_key="test-key")
129+
try:
130+
assert client.transport.client.headers["X-Team-Trace"] == "env-async"
131+
finally:
132+
await client.close()
133+
134+
asyncio.run(run())
135+
136+
137+
def test_client_constructor_rejects_invalid_env_headers(monkeypatch):
138+
monkeypatch.setenv("HYPERBROWSER_HEADERS", "{invalid")
139+
140+
with pytest.raises(HyperbrowserError, match="HYPERBROWSER_HEADERS"):
141+
Hyperbrowser(api_key="test-key")
142+
143+
114144
def test_client_constructor_rejects_mixed_config_and_direct_args():
115145
with pytest.raises(TypeError, match="Pass either `config`"):
116146
Hyperbrowser(

0 commit comments

Comments
 (0)