Skip to content

Commit 013e789

Browse files
author
paperclip-resolver[bot]
committed
fix: normalize Python SDK base URL
1 parent 4d4ff0c commit 013e789

5 files changed

Lines changed: 36 additions & 3 deletions

File tree

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,4 @@ build/
1414
htmlcov/
1515
.claude/
1616
.DS_Store
17-
tests/
1817
.benchmarks/

src/sharpapi/_base.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,15 @@
3333
RETRY_MAX_DELAY = 4.0
3434

3535

36+
def normalize_base_url(base_url: str) -> str:
37+
"""Return the API origin URL, accepting values with a trailing /api/v1."""
38+
cleaned = base_url.rstrip("/")
39+
suffix = "/api/v1"
40+
if cleaned.endswith(suffix):
41+
return cleaned[: -len(suffix)]
42+
return cleaned
43+
44+
3645
def should_retry(response: httpx.Response | None, exc: Exception | None) -> bool:
3746
"""True for transient upstream failures worth retrying."""
3847
if exc is not None:

src/sharpapi/async_client.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
AuthMethod,
1616
handle_errors,
1717
make_headers,
18+
normalize_base_url,
1819
parse_rate_limit,
1920
parse_response,
2021
retry_delay,
@@ -89,7 +90,7 @@ def __init__(
8990

9091
self._api_key = api_key
9192
self._auth_method: AuthMethod = auth_method
92-
self._base_url = base_url.rstrip("/")
93+
self._base_url = normalize_base_url(base_url)
9394
self._timeout = timeout
9495
self._http = httpx.AsyncClient(
9596
base_url=f"{self._base_url}/api/v1",

src/sharpapi/client.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
AuthMethod,
1616
handle_errors,
1717
make_headers,
18+
normalize_base_url,
1819
parse_rate_limit,
1920
parse_response,
2021
retry_delay,
@@ -97,7 +98,7 @@ def __init__(
9798

9899
self._api_key = api_key
99100
self._auth_method: AuthMethod = auth_method
100-
self._base_url = base_url.rstrip("/")
101+
self._base_url = normalize_base_url(base_url)
101102
self._timeout = timeout
102103
self._http = httpx.Client(
103104
base_url=f"{self._base_url}/api/v1",

tests/test_base_url.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from sharpapi import AsyncSharpAPI, SharpAPI
2+
3+
4+
def test_sync_client_accepts_api_v1_base_url_without_double_prefix():
5+
client = SharpAPI("sk_test", base_url="https://api.sharpapi.io/api/v1/")
6+
7+
assert str(client._http.base_url) == "https://api.sharpapi.io/api/v1/"
8+
assert client._base_url == "https://api.sharpapi.io"
9+
10+
stream = client.stream.odds()
11+
assert stream._url.startswith("https://api.sharpapi.io/api/v1/stream?")
12+
assert "/api/v1/api/v1/" not in stream._url
13+
14+
client.close()
15+
16+
17+
async def test_async_client_accepts_api_v1_base_url_without_double_prefix():
18+
client = AsyncSharpAPI("sk_test", base_url="https://api.sharpapi.io/api/v1/")
19+
20+
assert str(client._http.base_url) == "https://api.sharpapi.io/api/v1/"
21+
assert client._base_url == "https://api.sharpapi.io"
22+
23+
await client.close()

0 commit comments

Comments
 (0)