python-google-weather-api 0.0.8__tar.gz → 0.0.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (17) hide show
  1. {python_google_weather_api-0.0.8/src/python_google_weather_api.egg-info → python_google_weather_api-0.0.9}/PKG-INFO +1 -1
  2. {python_google_weather_api-0.0.8 → python_google_weather_api-0.0.9}/pyproject.toml +1 -1
  3. {python_google_weather_api-0.0.8 → python_google_weather_api-0.0.9}/src/google_weather_api/api.py +6 -3
  4. {python_google_weather_api-0.0.8 → python_google_weather_api-0.0.9/src/python_google_weather_api.egg-info}/PKG-INFO +1 -1
  5. {python_google_weather_api-0.0.8 → python_google_weather_api-0.0.9}/tests/test_api.py +20 -2
  6. {python_google_weather_api-0.0.8 → python_google_weather_api-0.0.9}/LICENSE +0 -0
  7. {python_google_weather_api-0.0.8 → python_google_weather_api-0.0.9}/README.md +0 -0
  8. {python_google_weather_api-0.0.8 → python_google_weather_api-0.0.9}/setup.cfg +0 -0
  9. {python_google_weather_api-0.0.8 → python_google_weather_api-0.0.9}/src/google_weather_api/__init__.py +0 -0
  10. {python_google_weather_api-0.0.8 → python_google_weather_api-0.0.9}/src/google_weather_api/exceptions.py +0 -0
  11. {python_google_weather_api-0.0.8 → python_google_weather_api-0.0.9}/src/google_weather_api/model.py +0 -0
  12. {python_google_weather_api-0.0.8 → python_google_weather_api-0.0.9}/src/google_weather_api/py.typed +0 -0
  13. {python_google_weather_api-0.0.8 → python_google_weather_api-0.0.9}/src/python_google_weather_api.egg-info/SOURCES.txt +0 -0
  14. {python_google_weather_api-0.0.8 → python_google_weather_api-0.0.9}/src/python_google_weather_api.egg-info/dependency_links.txt +0 -0
  15. {python_google_weather_api-0.0.8 → python_google_weather_api-0.0.9}/src/python_google_weather_api.egg-info/requires.txt +0 -0
  16. {python_google_weather_api-0.0.8 → python_google_weather_api-0.0.9}/src/python_google_weather_api.egg-info/top_level.txt +0 -0
  17. {python_google_weather_api-0.0.8 → python_google_weather_api-0.0.9}/tests/test_model.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-google-weather-api
3
- Version: 0.0.8
3
+ Version: 0.0.9
4
4
  Summary: A python client library for Google Weather API
5
5
  Author-email: tronikos <tronikos@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-google-weather-api"
3
- version = "0.0.8"
3
+ version = "0.0.9"
4
4
  license = "Apache-2.0"
5
5
  authors = [
6
6
  { name="tronikos", email="tronikos@gmail.com" },
@@ -23,6 +23,8 @@ from .model import (
23
23
  _LOGGER = logging.getLogger(__name__)
24
24
 
25
25
  _BASE_URL = "https://weather.googleapis.com/v1"
26
+ # See https://docs.cloud.google.com/docs/authentication/api-keys-use
27
+ _API_KEY_HEADER = "X-Goog-Api-Key"
26
28
  _USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36"
27
29
 
28
30
 
@@ -49,7 +51,10 @@ class GoogleWeatherApi:
49
51
  async def _async_get(self, endpoint: str, params: dict[str, Any], *, include_language_code: bool = True) -> dict[str, Any]:
50
52
  """Perform a GET request."""
51
53
  url = f"{_BASE_URL}/{endpoint}"
52
- headers = {aiohttp.hdrs.USER_AGENT: _USER_AGENT}
54
+ # Send the API key in a header rather than the "key" query parameter so it
55
+ # never ends up in a URL, which aiohttp includes in exceptions such as
56
+ # ContentTypeError and which callers commonly log.
57
+ headers = {aiohttp.hdrs.USER_AGENT: _USER_AGENT, _API_KEY_HEADER: self.api_key}
53
58
  if self.referrer:
54
59
  headers[aiohttp.hdrs.REFERER] = self.referrer
55
60
  params = {
@@ -58,9 +63,7 @@ class GoogleWeatherApi:
58
63
  }
59
64
  if include_language_code:
60
65
  params["language_code"] = self.language_code
61
- # Log before adding the API key so it doesn't end up in debug logs.
62
66
  _LOGGER.debug("GET %s with params: %s", url, params)
63
- params["key"] = self.api_key
64
67
  try:
65
68
  async with self.session.get(
66
69
  url,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-google-weather-api
3
- Version: 0.0.8
3
+ Version: 0.0.9
4
4
  Summary: A python client library for Google Weather API
5
5
  Author-email: tronikos <tronikos@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -41,7 +41,8 @@ async def test_request_params_and_headers(api: GoogleWeatherApi, mock_server: Mo
41
41
  await api.async_get_current_conditions(LATITUDE, LONGITUDE)
42
42
 
43
43
  request = mock_server.requests[0]
44
- assert request.query["key"] == API_KEY
44
+ assert request.headers["X-Goog-Api-Key"] == API_KEY
45
+ assert "key" not in request.query
45
46
  assert request.query["location.latitude"] == str(LATITUDE)
46
47
  assert request.query["location.longitude"] == str(LONGITUDE)
47
48
  assert request.query["language_code"] == "en"
@@ -126,7 +127,7 @@ async def test_get_hourly_forecast_follows_pagination(api: GoogleWeatherApi, moc
126
127
  assert first.query["hours"] == "4"
127
128
  assert second.query["page_token"] == page1["nextPageToken"]
128
129
  assert second.query["hours"] == "4"
129
- assert second.query["key"] == API_KEY
130
+ assert second.headers["X-Goog-Api-Key"] == API_KEY
130
131
 
131
132
 
132
133
  async def test_hourly_forecast_stops_once_enough_records(api: GoogleWeatherApi, mock_server: MockServer) -> None:
@@ -238,6 +239,23 @@ async def test_non_json_response(api: GoogleWeatherApi, mock_server: MockServer)
238
239
  await api.async_get_current_conditions(LATITUDE, LONGITUDE)
239
240
 
240
241
 
242
+ async def test_api_key_not_in_error_message(api: GoogleWeatherApi, mock_server: MockServer) -> None:
243
+ """Test the API key does not leak into errors that include the request URL.
244
+
245
+ aiohttp includes the full request URL in ContentTypeError, which callers
246
+ commonly log, e.g. for a 502 HTML page from a proxy in front of the API.
247
+ """
248
+ mock_server.add_response("<html>502 Bad Gateway</html>", status=HTTPStatus.BAD_GATEWAY)
249
+
250
+ with pytest.raises(GoogleWeatherApiConnectionError) as exc_info:
251
+ await api.async_get_current_conditions(LATITUDE, LONGITUDE)
252
+
253
+ assert "currentConditions:lookup" in str(exc_info.value)
254
+ assert API_KEY not in str(exc_info.value)
255
+ # Tracebacks render chained exceptions with str(), so the cause must be clean too.
256
+ assert API_KEY not in str(exc_info.value.__cause__)
257
+
258
+
241
259
  @pytest.mark.parametrize(
242
260
  ("raised", "expected_message"),
243
261
  [