Skip to content

fix: importable headless (no screenshot at import) + persist pixel_ratio on the recording model#24

Merged
abrichr merged 2 commits into
mainfrom
fix/headless-import-and-pixel-ratio
Jul 12, 2026
Merged

fix: importable headless (no screenshot at import) + persist pixel_ratio on the recording model#24
abrichr merged 2 commits into
mainfrom
fix/headless-import-and-pixel-ratio

Conversation

@abrichr

@abrichr abrichr commented Jul 12, 2026

Copy link
Copy Markdown
Member

Fixes two defects surfaced by the downstream openadapt-flow capture adapter.

Fix 1 (HIGH): package must be importable on a headless host

import openadapt_capture must never crash where there is no display (CI, containers, sandboxes). PR #23 already moved the module-level monitor-dimension screenshot into a function, but the package import guard around from openadapt_capture.recorder import Recorder only caught ImportError.

  • Broaden that guard to also catch a display/screenshot failure (mss.exception.ScreenShotError) and OSError, so a headless import degrades to Recorder = None instead of crashing the whole package.
  • Add a runtime headless-import test: imports the package + CaptureSession fresh in a subprocess where mss.grab raises ScreenShotError and take_screenshot raises, asserting the import still succeeds. Complements the existing static (AST) guard.

Fix 2 (MEDIUM): pixel_ratio wasn't persisted on the SQLAlchemy recording

CaptureSession.pixel_ratio read pixel_ratio off the SQLAlchemy Recording model, but that model had no such column (only the legacy raw-sqlite schema did). A HiDPI capture whose config JSON lacked it silently scaled at 1.0, under-scaling downstream coordinate mapping.

  • Add a nullable pixel_ratio column to the Recording model.
  • Write the captured display's ratio (platform.get_display_pixel_ratio()) into it at record time in create_recording.
  • Add an additive ALTER TABLE ... ADD COLUMN migration (migrate_missing_columns) run when opening any per-capture DB (these ephemeral SQLite DBs have no migration framework). Older recording.db files that predate the column still load: the column is added as NULL and CaptureSession.pixel_ratio falls back to the config JSON, then 1.0 when genuinely unknown.

Tests

  • tests/test_headless_import.py: import + CaptureSession succeed when the screenshot call raises (simulated headless).
  • tests/test_highlevel.py::TestPixelRatio: pixel_ratio 2.0 round-trips through the SQLAlchemy model; an old recording without the column still loads and falls back (config JSON -> 1.5; no config -> 1.0).

Full fast suite green (153 passed), ruff clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CKrVJJy5jWVCkXAqgUqtqZ

abrichr and others added 2 commits July 12, 2026 19:53
The recorder no longer takes a screenshot at module load (PR #23), but
the package import guard only caught ImportError. Broaden it so a
display/screenshot failure (mss ScreenShotError) during recorder import
also degrades to `Recorder = None` instead of crashing the whole
package on a headless host (CI, containers, sandboxes).

Add a runtime headless-import test that imports the package fresh in a
subprocess where mss.grab raises ScreenShotError, complementing the
existing static (AST) guard.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CKrVJJy5jWVCkXAqgUqtqZ
CaptureSession.pixel_ratio read `pixel_ratio` off the SQLAlchemy
Recording model, but that model had no such column (only the legacy
raw-sqlite schema did), so a HiDPI capture whose config JSON lacked it
silently scaled at 1.0, under-scaling downstream coordinate mapping.

Add a nullable `pixel_ratio` column to the Recording model and write the
captured display's ratio (platform.get_display_pixel_ratio()) at record
time in create_recording. Add an additive ALTER TABLE ADD COLUMN
migration (migrate_missing_columns) run when opening any per-capture DB,
so older recording.db files that predate the column still load: the
column is added as NULL, and CaptureSession.pixel_ratio falls back to the
config JSON, then 1.0 when genuinely unknown.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CKrVJJy5jWVCkXAqgUqtqZ
@abrichr
abrichr merged commit a98322a into main Jul 12, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant