style: say which lint rules this repo keeps, then keep them

`[tool.ruff]` set a line length and a target version and nothing else, so which
rules ran depended on the installed ruff's defaults — fine while nobody ran it,
not fine for a CI job that can turn a push red because a tool changed its mind.

The set is named explicitly (E, F, I, UP, SIM, B, RUF) with three ignores that
are each a real decision rather than a shrug: B008, because FastAPI's whole
dependency idiom is a call in a default argument; E501, because wrapping prose
is a formatter's business; and RUF001, because the reports and the bot speak
Russian and "ambiguous character" is the wrong complaint about a string the
user is meant to read.

The 73 findings that remained are fixed — mostly `datetime.timezone.utc` →
`datetime.UTC`, `typing.Callable` → `collections.abc.Callable`, import order,
and unused `noqa` markers — plus seven by hand: `contextlib.suppress` for two
try/except/pass pairs, unpacking instead of list concatenation, one negated
return.

`ruff format` is deliberately *not* adopted: it would reformat 51 files, and
some of the layout here is hand-set (the comment tables, the DNS wire-format
structs) in ways a formatter flattens.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
timka123andClaude Opus 5 committed 2026-10-22 17:28:57 +03:00
1 parent 5facf41f10
commit ec7eedbc66
28 files changed
+84 -68

No files matched your search

+3 -3
View File
@@ -8,7 +8,7 @@ day so a re-send shows the same numbers.
from __future__ import annotations from __future__ import annotations
from datetime import date, datetime, timezone from datetime import UTC, date, datetime
from typing import Any from typing import Any
from sqlalchemy import ( from sqlalchemy import (
@@ -30,7 +30,7 @@ from app.db import Base
def utcnow() -> datetime: def utcnow() -> datetime:
return datetime.now(timezone.utc) return datetime.now(UTC)
class SettingsRow(Base): class SettingsRow(Base):
@@ -80,7 +80,7 @@ class PomodoroSession(Base):
note: Mapped[str | None] = mapped_column(Text) note: Mapped[str | None] = mapped_column(Text)
abandon_reason: Mapped[str | None] = mapped_column(Text) abandon_reason: Mapped[str | None] = mapped_column(Text)
block_events: Mapped[list["BlockEvent"]] = relationship(back_populates="session") block_events: Mapped[list[BlockEvent]] = relationship(back_populates="session")
@property @property
def is_work(self) -> bool: def is_work(self) -> bool:
+2 -1
View File
@@ -8,9 +8,10 @@ the Telegram message at once.
from __future__ import annotations from __future__ import annotations
import logging import logging
from collections.abc import Callable
from dataclasses import dataclass from dataclasses import dataclass
from datetime import date from datetime import date
from typing import Any, Callable from typing import Any
Metrics = dict[str, Any] Metrics = dict[str, Any]
Context = dict[str, Any] Context = dict[str, Any]
+5 -5
View File
@@ -17,7 +17,7 @@ import hashlib
import hmac import hmac
import secrets import secrets
import time import time
from datetime import datetime, timedelta, timezone from datetime import UTC, datetime, timedelta
from typing import Any from typing import Any
from sqlalchemy import delete from sqlalchemy import delete
@@ -84,7 +84,7 @@ def create_session(session: Session, method: str, session_days: int) -> AuthSess
row = AuthSession( row = AuthSession(
token=secrets.token_urlsafe(32), token=secrets.token_urlsafe(32),
method=method, method=method,
expires_at=datetime.now(timezone.utc) + timedelta(days=session_days), expires_at=datetime.now(UTC) + timedelta(days=session_days),
) )
session.add(row) session.add(row)
session.commit() session.commit()
@@ -93,7 +93,7 @@ def create_session(session: Session, method: str, session_days: int) -> AuthSess
def _aware(dt: datetime) -> datetime: def _aware(dt: datetime) -> datetime:
"""SQLite drops the tzinfo it was stored with; Postgres does not.""" """SQLite drops the tzinfo it was stored with; Postgres does not."""
return dt if dt.tzinfo else dt.replace(tzinfo=timezone.utc) return dt if dt.tzinfo else dt.replace(tzinfo=UTC)
def valid_session(session: Session, token: str | None) -> AuthSession | None: def valid_session(session: Session, token: str | None) -> AuthSession | None:
@@ -102,7 +102,7 @@ def valid_session(session: Session, token: str | None) -> AuthSession | None:
row = session.get(AuthSession, token) row = session.get(AuthSession, token)
if row is None: if row is None:
return None return None
if _aware(row.expires_at) < datetime.now(timezone.utc): if _aware(row.expires_at) < datetime.now(UTC):
session.delete(row) session.delete(row)
session.commit() session.commit()
return None return None
@@ -117,7 +117,7 @@ def delete_session(session: Session, token: str | None) -> None:
def _prune_expired(session: Session) -> None: def _prune_expired(session: Session) -> None:
session.execute(delete(AuthSession).where(AuthSession.expires_at < datetime.now(timezone.utc))) session.execute(delete(AuthSession).where(AuthSession.expires_at < datetime.now(UTC)))
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
+2 -1
View File
@@ -13,7 +13,8 @@ Three jobs:
from __future__ import annotations from __future__ import annotations
import threading import threading
from typing import Any, Callable from collections.abc import Callable
from typing import Any
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
+2 -1
View File
@@ -14,8 +14,9 @@ is recorded as closed today.
from __future__ import annotations from __future__ import annotations
import logging import logging
from collections.abc import Iterable
from datetime import date from datetime import date
from typing import Any, Iterable from typing import Any
from uuid import uuid4 from uuid import uuid4
from sqlalchemy import select from sqlalchemy import select
+2 -1
View File
@@ -10,7 +10,8 @@ from __future__ import annotations
import logging import logging
import threading import threading
import time as time_module import time as time_module
from typing import Any, Callable from collections.abc import Callable
from typing import Any
import httpx import httpx
+1 -1
View File
@@ -51,7 +51,7 @@ class SourceResult:
error: str | None = None error: str | None = None
@classmethod @classmethod
def failed(cls, source: str, error: str) -> "SourceResult": def failed(cls, source: str, error: str) -> SourceResult:
return cls(source=source, items=[], ok=False, error=error) return cls(source=source, items=[], ok=False, error=error)
+2 -1
View File
@@ -11,8 +11,9 @@ still things you can spend a pomodoro on.
from __future__ import annotations from __future__ import annotations
from collections.abc import Iterator
from datetime import date from datetime import date
from typing import Any, Iterator from typing import Any
import httpx import httpx
+2 -1
View File
@@ -13,8 +13,9 @@ notices it has left the "open issues" query. One owner per task.
from __future__ import annotations from __future__ import annotations
import re import re
from collections.abc import Iterator
from datetime import date from datetime import date
from typing import Any, Iterator from typing import Any
import httpx import httpx
+2 -1
View File
@@ -8,8 +8,9 @@ empty task list.
from __future__ import annotations from __future__ import annotations
import logging import logging
from collections.abc import Callable
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
from typing import Any, Callable from typing import Any
from app.settings_schema import AppSettings from app.settings_schema import AppSettings
from app.sources.base import SourceError, SourceResult, TodoItem from app.sources.base import SourceError, SourceResult, TodoItem
+2 -1
View File
@@ -9,8 +9,9 @@ for people who want the narrower list.
from __future__ import annotations from __future__ import annotations
from collections.abc import Iterator
from datetime import date from datetime import date
from typing import Any, Iterator from typing import Any
import httpx import httpx
+5 -5
View File
@@ -7,7 +7,7 @@ for" — here.
from __future__ import annotations from __future__ import annotations
from datetime import date, datetime, time, timedelta, timezone from datetime import UTC, date, datetime, time, timedelta
from zoneinfo import ZoneInfo from zoneinfo import ZoneInfo
@@ -19,7 +19,7 @@ def tz(name: str) -> ZoneInfo:
def now_utc() -> datetime: def now_utc() -> datetime:
return datetime.now(timezone.utc) return datetime.now(UTC)
def ensure_utc(moment: datetime | None) -> datetime | None: def ensure_utc(moment: datetime | None) -> datetime | None:
@@ -31,12 +31,12 @@ def ensure_utc(moment: datetime | None) -> datetime | None:
""" """
if moment is None: if moment is None:
return None return None
return moment if moment.tzinfo is not None else moment.replace(tzinfo=timezone.utc) return moment if moment.tzinfo is not None else moment.replace(tzinfo=UTC)
def to_local(moment: datetime, tz_name: str) -> datetime: def to_local(moment: datetime, tz_name: str) -> datetime:
if moment.tzinfo is None: if moment.tzinfo is None:
moment = moment.replace(tzinfo=timezone.utc) moment = moment.replace(tzinfo=UTC)
return moment.astimezone(tz(tz_name)) return moment.astimezone(tz(tz_name))
@@ -54,7 +54,7 @@ def day_bounds_utc(day: date, tz_name: str) -> tuple[datetime, datetime]:
zone = tz(tz_name) zone = tz(tz_name)
start = datetime.combine(day, time.min, tzinfo=zone) start = datetime.combine(day, time.min, tzinfo=zone)
end = start + timedelta(days=1) end = start + timedelta(days=1)
return start.astimezone(timezone.utc), end.astimezone(timezone.utc) return start.astimezone(UTC), end.astimezone(UTC)
def in_window(moment: time, start: time, end: time) -> bool: def in_window(moment: time, start: time, end: time) -> bool:
+17
View File
@@ -35,6 +35,23 @@ packages = ["app"]
line-length = 90 line-length = 90
target-version = "py312" target-version = "py312"
# Named rather than left to whatever the installed ruff defaults to, because CI
# runs this and a silent default change should not turn a push red. `format` is
# deliberately not enforced: the layout here is hand-set in places (comment
# tables, the DNS wire-format structs) and a formatter would flatten it.
[tool.ruff.lint]
select = ["E", "F", "I", "UP", "SIM", "B", "RUF"]
ignore = [
# FastAPI's whole dependency idiom is a call in a default argument.
"B008",
# A line-length cap on comments and docstrings is for a formatter to decide.
"E501",
# The reports and the bot speak Russian as well as English, and the typography
# is deliberate (× for counts, – in the laundry source's labels). "Ambiguous
# character" is the wrong complaint about a string the user is meant to read.
"RUF001",
]
[tool.pytest.ini_options] [tool.pytest.ini_options]
pythonpath = ["."] pythonpath = ["."]
testpaths = ["tests"] testpaths = ["tests"]
+1 -2
View File
@@ -61,9 +61,8 @@ def settings(session) -> AppSettings:
@pytest.fixture() @pytest.fixture()
def client(): def client():
from fastapi.testclient import TestClient
from app.main import app from app.main import app
from fastapi.testclient import TestClient
Base.metadata.drop_all(bind=engine) Base.metadata.drop_all(bind=engine)
init_db() init_db()
+1 -2
View File
@@ -13,10 +13,9 @@ known-dead columns after `create_all`; this pins that behaviour down.
from __future__ import annotations from __future__ import annotations
from sqlalchemy import text
from app.db import SessionLocal, engine, init_db from app.db import SessionLocal, engine, init_db
from app.models import TaskRecord from app.models import TaskRecord
from sqlalchemy import text
def _simulate_pre_refactor_database() -> None: def _simulate_pre_refactor_database() -> None:
-1
View File
@@ -3,7 +3,6 @@
from __future__ import annotations from __future__ import annotations
import pytest import pytest
from app.models import PomodoroSession from app.models import PomodoroSession
from app.services import pomodoro, settings_service from app.services import pomodoro, settings_service
+1 -1
View File
@@ -5,8 +5,8 @@ from __future__ import annotations
from datetime import timedelta from datetime import timedelta
from app.models import utcnow from app.models import utcnow
from app.timeutil import local_date
from app.services import pomodoro, report, settings_service, stats, tasks_service from app.services import pomodoro, report, settings_service, stats, tasks_service
from app.timeutil import local_date
def _close(session, settings, task) -> None: def _close(session, settings, task) -> None:
+1 -1
View File
@@ -38,7 +38,7 @@ def _remote(verb: str) -> int:
from PyQt6.QtCore import QCoreApplication from PyQt6.QtCore import QCoreApplication
from PyQt6.QtDBus import QDBusConnection, QDBusInterface from PyQt6.QtDBus import QDBusConnection, QDBusInterface
_app = QCoreApplication(sys.argv) # noqa: F841 - needed for the D-Bus loop _app = QCoreApplication(sys.argv)
interface = QDBusInterface( interface = QDBusInterface(
"org.kadence.Desktop", "/Timer", "org.kadence.Timer", QDBusConnection.sessionBus() "org.kadence.Desktop", "/Timer", "org.kadence.Timer", QDBusConnection.sessionBus()
) )
+5 -6
View File
@@ -12,6 +12,7 @@ hostname, so the worst a compromised client can do is block a site.
from __future__ import annotations from __future__ import annotations
import contextlib
import logging import logging
import os import os
import re import re
@@ -35,7 +36,7 @@ def valid_hostname(name: str) -> bool:
def current_block() -> list[str]: def current_block() -> list[str]:
"""The hostnames currently inside our managed region of `/etc/hosts`.""" """The hostnames currently inside our managed region of `/etc/hosts`."""
try: try:
with open(HOSTS_PATH, "r", encoding="utf-8") as handle: with open(HOSTS_PATH, encoding="utf-8") as handle:
lines = handle.read().splitlines() lines = handle.read().splitlines()
except OSError: except OSError:
return [] return []
@@ -124,12 +125,10 @@ class HostsBlocker:
list_path = handle.name list_path = handle.name
os.chmod(list_path, 0o644) # the helper reads it as root from /tmp os.chmod(list_path, 0o644) # the helper reads it as root from /tmp
try: try:
return self._run(runner + ["apply", "--ip", redirect_ip, "--file", list_path]) return self._run([*runner, "apply", "--ip", redirect_ip, "--file", list_path])
finally: finally:
try: with contextlib.suppress(OSError):
os.unlink(list_path) os.unlink(list_path)
except OSError:
pass
def clear(self) -> bool: def clear(self) -> bool:
"""Remove the managed region. Safe to call when it is not there.""" """Remove the managed region. Safe to call when it is not there."""
@@ -138,7 +137,7 @@ class HostsBlocker:
runner = self._runner() runner = self._runner()
if runner is None: if runner is None:
return False return False
return self._run(runner + ["clear"]) return self._run([*runner, "clear"])
def _run(self, command: list[str]) -> bool: def _run(self, command: list[str]) -> bool:
try: try:
+2 -2
View File
@@ -26,8 +26,8 @@ from __future__ import annotations
import logging import logging
import time import time
from collections.abc import Callable
from types import ModuleType from types import ModuleType
from typing import Callable
from kadence_desktop.blocking import processes from kadence_desktop.blocking import processes
from kadence_desktop.blocking.hosts import HostsBlocker from kadence_desktop.blocking.hosts import HostsBlocker
@@ -262,7 +262,7 @@ class BlockerManager:
self._asked[identity] = now self._asked[identity] = now
self._announce(app, "killed") self._announce(app, "killed")
def _announce(self, app: "processes.RunningApp", action: str) -> None: def _announce(self, app: processes.RunningApp, action: str) -> None:
"""Report and notify, at most once per process per cooldown.""" """Report and notify, at most once per process per cooldown."""
if not self._claim_report(app.identity): if not self._claim_report(app.identity):
return return
@@ -23,8 +23,8 @@ from __future__ import annotations
import logging import logging
import os import os
import signal import signal
from fnmatch import fnmatch
from dataclasses import dataclass from dataclasses import dataclass
from fnmatch import fnmatch
from pathlib import Path from pathlib import Path
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
+8 -9
View File
@@ -31,6 +31,7 @@ startup whenever a drop-in is found lying around.
from __future__ import annotations from __future__ import annotations
import concurrent.futures import concurrent.futures
import contextlib
import fnmatch import fnmatch
import ipaddress import ipaddress
import logging import logging
@@ -363,10 +364,8 @@ class StubResolver:
for sock in (self._udp, self._tcp): for sock in (self._udp, self._tcp):
if sock is None: if sock is None:
continue continue
try: with contextlib.suppress(OSError):
sock.close() # the blocking recv/accept raises, ending the loop sock.close() # the blocking recv/accept raises, ending the loop
except OSError:
pass
for thread in self._threads: for thread in self._threads:
thread.join(timeout=1) thread.join(timeout=1)
for pool in (self._handlers, self._forwards): for pool in (self._handlers, self._forwards):
@@ -407,7 +406,7 @@ class StubResolver:
while self._running and self._udp is not None: while self._running and self._udp is not None:
try: try:
data, address = self._udp.recvfrom(4096) data, address = self._udp.recvfrom(4096)
except socket.timeout: except TimeoutError:
continue continue
except OSError: except OSError:
break break
@@ -431,7 +430,7 @@ class StubResolver:
while self._running and self._tcp is not None: while self._running and self._tcp is not None:
try: try:
connection, _ = self._tcp.accept() connection, _ = self._tcp.accept()
except socket.timeout: except TimeoutError:
continue continue
except OSError: except OSError:
break break
@@ -632,7 +631,7 @@ def _parse_nameserver(token: str) -> tuple[str, int] | None:
def _parse_resolv_conf(path: str) -> list[tuple[str, int]]: def _parse_resolv_conf(path: str) -> list[tuple[str, int]]:
servers: list[tuple[str, int]] = [] servers: list[tuple[str, int]] = []
try: try:
with open(path, "r", encoding="utf-8", errors="replace") as handle: with open(path, encoding="utf-8", errors="replace") as handle:
lines = handle.read().splitlines() lines = handle.read().splitlines()
except OSError: except OSError:
return servers return servers
@@ -704,7 +703,7 @@ class ResolvedRoute:
def _read_dropin(self) -> tuple[tuple[str, ...], int] | None: def _read_dropin(self) -> tuple[tuple[str, ...], int] | None:
"""What the drop-in currently says, if it is one of ours.""" """What the drop-in currently says, if it is one of ours."""
try: try:
with open(self.dropin_path, "r", encoding="utf-8") as handle: with open(self.dropin_path, encoding="utf-8") as handle:
text = handle.read() text = handle.read()
except OSError: except OSError:
return None return None
@@ -739,7 +738,7 @@ class ResolvedRoute:
runner = self._runner() runner = self._runner()
if runner is None: if runner is None:
return False return False
command = runner + ["resolved-on", "--port", str(port), "--domains", ",".join(wanted)] command = [*runner, "resolved-on", "--port", str(port), "--domains", ",".join(wanted)]
if not self._run(command, f"route {len(wanted)} zone(s) through the local stub"): if not self._run(command, f"route {len(wanted)} zone(s) through the local stub"):
return False return False
self._applied = (wanted, port) self._applied = (wanted, port)
@@ -753,7 +752,7 @@ class ResolvedRoute:
runner = self._runner() runner = self._runner()
if runner is None: if runner is None:
return False return False
if not self._run(runner + ["resolved-off"], "stop routing DNS through the local stub"): if not self._run([*runner, "resolved-off"], "stop routing DNS through the local stub"):
return False return False
self._applied = None self._applied = None
return True return True
+1 -1
View File
@@ -107,7 +107,7 @@ class DesktopConfig:
window: dict = field(default_factory=lambda: {"width": 1100, "height": 760}) window: dict = field(default_factory=lambda: {"width": 1100, "height": 760})
@classmethod @classmethod
def load(cls) -> "DesktopConfig": def load(cls) -> DesktopConfig:
if not CONFIG_PATH.exists(): if not CONFIG_PATH.exists():
config = cls() config = cls()
config.save() config.save()
@@ -19,7 +19,7 @@ every documented command then hits.
from __future__ import annotations from __future__ import annotations
import logging import logging
from typing import Callable from collections.abc import Callable
from PyQt6.QtCore import QObject, pyqtClassInfo, pyqtSlot from PyQt6.QtCore import QObject, pyqtClassInfo, pyqtSlot
from PyQt6.QtDBus import QDBusAbstractAdaptor, QDBusConnection from PyQt6.QtDBus import QDBusAbstractAdaptor, QDBusConnection
@@ -92,60 +92,60 @@ class TimerAdaptor(QDBusAbstractAdaptor):
# D-Bus methods are PascalCase by convention. # D-Bus methods are PascalCase by convention.
@pyqtSlot(result=str) @pyqtSlot(result=str)
def StartWork(self) -> str: # noqa: N802 def StartWork(self) -> str:
"""Start a focus interval.""" """Start a focus interval."""
return self._service.run("start_work") return self._service.run("start_work")
@pyqtSlot(result=str) @pyqtSlot(result=str)
def StartBreak(self) -> str: # noqa: N802 def StartBreak(self) -> str:
return self._service.run("start_break") return self._service.run("start_break")
@pyqtSlot(result=str) @pyqtSlot(result=str)
def Toggle(self) -> str: # noqa: N802 def Toggle(self) -> str:
"""Start if idle, pause if running, resume if paused — one hotkey.""" """Start if idle, pause if running, resume if paused — one hotkey."""
return self._service.run("toggle") return self._service.run("toggle")
@pyqtSlot(result=str) @pyqtSlot(result=str)
def Pause(self) -> str: # noqa: N802 def Pause(self) -> str:
return self._service.run("pause") return self._service.run("pause")
@pyqtSlot(result=str) @pyqtSlot(result=str)
def Resume(self) -> str: # noqa: N802 def Resume(self) -> str:
return self._service.run("resume") return self._service.run("resume")
@pyqtSlot(result=str) @pyqtSlot(result=str)
def Complete(self) -> str: # noqa: N802 def Complete(self) -> str:
return self._service.run("complete") return self._service.run("complete")
@pyqtSlot(result=str) @pyqtSlot(result=str)
def Stop(self) -> str: # noqa: N802 def Stop(self) -> str:
return self._service.run("stop") return self._service.run("stop")
@pyqtSlot(result=str) @pyqtSlot(result=str)
def Extend(self) -> str: # noqa: N802 def Extend(self) -> str:
return self._service.run("extend") return self._service.run("extend")
@pyqtSlot(result=str) @pyqtSlot(result=str)
def Interrupt(self) -> str: # noqa: N802 def Interrupt(self) -> str:
"""Count a distraction without stopping the clock.""" """Count a distraction without stopping the clock."""
return self._service.run("interrupt") return self._service.run("interrupt")
@pyqtSlot(result=str) @pyqtSlot(result=str)
def Status(self) -> str: # noqa: N802 def Status(self) -> str:
"""One line: what is running and how much is left.""" """One line: what is running and how much is left."""
return self._service.run("status") return self._service.run("status")
@pyqtSlot(result=str) @pyqtSlot(result=str)
def SendReport(self) -> str: # noqa: N802 def SendReport(self) -> str:
return self._service.run("send_report") return self._service.run("send_report")
@pyqtSlot(result=str) @pyqtSlot(result=str)
def ToggleMini(self) -> str: # noqa: N802 def ToggleMini(self) -> str:
"""Show or hide the floating mini timer.""" """Show or hide the floating mini timer."""
return self._service.run("toggle_mini") return self._service.run("toggle_mini")
@pyqtSlot(result=str) @pyqtSlot(result=str)
def Raise(self) -> str: # noqa: N802 def Raise(self) -> str:
"""Show the main window (used by a second instance).""" """Show the main window (used by a second instance)."""
return self._service.run("raise_window") return self._service.run("raise_window")
@@ -155,9 +155,7 @@ def up_to_date(position: tuple[int, int] | None = None) -> bool:
if any(parser.get(section, key, fallback=None) != value for key, value in wanted.items()): if any(parser.get(section, key, fallback=None) != value for key, value in wanted.items()):
return False return False
# A position left over from a previous corner must not linger. # A position left over from a previous corner must not linger.
if position is None and parser.has_option(section, "position"): return not (position is None and parser.has_option(section, "position"))
return False
return True
except (configparser.Error, OSError): except (configparser.Error, OSError):
return False return False
@@ -10,7 +10,7 @@ so a running timer never stacks up a column of toasts.
from __future__ import annotations from __future__ import annotations
import logging import logging
from typing import Callable from collections.abc import Callable
from PyQt6.QtCore import QMetaType, QObject, QVariant, pyqtSlot from PyQt6.QtCore import QMetaType, QObject, QVariant, pyqtSlot
from PyQt6.QtDBus import QDBusArgument, QDBusConnection, QDBusInterface, QDBusMessage from PyQt6.QtDBus import QDBusArgument, QDBusConnection, QDBusInterface, QDBusMessage
-1
View File
@@ -7,7 +7,6 @@ that silently puts a window half off the screen on somebody else's monitor.
from __future__ import annotations from __future__ import annotations
import pytest import pytest
from kadence_desktop.integrations.placement import CORNER_LABELS, CORNERS, Rect, point_for from kadence_desktop.integrations.placement import CORNER_LABELS, CORNERS, Rect, point_for
SCREEN = Rect(0, 0, 1536, 960) SCREEN = Rect(0, 0, 1536, 960)
-1
View File
@@ -18,7 +18,6 @@ import threading
from pathlib import Path from pathlib import Path
import pytest import pytest
from kadence_desktop.blocking.resolver import ( from kadence_desktop.blocking.resolver import (
ResolvedRoute, ResolvedRoute,
StubResolver, StubResolver,