From 576d6d7b397ab2ac1e7a4b1ded4e23cc4fb1c796 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 31 Jul 2025 08:10:39 +0000 Subject: [PATCH] Remove timezonefinder dependency and automatic timezone lookup Remove the `timezonefinder` dependency to significantly reduce package size by 66M+ and improve ARM platform support, as no pre-built wheels are available anymore in PyPI. Signed-off-by: Leandro Lucarella Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- RELEASE_NOTES.md | 27 ++++++++--- pyproject.toml | 1 - src/frequenz/client/microgrid/_metadata.py | 21 +-------- tests/test_metadata.py | 54 +++------------------- 4 files changed, 29 insertions(+), 74 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 80e5f0cb..33e27ce8 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,16 +2,31 @@ ## Summary - +This release removes the `timezonefinder` dependency to significantly reduce package size by 66M+ and enable ARM platform support. + +> [!WARNING] +> This is a **breaking change** shipped in a patch release because this feature has no known users. ## Upgrading - +The `Location.timezone` field no longer performs automatic timezone lookup from latitude/longitude coordinates. -## New Features +```python +# Automatic timezone lookup (no longer works) +location = Location(latitude=52.52, longitude=13.405) +print(location.timezone) # Previously: ZoneInfo('Europe/Berlin'), now None +``` - +If you need timezone lookup from coordinates, install [`timezonefinder`](https://pypi.org/project/timezonefinder/) separately and implement manual lookup: -## Bug Fixes +```python +# Install: pip install timezonefinder +from timezonefinder import TimezoneFinder +from zoneinfo import ZoneInfo +from frequenz.client.microgrid import Location - +tf = TimezoneFinder() +tz_name = tf.timezone_at(lat=52.52, lng=13.405) +timezone = ZoneInfo(tz_name) if tz_name else None +location = Location(latitude=52.52, longitude=13.405, timezone=timezone) +``` diff --git a/pyproject.toml b/pyproject.toml index ed4496ea..fe9fcee4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,7 +42,6 @@ dependencies = [ "frequenz-client-common >= 0.3.2, < 0.4.0", "grpcio >= 1.63.0, < 2", "protobuf >= 5.26.1, < 7", - "timezonefinder >= 6.2.0, < 7", "typing-extensions >= 4.13.0, < 5", ] dynamic = ["version"] diff --git a/src/frequenz/client/microgrid/_metadata.py b/src/frequenz/client/microgrid/_metadata.py index 135dafca..b48c8fd6 100644 --- a/src/frequenz/client/microgrid/_metadata.py +++ b/src/frequenz/client/microgrid/_metadata.py @@ -7,9 +7,6 @@ from zoneinfo import ZoneInfo from frequenz.client.common.microgrid import MicrogridId -from timezonefinder import TimezoneFinder - -_timezone_finder = TimezoneFinder() @dataclass(frozen=True, kw_only=True) @@ -23,23 +20,7 @@ class Location: """The longitude of the microgrid in degree.""" timezone: ZoneInfo | None = None - """The timezone of the microgrid. - - If not passed during construction (or `None` is passed), and there is a `longitude` - and `latitude`, then the timezone wil be looked up in a database based on the - coordinates. This lookup could fail, in which case the timezone will still be - `None`. - """ - - def __post_init__(self) -> None: - """Initialize the timezone of the microgrid.""" - if self.latitude is None or self.longitude is None or self.timezone is not None: - return - - timezone = _timezone_finder.timezone_at(lat=self.latitude, lng=self.longitude) - if timezone: - # The dataclass is frozen, so it needs to use __setattr__ to set the timezone. - object.__setattr__(self, "timezone", ZoneInfo(key=timezone)) + """The timezone of the microgrid.""" @dataclass(frozen=True, kw_only=True) diff --git a/tests/test_metadata.py b/tests/test_metadata.py index 1276d2b5..b14eb35f 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -3,8 +3,6 @@ """Tests for the microgrid metadata types.""" -from collections.abc import Iterator -from unittest.mock import MagicMock, patch from zoneinfo import ZoneInfo import pytest @@ -13,59 +11,20 @@ from frequenz.client.microgrid import Location, Metadata -@pytest.fixture -def timezone_finder() -> Iterator[MagicMock]: - """Return a mock timezone finder.""" - with patch( - "frequenz.client.microgrid._metadata._timezone_finder", autospec=True - ) as mock_timezone_finder: - yield mock_timezone_finder - - -@pytest.mark.parametrize( - "latitude, longitude, timezone", - [ - (None, None, None), - (52.52, None, None), - (None, 13.405, None), - (None, None, ZoneInfo(key="UTC")), - (52.52, None, ZoneInfo(key="UTC")), - (None, 13.405, ZoneInfo(key="UTC")), - (52.52, 13.405, ZoneInfo(key="UTC")), - ], - ids=str, -) -def test_location_timezone_not_looked_up_if_not_possible_or_necessary( - timezone_finder: MagicMock, +@pytest.mark.parametrize("latitude", [None, 52.52], ids=str) +@pytest.mark.parametrize("longitude", [None, 13.405], ids=str) +@pytest.mark.parametrize("timezone", [None, ZoneInfo(key="UTC")], ids=str) +def test_location_initialization( latitude: float | None, longitude: float | None, timezone: ZoneInfo | None, ) -> None: - """Test the location timezone is not looked up if is not necessary or possible.""" - timezone_finder.timezone_at.return_value = "Europe/Berlin" - + """Test location initialization with different combinations of parameters.""" location = Location(latitude=latitude, longitude=longitude, timezone=timezone) assert location.latitude == latitude assert location.longitude == longitude assert location.timezone == timezone - timezone_finder.timezone_at.assert_not_called() - - -@pytest.mark.parametrize("timezone", [None, "Europe/Berlin"], ids=str) -def test_location_timezone_lookup( - timezone_finder: MagicMock, timezone: str | None -) -> None: - """Test the location timezone is looked up if not provided and there is enough info.""" - timezone_finder.timezone_at.return_value = timezone - - location = Location(latitude=52.52, longitude=13.405) - - if timezone is None: - assert location.timezone is None - else: - assert location.timezone == ZoneInfo(key=timezone) - timezone_finder.timezone_at.assert_called_once_with(lat=52.52, lng=13.405) def test_metadata_initialization() -> None: @@ -81,11 +40,12 @@ def test_metadata_initialization() -> None: assert metadata.microgrid_id == microgrid_id assert metadata.location is None - # Test with only location + # Test with only location - timezone should be None even with lat/lng location = Location(latitude=52.52, longitude=13.405) metadata = Metadata(location=location) assert metadata.microgrid_id is None assert metadata.location == location + assert metadata.location.timezone is None # Test with both parameters metadata = Metadata(microgrid_id=microgrid_id, location=location)