mirror of
https://github.com/kuhyx/screen-locker.git
synced 2026-11-10 19:07:16 +01:00
`requires-python` and the CI pin already said 3.14, but ruff, mypy and pylint were still grading against 3.10 -- three tools judging an interpreter nothing runs any more, and ruff withholding the pyupgrade fixes that only become safe at the newer target. `pin_ci_python.py` rewrites exactly these three knobs. What the stale target was hiding: 118 sites on `datetime.timezone.utc` instead of `datetime.UTC` (UP017), two classes still written with `Generic[...]` rather than PEP 695 syntax, and ruff's stable formatter applying PEP 758 -- `except (A, B):` becomes `except A, B:`. That last one makes the source 3.14-only at the syntax level, which is what `requires-python = ">=3.14"` already promised; kuhy chose it deliberately over holding ruff a release back (2026-08-29). The PEP 695 fix needed a hand: ruff rewrote `class WidgetGroup(Generic[_W])` to `class WidgetGroup[W: tk.Misc]` but left the body annotated with the module-level `_W`, a *different* type variable -- mypy caught it as "a function returning TypeVar should receive at least one argument containing the same TypeVar". Renamed the body to the new parameter, gave `FrameGroup.__init__` the concrete `list[tk.Frame]` it always wrapped, and deleted the two now-dead module-level TypeVars. ruff, ruff-format, mypy --strict and both pylint passes clean at the new target. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SfnJmxxsZ534AKk16rC2Gr
94 lines
2.9 KiB
Python
94 lines
2.9 KiB
Python
"""rtcwake wake-alarm scheduling, split out of the shutdown mixin.
|
|
|
|
Split out of :mod:`screen_locker._shutdown` to keep every file under the
|
|
250-line cap. Composed back into ``ShutdownMixin`` there, so callers see no
|
|
change.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import calendar
|
|
from datetime import UTC, datetime, timedelta
|
|
import logging
|
|
import subprocess
|
|
|
|
from screen_locker._constants import ALARM_DAYS, RTCWAKE_BIN, WAKE_AFTER_HOURS
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
class WakeAlarmMixin:
|
|
"""Schedules an RTC wake alarm for the next alarm day."""
|
|
|
|
# ------------------------------------------------------------------
|
|
# rtcwake integration for weekend wake alarm
|
|
# ------------------------------------------------------------------
|
|
|
|
@staticmethod
|
|
def _is_tomorrow_alarm_day() -> bool:
|
|
"""Check if tomorrow is an alarm day."""
|
|
tomorrow = datetime.now(tz=UTC) + timedelta(days=1)
|
|
return tomorrow.weekday() in ALARM_DAYS
|
|
|
|
@staticmethod
|
|
def _compute_wake_timestamp() -> int:
|
|
"""Compute the UTC epoch timestamp for the next wake alarm.
|
|
|
|
Returns:
|
|
Epoch seconds WAKE_AFTER_HOURS from now.
|
|
"""
|
|
wake_time = datetime.now(tz=UTC) + timedelta(
|
|
hours=WAKE_AFTER_HOURS,
|
|
)
|
|
return calendar.timegm(wake_time.utctimetuple())
|
|
|
|
@staticmethod
|
|
def _schedule_rtcwake() -> bool:
|
|
"""Set rtcwake to power on the PC after WAKE_AFTER_HOURS.
|
|
|
|
Uses ``rtcwake -m disk`` to hibernate immediately while programming
|
|
the RTC to restore power at wake_epoch. Hibernate is completely
|
|
silent and dark (state written to swap file), making it suitable
|
|
when the PC is in a bedroom.
|
|
|
|
Returns:
|
|
True if rtcwake was set successfully, False otherwise.
|
|
"""
|
|
wake_epoch = WakeAlarmMixin._compute_wake_timestamp()
|
|
cmd = [
|
|
"/usr/bin/sudo",
|
|
RTCWAKE_BIN,
|
|
"-m",
|
|
"disk",
|
|
"-t",
|
|
str(wake_epoch),
|
|
]
|
|
try:
|
|
subprocess.run(
|
|
cmd,
|
|
check=True,
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
except subprocess.SubprocessError as exc:
|
|
_logger.warning("Failed to set rtcwake: %s", exc)
|
|
return False
|
|
_logger.info(
|
|
"rtcwake set: PC will wake at epoch %d",
|
|
wake_epoch,
|
|
)
|
|
return True
|
|
|
|
def schedule_wake_if_needed(self) -> bool:
|
|
"""Schedule rtcwake if tomorrow is an alarm day.
|
|
|
|
Call this at shutdown time.
|
|
|
|
Returns:
|
|
True if wake was scheduled, False if not needed or failed.
|
|
"""
|
|
if not self._is_tomorrow_alarm_day():
|
|
_logger.info("Tomorrow is not an alarm day — skipping rtcwake")
|
|
return False
|
|
return self._schedule_rtcwake()
|