Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Address Ruff's rules, add few enhancements #88

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 30 additions & 7 deletions custom_components/feedparser/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@


async def async_setup_platform(
hass: HomeAssistant,
hass: HomeAssistant, # noqa: ARG001
config: ConfigType,
async_add_devices: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
discovery_info: DiscoveryInfoType | None = None, # noqa: ARG001
) -> None:
"""Set up the Feedparser sensor."""
async_add_devices(
Expand Down Expand Up @@ -92,6 +92,7 @@ def __init__(
scan_interval: timedelta,
local_time: bool,
) -> None:
"""Initialize the Feedparser sensor."""
self._feed = feed
self._attr_name = name
self._attr_icon = "mdi:rss"
Expand All @@ -112,6 +113,7 @@ def update(self: FeedParserSensor) -> None:
self._attr_native_value = None
return

# set the sensor value to the amount of entries
self._attr_native_value = (
self._show_topn
if len(parsed_feed.entries) > self._show_topn
Expand All @@ -120,7 +122,8 @@ def update(self: FeedParserSensor) -> None:
self._entries.extend(self._generate_entries(parsed_feed))

def _generate_entries(
self: FeedParserSensor, parsed_feed: FeedParserDict
self: FeedParserSensor,
parsed_feed: FeedParserDict,
) -> list[dict[str, str]]:
return [
self._generate_sensor_entry(feed_entry)
Expand All @@ -130,7 +133,8 @@ def _generate_entries(
]

def _generate_sensor_entry(
self: FeedParserSensor, feed_entry: FeedParserDict
self: FeedParserSensor,
feed_entry: FeedParserDict,
) -> dict[str, str]:
sensor_entry = {}
for key, value in feed_entry.items():
Expand Down Expand Up @@ -166,14 +170,16 @@ def _parse_date(self: FeedParserSensor, date: str) -> datetime:
if not parsed_time.tzname():
# replace tzinfo with UTC offset if tzinfo does not contain a TZ name
parsed_time = parsed_time.replace(
tzinfo=timezone(parsed_time.utcoffset()) # type: ignore[arg-type]
tzinfo=timezone(parsed_time.utcoffset()), # type: ignore[arg-type]
)
if self._local_time:
parsed_time = dt.as_local(parsed_time)
return parsed_time

def _process_image(
self: FeedParserSensor, feed_entry: FeedParserDict, sensor_entry: dict[str, str]
self: FeedParserSensor,
feed_entry: FeedParserDict,
sensor_entry: dict[str, str],
) -> None:
if "image" in self._inclusions and "image" not in sensor_entry.keys():
if "enclosures" in feed_entry:
Expand All @@ -191,7 +197,24 @@ def _process_image(
"image"
] = DEFAULT_THUMBNAIL # use default image if no image found

@property
def feed_entries(self: FeedParserSensor) -> list[dict[str, str]]:
"""Return feed entries."""
if hasattr(self, "_entries"):
return self._entries
return []

@property
def local_time(self: FeedParserSensor) -> bool:
"""Return local_time."""
return self._local_time

@local_time.setter
def local_time(self: FeedParserSensor, value: bool) -> None:
"""Set local_time."""
self._local_time = value

@property
def extra_state_attributes(self: FeedParserSensor) -> dict[str, list]:
"""Return entity specific state attributes."""
return {"entries": self._entries}
return {"entries": self.feed_entries}
42 changes: 31 additions & 11 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ dev = [
"types-python-dateutil",
"types-PyYAML",
"voluptuous-stubs",
"pyyaml"
"pyyaml",

]
[project.urls]
Expand Down Expand Up @@ -67,26 +67,41 @@ exclude = '''
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
# McCabe complexity (`C901`) by default.
select = [
"E",
"F",
"ANN",
"ARG",
"ARG",
"B",
"RET",
"PL",
"C",
"COM",
"D",
"ANN",
"D",
"DTZ",
"E",
"EM",
"F",
"FBT",
"FBT",
"ARG",
"I",
"UP",
"PGH",
"PL",
"PLR",
"PT",
"RET",
"RUF",
"SIM",
"SLF",
"TCH",
"TRY",
"UP",
]

# Q000,ANN,PT009,D,E501,
ignore = [
"D107", # Missing docstring in __init__
"FBT001", # Boolean positional arg in function definition
"PLR0913", # Too many arguments to function call
"ARG001", # Unused function argument
"D203", # 1 blank line required before class docstring
"D213", # Multi-line docstring summary should start at the first line
"FBT001" # Boolean positional argument in function definition
]

# Allow autofix for all enabled rules (when `--fix`) is provided.
Expand All @@ -113,7 +128,6 @@ exclude = [
"dist",
"venv",
]
per-file-ignores = {}

# Same as Black.
line-length = 88
Expand All @@ -124,6 +138,12 @@ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
# Assume Python 3.11.
target-version = "py311"

[tool.ruff.per-file-ignores]
"tests/**" = ["S101"]

[tool.ruff.pylint]
max-args = 9

[[tool.mypy.overrides]]
module = "feedparser.*"
# Workaround till https://github.com/kurtmckee/feedparser/pull/282 gets merged to the main branch
Expand Down
7 changes: 3 additions & 4 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import pytest
from constants import TEST_FEEDS
from feedsource import FeedSource
from pytest import FixtureRequest

from custom_components.feedparser.sensor import FeedParserSensor

Expand All @@ -20,13 +19,13 @@ def pytest_generate_tests(metafunc: pytest.Metafunc) -> None:
metafunc.parametrize("feed", feeds, ids=[f.name for f in feeds], indirect=True)


@pytest.fixture
def feed(request: FixtureRequest) -> FeedSource:
@pytest.fixture()
def feed(request: pytest.FixtureRequest) -> FeedSource:
"""Return feed file source."""
return request.param


@pytest.fixture
@pytest.fixture()
def feed_sensor(feed: FeedSource) -> FeedParserSensor:
"""Return feed sensor initialized with the local RSS feed."""
return FeedParserSensor(**feed.sensor_config_local_feed)
9 changes: 4 additions & 5 deletions tests/download.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
"""Download RSS feeds for testing."""
import asyncio
import datetime
import json
from datetime import datetime

import aiohttp
from constants import TEST_FEEDS
from feedsource import FeedSource

base_date = datetime.now()
base_date = datetime.datetime.now(datetime.UTC)


async def run_feed(feed: FeedSource) -> None:
"""Download feed and store its metadata and content."""
async with aiohttp.ClientSession() as session:
async with session.get(feed.url) as response:
text = await response.text()
async with aiohttp.ClientSession() as session, session.get(feed.url) as response:
text = await response.text()
metadata = feed.raw
metadata["download_date"] = base_date.isoformat()
feed.metadata_path.write_text(json.dumps(metadata, indent=4) + "\n")
Expand Down
11 changes: 8 additions & 3 deletions tests/feedsource.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,12 @@ def download_date(self: "FeedSource") -> datetime:
try:
return datetime.fromisoformat(self.metadata["download_date"])
except KeyError as ke:
raise KeyError(
msg = (
f"download_date not found in {self.metadata_path}. "
"Is feed metadata downloaded?"
)
raise KeyError(
msg,
) from ke

@property
Expand Down Expand Up @@ -121,14 +124,16 @@ def ha_config_entry(self: "FeedSource") -> dict[str, Any]:

@classmethod
def gen_ha_sensors_yml_config(
cls: type["FeedSource"], sensors: list["FeedSource"]
cls: type["FeedSource"],
sensors: list["FeedSource"],
) -> str:
"""Generate HA "sensors" config."""
return yaml.dump([s.ha_config_entry for s in sensors])

@classmethod
def create_ha_sensors_config_file(
cls: type["FeedSource"], sensors: list["FeedSource"]
cls: type["FeedSource"],
sensors: list["FeedSource"],
) -> None:
"""Create HA "sensors" config file."""
sensors_yml = TEST_HASS_PATH / "sensors.yaml"
Expand Down
2 changes: 1 addition & 1 deletion tests/generate_ha_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
from feedsource import FeedSource

fsources = [FeedSource(fs) for fs in TEST_FEEDS]
FeedSource.create_ha_sensors_config_file([fs for fs in fsources])
FeedSource.create_ha_sensors_config_file(fsources)
Loading