Skip to content

Commit e391c3a

Browse files
committed
requests->httpx
1 parent 26d352b commit e391c3a

File tree

6 files changed

+91
-134
lines changed

6 files changed

+91
-134
lines changed

dune_client/api/base.py

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@
1111
from json import JSONDecodeError
1212
from typing import IO, TYPE_CHECKING, Any
1313

14+
import httpx
1415
from deprecated import deprecated
15-
from requests import Response, Session
16-
from requests.adapters import HTTPAdapter, Retry
1716

1817
from dune_client.util import get_package_version
1918

@@ -53,17 +52,14 @@ def __init__(
5352
self.performance = performance
5453
self.logger = logging.getLogger(__name__)
5554
logging.basicConfig(format="%(asctime)s %(levelname)s %(name)s %(message)s")
56-
retry_strategy = Retry(
57-
total=5,
58-
backoff_factor=0.5,
59-
status_forcelist={429, 502, 503, 504},
60-
allowed_methods={"GET", "POST", "PATCH"},
61-
raise_on_status=True,
55+
56+
# Configure retry transport for httpx
57+
transport = httpx.HTTPTransport(retries=5)
58+
self.http = httpx.Client(
59+
base_url=base_url,
60+
timeout=request_timeout,
61+
transport=transport,
6262
)
63-
adapter = HTTPAdapter(max_retries=retry_strategy)
64-
self.http = Session()
65-
self.http.mount("https://", adapter)
66-
self.http.mount("http://", adapter)
6763

6864
@classmethod
6965
@deprecated(
@@ -140,7 +136,7 @@ def _build_parameters(
140136
class BaseRouter(BaseDuneClient):
141137
"""Extending the Base Client with elementary api routing"""
142138

143-
def _handle_response(self, response: Response) -> Any:
139+
def _handle_response(self, response: httpx.Response) -> Any:
144140
"""Generic response handler utilized by all Dune API routes"""
145141
try:
146142
# Some responses can be decoded and converted to DuneErrors
@@ -158,13 +154,12 @@ def _handle_response(self, response: Response) -> Any:
158154

159155
def _route_url(self, route: str | None = None, url: str | None = None) -> str:
160156
if route is not None:
161-
final_url = f"{self.base_url}{self.api_version}{route}"
162-
elif url is not None:
163-
final_url = url
164-
else:
165-
assert route is not None or url is not None
166-
167-
return final_url
157+
# Return relative path - httpx.Client will prepend base_url
158+
return f"{self.api_version}{route}"
159+
if url is not None:
160+
# Absolute URL provided (e.g., for pagination next_uri)
161+
return url
162+
raise ValueError("Either route or url must be provided")
168163

169164
def _get(
170165
self,
@@ -197,12 +192,12 @@ def _post(
197192
"""Generic interface for the POST method of a Dune API request"""
198193
url = self._route_url(route)
199194
self.logger.debug(f"POST received input url={url}, params={params}")
195+
merged_headers = dict(self.default_headers(), **headers if headers else {})
200196
response = self.http.post(
201197
url=url,
202198
json=params,
203-
headers=dict(self.default_headers(), **headers if headers else {}),
204-
timeout=self.request_timeout,
205-
data=data,
199+
headers=merged_headers,
200+
content=data,
206201
)
207202
return self._handle_response(response)
208203

@@ -214,7 +209,6 @@ def _patch(self, route: str, params: Any) -> Any:
214209
url=url,
215210
json=params,
216211
headers=self.default_headers(),
217-
timeout=self.request_timeout,
218212
)
219213
return self._handle_response(response)
220214

@@ -225,6 +219,5 @@ def _delete(self, route: str) -> Any:
225219
response = self.http.delete(
226220
url=url,
227221
headers=self.default_headers(),
228-
timeout=self.request_timeout,
229222
)
230223
return self._handle_response(response)

dune_client/api/execution.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,6 @@ def _get_execution_results_by_url(
131131
"""
132132
GET results from Dune API with a given URL. This is particularly useful for pagination.
133133
"""
134-
assert url.startswith(self.base_url)
135-
136134
response_json = self._get(url=url, params=params)
137135
try:
138136
result = ResultsResponse.from_dict(response_json)
@@ -159,8 +157,6 @@ def _get_execution_results_csv_by_url(
159157
use this method for large results where you want lower CPU and memory overhead
160158
if you need metadata information use get_results() or get_status()
161159
"""
162-
assert url.startswith(self.base_url)
163-
164160
response = self._get(url=url, params=params, raw=True)
165161
response.raise_for_status()
166162
next_uri = response.headers.get(DUNE_CSV_NEXT_URI_HEADER)

pyproject.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ dependencies = [
2323
"aiohttp~=3.12.15",
2424
"dataclasses-json~=0.6.4",
2525
"python-dateutil~=2.9.0",
26-
"requests~=2.32.5",
26+
"httpx~=0.28.1",
2727
"ndjson~=0.3.1",
2828
"Deprecated~=1.2.18",
2929
]
@@ -43,7 +43,6 @@ dev = [
4343
"tox-uv>=1.28.0",
4444
# Type stubs
4545
"types-python-dateutil>=2.8.19.14",
46-
"types-requests>=2.28.0",
4746
"types-Deprecated>=1.2.9.3",
4847
# Optional features
4948
"pandas>=1.0.0",

tests/e2e/test_client.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
import warnings
55
from pathlib import Path
66

7+
import httpx
78
import pandas as pd
89
import pytest
9-
from requests.exceptions import HTTPError
1010

1111
from dune_client.client import DuneClient
1212
from dune_client.models import (
@@ -162,13 +162,13 @@ def test_cancel_execution(self):
162162

163163
def test_invalid_api_key_error(self):
164164
dune = DuneClient(api_key="Invalid Key")
165-
with pytest.raises(HTTPError) as err:
165+
with pytest.raises(httpx.HTTPStatusError) as err:
166166
dune.execute_query(self.query)
167167
assert err.value.response.status_code == 401
168-
with pytest.raises(HTTPError) as err:
168+
with pytest.raises(httpx.HTTPStatusError) as err:
169169
dune.get_execution_status("wonky job_id")
170170
assert err.value.response.status_code == 401
171-
with pytest.raises(HTTPError) as err:
171+
with pytest.raises(httpx.HTTPStatusError) as err:
172172
dune.get_execution_results("wonky job_id")
173173
assert err.value.response.status_code == 401
174174

@@ -177,7 +177,7 @@ def test_query_not_found_error(self):
177177
query = copy.copy(self.query)
178178
query.query_id = 99999999 # Invalid Query Id.
179179

180-
with pytest.raises(HTTPError) as err:
180+
with pytest.raises(httpx.HTTPStatusError) as err:
181181
dune.execute_query(query)
182182
assert err.value.response.status_code == 404
183183

@@ -187,13 +187,13 @@ def test_internal_error(self):
187187
# This query ID is too large!
188188
query.query_id = 9999999999999
189189

190-
with pytest.raises(HTTPError) as err:
190+
with pytest.raises(httpx.HTTPStatusError) as err:
191191
dune.execute_query(query)
192192
assert err.value.response.status_code == 500
193193

194194
def test_invalid_job_id_error(self):
195195
dune = DuneClient()
196-
with pytest.raises(HTTPError) as err:
196+
with pytest.raises(httpx.HTTPStatusError) as err:
197197
dune.get_execution_status("Wonky Job ID")
198198
assert err.value.response.status_code == 400
199199

@@ -246,7 +246,7 @@ def test_create_table_error(self):
246246

247247
namespace = "test"
248248
table_name = "table"
249-
with pytest.raises(HTTPError) as err:
249+
with pytest.raises(httpx.HTTPStatusError) as err:
250250
client.create_table(
251251
namespace=namespace,
252252
table_name=table_name,

tests/unit/test_utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66

77
class TestUtils(unittest.TestCase):
88
def test_package_version_some(self):
9-
version_string = get_package_version("requests")
9+
version_string = get_package_version("httpx")
1010
parsed_version = list(map(int, version_string.split(".")))
11-
assert parsed_version >= [2, 31, 0]
11+
assert parsed_version >= [0, 28, 0]
1212

1313
def test_package_version_none(self):
1414
# Can't self refer (this should only work when user has dune-client installed).

0 commit comments

Comments
 (0)