Native Restart and Logout Dialogs with PyObjC
Recently I wanted to find a friendly way to prompt for logout or restart using the dialog prompts people were already used to. As part of a workflow users had to restart, but the only solutions I found to programmatically accomplish this were to force something like…
sudo shutdown -r now
That works decently, but can be easily interrupted by a blocking process, is abrupt, and isn’t what people are used to when they restart their Mac. When going to > Restart… everyone’s used to seeing this dialog pop up…
Luckily I found an old pudquick / frogor / Michael Lynn (thanks!) gist on generating native “polite” login window events using PyObjC.
Awesome. He took care of the deep dive API work for me and and I now have working code to generate a logout, restart, or shutdown dialog window on demand. The only problem being it’s written in Python 2. A while back I started shipping relocatable Python 3 interpeter to managed clients to get ready for Python 2 deprecation, and Apple eventually not including it by default with macOS. Running the gist unedited through Python 3.8 with PyObjC installed came back with a lot of errors. After messing around with encoding and a few other functions, I came up with a working Python 3 version. This has been tested with Python 3.8+ and PyObjC 6.1, but should work with most Python 3 versions.
The important bits here are the different AERegistry values.
# Defined in AERegistry.h
kAELogOut = OSType("logo")
kAEReallyLogOut = OSType("rlgo")
kAEShowRestartDialog = OSType("rrst")
kAEShowShutdownDialog = OSType("rsdn")
# Build a standalone application descriptor by bundle id
loginwindowDesc = NSAppleEventDescriptor.alloc().initWithDescriptorType_data_(
typeApplicationBundleID, memoryview(b"com.apple.loginwindow")
)
# Build an event descriptor with our app descriptor as the target and the kAELogOut eventID
event = NSAppleEventDescriptor.appleEventWithEventClass_eventID_targetDescriptor_returnID_transactionID_(
typeAppleEvent,
kAELogOut,
loginwindowDesc,
kAutoGenerateReturnID,
kAnyTransactionID,
)
On line 59 of my gist change the value kAELogOut
to any of the others in that list to get a different dialog and corresponding function. Unsurprisingly, kAELogOut
results in a logout dialog.
kAEShowRestartDialog
and kAEShowShutdownDialog
are self explanatory. Give them a try to see the usual dialog windows you would see from going to > Shutdown… and similar. Be careful when playing with kAEReallyLogOut
. Apparently “really log out” means log out immediately with no dialog prompt. In a future post I plan to explain how I used the restart dialog to prompt users when their uptime was over a certain number of days.