Skip to content

macOS: Check for display availability when looking for backends#27761

Open
QuLogic wants to merge 1 commit into
matplotlib:mainfrom
QuLogic:mac-display-check
Open

macOS: Check for display availability when looking for backends#27761
QuLogic wants to merge 1 commit into
matplotlib:mainfrom
QuLogic:mac-display-check

Conversation

@QuLogic

@QuLogic QuLogic commented Feb 8, 2024

Copy link
Copy Markdown
Member

PR summary

This checks CGSessionCopyCurrentDictionary similar to checking $DISPLAY/$WAYLAND_DISPLAY on Linux.

Fixes #26292

PR checklist

@QuLogic QuLogic added this to the v3.9.0 milestone Feb 8, 2024
@QuLogic QuLogic mentioned this pull request Feb 8, 2024
1 task
@tacaswell

Copy link
Copy Markdown
Member

I can confirm that this:

  • logged in (but locked) with screen plugged in -> True
  • logged in (but locked) with the hdmi unplugged -> True
  • logged out -> False

@tacaswell

Copy link
Copy Markdown
Member

Running the tests on this branch locally without a user logged in to the desktop (so launched via ssh) I get:

ERROR lib/matplotlib/tests/test_backend_macosx.py::test_cached_renderer - ImportError: Cannot load backend 'macosx' which requires the 'macosx' interactive framework, as 'headless' is currently running
ERROR lib/matplotlib/tests/test_backend_macosx.py::test_savefig_rcparam - ImportError: Cannot load backend 'macosx' which requires the 'macosx' interactive framework, as 'headless' is currently running

This suggests that what is going on the CI is slightly different than not having a display?

@QuLogic

QuLogic commented Feb 8, 2024

Copy link
Copy Markdown
Member Author

On Actions, the display does appear to exist, according to the results from #27723:

mainScreen: Built-in Display (1920.000000x1080.000000)+0.000000+0.000000 @8
deepestScreen: Built-in Display (1920.000000x1080.000000)+0.000000+0.000000 @8
Available screens: 1
    0: Built-in Display (1920.000000x1080.000000)+0.000000+0.000000 @8

For your failures, I think that's because the macOS tests don't have a skip, so I hadn't added one (only modified existing ones.)

@QuLogic QuLogic marked this pull request as ready for review February 8, 2024 21:24
@QuLogic QuLogic requested a review from greglucas February 8, 2024 21:24
greglucas
greglucas previously approved these changes Feb 8, 2024
@tacaswell

Copy link
Copy Markdown
Member

I tried re-running the failed mac3.11 jobs.

@QuLogic

QuLogic commented Feb 9, 2024

Copy link
Copy Markdown
Member Author

Looking at https://stackoverflow.com/questions/71954472/tkinter-crashes-on-ipython-but-not-in-python-on-a-m1-mac I think this might be because NSApplicationLoad messes with Tkinter somehow. I thought they were shared instances, but there must be something Tkinter is doing that confuses it if we create one ourselves.

@QuLogic

QuLogic commented Feb 9, 2024

Copy link
Copy Markdown
Member Author

Ah, here's a full explanation:

  • Both pyglet and Tk use AppKit to implement their GUI
  • AppKit uses an NSApplication class, and in particular a singleton instance of that class, to represent the application, which will get instantiated during application startup
  • The Tk library uses a sublclass of NSApplication with additional functionality (TkApplication).
  • When pyglet is started first the NSApplication singleton is an instance of NSApplication and not of TkApplication, but Tk's implementation assumes that the singleton is an instance of TkApplicationo.

but substitute the new code written here for pyglet above.

AFAICT from Googling though, we need the NSApplication instance to be sure that the NSScreen attributes are filled, though I've not tested it out myself.

Can you check if it works without the NSApplicationLoad?

@QuLogic QuLogic marked this pull request as draft February 9, 2024 07:35
@QuLogic QuLogic modified the milestones: v3.9.0, v3.10.0 Apr 2, 2024
@QuLogic QuLogic modified the milestones: v3.10.0, future releases Oct 2, 2024
Comment thread src/_objc_internal_utils.m Outdated

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.

@QuLogic - I'm not sure if you plan to merge this eventually, but you probably want to use CGSessionCopyCurrentDictionary() from CoreGraphics.framework to see if a GUI session is ready, rather than calling into NSApplicationLoad() which will always try to fire up AppKit and make the shared NSApplication.

The other typical approach is SessionGetInfo in Security.framework.

Both could likely live in _c_internal_utils.cpp since they aren't Obj-C APIs.

See "Getting Login Session Information" from the ancient Multiple User Environment Programming Topics documentation.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I was actually pretty much stuck and going to ask if you had any suggestions. It looks like I'll just need to call CGSessionCopyCurrentDictionary and see if it's not NULL.

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.

Let me know if you have any issues. The document in question is old, and Apple changed the exact timing of when WindowServer launches when you restart and then unlock your computer.

You may need to make sure that the dictionary actually has one of the expected keys (or use the Security API).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The main problem is that I don't have an actual mac to test... But it does appear that I did get the right incantation to build at least.

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'll try to check in 10 hours or so when I wake up!

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.

@QuLogic - On my Tahoe test machine:

iccir is logged in and has a GUI session.
testuser is not logged in.

If I SSH into the machine with testuser, CGSessionCopyCurrentDictionary() returns NULL.

If I SSH into the machine with iccir, CGSessionCopyCurrentDictionary() returns a dictionary:

2026-06-12 10:44:05.818 a.out[10671:1935474] {
    CGSSessionScreenIsLocked = 1;
    CGSSessionScreenLockedTime = 3928769798;
    CGSSessionUniqueSessionUUID = "2728BB0E-8FE4-42C1-BE6A-50D6AE021E90";
    kCGSSessionAuditIDKey = 100015;
    kCGSSessionGroupIDKey = 20;
    kCGSSessionLoginwindowSafeLogin = 0;
    kCGSSessionOnConsoleKey = 1;
    kCGSSessionSystemSafeBoot = 0;
    kCGSSessionUserIDKey = 501;
    kCGSSessionUserNameKey = iccir;
    kCGSessionLoginDoneKey = 1;
    kCGSessionLongUserNameKey = iccir;
    kSCSecuritySessionID = 100015;
}

@QuLogic QuLogic force-pushed the mac-display-check branch from 36e7a14 to 860d94e Compare June 12, 2026 06:31
@QuLogic

QuLogic commented Jun 12, 2026

Copy link
Copy Markdown
Member Author

Previously against main, for Python 3.14 we have:

==== 10082 passed, 354 skipped, 32 xfailed, 13 xpassed in 694.04s (0:11:34) ====

and now:

==== 10083 passed, 353 skipped, 31 xfailed, 14 xpassed in 697.11s (0:11:37) ====

It seems like one new test ran, but that appears to be because Noto Sans CJK SC was found. For Python 3.12 and 3.13, it's the opposite.

So at the least, in this PR, we aren't skipping anything new that was passing before.

@QuLogic QuLogic marked this pull request as ready for review June 12, 2026 07:57
@QuLogic QuLogic requested a review from greglucas June 12, 2026 07:57
@QuLogic QuLogic dismissed greglucas’s stale review June 12, 2026 07:58

New implementation, needs re-testing.

@tacaswell

Copy link
Copy Markdown
Member

This works as expected for me. Fell back to Agg when not logged in, opened a plot when logged in using the OSX backend.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: macOS backend run by non-logged-in user crashes in Ventura

4 participants