mirror of
https://github.com/Second-Hand-Friends/kleinanzeigen-bot.git
synced 2026-03-12 10:31:50 +01:00
refact: apply consistent formatting
This commit is contained in:
@@ -96,7 +96,7 @@ def save_dict(filepath:str, content:dict[str, Any]) -> None:
|
||||
yaml.indent(mapping = 2, sequence = 4, offset = 2)
|
||||
yaml.representer.add_representer(str, # use YAML | block style for multi-line strings
|
||||
lambda dumper, data:
|
||||
dumper.represent_scalar('tag:yaml.org,2002:str', data, style = '|' if '\n' in data else None)
|
||||
dumper.represent_scalar("tag:yaml.org,2002:str", data, style = "|" if "\n" in data else None)
|
||||
)
|
||||
yaml.allow_duplicate_keys = False
|
||||
yaml.explicit_start = False
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
# SPDX-ArtifactOfProjectHomePage: https://github.com/Second-Hand-Friends/kleinanzeigen-bot/
|
||||
import sys, traceback # isort: skip
|
||||
from types import FrameType, TracebackType
|
||||
from typing import Any, Final
|
||||
from typing import Final
|
||||
|
||||
from . import loggers
|
||||
|
||||
LOG:Final[loggers.Logger] = loggers.get_logger(__name__)
|
||||
|
||||
|
||||
def on_exception(ex_type: type[BaseException] | None, ex_value: BaseException | None, ex_traceback: TracebackType | None) -> None:
|
||||
def on_exception(ex_type:type[BaseException] | None, ex_value:BaseException | None, ex_traceback:TracebackType | None) -> None:
|
||||
if ex_type is None or ex_value is None:
|
||||
LOG.error("Unknown exception occurred (missing exception info): ex_type=%s, ex_value=%s", ex_type, ex_value)
|
||||
return
|
||||
|
||||
@@ -11,6 +11,6 @@ class KleinanzeigenBotError(RuntimeError):
|
||||
class CaptchaEncountered(KleinanzeigenBotError):
|
||||
"""Raised when a Captcha was detected and auto-restart is enabled."""
|
||||
|
||||
def __init__(self, restart_delay: timedelta) -> None:
|
||||
def __init__(self, restart_delay:timedelta) -> None:
|
||||
super().__init__()
|
||||
self.restart_delay = restart_delay
|
||||
|
||||
@@ -42,7 +42,7 @@ class Locale(NamedTuple):
|
||||
return f"{self.language}{region_part}{encoding_part}"
|
||||
|
||||
@staticmethod
|
||||
def of(locale_string: str) -> 'Locale':
|
||||
def of(locale_string:str) -> "Locale":
|
||||
"""
|
||||
>>> Locale.of("en_US.UTF-8")
|
||||
Locale(language='en', region='US', encoding='UTF-8')
|
||||
@@ -86,11 +86,11 @@ def _detect_locale() -> Locale:
|
||||
return Locale.of(lang) if lang else Locale("en", "US", "UTF-8")
|
||||
|
||||
|
||||
_CURRENT_LOCALE: Locale = _detect_locale()
|
||||
_TRANSLATIONS: dict[str, Any] | None = None
|
||||
_CURRENT_LOCALE:Locale = _detect_locale()
|
||||
_TRANSLATIONS:dict[str, Any] | None = None
|
||||
|
||||
|
||||
def translate(text:object, caller: inspect.FrameInfo | None) -> str:
|
||||
def translate(text:object, caller:inspect.FrameInfo | None) -> str:
|
||||
text = str(text)
|
||||
if not caller:
|
||||
return text
|
||||
@@ -105,7 +105,7 @@ def translate(text:object, caller: inspect.FrameInfo | None) -> str:
|
||||
if not _TRANSLATIONS:
|
||||
return text
|
||||
|
||||
module_name = caller.frame.f_globals.get('__name__') # pylint: disable=redefined-outer-name
|
||||
module_name = caller.frame.f_globals.get("__name__") # pylint: disable=redefined-outer-name
|
||||
file_basename = os.path.splitext(os.path.basename(caller.filename))[0]
|
||||
if module_name and module_name.endswith(f".{file_basename}"):
|
||||
module_name = module_name[:-(len(file_basename) + 1)]
|
||||
@@ -124,9 +124,9 @@ gettext.gettext = lambda message: translate(_original_gettext(message), reflect.
|
||||
for module_name, module in sys.modules.items():
|
||||
if module is None or module_name in sys.builtin_module_names:
|
||||
continue
|
||||
if hasattr(module, '_') and module._ is _original_gettext:
|
||||
if hasattr(module, "_") and module._ is _original_gettext:
|
||||
module._ = gettext.gettext # type: ignore[attr-defined]
|
||||
if hasattr(module, 'gettext') and module.gettext is _original_gettext:
|
||||
if hasattr(module, "gettext") and module.gettext is _original_gettext:
|
||||
module.gettext = gettext.gettext # type: ignore[attr-defined]
|
||||
|
||||
|
||||
@@ -190,8 +190,8 @@ def pluralize(noun:str, count:int | Sized, *, prefix_with_count:bool = True) ->
|
||||
# English
|
||||
if len(noun) < 2: # noqa: PLR2004 Magic value used in comparison
|
||||
return f"{prefix}{noun}s"
|
||||
if noun.endswith(('s', 'sh', 'ch', 'x', 'z')):
|
||||
if noun.endswith(("s", "sh", "ch", "x", "z")):
|
||||
return f"{prefix}{noun}es"
|
||||
if noun.endswith('y') and noun[-2].lower() not in "aeiou":
|
||||
if noun.endswith("y") and noun[-2].lower() not in "aeiou":
|
||||
return f"{prefix}{noun[:-1]}ies"
|
||||
return f"{prefix}{noun}s"
|
||||
|
||||
@@ -28,11 +28,11 @@ LOG_ROOT:Final[logging.Logger] = logging.getLogger()
|
||||
|
||||
class _MaxLevelFilter(logging.Filter):
|
||||
|
||||
def __init__(self, level: int) -> None:
|
||||
def __init__(self, level:int) -> None:
|
||||
super().__init__()
|
||||
self.level = level
|
||||
|
||||
def filter(self, record: logging.LogRecord) -> bool:
|
||||
def filter(self, record:logging.LogRecord) -> bool:
|
||||
return record.levelno <= self.level
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ def configure_console_logging() -> None:
|
||||
class LogFileHandle:
|
||||
"""Encapsulates a log file handler with close and status methods."""
|
||||
|
||||
def __init__(self, file_path: str, handler: RotatingFileHandler, logger: logging.Logger) -> None:
|
||||
def __init__(self, file_path:str, handler:RotatingFileHandler, logger:logging.Logger) -> None:
|
||||
self.file_path = file_path
|
||||
self._handler:RotatingFileHandler | None = handler
|
||||
self._logger = logger
|
||||
@@ -146,14 +146,14 @@ def flush_all_handlers() -> None:
|
||||
handler.flush()
|
||||
|
||||
|
||||
def get_logger(name: str | None = None) -> logging.Logger:
|
||||
def get_logger(name:str | None = None) -> logging.Logger:
|
||||
"""
|
||||
Returns a localized logger
|
||||
"""
|
||||
|
||||
class TranslatingLogger(logging.Logger):
|
||||
|
||||
def _log(self, level: int, msg: object, *args: Any, **kwargs: Any) -> None:
|
||||
def _log(self, level:int, msg:object, *args:Any, **kwargs:Any) -> None:
|
||||
if level != DEBUG: # debug messages should not be translated
|
||||
msg = i18n.translate(msg, reflect.get_caller(2))
|
||||
super()._log(level, msg, *args, **kwargs)
|
||||
|
||||
@@ -10,7 +10,7 @@ from typing import Any, TypeVar
|
||||
from . import i18n
|
||||
|
||||
# https://mypy.readthedocs.io/en/stable/generics.html#generic-functions
|
||||
T = TypeVar('T')
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
def ensure(condition:Any | bool | Callable[[], bool], error_message:str, timeout:float = 5, poll_requency:float = 0.5) -> None:
|
||||
@@ -49,7 +49,7 @@ def is_frozen() -> bool:
|
||||
return getattr(sys, "frozen", False)
|
||||
|
||||
|
||||
async def ainput(prompt: str) -> str:
|
||||
async def ainput(prompt:str) -> str:
|
||||
return await asyncio.to_thread(input, f'{prompt} ')
|
||||
|
||||
|
||||
@@ -84,10 +84,10 @@ def parse_decimal(number:float | int | str) -> decimal.Decimal:
|
||||
|
||||
|
||||
def parse_datetime(
|
||||
date: datetime | str | None,
|
||||
date:datetime | str | None,
|
||||
*,
|
||||
add_timezone_if_missing: bool = True,
|
||||
use_local_timezone: bool = True
|
||||
add_timezone_if_missing:bool = True,
|
||||
use_local_timezone:bool = True
|
||||
) -> datetime | None:
|
||||
"""
|
||||
Parses a datetime object or ISO-formatted string.
|
||||
@@ -152,22 +152,22 @@ def parse_duration(text:str) -> timedelta:
|
||||
>>> parse_duration("invalid input")
|
||||
datetime.timedelta(0)
|
||||
"""
|
||||
pattern = re.compile(r'(\d+)\s*([dhms])')
|
||||
pattern = re.compile(r"(\d+)\s*([dhms])")
|
||||
parts = pattern.findall(text.lower())
|
||||
kwargs: dict[str, int] = {}
|
||||
kwargs:dict[str, int] = {}
|
||||
for value, unit in parts:
|
||||
if unit == 'd':
|
||||
kwargs['days'] = kwargs.get('days', 0) + int(value)
|
||||
elif unit == 'h':
|
||||
kwargs['hours'] = kwargs.get('hours', 0) + int(value)
|
||||
elif unit == 'm':
|
||||
kwargs['minutes'] = kwargs.get('minutes', 0) + int(value)
|
||||
elif unit == 's':
|
||||
kwargs['seconds'] = kwargs.get('seconds', 0) + int(value)
|
||||
if unit == "d":
|
||||
kwargs["days"] = kwargs.get("days", 0) + int(value)
|
||||
elif unit == "h":
|
||||
kwargs["hours"] = kwargs.get("hours", 0) + int(value)
|
||||
elif unit == "m":
|
||||
kwargs["minutes"] = kwargs.get("minutes", 0) + int(value)
|
||||
elif unit == "s":
|
||||
kwargs["seconds"] = kwargs.get("seconds", 0) + int(value)
|
||||
return timedelta(**kwargs)
|
||||
|
||||
|
||||
def format_timedelta(td: timedelta) -> str:
|
||||
def format_timedelta(td:timedelta) -> str:
|
||||
"""
|
||||
Formats a timedelta into a human-readable string using the pluralize utility.
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import inspect
|
||||
from typing import Any
|
||||
|
||||
|
||||
def get_caller(depth: int = 1) -> inspect.FrameInfo | None:
|
||||
def get_caller(depth:int = 1) -> inspect.FrameInfo | None:
|
||||
stack = inspect.stack()
|
||||
try:
|
||||
for frame in stack[depth + 1:]:
|
||||
|
||||
@@ -165,7 +165,7 @@ class WebScrapingMixin:
|
||||
prefs_file = os.path.join(profile_dir, "Preferences")
|
||||
if not os.path.exists(prefs_file):
|
||||
LOG.info(" -> Setting chrome prefs [%s]...", prefs_file)
|
||||
with open(prefs_file, "w", encoding = 'UTF-8') as fd:
|
||||
with open(prefs_file, "w", encoding = "UTF-8") as fd:
|
||||
json.dump({
|
||||
"credentials_enable_service": False,
|
||||
"enable_do_not_track": True,
|
||||
@@ -234,16 +234,16 @@ class WebScrapingMixin:
|
||||
|
||||
case "Windows":
|
||||
browser_paths = [
|
||||
os.environ.get("PROGRAMFILES", "C:\\Program Files") + r'\Microsoft\Edge\Application\msedge.exe',
|
||||
os.environ.get("PROGRAMFILES(X86)", "C:\\Program Files (x86)") + r'\Microsoft\Edge\Application\msedge.exe',
|
||||
os.environ.get("PROGRAMFILES", "C:\\Program Files") + r"\Microsoft\Edge\Application\msedge.exe",
|
||||
os.environ.get("PROGRAMFILES(X86)", "C:\\Program Files (x86)") + r"\Microsoft\Edge\Application\msedge.exe",
|
||||
|
||||
os.environ["PROGRAMFILES"] + r'\Chromium\Application\chrome.exe',
|
||||
os.environ["PROGRAMFILES(X86)"] + r'\Chromium\Application\chrome.exe',
|
||||
os.environ["LOCALAPPDATA"] + r'\Chromium\Application\chrome.exe',
|
||||
os.environ["PROGRAMFILES"] + r"\Chromium\Application\chrome.exe",
|
||||
os.environ["PROGRAMFILES(X86)"] + r"\Chromium\Application\chrome.exe",
|
||||
os.environ["LOCALAPPDATA"] + r"\Chromium\Application\chrome.exe",
|
||||
|
||||
os.environ["PROGRAMFILES"] + r'\Chrome\Application\chrome.exe',
|
||||
os.environ["PROGRAMFILES(X86)"] + r'\Chrome\Application\chrome.exe',
|
||||
os.environ["LOCALAPPDATA"] + r'\Chrome\Application\chrome.exe',
|
||||
os.environ["PROGRAMFILES"] + r"\Chrome\Application\chrome.exe",
|
||||
os.environ["PROGRAMFILES(X86)"] + r"\Chrome\Application\chrome.exe",
|
||||
os.environ["LOCALAPPDATA"] + r"\Chrome\Application\chrome.exe",
|
||||
|
||||
shutil.which("msedge.exe"),
|
||||
shutil.which("chromium.exe"),
|
||||
@@ -259,8 +259,8 @@ class WebScrapingMixin:
|
||||
|
||||
raise AssertionError(_("Installed browser could not be detected"))
|
||||
|
||||
async def web_await(self, condition: Callable[[], T | Never | Coroutine[Any, Any, T | Never]], *,
|
||||
timeout:int | float = 5, timeout_error_message: str = "") -> T:
|
||||
async def web_await(self, condition:Callable[[], T | Never | Coroutine[Any, Any, T | Never]], *,
|
||||
timeout:int | float = 5, timeout_error_message:str = "") -> T:
|
||||
"""
|
||||
Blocks/waits until the given condition is met.
|
||||
|
||||
@@ -523,7 +523,7 @@ class WebScrapingMixin:
|
||||
return response
|
||||
# pylint: enable=dangerous-default-value
|
||||
|
||||
async def web_scroll_page_down(self, scroll_length: int = 10, scroll_speed: int = 10_000, *, scroll_back_top: bool = False) -> None:
|
||||
async def web_scroll_page_down(self, scroll_length:int = 10, scroll_speed:int = 10_000, *, scroll_back_top:bool = False) -> None:
|
||||
"""
|
||||
Smoothly scrolls the current web page down.
|
||||
|
||||
@@ -532,7 +532,7 @@ class WebScrapingMixin:
|
||||
:param scroll_back_top: whether to scroll the page back to the top after scrolling to the bottom
|
||||
"""
|
||||
current_y_pos = 0
|
||||
bottom_y_pos: int = await self.web_execute('document.body.scrollHeight') # get bottom position
|
||||
bottom_y_pos:int = await self.web_execute("document.body.scrollHeight") # get bottom position
|
||||
while current_y_pos < bottom_y_pos: # scroll in steps until bottom reached
|
||||
current_y_pos += scroll_length
|
||||
await self.web_execute(f'window.scrollTo(0, {current_y_pos})') # scroll one step
|
||||
|
||||
Reference in New Issue
Block a user