I'm trying to build a daemon in python and I want to get the name of the current active application.
For the daemon I'm using this nice code snipped from Sander Marechal
The following line works perfectly on OS X 10.7 when I DON'T run the application as a daemon, although the documentation says "activeApplication()" was deprecated on 10.6+
activeAppName = str(NSWorkspace.sharedWorkspace().activeApplication()['NSApplicationName'])
But as soon as I run the application as a daemon, the application crashes.
However, the daemon doesn't crash when I only do
workspace = str(NSWorkspace.sharedWorkspace())
which returns:
<NSWorkspace: 0x7ffe7cc013c0>
So my questions are:
I don't understand the error message, but maybe one of you does:
Process: Python [7920]
Path: /System/Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python
Identifier: Python
Version: ??? (???)
Code Type: X86-64 (Native)
Parent Process: ??? [1]
Date/Time: 2012-02-29 23:35:25.202 +0100
OS Version: Mac OS X 10.7.3 (11D50b)
Report Version: 9
Interval Since Last Report: 818421 sec
Crashes Since Last Report: 21
Per-App Crashes Since Last Report: 15
Anonymous UUID: 05B412BD-4629-472B-964D-BE4A88B06DD1
Crashed Thread: 0 Dispatch queue: com.apple.main-thread
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x0000000000000108
VM Regions Near 0x108:
-->
__TEXT 0000000102e90000-0000000102e91000 [ 4K] r-x/rwx SM=COW /System/Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python
Application Specific Information:
*** single-threaded process forked ***
objc[7918]: garbage collection is OFF
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 libdispatch.dylib 0x00007fff8ceb7ce9 _dispatch_wakeup + 108
1 libdispatch.dylib 0x00007fff8ceba876 _dispatch_resume_slow + 20
2 com.apple.CoreServices.CarbonCore 0x00007fff8d34f919 _ZL22connectToCoreServicesDv + 269
3 com.apple.CoreServices.CarbonCore 0x00007fff8d34f7d5 _ZL9getStatusv + 24
4 com.apple.CoreServices.CarbonCore 0x00007fff8d34f74f scCreateSystemServiceVersion + 50
5 com.apple.LaunchServices 0x00007fff90b5ace1 _ZL45SetupCoreApplicationServicesCommunicationPortv + 147
6 com.apple.LaunchServices 0x00007fff90b5b37a getProcessDispatchTable() + 19
7 com.apple.LaunchServices 0x00007fff90b56de0 LSClientSideSharedMemory::GetClientSideSharedMemory(LSSessionID, bool) + 158
8 com.apple.LaunchServices 0x00007fff90b6b152 _LSCopyFrontApplication + 42
9 com.apple.AppKit 0x00007fff899adc5d -[NSWorkspace activeApplication] + 26
10 libffi.dylib 0x00007fff91df2e7c ffi_call_unix64 + 76
11 libffi.dylib 0x00007fff91df3ae9 ffi_call + 728
12 _objc.so 0x00000001031c7d60 PyObjCFFI_Caller + 2272
13 _objc.so 0x00000001031dd169 0x1031ae000 + 192873
14 org.python.python 0x0000000102ea0d32 PyObject_Call + 97
15 org.python.python 0x0000000102f20f63 PyEval_EvalFrameEx + 14353
16 org.python.python 0x0000000102f23df7 0x102e99000 + 568823
17 org.python.python 0x0000000102f20e0a PyEval_EvalFrameEx + 14008
18 org.python.python 0x0000000102f23df7 0x102e99000 + 568823
19 org.python.python 0x0000000102f20e0a PyEval_EvalFrameEx + 14008
20 org.python.python 0x0000000102f23cd8 PyEval_EvalCodeEx + 1996
21 org.python.python 0x0000000102f23d4d PyEval_EvalCode + 54
22 org.python.python 0x0000000102f3b08f 0x102e99000 + 663695
23 org.python.python 0x0000000102f3b14f PyRun_FileExFlags + 157
24 org.python.python 0x0000000102f3c2a2 PyRun_SimpleFileExFlags + 392
25 org.python.python 0x0000000102f4c2af Py_Main + 2715
26 org.python.python 0x0000000102e90e88 0x102e90000 + 3720
I did some tests with this, and I think your issue might be the way you are daemonizing this tool and then trying to make a call that requires windows services that may not be available. This link here hints at such a situation: http://grokbase.com/t/python/pythonmac-sig/08axst378p/appscript-and-launching-apps-from-background-only-python-processes
I had first tested this theory using your daemon script and making an osascript call to find the active application via AppleScript:
from subprocess import Popen, PIPE
cmd = """osascript \
-e 'tell application "System Events"' \
-e 'set app_name to name of the first process whose frontmost is true' \
-e 'end tell' """
v = Popen(cmd, shell=True, stdout=PIPE).stdout.read()
Popen is a way to launch a system command in a subprocess and be able to check its return code or read its output (or send input) http://docs.python.org/library/subprocess.html. . Osascript is a command line tool for calling apple scripts.
For me, this works because its starting a new subprocess that I think does have access to the window server?
I then created a launchd plist instead of using your daemon script. This works:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.company.test</string>
<key>Nice</key>
<integer>1</integer>
<key>OnDemand</key>
<false/>
<key>Program</key>
<string>/path/to/script.py</string>
</dict>
</plist>
Launchd is the OSX daemon process manager which does seem to launch programs in a way that they have complete access to the windowserver.
For script.py, I simply had it loop, writing the frontmost application name to a file and sleeping.
Update
Since you had mentioned that your pyobjc approach was deprecated, and you seemed to like the applescript approach, I thought I would tack on a pythonic way of doing that using appscript - the python bindings to apple script
from appscript import app, its
activeApp = app('System Events').processes[its.frontmost == True].first()
print activeApp
#result
app(u'/System/Library/CoreServices/System Events.app').application_processes[u'Terminal']
activeApp is an object representing the frontmost application, reported by the System Events app.
The crash eventually happens in libdispatch, and it looks like it has something to do with shared memory; see the GetClientSideSharedMemory call in the call stack. So it looks like your daemonize code runs afoul of the grand central dispatch technology built into OS X.
Have a look at the python daemon module. It does things (conform PEP 3143) that the code snippet you are using overlooks.
Since OS X is basically a UNIX with extra bells and whistles, it is possible that you need to add some OS X specific steps when trying to run a program as a daemon.
You could study the source code of other daemons that work under OS X, like e.g. Postfix.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With