Introduction to PyObjC at MacADUK 2017

by Greg Neagle, Sr. Systems Engineer at Walt Disney Animation Studios:

Many systems administrators rely on scripting to help them get their jobs done. Scripts can help perform a complex task in a repeatable manner, or perform a task over and over; perhaps on many, many machines.

There are many scripting languages to choose from. Some are better for some tasks, some are better for others. Many Mac admins start with Bash or AppleScript; either can be a good choice for specific tasks. Another popular scripting language among Mac admins is Python. It has a large, useful standard library, it handles complex data structures well, and there are lots of online resources to help you learn and use Python.

But there is one feature of Python on MacOS that elevates it above Perl, Ruby, and Bash: easy access to Mac systems frameworks. You can write a script in Python that calls Cocoa functions and classes – resources that traditionally required writing code in Objective-C to use. This is possible due to “PyObjC”: a module that bridges Python and many Objective-C frameworks.

Some tasks that PyObjC makes easier: working with macOS preferences in a way that is compatible with MCX and configuration profiles; sending and listening for macOS distributed notifications; displaying native GUI elements, and more.

Another reason you might be interested in PyObjC is as way to start learning Apple’s macOS frameworks. You might feel like you need to learn Objective-C and/or Swift to be a “real” Mac programmer, but in reality, much of your time will be spent learning Apple’s Cocoa, Foundation, and CoreFoundation frameworks. If you already know (or are learning) Python, you can start learning and experimenting with Apple’s frameworks while still using Python.

Let’s look at a quick example

It’s not uncommon in a script to need to determine the user currently logged in and using the GUI. Apple has provided a tech note with some sample Objective-C code:

Here’s the Objective-C code:


static CFStringRef CopyCurrentConsoleUsername(SCDynamicStoreRef store)
  // Returns the name of the current console user, or NULL if there is
  // none. store may be NULL, in which case a transient dynamic store
  // session is used.

{
  CFStringRef result;


  result = SCDynamicStoreCopyConsoleUser(store, NULL, NULL);


  // If the current console user is “loginwindow”, treat that as
  // equivalent to none.


  if ( (result != NULL) && CFEqual(result, CFSTR(“loginwindow”)) ) {
    CFRelease(result);
    result = NULL;
  }


  return result;
}

An equivalent in Python, using PyObjC:

from SystemConfiguration import SCDynamicStoreCopyConsoleUser

def CopyCurrentConsoleUsername(store=None):
  '''Returns the name of the current console user, or None if there is
  none. store may be None, in which case a transient dynamic store session
  is used'''


  result, _, _ = SCDynamicStoreCopyConsoleUser(store, None, None)


  # If the current console user is “loginwindow”, treat that as equivalent
  # to none.
  if result == ‘loginwindow’:
    result = None


  return result


print CopyCurrentConsoleUsername()

As you can see, we can use Python to get the current console user exactly as it would be done from Objective-C.

If you’d like to learn more about PyObjC, register for MacADUK today and come to my session on Wednesday 8th February 2017 at 10:15.


About Greg Neagle

Greg is the engineer primarily responsible for deploying and managing macOS machines at Walt Disney Animation Studios, a studio with a long history of family entertainment reaching back to “Snow White and the Seven Dwarves” and forward to its latest films, “Zootopia” and “Moana”.

Several Mac management tools developed by Greg have been released as open source by Disney Animation. Among those are Munki, a software deployment framework, Reposado, a platform-agnostic replacement for Apple’s Software Update service, and the “createOSXinstallerPkg” tool set. Greg is also a contributor to the popular AutoPkg tool.

Leave a Reply

Your email address will not be published. Required fields are marked *