Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Increase client timeouts and tweak response_fields behaviour #1514

Merged
merged 4 commits into from
Feb 16, 2023
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
19 changes: 18 additions & 1 deletion optimade/client/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@
default=None,
nargs=-1,
)
@click.option(
"--http-timeout",
type=float,
help="The timeout to use for each HTTP request.",
)
def get(
use_async,
filter,
Expand All @@ -87,6 +92,7 @@ def get(
include_providers,
exclude_providers,
exclude_databases,
http_timeout,
):
return _get(
use_async,
Expand All @@ -102,6 +108,7 @@ def get(
include_providers,
exclude_providers,
exclude_databases,
http_timeout,
)


Expand All @@ -119,6 +126,7 @@ def _get(
include_providers,
exclude_providers,
exclude_databases,
http_timeout,
**kwargs,
):
if output_file:
Expand All @@ -130,7 +138,7 @@ def _get(
f"Desired output file {output_file} already exists, not overwriting."
)

client = OptimadeClient(
args = dict(
base_urls=base_url,
use_async=use_async,
max_results_per_provider=max_results_per_provider,
Expand All @@ -143,6 +151,15 @@ def _get(
exclude_databases=set(_.strip() for _ in exclude_databases.split(","))
if exclude_databases
else None,
)

# Only set http timeout if its not null to avoid overwriting or duplicating the
# default value set on the OptimadeClient class
if http_timeout:
args["http_timeout"] = http_timeout

client = OptimadeClient(
**args,
**kwargs,
)
if response_fields:
Expand Down
37 changes: 26 additions & 11 deletions optimade/client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class OptimadeClient:
headers: Dict = {"User-Agent": f"optimade-python-tools/{__version__}"}
"""Additional HTTP headers."""

http_timeout: int
http_timeout: httpx.Timeout = httpx.Timeout(10.0, read=1000.0)
"""The timeout to use for each HTTP request."""

max_attempts: int
Expand Down Expand Up @@ -106,7 +106,7 @@ def __init__(
base_urls: Optional[Union[str, Iterable[str]]] = None,
max_results_per_provider: int = 1000,
headers: Optional[Dict] = None,
http_timeout: int = 10,
http_timeout: Optional[Union[httpx.Timeout, float]] = None,
max_attempts: int = 5,
use_async: bool = True,
exclude_providers: Optional[List[str]] = None,
Expand All @@ -121,7 +121,9 @@ def __init__(
max_results_per_provider: The maximum number of results to download
from each provider.
headers: Any additional HTTP headers to use for the queries.
http_timeout: The HTTP timeout to use per request.
http_timeout: The timeout to use per request. Defaults to 10
seconds with 1000 seconds for reads specifically. Overriding this value
will replace all timeouts (connect, read, write and pool) with this value.
max_attempts: The maximum number of times to repeat a failing query.
use_async: Whether or not to make all requests asynchronously.
exclude_providers: A set or collection of provider IDs to exclude from queries.
Expand Down Expand Up @@ -165,7 +167,12 @@ def __init__(
if headers:
self.headers.update(headers)

self.http_timeout = http_timeout
if http_timeout:
if isinstance(http_timeout, httpx.Timeout):
self.http_timeout = http_timeout
else:
self.http_timeout = httpx.Timeout(http_timeout)

self.max_attempts = max_attempts

self.use_async = use_async
Expand Down Expand Up @@ -309,7 +316,7 @@ def count(
endpoint,
page_limit=1,
paginate=False,
response_fields=None,
response_fields=[],
sort=None,
)
count_results = {}
Expand All @@ -321,7 +328,7 @@ def count(

if count_results[base_url] is None:
self._progress.print(
f"Warning: {base_url} did not return a value for `meta->data_returned`, unable to count results."
f"Warning: {base_url} did not return a value for `meta->data_returned`, unable to count results. Full response: {results[base_url]}"
)

self.count_results[endpoint][filter] = count_results
Expand Down Expand Up @@ -429,7 +436,9 @@ def get_one(
)
except (RuntimeError, httpx.TimeoutException, json.JSONDecodeError) as exc:
error_query_results = QueryResults()
error_query_results.errors = [str(exc)]
error_query_results.errors = [
f"{exc.__class__.__name__}: {str(exc.args[0])}"
]
self._progress.print(
f"[red]Error[/red]: Provider {str(base_url)!r} returned: [red i]{exc}[/red i]"
)
Expand Down Expand Up @@ -574,9 +583,11 @@ async def get_one_async(
Exception,
) as exc:
error_query_results = QueryResults()
error_query_results.errors = [str(exc)]
error_query_results.errors = [
f"{exc.__class__.__name__}: {str(exc.args[0])}"
]
self._progress.print(
f"[red]Error[/red]: Provider {str(base_url)!r} returned: [red i]{exc}[/red i]"
f"[red]Error[/red]: Provider {str(base_url)!r} returned: [red i]{error_query_results.errors}[/red i]"
)
return {base_url: error_query_results}

Expand Down Expand Up @@ -765,8 +776,12 @@ def _build_url(

if filter:
_filter = f"filter={filter}"
if response_fields:
_response_fields = f'response_fields={",".join(response_fields)}'
if response_fields is not None:
# If we have requested no response fields (e.g., in the case of --count) then just ask for IDs
if len(response_fields) == 0:
_response_fields = "response_fields=id"
else:
_response_fields = f'response_fields={",".join(response_fields)}'
if page_limit:
_page_limit = f"page_limit={page_limit}"
if sort:
Expand Down
3 changes: 3 additions & 0 deletions tests/server/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ def test_client_sort(http_client, use_async):

@pytest.mark.parametrize("use_async", [False])
def test_command_line_client(http_client, use_async, capsys):
import httpx

from optimade.client.cli import _get

args = dict(
Expand All @@ -174,6 +176,7 @@ def test_command_line_client(http_client, use_async, capsys):
exclude_providers=None,
exclude_databases=None,
http_client=http_client,
http_timeout=httpx.Timeout(2.0),
)

# Test multi-provider query
Expand Down