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:
28 files changed
+84
-68
No files matched your search
@@ -8,7 +8,7 @@ day so a re-send shows the same numbers.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import date, datetime, timezone
|
||||
from datetime import UTC, date, datetime
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import (
|
||||
@@ -30,7 +30,7 @@ from app.db import Base
|
||||
|
||||
|
||||
def utcnow() -> datetime:
|
||||
return datetime.now(timezone.utc)
|
||||
return datetime.now(UTC)
|
||||
|
||||
|
||||
class SettingsRow(Base):
|
||||
@@ -80,7 +80,7 @@ class PomodoroSession(Base):
|
||||
note: 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
|
||||
def is_work(self) -> bool:
|
||||
|
||||
@@ -8,9 +8,10 @@ the Telegram message at once.
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from datetime import date
|
||||
from typing import Any, Callable
|
||||
from typing import Any
|
||||
|
||||
Metrics = dict[str, Any]
|
||||
Context = dict[str, Any]
|
||||
|
||||
@@ -17,7 +17,7 @@ import hashlib
|
||||
import hmac
|
||||
import secrets
|
||||
import time
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import delete
|
||||
@@ -84,7 +84,7 @@ def create_session(session: Session, method: str, session_days: int) -> AuthSess
|
||||
row = AuthSession(
|
||||
token=secrets.token_urlsafe(32),
|
||||
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.commit()
|
||||
@@ -93,7 +93,7 @@ def create_session(session: Session, method: str, session_days: int) -> AuthSess
|
||||
|
||||
def _aware(dt: datetime) -> datetime:
|
||||
"""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:
|
||||
@@ -102,7 +102,7 @@ def valid_session(session: Session, token: str | None) -> AuthSession | None:
|
||||
row = session.get(AuthSession, token)
|
||||
if row is None:
|
||||
return None
|
||||
if _aware(row.expires_at) < datetime.now(timezone.utc):
|
||||
if _aware(row.expires_at) < datetime.now(UTC):
|
||||
session.delete(row)
|
||||
session.commit()
|
||||
return None
|
||||
@@ -117,7 +117,7 @@ def delete_session(session: Session, token: str | None) -> 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)))
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
@@ -13,7 +13,8 @@ Three jobs:
|
||||
from __future__ import annotations
|
||||
|
||||
import threading
|
||||
from typing import Any, Callable
|
||||
from collections.abc import Callable
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
|
||||
@@ -14,8 +14,9 @@ is recorded as closed today.
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from collections.abc import Iterable
|
||||
from datetime import date
|
||||
from typing import Any, Iterable
|
||||
from typing import Any
|
||||
from uuid import uuid4
|
||||
|
||||
from sqlalchemy import select
|
||||
|
||||
@@ -10,7 +10,8 @@ from __future__ import annotations
|
||||
import logging
|
||||
import threading
|
||||
import time as time_module
|
||||
from typing import Any, Callable
|
||||
from collections.abc import Callable
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ class SourceResult:
|
||||
error: str | None = None
|
||||
|
||||
@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)
|
||||
|
||||
|
||||
|
||||
@@ -11,8 +11,9 @@ still things you can spend a pomodoro on.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Iterator
|
||||
from datetime import date
|
||||
from typing import Any, Iterator
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
|
||||
|
||||
@@ -13,8 +13,9 @@ notices it has left the "open issues" query. One owner per task.
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from collections.abc import Iterator
|
||||
from datetime import date
|
||||
from typing import Any, Iterator
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
|
||||
|
||||
@@ -8,8 +8,9 @@ empty task list.
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from collections.abc import Callable
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from typing import Any, Callable
|
||||
from typing import Any
|
||||
|
||||
from app.settings_schema import AppSettings
|
||||
from app.sources.base import SourceError, SourceResult, TodoItem
|
||||
|
||||
@@ -9,8 +9,9 @@ for people who want the narrower list.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Iterator
|
||||
from datetime import date
|
||||
from typing import Any, Iterator
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ for" — here.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import date, datetime, time, timedelta, timezone
|
||||
from datetime import UTC, date, datetime, time, timedelta
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ def tz(name: str) -> ZoneInfo:
|
||||
|
||||
|
||||
def now_utc() -> datetime:
|
||||
return datetime.now(timezone.utc)
|
||||
return datetime.now(UTC)
|
||||
|
||||
|
||||
def ensure_utc(moment: datetime | None) -> datetime | None:
|
||||
@@ -31,12 +31,12 @@ def ensure_utc(moment: datetime | None) -> datetime | None:
|
||||
"""
|
||||
if moment is 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:
|
||||
if moment.tzinfo is None:
|
||||
moment = moment.replace(tzinfo=timezone.utc)
|
||||
moment = moment.replace(tzinfo=UTC)
|
||||
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)
|
||||
start = datetime.combine(day, time.min, tzinfo=zone)
|
||||
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:
|
||||
|
||||
@@ -35,6 +35,23 @@ packages = ["app"]
|
||||
line-length = 90
|
||||
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]
|
||||
pythonpath = ["."]
|
||||
testpaths = ["tests"]
|
||||
@@ -61,9 +61,8 @@ def settings(session) -> AppSettings:
|
||||
|
||||
@pytest.fixture()
|
||||
def client():
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.main import app
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
Base.metadata.drop_all(bind=engine)
|
||||
init_db()
|
||||
|
||||
@@ -13,10 +13,9 @@ known-dead columns after `create_all`; this pins that behaviour down.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from sqlalchemy import text
|
||||
|
||||
from app.db import SessionLocal, engine, init_db
|
||||
from app.models import TaskRecord
|
||||
from sqlalchemy import text
|
||||
|
||||
|
||||
def _simulate_pre_refactor_database() -> None:
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from app.models import PomodoroSession
|
||||
from app.services import pomodoro, settings_service
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ from __future__ import annotations
|
||||
from datetime import timedelta
|
||||
|
||||
from app.models import utcnow
|
||||
from app.timeutil import local_date
|
||||
from app.services import pomodoro, report, settings_service, stats, tasks_service
|
||||
from app.timeutil import local_date
|
||||
|
||||
|
||||
def _close(session, settings, task) -> None:
|
||||
|
||||
@@ -38,7 +38,7 @@ def _remote(verb: str) -> int:
|
||||
from PyQt6.QtCore import QCoreApplication
|
||||
from PyQt6.QtDBus import QDBusConnection, QDBusInterface
|
||||
|
||||
_app = QCoreApplication(sys.argv) # noqa: F841 - needed for the D-Bus loop
|
||||
_app = QCoreApplication(sys.argv)
|
||||
interface = QDBusInterface(
|
||||
"org.kadence.Desktop", "/Timer", "org.kadence.Timer", QDBusConnection.sessionBus()
|
||||
)
|
||||
|
||||
@@ -12,6 +12,7 @@ hostname, so the worst a compromised client can do is block a site.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
@@ -35,7 +36,7 @@ def valid_hostname(name: str) -> bool:
|
||||
def current_block() -> list[str]:
|
||||
"""The hostnames currently inside our managed region of `/etc/hosts`."""
|
||||
try:
|
||||
with open(HOSTS_PATH, "r", encoding="utf-8") as handle:
|
||||
with open(HOSTS_PATH, encoding="utf-8") as handle:
|
||||
lines = handle.read().splitlines()
|
||||
except OSError:
|
||||
return []
|
||||
@@ -124,12 +125,10 @@ class HostsBlocker:
|
||||
list_path = handle.name
|
||||
os.chmod(list_path, 0o644) # the helper reads it as root from /tmp
|
||||
try:
|
||||
return self._run(runner + ["apply", "--ip", redirect_ip, "--file", list_path])
|
||||
return self._run([*runner, "apply", "--ip", redirect_ip, "--file", list_path])
|
||||
finally:
|
||||
try:
|
||||
with contextlib.suppress(OSError):
|
||||
os.unlink(list_path)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def clear(self) -> bool:
|
||||
"""Remove the managed region. Safe to call when it is not there."""
|
||||
@@ -138,7 +137,7 @@ class HostsBlocker:
|
||||
runner = self._runner()
|
||||
if runner is None:
|
||||
return False
|
||||
return self._run(runner + ["clear"])
|
||||
return self._run([*runner, "clear"])
|
||||
|
||||
def _run(self, command: list[str]) -> bool:
|
||||
try:
|
||||
|
||||
@@ -26,8 +26,8 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import time
|
||||
from collections.abc import Callable
|
||||
from types import ModuleType
|
||||
from typing import Callable
|
||||
|
||||
from kadence_desktop.blocking import processes
|
||||
from kadence_desktop.blocking.hosts import HostsBlocker
|
||||
@@ -262,7 +262,7 @@ class BlockerManager:
|
||||
self._asked[identity] = now
|
||||
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."""
|
||||
if not self._claim_report(app.identity):
|
||||
return
|
||||
|
||||
@@ -23,8 +23,8 @@ from __future__ import annotations
|
||||
import logging
|
||||
import os
|
||||
import signal
|
||||
from fnmatch import fnmatch
|
||||
from dataclasses import dataclass
|
||||
from fnmatch import fnmatch
|
||||
from pathlib import Path
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -31,6 +31,7 @@ startup whenever a drop-in is found lying around.
|
||||
from __future__ import annotations
|
||||
|
||||
import concurrent.futures
|
||||
import contextlib
|
||||
import fnmatch
|
||||
import ipaddress
|
||||
import logging
|
||||
@@ -363,10 +364,8 @@ class StubResolver:
|
||||
for sock in (self._udp, self._tcp):
|
||||
if sock is None:
|
||||
continue
|
||||
try:
|
||||
with contextlib.suppress(OSError):
|
||||
sock.close() # the blocking recv/accept raises, ending the loop
|
||||
except OSError:
|
||||
pass
|
||||
for thread in self._threads:
|
||||
thread.join(timeout=1)
|
||||
for pool in (self._handlers, self._forwards):
|
||||
@@ -407,7 +406,7 @@ class StubResolver:
|
||||
while self._running and self._udp is not None:
|
||||
try:
|
||||
data, address = self._udp.recvfrom(4096)
|
||||
except socket.timeout:
|
||||
except TimeoutError:
|
||||
continue
|
||||
except OSError:
|
||||
break
|
||||
@@ -431,7 +430,7 @@ class StubResolver:
|
||||
while self._running and self._tcp is not None:
|
||||
try:
|
||||
connection, _ = self._tcp.accept()
|
||||
except socket.timeout:
|
||||
except TimeoutError:
|
||||
continue
|
||||
except OSError:
|
||||
break
|
||||
@@ -632,7 +631,7 @@ def _parse_nameserver(token: str) -> tuple[str, int] | None:
|
||||
def _parse_resolv_conf(path: str) -> list[tuple[str, int]]:
|
||||
servers: list[tuple[str, int]] = []
|
||||
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()
|
||||
except OSError:
|
||||
return servers
|
||||
@@ -704,7 +703,7 @@ class ResolvedRoute:
|
||||
def _read_dropin(self) -> tuple[tuple[str, ...], int] | None:
|
||||
"""What the drop-in currently says, if it is one of ours."""
|
||||
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()
|
||||
except OSError:
|
||||
return None
|
||||
@@ -739,7 +738,7 @@ class ResolvedRoute:
|
||||
runner = self._runner()
|
||||
if runner is None:
|
||||
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"):
|
||||
return False
|
||||
self._applied = (wanted, port)
|
||||
@@ -753,7 +752,7 @@ class ResolvedRoute:
|
||||
runner = self._runner()
|
||||
if runner is None:
|
||||
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
|
||||
self._applied = None
|
||||
return True
|
||||
|
||||
@@ -107,7 +107,7 @@ class DesktopConfig:
|
||||
window: dict = field(default_factory=lambda: {"width": 1100, "height": 760})
|
||||
|
||||
@classmethod
|
||||
def load(cls) -> "DesktopConfig":
|
||||
def load(cls) -> DesktopConfig:
|
||||
if not CONFIG_PATH.exists():
|
||||
config = cls()
|
||||
config.save()
|
||||
|
||||
@@ -19,7 +19,7 @@ every documented command then hits.
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Callable
|
||||
from collections.abc import Callable
|
||||
|
||||
from PyQt6.QtCore import QObject, pyqtClassInfo, pyqtSlot
|
||||
from PyQt6.QtDBus import QDBusAbstractAdaptor, QDBusConnection
|
||||
@@ -92,60 +92,60 @@ class TimerAdaptor(QDBusAbstractAdaptor):
|
||||
|
||||
# D-Bus methods are PascalCase by convention.
|
||||
@pyqtSlot(result=str)
|
||||
def StartWork(self) -> str: # noqa: N802
|
||||
def StartWork(self) -> str:
|
||||
"""Start a focus interval."""
|
||||
return self._service.run("start_work")
|
||||
|
||||
@pyqtSlot(result=str)
|
||||
def StartBreak(self) -> str: # noqa: N802
|
||||
def StartBreak(self) -> str:
|
||||
return self._service.run("start_break")
|
||||
|
||||
@pyqtSlot(result=str)
|
||||
def Toggle(self) -> str: # noqa: N802
|
||||
def Toggle(self) -> str:
|
||||
"""Start if idle, pause if running, resume if paused — one hotkey."""
|
||||
return self._service.run("toggle")
|
||||
|
||||
@pyqtSlot(result=str)
|
||||
def Pause(self) -> str: # noqa: N802
|
||||
def Pause(self) -> str:
|
||||
return self._service.run("pause")
|
||||
|
||||
@pyqtSlot(result=str)
|
||||
def Resume(self) -> str: # noqa: N802
|
||||
def Resume(self) -> str:
|
||||
return self._service.run("resume")
|
||||
|
||||
@pyqtSlot(result=str)
|
||||
def Complete(self) -> str: # noqa: N802
|
||||
def Complete(self) -> str:
|
||||
return self._service.run("complete")
|
||||
|
||||
@pyqtSlot(result=str)
|
||||
def Stop(self) -> str: # noqa: N802
|
||||
def Stop(self) -> str:
|
||||
return self._service.run("stop")
|
||||
|
||||
@pyqtSlot(result=str)
|
||||
def Extend(self) -> str: # noqa: N802
|
||||
def Extend(self) -> str:
|
||||
return self._service.run("extend")
|
||||
|
||||
@pyqtSlot(result=str)
|
||||
def Interrupt(self) -> str: # noqa: N802
|
||||
def Interrupt(self) -> str:
|
||||
"""Count a distraction without stopping the clock."""
|
||||
return self._service.run("interrupt")
|
||||
|
||||
@pyqtSlot(result=str)
|
||||
def Status(self) -> str: # noqa: N802
|
||||
def Status(self) -> str:
|
||||
"""One line: what is running and how much is left."""
|
||||
return self._service.run("status")
|
||||
|
||||
@pyqtSlot(result=str)
|
||||
def SendReport(self) -> str: # noqa: N802
|
||||
def SendReport(self) -> str:
|
||||
return self._service.run("send_report")
|
||||
|
||||
@pyqtSlot(result=str)
|
||||
def ToggleMini(self) -> str: # noqa: N802
|
||||
def ToggleMini(self) -> str:
|
||||
"""Show or hide the floating mini timer."""
|
||||
return self._service.run("toggle_mini")
|
||||
|
||||
@pyqtSlot(result=str)
|
||||
def Raise(self) -> str: # noqa: N802
|
||||
def Raise(self) -> str:
|
||||
"""Show the main window (used by a second instance)."""
|
||||
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()):
|
||||
return False
|
||||
# A position left over from a previous corner must not linger.
|
||||
if position is None and parser.has_option(section, "position"):
|
||||
return False
|
||||
return True
|
||||
return not (position is None and parser.has_option(section, "position"))
|
||||
except (configparser.Error, OSError):
|
||||
return False
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ so a running timer never stacks up a column of toasts.
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Callable
|
||||
from collections.abc import Callable
|
||||
|
||||
from PyQt6.QtCore import QMetaType, QObject, QVariant, pyqtSlot
|
||||
from PyQt6.QtDBus import QDBusArgument, QDBusConnection, QDBusInterface, QDBusMessage
|
||||
|
||||
@@ -7,7 +7,6 @@ that silently puts a window half off the screen on somebody else's monitor.
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from kadence_desktop.integrations.placement import CORNER_LABELS, CORNERS, Rect, point_for
|
||||
|
||||
SCREEN = Rect(0, 0, 1536, 960)
|
||||
|
||||
@@ -18,7 +18,6 @@ import threading
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from kadence_desktop.blocking.resolver import (
|
||||
ResolvedRoute,
|
||||
StubResolver,
|
||||
|
||||
Reference in new issue
Block a user