Fix linting errors: Remove unused imports and reformat with black

This commit is contained in:
aserper
2025-12-13 23:20:03 -05:00
parent 46b2177cb2
commit 16318f89cf
3 changed files with 36 additions and 21 deletions

1
bot.py
View File

@@ -1,7 +1,6 @@
"""Mastodon RSS Bot - Core functionality"""
import logging
import os
import time
from pathlib import Path
from typing import List, Optional, Set

35
main.py
View File

@@ -3,7 +3,7 @@ import os
import sys
from dataclasses import dataclass, field
from pathlib import Path
from typing import List, Optional
from typing import List
from bot import MastodonRSSBot
@@ -11,7 +11,7 @@ from bot import MastodonRSSBot
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
handlers=[logging.StreamHandler(sys.stdout)]
handlers=[logging.StreamHandler(sys.stdout)],
)
logger = logging.getLogger(__name__)
@@ -19,6 +19,7 @@ logger = logging.getLogger(__name__)
@dataclass
class Config:
"""Configuration loaded from environment variables."""
instance_url: str
client_id: str
client_secret: str
@@ -26,7 +27,9 @@ class Config:
feed_urls: List[str] = field(default_factory=list)
toot_visibility: str = "public"
check_interval: int = 300
state_file: Path = field(default_factory=lambda: Path("/state/processed_entries.txt"))
state_file: Path = field(
default_factory=lambda: Path("/state/processed_entries.txt")
)
@classmethod
def from_env(cls) -> "Config":
@@ -38,18 +41,22 @@ class Config:
if not all([instance_url, client_id, client_secret, access_token]):
missing = [
k for k, v in {
k
for k, v in {
"MASTODON_INSTANCE_URL": instance_url,
"MASTODON_CLIENT_ID": client_id,
"MASTODON_CLIENT_SECRET": client_secret,
"MASTODON_ACCESS_TOKEN": access_token
}.items() if not v
"MASTODON_ACCESS_TOKEN": access_token,
}.items()
if not v
]
raise ValueError(f"Missing required environment variables: {', '.join(missing)}")
raise ValueError(
f"Missing required environment variables: {', '.join(missing)}"
)
# Parse feeds
feed_urls = []
# 1. Legacy single feed URL
if os.environ.get("RSS_FEED_URL"):
feed_urls.append(os.environ["RSS_FEED_URL"])
@@ -83,17 +90,21 @@ class Config:
unique_feed_urls = list(dict.fromkeys(feed_urls))
if not unique_feed_urls:
raise ValueError("No RSS feeds configured. Please set RSS_FEED_URL, RSS_FEEDS, or FEEDS_FILE.")
raise ValueError(
"No RSS feeds configured. Please set RSS_FEED_URL, RSS_FEEDS, or FEEDS_FILE."
)
return cls(
instance_url=instance_url, # type: ignore # checked above
client_id=client_id, # type: ignore
client_secret=client_secret,# type: ignore
client_id=client_id, # type: ignore
client_secret=client_secret, # type: ignore
access_token=access_token, # type: ignore
feed_urls=unique_feed_urls,
toot_visibility=os.environ.get("TOOT_VISIBILITY", "public"),
check_interval=int(os.environ.get("CHECK_INTERVAL", "300")),
state_file=Path(os.environ.get("PROCESSED_ENTRIES_FILE", "/state/processed_entries.txt"))
state_file=Path(
os.environ.get("PROCESSED_ENTRIES_FILE", "/state/processed_entries.txt")
),
)

View File

@@ -52,7 +52,7 @@ class TestMastodonRSSBot(unittest.TestCase):
def test_save_processed_entries_error(self, mock_mastodon):
"""Test error handling when saving processed entries fails"""
bot = MastodonRSSBot(**self.test_config)
# Mock Path.write_text to raise exception
with patch.object(Path, "write_text", side_effect=Exception("Disk full")):
# Should not raise exception
@@ -70,17 +70,17 @@ class TestMastodonRSSBot(unittest.TestCase):
feed = bot.parse_feed("https://example.com/feed.xml")
self.assertIsNotNone(feed)
# We can't easily assert the log/print was called without mocking logging,
# We can't easily assert the log/print was called without mocking logging,
# but execution flow is covered.
@patch("bot.Mastodon")
def test_run_keyboard_interrupt(self, mock_mastodon):
"""Test clean exit on KeyboardInterrupt"""
bot = MastodonRSSBot(**self.test_config)
# Mock process_new_entries to raise KeyboardInterrupt
bot.process_new_entries = Mock(side_effect=KeyboardInterrupt)
# Should exit cleanly
bot.run()
bot.process_new_entries.assert_called_once()
@@ -90,12 +90,14 @@ class TestMastodonRSSBot(unittest.TestCase):
def test_run_exception_retry(self, mock_mastodon, mock_sleep):
"""Test retry logic on exception in main loop"""
bot = MastodonRSSBot(**self.test_config)
# Raise exception once, then KeyboardInterrupt to exit loop
bot.process_new_entries = Mock(side_effect=[Exception("Network Error"), KeyboardInterrupt])
bot.process_new_entries = Mock(
side_effect=[Exception("Network Error"), KeyboardInterrupt]
)
bot.run()
self.assertEqual(bot.process_new_entries.call_count, 2)
mock_sleep.assert_called_with(bot.check_interval)
@@ -107,6 +109,7 @@ class TestMainEntry(unittest.TestCase):
def test_config_missing_vars(self):
"""Test Config raises ValueError when env vars are missing"""
from main import Config
with self.assertRaises(ValueError):
Config.from_env()
@@ -123,6 +126,7 @@ class TestMainEntry(unittest.TestCase):
def test_config_no_feeds(self):
"""Test Config raises ValueError when no feeds are configured"""
from main import Config
with self.assertRaises(ValueError):
Config.from_env()
@@ -139,6 +143,7 @@ class TestMainEntry(unittest.TestCase):
def test_config_feed_file_error(self):
"""Test Config handles missing/bad feeds file gracefully (logs warning but continues check)"""
from main import Config
# Should raise ValueError ultimately because no feeds are found,
# but cover the file reading path
with self.assertRaises(ValueError) as cm: