diff --git a/.github/workflows/ci-pr.yaml b/.github/workflows/ci-pr.yaml index 2c74ec0c..4ad3582a 100644 --- a/.github/workflows/ci-pr.yaml +++ b/.github/workflows/ci-pr.yaml @@ -17,7 +17,7 @@ jobs: steps: - name: Run nox - uses: frequenz-floss/gh-action-nox@v1.0.0 + uses: frequenz-floss/gh-action-nox@v1.0.1 with: python-version: "3.11" nox-session: ci_checks_max @@ -30,12 +30,12 @@ jobs: uses: frequenz-floss/gh-action-setup-git@v1.0.0 - name: Fetch sources - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: submodules: true - name: Setup Python - uses: frequenz-floss/gh-action-setup-python-with-deps@v1.0.0 + uses: frequenz-floss/gh-action-setup-python-with-deps@v1.0.1 with: python-version: ${{ env.DEFAULT_PYTHON_VERSION }} dependencies: .[dev-mkdocs] diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 55dddda6..8f8355ae 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -45,7 +45,7 @@ jobs: steps: - name: Run nox - uses: frequenz-floss/gh-action-nox@v1.0.0 + uses: frequenz-floss/gh-action-nox@v1.0.1 with: python-version: ${{ matrix.python }} nox-session: ${{ matrix.nox-session }} @@ -79,12 +79,12 @@ jobs: uses: frequenz-floss/gh-action-setup-git@v1.0.0 - name: Fetch sources - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: submodules: true - name: Setup Python - uses: frequenz-floss/gh-action-setup-python-with-deps@v1.0.0 + uses: frequenz-floss/gh-action-setup-python-with-deps@v1.0.1 with: python-version: ${{ env.DEFAULT_PYTHON_VERSION }} dependencies: build @@ -123,7 +123,7 @@ jobs: run: env - name: Download package - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: dist-packages path: dist @@ -143,7 +143,7 @@ jobs: > pyproject.toml - name: Setup Python - uses: frequenz-floss/gh-action-setup-python-with-deps@v1.0.0 + uses: frequenz-floss/gh-action-setup-python-with-deps@v1.0.1 with: python-version: ${{ matrix.python }} dependencies: dist/*.whl @@ -177,12 +177,12 @@ jobs: uses: frequenz-floss/gh-action-setup-git@v1.0.0 - name: Fetch sources - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: submodules: true - name: Setup Python - uses: frequenz-floss/gh-action-setup-python-with-deps@v1.0.0 + uses: frequenz-floss/gh-action-setup-python-with-deps@v1.0.1 with: python-version: ${{ env.DEFAULT_PYTHON_VERSION }} dependencies: .[dev-mkdocs] @@ -213,12 +213,12 @@ jobs: uses: frequenz-floss/gh-action-setup-git@v1.0.0 - name: Fetch sources - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: submodules: true - name: Setup Python - uses: frequenz-floss/gh-action-setup-python-with-deps@v1.0.0 + uses: frequenz-floss/gh-action-setup-python-with-deps@v1.0.1 with: python-version: ${{ env.DEFAULT_PYTHON_VERSION }} dependencies: .[dev-mkdocs] @@ -279,7 +279,7 @@ jobs: runs-on: ubuntu-24.04 steps: - name: Download distribution files - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: dist-packages path: dist @@ -325,7 +325,7 @@ jobs: id-token: write steps: - name: Download distribution files - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: dist-packages path: dist diff --git a/pyproject.toml b/pyproject.toml index e483582a..7e8ca2d4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,8 +4,8 @@ [build-system] requires = [ "setuptools == 80.9.0", - "setuptools_scm[toml] == 8.3.1", - "frequenz-repo-config[lib] == 0.13.4", + "setuptools_scm[toml] == 9.2.0", + "frequenz-repo-config[lib] == 0.13.5", ] build-backend = "setuptools.build_meta" @@ -42,7 +42,6 @@ dependencies = [ "frequenz-client-common >= 0.3.2, < 0.4.0", "grpcio >= 1.72.1, < 2", "protobuf >= 6.31.1, < 7", - "timezonefinder >= 6.2.0, < 7", "typing-extensions >= 4.13.0, < 5", ] dynamic = ["version"] @@ -53,45 +52,45 @@ email = "floss@frequenz.com" [project.optional-dependencies] dev-flake8 = [ - "flake8 == 7.2.0", + "flake8 == 7.3.0", "flake8-docstrings == 1.7.0", "flake8-pyproject == 1.2.3", # For reading the flake8 config from pyproject.toml - "pydoclint == 0.6.6", + "pydoclint == 0.6.11", "pydocstyle == 6.3.0", ] dev-formatting = ["black == 25.1.0", "isort == 6.0.1"] dev-mkdocs = [ "black == 25.1.0", - "Markdown==3.8", + "Markdown==3.8.2", "mike == 2.1.3", "mkdocs-gen-files == 0.5.0", "mkdocs-literate-nav == 0.6.2", - "mkdocs-macros-plugin == 1.3.7", - "mkdocs-material == 9.6.14", - "mkdocstrings[python] == 0.29.1", - "mkdocstrings-python == 1.16.11", - "frequenz-repo-config[lib] == 0.13.4", + "mkdocs-macros-plugin == 1.3.9", + "mkdocs-material == 9.6.18", + "mkdocstrings[python] == 0.30.0", + "mkdocstrings-python == 1.18.2", + "frequenz-repo-config[lib] == 0.13.5", ] dev-mypy = [ - "mypy == 1.16.0", + "mypy == 1.17.1", "grpc-stubs == 1.53.0.6", - "types-Markdown == 3.8.0.20250415", - "types-protobuf == 6.30.2.20250516", + "types-Markdown == 3.8.0.20250809", + "types-protobuf == 6.30.2.20250822", # For checking the noxfile, docs/ script, and tests "frequenz-client-microgrid[dev-mkdocs,dev-noxfile,dev-pytest]", ] -dev-noxfile = ["nox == 2025.5.1", "frequenz-repo-config[lib] == 0.13.4"] +dev-noxfile = ["nox == 2025.5.1", "frequenz-repo-config[lib] == 0.13.5"] dev-pylint = [ - "pylint == 3.3.7", + "pylint == 3.3.8", # For checking the noxfile, docs/ script, and tests "frequenz-client-microgrid[dev-mkdocs,dev-noxfile,dev-pytest]", ] dev-pytest = [ - "pytest == 8.3.5", - "frequenz-repo-config[extra-lint-examples] == 0.13.4", + "pytest == 8.4.1", + "frequenz-repo-config[extra-lint-examples] == 0.13.5", "pytest-mock == 3.14.1", - "pytest-asyncio == 0.26.0", - "async-solipsism == 0.7", + "pytest-asyncio == 1.1.0", + "async-solipsism == 0.8", ] dev = [ "frequenz-client-microgrid[dev-mkdocs,dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-pylint,dev-pytest]", diff --git a/src/frequenz/client/microgrid/_location.py b/src/frequenz/client/microgrid/_location.py index af565e08..fea238ec 100644 --- a/src/frequenz/client/microgrid/_location.py +++ b/src/frequenz/client/microgrid/_location.py @@ -4,15 +4,7 @@ """Location information for a microgrid.""" -import logging from dataclasses import dataclass -from functools import cached_property -from zoneinfo import ZoneInfo - -import timezonefinder - -_timezone_finder = timezonefinder.TimezoneFinder() -_logger = logging.getLogger(__name__) @dataclass(frozen=True, kw_only=True) @@ -28,17 +20,6 @@ class Location: country_code: str | None """The country code of the microgrid in ISO 3166-1 Alpha 2 format.""" - @cached_property - def timezone(self) -> ZoneInfo | None: - """The timezone of the microgrid, or `None` if it could not be determined.""" - if self.latitude is None or self.longitude is None: - _logger.warning( - "Latitude (%s) or longitude (%s) missing, cannot determine timezone" - ) - return None - timezone = _timezone_finder.timezone_at(lat=self.latitude, lng=self.longitude) - return ZoneInfo(key=timezone) if timezone else None - def __str__(self) -> str: """Return the short string representation of this instance.""" country = self.country_code or "" diff --git a/tests/test_location.py b/tests/test_location.py index a63dc5ed..15450edf 100644 --- a/tests/test_location.py +++ b/tests/test_location.py @@ -3,10 +3,7 @@ """Tests for the microgrid metadata types.""" -from collections.abc import Iterator from dataclasses import dataclass -from unittest.mock import MagicMock, patch -from zoneinfo import ZoneInfo import pytest from frequenz.api.common.v1 import location_pb2 @@ -44,43 +41,22 @@ class _ProtoConversionTestCase: # pylint: disable=too-many-instance-attributes """Whether to expect a warning during conversion.""" -@pytest.fixture -def timezone_finder() -> Iterator[MagicMock]: - """Return a mock timezone finder.""" - with patch( - "frequenz.client.microgrid._location._timezone_finder", autospec=True - ) as mock_timezone_finder: - yield mock_timezone_finder - - -def test_timezone_not_looked_up_if_unused(timezone_finder: MagicMock) -> None: - """Test the location timezone is not looked up if it is not used.""" - location = Location(latitude=52.52, longitude=13.405, country_code="DE") - - assert location.latitude == 52.52 - assert location.longitude == 13.405 - assert location.country_code == "DE" - timezone_finder.timezone_at.assert_not_called() - - -def test_timezone_looked_up_but_not_found(timezone_finder: MagicMock) -> None: - """Test the location timezone is not looked up if it is not used.""" - timezone_finder.timezone_at.return_value = None - - location = Location(latitude=52.52, longitude=13.405, country_code="DE") - - assert location.timezone is None - timezone_finder.timezone_at.assert_called_once_with(lat=52.52, lng=13.405) - - -def test_timezone_looked_up_and_found(timezone_finder: MagicMock) -> None: - """Test the location timezone is not looked up if it is not used.""" - timezone_finder.timezone_at.return_value = "Europe/Berlin" - - location = Location(latitude=52.52, longitude=13.405, country_code="DE") +@pytest.mark.parametrize("latitude", [None, 52.52], ids=str) +@pytest.mark.parametrize("longitude", [None, 13.405], ids=str) +@pytest.mark.parametrize("country_code", [None, "DE"], ids=str) +def test_location_initialization( + latitude: float | None, + longitude: float | None, + country_code: str | None, +) -> None: + """Test location initialization with different combinations of parameters.""" + location = Location( + latitude=latitude, longitude=longitude, country_code=country_code + ) - assert location.timezone == ZoneInfo(key="Europe/Berlin") - timezone_finder.timezone_at.assert_called_once_with(lat=52.52, lng=13.405) + assert location.latitude == latitude + assert location.longitude == longitude + assert location.country_code == country_code @pytest.mark.parametrize(