We have a lot of script at Wikia. About 6k lines of code of Python. Most of them are usually run in loops like this:
for app_directory in app_directories: script()
This can take a lot of time, especially that some of the scripts use Selenium and we have well over a hundred apps.
I often run those script loops on meetings, or during lunch break or if it's a big one, I leave it running when I finish my job and hope nothing will crash over night.
There was a common problem with this. Often I forgot to trigger
caffeinate or any other setting that would prevent my Mac from going to sleep and I didn't want to turn off the sleeping completely because my battery would drain.
I thought that OS X provided the interface for Power Assertions. I thought I could use something like this in Python. Fortunately there's
pyobj and I could run Objective-C code from python.
After taking some code from here I came up with my
nosleep.py python module.
The only thing you have to do is to import it, and it will create a power assertion on import, and release it at the program exit:
The output then looks like this, it prints assertion logs on
% program-with-nosleep Creating power assertion: status 0, id c_uint(1030L) <usual program output> Releasing power assertion: id c_uint(1030L)
The program doesn't let your OS X sleep, while it's running. Every program creates a separate assertion, OS X can sleep if all the assertions are released.
This is the whole module, tested only on OS X 10.10:
#coding=utf-8 import time import sys import ctypes import CoreFoundation import objc import subprocess import time import atexit import inspect __author__ = 'alistra' def setup_IO_framework(): # load the IOKit library framework = ctypes.cdll.LoadLibrary('/System/Library/Frameworks/IOKit.framework/IOKit') # declare parameters as described in IOPMLib.h framework.IOPMAssertionCreateWithName.argtypes = [ ctypes.c_void_p, # CFStringRef ctypes.c_uint32, # IOPMAssertionLevel ctypes.c_void_p, # CFStringRef ctypes.POINTER(ctypes.c_uint32)] # IOPMAssertionID framework.IOPMAssertionRelease.argtypes = [ ctypes.c_uint32] # IOPMAssertionID return framework def StringToCFString(string): # we'll need to convert our strings before use return objc.pyobjc_id( CoreFoundation.CFStringCreateWithCString( None, string, CoreFoundation.kCFStringEncodingASCII).nsstring()) def AssertionCreateWithName(framework, a_type, a_level, a_reason): # this method will create an assertion using the IOKit library # several parameters a_id = ctypes.c_uint32(0) a_type = StringToCFString(a_type) a_reason = StringToCFString(a_reason) a_error = framework.IOPMAssertionCreateWithName( a_type, a_level, a_reason, ctypes.byref(a_id)) # we get back a 0 or stderr, along with a unique c_uint # representing the assertion ID so we can release it later print >> sys.stderr, 'Creating power assertion: status %s, id %s' % (a_error, a_id) return a_error, a_id def AssertionRelease(framework, assertion_id): # releasing the assertion is easy, and also returns a 0 on # success, or stderr otherwise print >> sys.stderr, 'Releasing power assertion: id %s' % a_id return framework.IOPMAssertionRelease(assertion_id) framework = setup_IO_framework() ret, a_id = AssertionCreateWithName(framework, 'NoDisplaySleepAssertion', 255, 'CA Script %s' % inspect.stack()[-1].split('/')[-1]) @atexit.register def release_assertion(): AssertionRelease(framework, a_id)