Skip to content

Commit

Permalink
Refactor the code by linting with Ruff
Browse files Browse the repository at this point in the history
  • Loading branch information
klaasnicolaas committed May 2, 2024
1 parent ccc28fc commit 906200b
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 46 deletions.
31 changes: 19 additions & 12 deletions examples/estimate.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
"""Example of how to get an estimate from the Forecast.Solar API."""

import asyncio
import dataclasses
from datetime import datetime, timezone, timedelta
from pprint import pprint
from datetime import UTC, datetime, timedelta
from pprint import pprint # noqa: F401

from forecast_solar import ForecastSolar, ForecastSolarRatelimitError


async def main():
"""Simple function to test the output."""
async def main() -> None:
"""Get an estimate from the Forecast.Solar API."""
async with ForecastSolar(
latitude=52.16,
longitude=4.47,
Expand All @@ -22,7 +23,7 @@ async def main():
except ForecastSolarRatelimitError as err:
print("Ratelimit reached")
print(f"Rate limit resets at {err.reset_at}")
reset_period = err.reset_at - datetime.now(timezone.utc)
reset_period = err.reset_at - datetime.now(UTC)
# Strip microseconds as they are not informative
reset_period -= timedelta(microseconds=reset_period.microseconds)
print(f"That's in {reset_period}")
Expand All @@ -33,28 +34,34 @@ async def main():
print()
print(f"energy_production_today: {estimate.energy_production_today}")
print(
f"energy_production_today_remaining: {estimate.energy_production_today_remaining}"
f"energy_production_today_remaining: "
f"{estimate.energy_production_today_remaining}"
)
print(
f"power_highest_peak_time_today: {estimate.power_highest_peak_time_today}"
)
print(f"energy_production_tomorrow: {estimate.energy_production_tomorrow}")
print(
f"power_highest_peak_time_tomorrow: {estimate.power_highest_peak_time_tomorrow}"
f"power_highest_peak_time_tomorrow: "
f"{estimate.power_highest_peak_time_tomorrow}"
)
print()
print(f"power_production_now: {estimate.power_production_now}")
print(
f"power_production in 1 hour: {estimate.power_production_at_time(estimate.now() + timedelta(hours=1))}"
f"power_production in 1 hour: "
f"{estimate.power_production_at_time(estimate.now() + timedelta(hours=1))}"
)
print(
f"power_production in 6 hours: {estimate.power_production_at_time(estimate.now() + timedelta(hours=6))}"
f"power_production in 6 hours: "
f"{estimate.power_production_at_time(estimate.now() + timedelta(hours=6))}"
)
print(
f"power_production in 12 hours: {estimate.power_production_at_time(estimate.now() + timedelta(hours=12))}"
f"power_production in 12 hours: "
f"{estimate.power_production_at_time(estimate.now() + timedelta(hours=12))}"
)
print(
f"power_production in 24 hours: {estimate.power_production_at_time(estimate.now() + timedelta(hours=24))}"
f"power_production in 24 hours: "
f"{estimate.power_production_at_time(estimate.now() + timedelta(hours=24))}"
)
print()
print(f"energy_current_hour: {estimate.energy_current_hour}")
Expand Down
3 changes: 2 additions & 1 deletion examples/ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
extend = "../pyproject.toml"

lint.extend-ignore = [
"T201", # Allow the use of print() in examples
"T201", # Allow the use of print() in examples
"ERA001", # Allow the use of comments in examples
]
9 changes: 5 additions & 4 deletions src/forecast_solar/__init__.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
"""Asynchronous Python client for the Forecast.Solar API."""

from .exceptions import (
ForecastSolarError,
ForecastSolarConnectionError,
ForecastSolarConfigError,
ForecastSolarAuthenticationError,
ForecastSolarConfigError,
ForecastSolarConnectionError,
ForecastSolarError,
ForecastSolarRatelimit,
ForecastSolarRequestError,
ForecastSolarRatelimitError,
)
from .models import Estimate, AccountType, Ratelimit
from .forecast_solar import ForecastSolar
from .models import AccountType, Estimate, Ratelimit

__all__ = [
"AccountType",
Expand Down
39 changes: 25 additions & 14 deletions src/forecast_solar/forecast_solar.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from __future__ import annotations

from dataclasses import dataclass
from typing import Any
from typing import Any, Self

from aiodns import DNSResolver
from aiodns.error import DNSError
Expand Down Expand Up @@ -46,8 +46,8 @@ async def _request(
self,
uri: str,
*,
rate_limit=True,
authenticate=True,
rate_limit: bool = True,
authenticate: bool = True,
params: dict[str, Any] | None = None,
) -> dict[str, Any]:
"""Handle a request to the Forecast.Solar API.
Expand All @@ -56,17 +56,20 @@ async def _request(
the Forecast.Solar API.
Args:
----
uri: Request URI, for example, 'estimate'
rate_limit: Parse rate limit from response. Set to False for
endpoints that are missing rate limiting headers in response.
authenticate: Prefix request with api_key. Set to False for
endpoints that do not provide authentication.
Returns:
-------
A Python dictionary (JSON decoded) with the response from
the Forecast.Solar API.
Raises:
------
ForecastSolarAuthenticationError: If the API key is invalid.
ForecastSolarConnectionError: An error occurred while communicating
with the Forecast.Solar API.
Expand All @@ -76,8 +79,8 @@ async def _request(
variables used in the request.
ForecastSolarRatelimitError: The number of requests has exceeded
the rate limit of the Forecast.Solar API.
"""
"""
# Forecast.Solar is currently experiencing IPv6 issues.
# However, their DNS does return an non-working IPv6 address.
# This ensures we use the IPv4 address.
Expand Down Expand Up @@ -150,12 +153,13 @@ async def _request(
return await response.json()

async def validate_plane(self) -> bool:
"""Validate plane by calling the Forecast.Solar API
"""Validate plane by calling the Forecast.Solar API.
Returns:
Returns
-------
True, if plane is valid.
"""
"""
await self._request(
f"check/{self.latitude}/{self.longitude}"
f"/{self.declination}/{self.azimuth}/{self.kwp}",
Expand All @@ -166,21 +170,24 @@ async def validate_plane(self) -> bool:
return True

async def validate_api_key(self) -> bool:
"""Validate api key by calling the Forecast.Solar API
"""Validate api key by calling the Forecast.Solar API.
Returns:
Returns
-------
True, if api key is valid
"""
"""
await self._request("info", rate_limit=False)

return True

async def estimate(self) -> Estimate:
"""Get solar production estimations from the Forecast.Solar API.
Returns:
Returns
-------
A Estimate object, with a estimated production forecast.
"""
params = {"time": "iso8601", "damping": str(self.damping)}
if self.inverter is not None:
Expand All @@ -203,18 +210,22 @@ async def close(self) -> None:
if self.session and self._close_session:
await self.session.close()

async def __aenter__(self) -> ForecastSolar:
async def __aenter__(self) -> Self:
"""Async enter.
Returns:
Returns
-------
The ForecastSolar object.
"""
return self

async def __aexit__(self, *_exc_info) -> None:
async def __aexit__(self, *_exc_info: object) -> None:
"""Async exit.
Args:
----
_exc_info: Exec type.
"""
await self.close()
18 changes: 12 additions & 6 deletions src/forecast_solar/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

from __future__ import annotations

from zoneinfo import ZoneInfo
from dataclasses import dataclass
from datetime import datetime, timedelta, date
from datetime import date, datetime, timedelta
from enum import Enum
from typing import Any
from typing import TYPE_CHECKING, Any
from zoneinfo import ZoneInfo

from aiohttp import ClientResponse
if TYPE_CHECKING:
from aiohttp import ClientResponse


def _timed_value(at: datetime, data: dict[datetime, int]) -> int | None:
Expand All @@ -26,7 +27,6 @@ def _interval_value_sum(
interval_begin: datetime, interval_end: datetime, data: dict[datetime, int]
) -> int:
"""Return the sum of values in interval."""

total = 0

for timestamp, wh in data.items():
Expand Down Expand Up @@ -54,10 +54,12 @@ class AccountType(str, Enum):
class Estimate:
"""Object holding estimate forecast results from Forecast.Solar.
Attributes:
Attributes
----------
watts: Estimated solar power output per time period.
wh_period: Estimated solar energy production differences per hour.
wh_days: Estimated solar energy production per day.
"""

watts: dict[datetime, int]
Expand Down Expand Up @@ -148,6 +150,7 @@ def peak_production_time(self, specific_date: date) -> datetime:
) in self.watts.items():
if watt == value:
return timestamp
return None

def power_production_at_time(self, time: datetime) -> int:
"""Return estimated power production at a specific time."""
Expand All @@ -168,10 +171,13 @@ def from_dict(cls: type[Estimate], data: dict[str, Any]) -> Estimate:
a Estimate object.
Args:
----
data: The estimate response from the Forecast.Solar API.
Returns:
-------
An Estimate object.
"""
return cls(
watts={
Expand Down
4 changes: 3 additions & 1 deletion tests/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
ForecastSolarRatelimitError,
ForecastSolarConfigError,
ForecastSolarAuthenticationError,
ForecastSolarRequestError,
ForecastSolarConfigError,
ForecastSolarConnectionError,
ForecastSolarRatelimit,
ForecastSolarRequestError,
)

from . import load_fixtures
Expand Down
6 changes: 1 addition & 5 deletions tests/test_forecast.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
"""Tests for Forecast.Solar."""

# pylint: disable=protected-access
import asyncio
from unittest.mock import patch

import pytest
from aiohttp import ClientError, ClientResponse, ClientSession
from aresponses import Response, ResponsesMockServer
from aresponses import ResponsesMockServer

from forecast_solar import (
ForecastSolar,
ForecastSolarConnectionError,
ForecastSolarError,
)

Expand Down
6 changes: 3 additions & 3 deletions tests/test_models.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"""Test the models."""

import pytest
from datetime import datetime

import pytest
from aresponses import ResponsesMockServer
from syrupy.assertion import SnapshotAssertion

from forecast_solar import ForecastSolar, Estimate, AccountType

from forecast_solar import AccountType, Estimate, ForecastSolar

from . import load_fixtures

Expand Down

0 comments on commit 906200b

Please sign in to comment.