Skip to content
Draft
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
10 changes: 5 additions & 5 deletions dataretrieval/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ def query(
url: string
URL to query
payload: dict
query parameters passed to ``httpx.get``
query parameters passed to ``httpx.get``. Not mutated.
delimiter: string
Comment on lines 419 to 423
delimiter to use with lists
ssl_check: bool
Expand All @@ -442,18 +442,18 @@ def query(
``httpx`` exception on ``__cause__``.
"""

for key, value in payload.items():
payload[key] = to_str(value, delimiter)
# Build a fresh params dict; never mutate the caller's payload.
params = {key: to_str(value, delimiter) for key, value in payload.items()}
# httpx serializes None params as ``foo=``; USGS rejects with 400.
# Drop them. (``to_str`` returns None for non-iterable scalars like bools.)
payload = {k: v for k, v in payload.items() if v is not None}
params = {k: v for k, v in params.items() if v is not None}

user_agent = {"user-agent": f"python-dataretrieval/{dataretrieval.__version__}"}

try:
response = _get(
url,
params=payload,
params=params,
headers=user_agent,
verify=ssl_check,
**HTTPX_DEFAULTS,
Expand Down
12 changes: 12 additions & 0 deletions tests/utils_test.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Unit tests for functions in utils.py"""

import copy
from unittest import mock

import pandas as pd
Expand Down Expand Up @@ -33,6 +34,17 @@ def test_header(self, httpx_mock):
assert response.status_code == 200 # GET was successful
assert "user-agent" in response.request.headers

def test_does_not_mutate_caller_payload(self, httpx_mock):
"""query() builds its own params dict and must not mutate the caller's
payload, which it previously stringified in place (so a reused dict
carried delimiter-joined values into the next call)."""
httpx_mock.add_response(method="GET", json={"value": {}})
url = "https://waterservices.usgs.gov/nwis/dv"
payload = {"sites": ["01646500", "01646501"], "format": "json"}
original = copy.deepcopy(payload)
utils.query(url, payload)
assert payload == original


class Test_error_taxonomy:
"""The unified request-error hierarchy.
Expand Down