Making your OS X not sleep while running scripts
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:
import nosleep
The output then looks like this, it prints assertion logs on stderr
:
% 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][1].split('/')[-1])
@atexit.register
def release_assertion():
AssertionRelease(framework, a_id)