gatelock 0.8.1: public recovery/detector handles; declarative types import from _config
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file length / file-length (push) Waiting to run
gatelock pre-commit / pre-commit (push) Waiting to run
gatelock tests / test (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
palette drift / palette (push) Waiting to run
shared gate tests / gate unit tests (push) Waiting to run

leetcode-guard's study mode has to stop the re-assertion loop and the
output-change watcher before it releases the grab, and could only reach
them as LockWindow._recovery / _detector -- attributes 0.8.0 folded into
the arming bundle, which broke that documented private dependency.
They are public read-only properties now, next to `surfaces`.

_window no longer re-exports the LockConfig family; it was the one
thing keeping it over the 250-line cap, and every importer now takes
those names from gatelock._config (or the package) directly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HHCbkJWXHjtTUaqvnHRAqA
This commit is contained in:
Krzysztof Rudnicki 2026-09-12 19:41:13 +02:00
parent 68ec316b28
commit 0cb575d15f
21 changed files with 40 additions and 55 deletions

View File

@ -15,7 +15,7 @@ from gatelock._arbiter import (
Arbiter,
)
from gatelock._claims import ArbiterVerdict, Claim, default_runtime_dir, grab_strength
from gatelock._config import GrabPolicy
from gatelock._config import GrabPolicy, LockConfig, SpaceStep, TypeRole
from gatelock._detect import OutputChangeDetector
from gatelock._escape import (
EscapeDraft,
@ -25,6 +25,7 @@ from gatelock._escape import (
)
from gatelock._fitcheck import FitResult, measure_fit, report_fit
from gatelock._guards import assert_not_under_pytest, wait_for_x_server
from gatelock._hooks import LockWindowHooks
from gatelock._keyboard import bind_activate, bind_cancel, escape_text_tab_trap
from gatelock._outputs import (
Output,
@ -56,13 +57,7 @@ from gatelock._surfaces import (
)
from gatelock._theme import LockPalette, LockSpacing, LockTypography
from gatelock._vt import disable_vt_switching, restore_vt_switching
from gatelock._window import (
LockConfig,
LockWindow,
LockWindowHooks,
SpaceStep,
TypeRole,
)
from gatelock._window import LockWindow
from gatelock._xrandr import parse_xrandr_query
from gatelock.widget_group import WidgetGroup
from gatelock.widgets import (

View File

@ -6,9 +6,6 @@ lifecycle) and the *declarative* surface an embedding app actually fills in
can each be read in one piece. :class:`LockConfig` carries no behaviour beyond
resolving its own presets and the design-system scales, and nothing here
imports the window machinery -- the dependency runs one way only.
Imported from :mod:`gatelock._window` as well, so ``from gatelock._window
import LockConfig`` keeps working for the modules and tests that already do.
"""
from __future__ import annotations

View File

@ -29,8 +29,8 @@ import tkinter as tk
from typing import TYPE_CHECKING
from gatelock import _density
from gatelock._config import LockConfig
from gatelock._scrollable import ScrollableSurface
from gatelock._window import LockConfig
if TYPE_CHECKING:
from collections.abc import Callable

View File

@ -32,7 +32,7 @@ from gatelock._scrollkeys import bind_scroll_keys, track_focus
from gatelock._userintent import UserIntentTracker, viewport_targets
if TYPE_CHECKING:
from gatelock._window import LockConfig
from gatelock._config import LockConfig
# Extra px kept visible around a widget scrolled into view, so a focused
# field never sits flush against the viewport edge.

View File

@ -68,8 +68,8 @@ __all__ = [
]
if TYPE_CHECKING:
from gatelock._config import LockConfig
from gatelock._outputs import OutputScan
from gatelock._window import LockConfig
_logger = logging.getLogger(__name__)

View File

@ -13,7 +13,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING, Literal
if TYPE_CHECKING:
from gatelock._window import LockConfig
from gatelock._config import LockConfig
ButtonVariant = Literal["primary", "secondary", "danger"]
"""Emphasis role for :func:`gatelock.widgets.make_button`, not a colour."""

View File

@ -33,15 +33,7 @@ import tkinter as tk
from typing import TYPE_CHECKING
from gatelock import _arming, _preempt
from gatelock._config import (
GrabKind,
LockConfig,
LockMode,
SpaceStep,
TypeRole,
)
from gatelock._detect import OutputChangeDetector
from gatelock._hooks import LockWindowHooks
from gatelock._outputs import OutputEnumerator
from gatelock._recovery import RecoveryCollaborators, RecoveryLoop
from gatelock._surfaces import SurfaceSet
@ -51,22 +43,11 @@ if TYPE_CHECKING:
from types import FrameType
from gatelock._arbiter import Arbiter
from gatelock._config import LockConfig
from gatelock._hooks import LockWindowHooks
_logger = logging.getLogger(__name__)
# Re-exported so `from gatelock._window import LockConfig` -- which several
# sibling modules and the test suite already do -- keeps resolving after the
# declarative half moved to :mod:`gatelock._config`.
__all__ = [
"GrabKind",
"LockConfig",
"LockMode",
"LockWindow",
"LockWindowHooks",
"SpaceStep",
"TypeRole",
]
# Periodic no-op so a grabbed, event-starved loop keeps handing control back
# to Python, letting SIGTERM/SIGINT be serviced promptly.
_KEEPALIVE_MS = 250
@ -133,6 +114,22 @@ class LockWindow:
"""The live surface set, for apps that need to fan work out over it."""
return self._arming.surfaces
@property
def recovery(self) -> RecoveryLoop:
"""The re-assertion loop, for an app that suspends the lock.
Releasing the grab while this runs is pointless: it re-takes the grab
within a tick. An app that hands the screen to something else
(leetcode-guard's study mode) stops this and :attr:`detector` first,
and starts both again when it re-arms.
"""
return self._arming.recovery
@property
def detector(self) -> OutputChangeDetector:
"""The output-change watcher; see :attr:`recovery` for when to stop it."""
return self._arming.detector
# -- window mechanics -----------------------------------------------------
def setup(self) -> None:

View File

@ -17,10 +17,11 @@ from unittest.mock import MagicMock, patch
import pytest
from gatelock._config import LockConfig
from gatelock._outputs import Output, OutputRect, OutputScan
from gatelock._recovery import RecoveryCollaborators, RecoveryLoop
from gatelock._surfaces import SurfaceSet
from gatelock._window import LockConfig, LockWindow
from gatelock._window import LockWindow
if TYPE_CHECKING:
from collections.abc import Generator

View File

@ -17,7 +17,7 @@ import tkinter as tk
from typing import TYPE_CHECKING
from unittest.mock import MagicMock, patch
from gatelock._window import LockConfig
from gatelock._config import LockConfig
from gatelock.tests.conftest import BOTH, DEAD_PRIMARY, build_loop
if TYPE_CHECKING:

View File

@ -20,8 +20,8 @@ import tkinter as tk
import pytest
from gatelock._config import LockConfig
from gatelock._scrollable import ScrollableSurface
from gatelock._window import LockConfig
pytestmark = pytest.mark.skipif(
not os.environ.get("DISPLAY"),

View File

@ -13,10 +13,10 @@ import tkinter as tk
import pytest
from gatelock._config import LockConfig
from gatelock._scrollable import ScrollableSurface
from gatelock._scrollkeys import bind_scroll_keys
from gatelock._userintent import viewport_targets
from gatelock._window import LockConfig
pytestmark = pytest.mark.skipif(
not os.environ.get("DISPLAY"),

View File

@ -7,13 +7,12 @@ from unittest.mock import MagicMock
import pytest
from gatelock._config import GrabPolicy
from gatelock._config import GrabPolicy, LockConfig
from gatelock._outputs import Output, OutputRect, OutputScan
from gatelock._surfaces import (
SurfaceSet,
needs_backdrop_root,
)
from gatelock._window import LockConfig
DP0 = Output("DP-0", connected=True, rect=OutputRect(0, 0, 3840, 2160), primary=True)
HDMI = Output("HDMI-0", connected=True, rect=OutputRect(3840, 0, 2560, 1440))

View File

@ -10,6 +10,7 @@ from unittest.mock import MagicMock
import pytest
from gatelock._config import LockConfig
from gatelock._outputs import Output, OutputRect, OutputScan
from gatelock._surfaces import (
SurfaceInfo,
@ -17,7 +18,6 @@ from gatelock._surfaces import (
TextMirror,
mirror_text_widgets,
)
from gatelock._window import LockConfig
DP0 = Output("DP-0", connected=True, rect=OutputRect(0, 0, 3840, 2160), primary=True)
HDMI = Output("HDMI-0", connected=True, rect=OutputRect(3840, 0, 2560, 1440))

View File

@ -16,7 +16,7 @@ from typing import TYPE_CHECKING
import pytest
from gatelock._window import LockConfig
from gatelock._config import LockConfig
from gatelock.widgets import (
DEFAULT_WRAP,
ButtonStyle,

View File

@ -6,9 +6,8 @@ from __future__ import annotations
import tkinter as tk
from unittest.mock import MagicMock, patch
from gatelock._config import GrabPolicy
from gatelock._config import GrabPolicy, LockConfig
from gatelock._theme import LockPalette
from gatelock._window import LockConfig
from gatelock.tests.conftest import make_window

View File

@ -9,8 +9,7 @@ from __future__ import annotations
import tkinter as tk
from unittest.mock import MagicMock, patch
from gatelock._config import GrabPolicy
from gatelock._window import LockConfig
from gatelock._config import GrabPolicy, LockConfig
from gatelock.tests.conftest import make_window

View File

@ -10,10 +10,9 @@ from gatelock._arbiter import (
RANK_SCREEN_LOCKER,
)
from gatelock._claims import Claim
from gatelock._config import GrabPolicy
from gatelock._config import GrabPolicy, LockConfig
from gatelock._detect import _RandrEventSource
from gatelock._outputs import Output, OutputRect
from gatelock._window import LockConfig
from gatelock.tests.conftest import make_window
# Captured before the autouse hermetic fixture replaces it.

View File

@ -12,9 +12,9 @@ from unittest.mock import MagicMock, patch
from gatelock._arbiter import (
RANK_DIET_GUARD,
)
from gatelock._config import LockConfig
from gatelock._detect import _RandrEventSource
from gatelock._outputs import Output, OutputRect
from gatelock._window import LockConfig
from gatelock.tests.conftest import make_window
# Captured before the autouse hermetic fixture replaces it.

View File

@ -17,10 +17,9 @@ from gatelock._arbiter import (
RANK_WAKE_ALARM,
)
from gatelock._claims import Claim
from gatelock._config import GrabPolicy
from gatelock._config import GrabPolicy, LockConfig
from gatelock._detect import _RandrEventSource
from gatelock._outputs import Output, OutputRect
from gatelock._window import LockConfig
from gatelock.tests.conftest import make_window
# Captured before the autouse hermetic fixture replaces it.

View File

@ -32,7 +32,7 @@ from gatelock._widget_fills import ButtonVariant, _button_fills, _lighten
if TYPE_CHECKING:
from collections.abc import Callable
from gatelock._window import LockConfig, TypeRole
from gatelock._config import LockConfig, TypeRole
@dataclass(frozen=True, slots=True)

View File

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "gatelock"
version = "0.8.0"
version = "0.8.1"
description = "Shared per-output lock-window + HMAC log-integrity backend for blocking-overlay apps"
requires-python = ">=3.14"
dependencies = [] # pure stdlib — tkinter is bundled with Python