Skip to content

Commit a450fbb

Browse files
committed
Address Ruff's rules, add few enhancements
1 parent e5af5b9 commit a450fbb

File tree

7 files changed

+111
-57
lines changed

7 files changed

+111
-57
lines changed

custom_components/feedparser/sensor.py

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,10 @@
5555

5656

5757
async def async_setup_platform(
58-
hass: HomeAssistant,
58+
hass: HomeAssistant, # noqa: ARG001
5959
config: ConfigType,
6060
async_add_devices: AddEntitiesCallback,
61-
discovery_info: DiscoveryInfoType | None = None,
61+
discovery_info: DiscoveryInfoType | None = None, # noqa: ARG001
6262
) -> None:
6363
"""Set up the Feedparser sensor."""
6464
async_add_devices(
@@ -92,6 +92,7 @@ def __init__(
9292
scan_interval: timedelta,
9393
local_time: bool,
9494
) -> None:
95+
"""Initialize the Feedparser sensor."""
9596
self._feed = feed
9697
self._attr_name = name
9798
self._attr_icon = "mdi:rss"
@@ -112,6 +113,7 @@ def update(self: FeedParserSensor) -> None:
112113
self._attr_native_value = None
113114
return
114115

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

122124
def _generate_entries(
123-
self: FeedParserSensor, parsed_feed: FeedParserDict
125+
self: FeedParserSensor,
126+
parsed_feed: FeedParserDict,
124127
) -> list[dict[str, str]]:
125128
return [
126129
self._generate_sensor_entry(feed_entry)
@@ -130,7 +133,8 @@ def _generate_entries(
130133
]
131134

132135
def _generate_sensor_entry(
133-
self: FeedParserSensor, feed_entry: FeedParserDict
136+
self: FeedParserSensor,
137+
feed_entry: FeedParserDict,
134138
) -> dict[str, str]:
135139
sensor_entry = {}
136140
for key, value in feed_entry.items():
@@ -166,14 +170,16 @@ def _parse_date(self: FeedParserSensor, date: str) -> datetime:
166170
if not parsed_time.tzname():
167171
# replace tzinfo with UTC offset if tzinfo does not contain a TZ name
168172
parsed_time = parsed_time.replace(
169-
tzinfo=timezone(parsed_time.utcoffset()) # type: ignore[arg-type]
173+
tzinfo=timezone(parsed_time.utcoffset()), # type: ignore[arg-type]
170174
)
171175
if self._local_time:
172176
parsed_time = dt.as_local(parsed_time)
173177
return parsed_time
174178

175179
def _process_image(
176-
self: FeedParserSensor, feed_entry: FeedParserDict, sensor_entry: dict[str, str]
180+
self: FeedParserSensor,
181+
feed_entry: FeedParserDict,
182+
sensor_entry: dict[str, str],
177183
) -> None:
178184
if "image" in self._inclusions and "image" not in sensor_entry.keys():
179185
if "enclosures" in feed_entry:
@@ -191,7 +197,24 @@ def _process_image(
191197
"image"
192198
] = DEFAULT_THUMBNAIL # use default image if no image found
193199

200+
@property
201+
def feed_entries(self: FeedParserSensor) -> list[dict[str, str]]:
202+
"""Return feed entries."""
203+
if hasattr(self, "_entries"):
204+
return self._entries
205+
return []
206+
207+
@property
208+
def local_time(self: FeedParserSensor) -> bool:
209+
"""Return local_time."""
210+
return self._local_time
211+
212+
@local_time.setter
213+
def local_time(self: FeedParserSensor, value: bool) -> None:
214+
"""Set local_time."""
215+
self._local_time = value
216+
194217
@property
195218
def extra_state_attributes(self: FeedParserSensor) -> dict[str, list]:
196219
"""Return entity specific state attributes."""
197-
return {"entries": self._entries}
220+
return {"entries": self.feed_entries}

pyproject.toml

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ dev = [
3535
"types-python-dateutil",
3636
"types-PyYAML",
3737
"voluptuous-stubs",
38-
"pyyaml"
38+
"pyyaml",
3939

4040
]
4141
[project.urls]
@@ -67,26 +67,41 @@ exclude = '''
6767
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
6868
# McCabe complexity (`C901`) by default.
6969
select = [
70-
"E",
71-
"F",
70+
"ANN",
71+
"ARG",
72+
"ARG",
7273
"B",
73-
"RET",
74-
"PL",
74+
"C",
75+
"COM",
7576
"D",
76-
"ANN",
77+
"D",
78+
"DTZ",
79+
"E",
80+
"EM",
81+
"F",
82+
"FBT",
7783
"FBT",
78-
"ARG",
7984
"I",
80-
"UP",
8185
"PGH",
86+
"PL",
87+
"PLR",
88+
"PT",
89+
"RET",
90+
"RUF",
91+
"SIM",
92+
"SLF",
93+
"TCH",
94+
"TRY",
95+
"UP",
8296
]
97+
98+
# Q000,ANN,PT009,D,E501,
8399
ignore = [
84100
"D107", # Missing docstring in __init__
85101
"FBT001", # Boolean positional arg in function definition
86-
"PLR0913", # Too many arguments to function call
87-
"ARG001", # Unused function argument
88102
"D203", # 1 blank line required before class docstring
89103
"D213", # Multi-line docstring summary should start at the first line
104+
"FBT001" # Boolean positional argument in function definition
90105
]
91106

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

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

141+
[tool.ruff.per-file-ignores]
142+
"tests/**" = ["S101"]
143+
144+
[tool.ruff.pylint]
145+
max-args = 9
146+
127147
[[tool.mypy.overrides]]
128148
module = "feedparser.*"
129149
# Workaround till https://github.com/kurtmckee/feedparser/pull/282 gets merged to the main branch

tests/conftest.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import pytest
44
from constants import TEST_FEEDS
55
from feedsource import FeedSource
6-
from pytest import FixtureRequest
76

87
from custom_components.feedparser.sensor import FeedParserSensor
98

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

2221

23-
@pytest.fixture
24-
def feed(request: FixtureRequest) -> FeedSource:
22+
@pytest.fixture()
23+
def feed(request: pytest.FixtureRequest) -> FeedSource:
2524
"""Return feed file source."""
2625
return request.param
2726

2827

29-
@pytest.fixture
28+
@pytest.fixture()
3029
def feed_sensor(feed: FeedSource) -> FeedParserSensor:
3130
"""Return feed sensor initialized with the local RSS feed."""
3231
return FeedParserSensor(**feed.sensor_config_local_feed)

tests/download.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
11
"""Download RSS feeds for testing."""
22
import asyncio
3+
import datetime
34
import json
4-
from datetime import datetime
55

66
import aiohttp
77
from constants import TEST_FEEDS
88
from feedsource import FeedSource
99

10-
base_date = datetime.now()
10+
base_date = datetime.datetime.now(datetime.UTC)
1111

1212

1313
async def run_feed(feed: FeedSource) -> None:
1414
"""Download feed and store its metadata and content."""
15-
async with aiohttp.ClientSession() as session:
16-
async with session.get(feed.url) as response:
17-
text = await response.text()
15+
async with aiohttp.ClientSession() as session, session.get(feed.url) as response:
16+
text = await response.text()
1817
metadata = feed.raw
1918
metadata["download_date"] = base_date.isoformat()
2019
feed.metadata_path.write_text(json.dumps(metadata, indent=4) + "\n")

tests/feedsource.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,12 @@ def download_date(self: "FeedSource") -> datetime:
7171
try:
7272
return datetime.fromisoformat(self.metadata["download_date"])
7373
except KeyError as ke:
74-
raise KeyError(
74+
msg = (
7575
f"download_date not found in {self.metadata_path}. "
7676
"Is feed metadata downloaded?"
77+
)
78+
raise KeyError(
79+
msg,
7780
) from ke
7881

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

122125
@classmethod
123126
def gen_ha_sensors_yml_config(
124-
cls: type["FeedSource"], sensors: list["FeedSource"]
127+
cls: type["FeedSource"],
128+
sensors: list["FeedSource"],
125129
) -> str:
126130
"""Generate HA "sensors" config."""
127131
return yaml.dump([s.ha_config_entry for s in sensors])
128132

129133
@classmethod
130134
def create_ha_sensors_config_file(
131-
cls: type["FeedSource"], sensors: list["FeedSource"]
135+
cls: type["FeedSource"],
136+
sensors: list["FeedSource"],
132137
) -> None:
133138
"""Create HA "sensors" config file."""
134139
sensors_yml = TEST_HASS_PATH / "sensors.yaml"

tests/generate_ha_config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
from feedsource import FeedSource
44

55
fsources = [FeedSource(fs) for fs in TEST_FEEDS]
6-
FeedSource.create_ha_sensors_config_file([fs for fs in fsources])
6+
FeedSource.create_ha_sensors_config_file(fsources)

0 commit comments

Comments
 (0)