Skip to content

Commit 740aeb6

Browse files
Wrap ClientConfig environment reads with explicit error context
Co-authored-by: Shri Sukhani <shrisukhani@users.noreply.github.com>
1 parent 70f6604 commit 740aeb6

2 files changed

Lines changed: 99 additions & 3 deletions

File tree

hyperbrowser/config.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ def normalize_base_url(base_url: str) -> str:
305305

306306
@classmethod
307307
def from_env(cls) -> "ClientConfig":
308-
api_key = os.environ.get("HYPERBROWSER_API_KEY")
308+
api_key = cls._read_env_value("HYPERBROWSER_API_KEY")
309309
if api_key is None:
310310
raise HyperbrowserError(
311311
"HYPERBROWSER_API_KEY environment variable is required"
@@ -316,11 +316,23 @@ def from_env(cls) -> "ClientConfig":
316316
)
317317

318318
base_url = cls.resolve_base_url_from_env(
319-
os.environ.get("HYPERBROWSER_BASE_URL")
319+
cls._read_env_value("HYPERBROWSER_BASE_URL")
320320
)
321-
headers = cls.parse_headers_from_env(os.environ.get("HYPERBROWSER_HEADERS"))
321+
headers = cls.parse_headers_from_env(cls._read_env_value("HYPERBROWSER_HEADERS"))
322322
return cls(api_key=api_key, base_url=base_url, headers=headers)
323323

324+
@staticmethod
325+
def _read_env_value(env_name: str) -> Optional[str]:
326+
try:
327+
return os.environ.get(env_name)
328+
except HyperbrowserError:
329+
raise
330+
except Exception as exc:
331+
raise HyperbrowserError(
332+
f"Failed to read {env_name} environment variable",
333+
original_error=exc,
334+
) from exc
335+
324336
@staticmethod
325337
def parse_headers_from_env(raw_headers: Optional[str]) -> Optional[Dict[str, str]]:
326338
return parse_headers_env_json(raw_headers)

tests/test_config.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,90 @@ def test_client_config_from_env_rejects_control_character_api_key(monkeypatch):
3333
ClientConfig.from_env()
3434

3535

36+
def test_client_config_from_env_wraps_api_key_env_read_runtime_errors(
37+
monkeypatch: pytest.MonkeyPatch,
38+
):
39+
original_get = config_module.os.environ.get
40+
41+
def _broken_get(env_name: str, default=None):
42+
if env_name == "HYPERBROWSER_API_KEY":
43+
raise RuntimeError("api key env read exploded")
44+
return original_get(env_name, default)
45+
46+
monkeypatch.setattr(config_module.os.environ, "get", _broken_get)
47+
48+
with pytest.raises(
49+
HyperbrowserError,
50+
match="api key env read exploded",
51+
) as exc_info:
52+
ClientConfig.from_env()
53+
54+
assert isinstance(exc_info.value.original_error, RuntimeError)
55+
56+
57+
def test_client_config_from_env_wraps_base_url_env_read_runtime_errors(
58+
monkeypatch: pytest.MonkeyPatch,
59+
):
60+
original_get = config_module.os.environ.get
61+
monkeypatch.setenv("HYPERBROWSER_API_KEY", "test-key")
62+
63+
def _broken_get(env_name: str, default=None):
64+
if env_name == "HYPERBROWSER_BASE_URL":
65+
raise RuntimeError("base url env read exploded")
66+
return original_get(env_name, default)
67+
68+
monkeypatch.setattr(config_module.os.environ, "get", _broken_get)
69+
70+
with pytest.raises(
71+
HyperbrowserError,
72+
match="base url env read exploded",
73+
) as exc_info:
74+
ClientConfig.from_env()
75+
76+
assert isinstance(exc_info.value.original_error, RuntimeError)
77+
78+
79+
def test_client_config_from_env_wraps_headers_env_read_runtime_errors(
80+
monkeypatch: pytest.MonkeyPatch,
81+
):
82+
original_get = config_module.os.environ.get
83+
monkeypatch.setenv("HYPERBROWSER_API_KEY", "test-key")
84+
monkeypatch.setenv("HYPERBROWSER_BASE_URL", "https://api.hyperbrowser.ai")
85+
86+
def _broken_get(env_name: str, default=None):
87+
if env_name == "HYPERBROWSER_HEADERS":
88+
raise RuntimeError("headers env read exploded")
89+
return original_get(env_name, default)
90+
91+
monkeypatch.setattr(config_module.os.environ, "get", _broken_get)
92+
93+
with pytest.raises(
94+
HyperbrowserError,
95+
match="headers env read exploded",
96+
) as exc_info:
97+
ClientConfig.from_env()
98+
99+
assert isinstance(exc_info.value.original_error, RuntimeError)
100+
101+
102+
def test_client_config_from_env_preserves_hyperbrowser_env_read_errors(
103+
monkeypatch: pytest.MonkeyPatch,
104+
):
105+
def _broken_read_env(env_name: str):
106+
if env_name == "HYPERBROWSER_API_KEY":
107+
raise HyperbrowserError("custom env read failure")
108+
return None
109+
110+
monkeypatch.setattr(
111+
ClientConfig, "_read_env_value", staticmethod(_broken_read_env)
112+
)
113+
114+
with pytest.raises(HyperbrowserError, match="custom env read failure") as exc_info:
115+
ClientConfig.from_env()
116+
117+
assert exc_info.value.original_error is None
118+
119+
36120
def test_client_config_from_env_reads_api_key_and_base_url(monkeypatch):
37121
monkeypatch.setenv("HYPERBROWSER_API_KEY", "test-key")
38122
monkeypatch.setenv("HYPERBROWSER_BASE_URL", "https://example.local")

0 commit comments

Comments
 (0)