launchd will not run Python script: "Service could not initialize"
I've written a Python script that I would like to have run automatically at specific times during the day. When I try to load it using launchctl, and then check by listing and grepping the launchd jobs, it has a code of 78. In other words, it never loads or runs. I am running El Capitan, v10.11.4.
When I look to see what's logged in the system log, I get the following:
Apr 23 18:26:45 iMac com.apple.xpc.launchd[1] (com.mariodiana.pingfloyd[29804]): Service could not initialize: 15E65: xpcproxy + 12684 [1462][B5ED8EB9-EEE2-35FD-9E48-227941C236E4]: 0xd
The script will run manually, using /usr/bin/python as the Python interpreter. The script uses two third-party libraries: python-twitter and BeautifulSoup.
I have named the Plist file com.mariodiana.pingfloyd.plist and have checked it with plural. It checks out OK. I've put the file in ~/Library/LaunchAgents. AlI'm including my Plist file and Python file, in case that helps.
Any idea if there is some specific issue with getting Python scripts to run that's giving me problems? Could it be finding the third-party libraries or something?
plist
<?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.mariodiana.pingfloyd</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/python</string>
<string>/opt/bin/pingfloyd</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/mario/pingfloyd.log</string>
<key>StandardErrorPath</key>
<string>/Users/mario/pingfloyd.log</string>
<key>StartCalendarInterval</key>
<array>
<dict>
<key>Hour</key>
<integer>8</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
<dict>
<key>Hour</key>
<integer>11</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
<dict>
<key>Hour</key>
<integer>14</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
<dict>
<key>Hour</key>
<integer>17</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
</array>
</dict>
</plist>
Python
#! /usr/bin/python
#
# FILE: pingfloyd.py
#
# AUTHOR: Mario Diana
#
# REQUIRES: Python 2.x. See URL of python-twitter project for dependencies.
#
# TOUCHED: 2016.04.23
#
"""
Screen scrape Floyd Rose site to see when sold out item is in stock.
"""
import urllib
# https://github.com/bear/python-twitter
import twitter
# http://www.crummy.com/software/BeautifulSoup/
import BeautifulSoup
# Classes
class TwitterWriter(object):
_client = None
@property
def client(self):
if self._client == None:
self._client = twitter.Api(**self._credentials)
return self._client
def __init__(self, credentials=None):
"""
Hard-coded with mario2dot0 account's credentials.
"""
object.__init__(self)
if not credentials:
credentials = {
'consumer_key':'N/A',
'consumer_secret':'N/A',
'access_token_key':'N/A',
'access_token_secret':'N/A'
}
self._credentials = credentials
def tweet(self, message):
"""
Tweet message.
"""
if len(message) > 140:
raise "Message exceeds character length limit of 140: %d" % (len(message))
response = self.client.PostUpdate(message)
return response
class FloydRoseSite(object):
def __init__(self, pageUri, itemNumber, itemDescription):
object.__init__(self)
self.pageUri = pageUri
self.itemNumber = itemNumber
self.itemDescription = itemDescription
def getPageHtml(self):
"""
Return HTML for page.
"""
f = urllib.urlopen(self.pageUri)
page = f.read()
f.close()
return page
class FloydRoseStore(object):
def __init__(self, html):
object.__init__(self)
self._data = BeautifulSoup.BeautifulSoup(html)
# Public method
def hasItem(self, itemNumber):
"""
Return True if store has item.
"""
item = self.getItemByNumber(itemNumber)
quantityField = item.find('input')
soldOut = False
soldOutGif = 'assets/themes/default/images/checkout/soldOut200.gif'
if quantityField.has_key('src') and quantityField['src'] == soldOutGif:
soldOut = True
return not soldOut
# Private methods
def getItemRows(self):
rows = self._data.findAll('tr')
return [r for r in rows if r.has_key('class')]
def getItemByNumber(self, itemNumber):
rows = self.getItemRows()
item = [r for r in rows if r['data-productid'] == itemNumber][0]
return item
# Functions
def main(floydrose, twitterClient):
html = floydrose.getPageHtml()
store = FloydRoseStore(html)
if store.hasItem(floydrose.itemNumber):
twitterClient.tweet("%s is available." % (floydrose.itemDescription))
if __name__ == '__main__':
pageUri = 'http://www.floydrose.com/catalog/parts/1000-series/locking-nut'
item = '39'
description = 'Floyd Rose Model 1000 chrome nut'
floydrose = FloydRoseSite(pageUri, item, description)
tw = TwitterWriter()
main(floydrose, tw)
iMac