From f219ae4472654f9732f7429818f550b85728e519 Mon Sep 17 00:00:00 2001 From: Vincent Gao Date: Fri, 26 Jun 2026 15:39:20 +0200 Subject: [PATCH] Handle empty batch geocode results --- src/geocodio/client.py | 37 ++++++++++++------------- tests/unit/test_geocode.py | 57 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 19 deletions(-) diff --git a/src/geocodio/client.py b/src/geocodio/client.py index 88ee4cd..6d613e7 100644 --- a/src/geocodio/client.py +++ b/src/geocodio/client.py @@ -379,26 +379,25 @@ def _parse_geocoding_response(self, response_json: dict) -> GeocodingResponse: and response_json["results"] and "response" in response_json["results"][0] ): - results = [ - GeocodingResult( - address_components=AddressComponents.from_api( - res["response"]["results"][0]["address_components"] - ), - formatted_address=res["response"]["results"][0][ - "formatted_address" - ], - location=Location(**res["response"]["results"][0]["location"]), - accuracy=res["response"]["results"][0].get("accuracy", 0.0), - accuracy_type=res["response"]["results"][0].get( - "accuracy_type", "" - ), - source=res["response"]["results"][0].get("source", ""), - fields=self._parse_fields( - res["response"]["results"][0].get("fields") - ), + results = [] + for res in response_json["results"]: + response_results = res.get("response", {}).get("results", []) + if not response_results: + continue + result = response_results[0] + results.append( + GeocodingResult( + address_components=AddressComponents.from_api( + result["address_components"] + ), + formatted_address=result["formatted_address"], + location=Location(**result["location"]), + accuracy=result.get("accuracy", 0.0), + accuracy_type=result.get("accuracy_type", ""), + source=result.get("source", ""), + fields=self._parse_fields(result.get("fields")), + ) ) - for res in response_json["results"] - ] return GeocodingResponse(results=results) # Handle single response format diff --git a/tests/unit/test_geocode.py b/tests/unit/test_geocode.py index d95ea67..34f3994 100644 --- a/tests/unit/test_geocode.py +++ b/tests/unit/test_geocode.py @@ -151,6 +151,63 @@ def batch_response_callback(request): assert resp.results[1].location.lat == 39.736792 +def test_geocode_batch_with_invalid_address_result(client, httpx_mock): + addresses = [ + "1109 N Highland St, Arlington VA", + "qwertyuiop asdfghjkl zxcvbnm", + "1600 Pennsylvania Ave NW, Washington DC", + ] + valid_result = sample_payload()["results"][0] + later_valid_result = { + **valid_result, + "formatted_address": "1600 Pennsylvania Ave NW, Washington, DC 20500", + } + + def batch_response_callback(request): + assert request.method == "POST" + assert json.loads(request.content) == addresses + return httpx.Response( + 200, + json={ + "results": [ + { + "query": addresses[0], + "response": {"results": [valid_result]}, + }, + { + "query": addresses[1], + "response": { + "error": "Could not geocode address. No matches found.", + "reference": ( + "https://www.geocod.io/geocodio-422-" + "unprocessable-entity/" + ), + "results": [], + }, + }, + { + "query": addresses[2], + "response": {"results": [later_valid_result]}, + }, + ] + }, + ) + + httpx_mock.add_callback( + callback=batch_response_callback, + url=httpx.URL("https://api.test/v2/geocode"), + match_headers={"Authorization": "Bearer TEST_KEY"}, + ) + + resp = client.geocode(addresses) + + assert len(resp.results) == 2 + assert [result.formatted_address for result in resp.results] == [ + valid_result["formatted_address"], + later_valid_result["formatted_address"], + ] + + def test_geocode_structured_address(client, httpx_mock): # Arrange: stub the API call structured_address = {