Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ query = QueryBase(
)
print("Results available at", query.url())

dune = DuneClient.from_env()
dune = DuneClient()
results = dune.run_query(query)

# or as CSV
Expand All @@ -84,7 +84,7 @@ You can specify a `max_age_hours` to re-run the query if the data is too outdate
```python
from dune_client.client import DuneClient

dune = DuneClient.from_env()
dune = DuneClient()
results = dune.get_latest_result(1215383, max_age_hours=8)
```

Expand All @@ -108,7 +108,7 @@ sql = """
LIMIT {{N}}
"""

dune = DuneClient.from_env()
dune = DuneClient()
query = dune.create_query(
name="Top {N} Most Expensive Transactions on Ethereum",
query_sql=sql,
Expand Down
22 changes: 14 additions & 8 deletions dune_client/api/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from json import JSONDecodeError
from typing import IO, Any

from deprecated import deprecated
from requests import Response, Session
from requests.adapters import HTTPAdapter, Retry

Expand All @@ -31,12 +32,17 @@ class BaseDuneClient:

def __init__(
self,
api_key: str,
base_url: str = "https://api.dune.com",
request_timeout: float = 10,
api_key: str | None = None,
base_url: str | None = None,
request_timeout: float | None = None,
client_version: str = "v1",
performance: str = "medium",
):
# Read from environment variables if not provided
api_key = api_key or os.environ["DUNE_API_KEY"]
base_url = base_url or os.environ.get("DUNE_API_BASE_URL", "https://api.dune.com")
request_timeout = request_timeout or float(os.environ.get("DUNE_API_REQUEST_TIMEOUT", "10"))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Constructor Fails to Respect Falsy Parameter Values

The BaseDuneClient constructor's use of the or operator for parameter fallback incorrectly overrides explicitly passed falsy values. This means api_key="", base_url="", or request_timeout=0.0 are ignored, falling back to environment variables or defaults instead of using the provided value.

Fix in Cursor Fix in Web


self.token = api_key
self.base_url = base_url
self.request_timeout = request_timeout
Expand All @@ -57,17 +63,17 @@ def __init__(
self.http.mount("http://", adapter)

@classmethod
@deprecated(
version="1.8.0",
reason="Use DuneClient() without any arguments instead, which will automatically read from environment variables",
)
def from_env(cls) -> BaseDuneClient:
"""
Constructor allowing user to instantiate a client from environment variable
without having to import dotenv or os manually
We use `DUNE_API_KEY` as the environment variable that holds the API key.
"""
return cls(
api_key=os.environ["DUNE_API_KEY"],
base_url=os.environ.get("DUNE_API_BASE_URL", "https://api.dune.com"),
request_timeout=float(os.environ.get("DUNE_API_REQUEST_TIMEOUT", "10")),
)
return cls()

@property
def api_version(self) -> str:
Expand Down
6 changes: 3 additions & 3 deletions dune_client/client_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ class AsyncDuneClient(BaseDuneClient):

def __init__(
self,
api_key: str,
base_url: str = "https://api.dune.com",
request_timeout: float = 10,
api_key: str | None = None,
base_url: str | None = None,
request_timeout: float | None = None,
client_version: str = "v1",
performance: str = "medium",
connection_limit: int = 3,
Expand Down
17 changes: 16 additions & 1 deletion tests/e2e/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import os
import time
import unittest
import warnings
from pathlib import Path

import dotenv
Expand Down Expand Up @@ -58,10 +59,24 @@ def copy_query_and_change_parameters(self) -> QueryBase:

def test_from_env_constructor(self):
try:
DuneClient.from_env()
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
DuneClient.from_env()
# Verify that a deprecation warning was raised
assert len(w) == 1
assert issubclass(w[-1].category, DeprecationWarning)
assert "deprecated" in str(w[-1].message).lower()
except KeyError:
self.fail("DuneClient.from_env raised unexpectedly!")

def test_default_constructor_reads_env(self):
"""Test that the default constructor automatically reads from environment variables"""
try:
# This should work the same as from_env() but without the warning
DuneClient()
except KeyError:
self.fail("DuneClient() without arguments should read from environment variables")

def test_get_execution_status(self):
query = QueryBase(name="No Name", query_id=1276442, params=[])
dune = DuneClient(self.valid_api_key)
Expand Down
Loading