Skip to content

Commit 12e2948

Browse files
authored
feat: add automatic API key detection from DECART_API_KEY env var (#15)
feat: add automatic API key detection from env var
1 parent 6d1dfe5 commit 12e2948

File tree

3 files changed

+70
-25
lines changed

3 files changed

+70
-25
lines changed

decart/client.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import os
12
from typing import Any, Optional
23
import aiohttp
34
from pydantic import ValidationError
@@ -20,14 +21,18 @@ class DecartClient:
2021
Decart API client for video and image generation/transformation.
2122
2223
Args:
23-
api_key: Your Decart API key
24+
api_key: Your Decart API key. Defaults to the DECART_API_KEY environment variable.
2425
base_url: API base URL (defaults to production)
2526
integration: Optional integration identifier (e.g., "langchain/0.1.0")
2627
2728
Example:
2829
```python
30+
# Option 1: Explicit API key
2931
client = DecartClient(api_key="your-key")
3032
33+
# Option 2: Using DECART_API_KEY environment variable
34+
client = DecartClient()
35+
3136
# Image generation (sync) - use process()
3237
image = await client.process({
3338
"model": models.image("lucy-pro-t2i"),
@@ -44,17 +49,19 @@ class DecartClient:
4449

4550
def __init__(
4651
self,
47-
api_key: str,
52+
api_key: Optional[str] = None,
4853
base_url: str = "https://api.decart.ai",
4954
integration: Optional[str] = None,
5055
) -> None:
51-
if not api_key or not api_key.strip():
56+
resolved_api_key = api_key or os.environ.get("DECART_API_KEY", "").strip() or None
57+
58+
if not resolved_api_key:
5259
raise InvalidAPIKeyError()
5360

5461
if not base_url.startswith(("http://", "https://")):
5562
raise InvalidBaseURLError(base_url)
5663

57-
self.api_key = api_key
64+
self.api_key = resolved_api_key
5865
self.base_url = base_url
5966
self.integration = integration
6067
self._session: Optional[aiohttp.ClientSession] = None

decart/errors.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ class InvalidAPIKeyError(DecartSDKError):
2626
"""Raised when API key is invalid or missing."""
2727

2828
def __init__(self) -> None:
29-
super().__init__("API key is required and must be a non-empty string")
29+
super().__init__(
30+
"Missing API key. Pass `api_key` to DecartClient() or set the DECART_API_KEY environment variable."
31+
)
3032

3133

3234
class InvalidBaseURLError(DecartSDKError):

tests/test_client.py

Lines changed: 56 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,59 @@
22
from decart import DecartClient, InvalidAPIKeyError, InvalidBaseURLError
33

44

5-
def test_create_client_success() -> None:
6-
client = DecartClient(api_key="test-key")
7-
assert client is not None
8-
assert client.process is not None
9-
10-
11-
def test_create_client_invalid_api_key() -> None:
12-
with pytest.raises(InvalidAPIKeyError):
13-
DecartClient(api_key="")
14-
15-
16-
def test_create_client_invalid_base_url() -> None:
17-
with pytest.raises(InvalidBaseURLError):
18-
DecartClient(api_key="test-key", base_url="invalid-url")
19-
20-
21-
def test_create_client_custom_base_url() -> None:
22-
client = DecartClient(api_key="test-key", base_url="https://custom.decart.ai")
23-
assert client is not None
24-
assert client.base_url == "https://custom.decart.ai"
5+
class TestDecartClient:
6+
"""Tests for DecartClient initialization."""
7+
8+
def test_create_client_with_explicit_api_key(self) -> None:
9+
"""Creates a client with explicit api_key."""
10+
client = DecartClient(api_key="test-key")
11+
assert client is not None
12+
assert client.process is not None
13+
assert client.api_key == "test-key"
14+
15+
def test_create_client_from_env_var(self, monkeypatch: pytest.MonkeyPatch) -> None:
16+
"""Creates a client using DECART_API_KEY env var."""
17+
monkeypatch.setenv("DECART_API_KEY", "env-api-key")
18+
client = DecartClient()
19+
assert client is not None
20+
assert client.api_key == "env-api-key"
21+
22+
def test_explicit_api_key_takes_precedence(self, monkeypatch: pytest.MonkeyPatch) -> None:
23+
"""Explicit api_key takes precedence over env var."""
24+
monkeypatch.setenv("DECART_API_KEY", "env-api-key")
25+
client = DecartClient(api_key="explicit-key")
26+
assert client.api_key == "explicit-key"
27+
28+
def test_create_client_no_api_key_no_env(self, monkeypatch: pytest.MonkeyPatch) -> None:
29+
"""Throws an error if api key is not provided and env var is not set."""
30+
monkeypatch.delenv("DECART_API_KEY", raising=False)
31+
with pytest.raises(InvalidAPIKeyError, match="Missing API key"):
32+
DecartClient()
33+
34+
def test_create_client_empty_api_key(self) -> None:
35+
"""Throws an error if api key is empty string."""
36+
with pytest.raises(InvalidAPIKeyError, match="Missing API key"):
37+
DecartClient(api_key="")
38+
39+
def test_create_client_empty_env_var(self, monkeypatch: pytest.MonkeyPatch) -> None:
40+
"""Throws an error if env var is empty string."""
41+
monkeypatch.setenv("DECART_API_KEY", "")
42+
with pytest.raises(InvalidAPIKeyError, match="Missing API key"):
43+
DecartClient()
44+
45+
def test_create_client_whitespace_env_var(self, monkeypatch: pytest.MonkeyPatch) -> None:
46+
"""Throws an error if env var is only whitespace."""
47+
monkeypatch.setenv("DECART_API_KEY", " ")
48+
with pytest.raises(InvalidAPIKeyError, match="Missing API key"):
49+
DecartClient()
50+
51+
def test_create_client_invalid_base_url(self) -> None:
52+
"""Throws an error if invalid base url is provided."""
53+
with pytest.raises(InvalidBaseURLError):
54+
DecartClient(api_key="test-key", base_url="invalid-url")
55+
56+
def test_create_client_custom_base_url(self) -> None:
57+
"""Creates a client with custom base url."""
58+
client = DecartClient(api_key="test-key", base_url="https://custom.decart.ai")
59+
assert client is not None
60+
assert client.base_url == "https://custom.decart.ai"

0 commit comments

Comments
 (0)