Skip to content

Add immutable Monitor model - #566

Draft
halldorfannar wants to merge 6 commits into
BoboTiG:mainfrom
halldorfannar:task/issue-470-monitor
Draft

Add immutable Monitor model#566
halldorfannar wants to merge 6 commits into
BoboTiG:mainfrom
halldorfannar:task/issue-470-monitor

Conversation

@halldorfannar

@halldorfannar halldorfannar commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Changes proposed in this PR

Fixes #470

Replace heterogeneous monitor dictionaries with a frozen, slotted Monitor dataclass that exposes typed geometry and
platform metadata attributes.

  • Update the macOS, GNU/Linux, and Windows backends to produce and consume Monitor objects.
  • Keep temporary string-key access for migration, while continuing to accept dictionaries and PIL-style tuples in
    grab().
  • Preserve immutable monitor descriptions in the video demos by deriving separate dictionaries for cropped capture
    regions.
  • Update filename formatting, examples, documentation, release notes, typing, and tests for the new model.
  • Update AGENTS.md with instructions on how to update test_sdist when files are added, removed, or renamed.

This gives callers a stable, read-only monitor representation without preventing existing applications from passing
dictionary-based capture regions.

  • Tests added/updated
  • Documentation updated
  • Changelog entry added
  • ./check.sh passed

Validation: uv run ./check.sh. The full pytest suite was not run during the final review.

AI assistance disclosure

  • AI assistance was used to generate this contribution.

Codex assisted with reviewing the implementation, identifying and implementing follow-up fixes in the demos and
ScreenShot initialization, updating the immutability test for static typing, running validation, and drafting this PR
description. The contributor reviewed and directed the API and capture-region decisions.

  Replace monitor dictionaries with a frozen, slotted Monitor dataclass while preserving temporary string-key
  access. Update platform enumeration, legacy grab inputs, filename formatting, typing, tests, examples, and
  migration documentation.
Some samples would not work correctly.
Also prepared for eventually phasing out string like access.
@halldorfannar halldorfannar changed the title feat: add immutable Monitor model Add immutable Monitor model Aug 3, 2026

@jholveck jholveck left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not really comfortable with this idea of making synthetic Monitor objects to send down through _impl.grab, rather than sending capture regions as dicts or tuples. Can you elaborate on why you made that choice?

Comment thread demos/video-capture.py Outdated
Comment thread demos/video-capture.py Outdated

#### Immutable monitor objects

{py:attr}`mss.MSS.monitors` now returns frozen, slotted {py:class}`mss.models.Monitor` objects instead of dictionaries.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why frozen? (I was going to ask about that in your latest proposal.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added documentation to explain why. It is important that customers cannot modify the monitors because they are owned by MSS and should be immutable.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify: I hid interpreted "frozen" in the sense of C++'s final, or possibly making some sort of future compatibility guarantee, but now that I see your latest commit, I see you meant it in the sense of "immutable".

Might that be a better term?

(I'm also not sure on why it matters to users that they're slotted, but that's not a problem.)

Comment thread src/mss/linux/base.py Outdated
Comment thread src/mss/linux/base.py Outdated
Comment thread src/mss/linux/base.py Outdated
Comment thread src/mss/base.py Outdated
Comment thread src/mss/screenshot.py Outdated

if isinstance(monitor, Monitor):
left, top = monitor.left, monitor.top
else: # remove this once we deprecate the string key access

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deprecate it where? Why does this allow a dict to be passed to the constructor?

This relates to https://github.com/BoboTiG/python-mss/pull/566/changes#r3709864024

@halldorfannar halldorfannar Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding was that during transition we wanted to support the key string access but eventually we will remove it. Just to soften the blow for existing customers and their code. We will mark this for deprecation and eventually remove it. My language is slightly off in the comment. We are marking it for deprecation and will later remove it. The process is normally:

  1. Deprecate
  2. At a later date remove

BUT now that we have moved to the CaptureRegion this code has all been thrown away and we will remain backwards compatible (no deprecation planned anymore).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ties into my earlier question about why we pass around these synthetic Monitor objects wherever we're talking about capture regions. Unfortunately, the link I got for that didn't work as expected.

At this point, when I wrote the review, the things that construct a ScreenShot - the _impl.grab methods - would always pass it a Monitor object, never a dict.

Also, this code would be in the context of allowing the caller to pass in a Monitor or a dict, but the comment about "string key access" ties into how a user can access the properties of a Monitor, which is a completely different matter.

But now that we're using CaptureRegion, then it's moot.

Comment thread AGENTS.md Outdated
  - MSS.grab() still accepts Monitor, dictionaries, and tuples, but normalizes them to CaptureRegion.
  - Platform backends and custom screenshot classes now receive regions, never synthetic monitors.
  - Added Monitor.as_capture_region().
  - Applied the Linux **output_ids and comment cleanups.
  - Fixed the demo import grouping.
  - Documented why monitors are immutable.
  - Added test_wheel guidance to AGENTS.md.
@halldorfannar

Copy link
Copy Markdown
Contributor Author

One overall thing. I'm wondering if the name CaptureRegion is too limiting. Maybe Region is better and more futureproof. It doesn't matter what the region will be used for inside MSS. It will always have the same coordinates associated with it. Using it directly for capture is just one application I can think of.

@jholveck

jholveck commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

One overall thing. I'm wondering if the name CaptureRegion is too limiting. Maybe Region is better and more futureproof. It doesn't matter what the region will be used for inside MSS. It will always have the same coordinates associated with it. Using it directly for capture is just one application I can think of.

Yeah, I think that Region may be better. For instance, we might use it in a Window object to give the window geometry to the user.

(Besides, as_capture_region felt a bit verbose; it was just the first thing that occurred to me when I wrote that comment.)

Food for thought: should a Region carry with it the metric space (nominal vs. physical)? Probably not practically, and definitely not in this PR, but it may be at least worth passing through the brain once.

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.

Change Monitor dict to a proper class

2 participants