Search code examples
pythonmacospython-2.7plistlaunchd

Launchd plist task python sys.path error


I'm trying to build a daily scheduled plist task using launchd that loads a python script that sends me an email containing a link via sendgrid.

My python script to send the email works from the command line with python dailyemail.py (see below)

import os, requests, bs4, sendgrid
from sendgrid.helpers.mail import *
url = 'https://apod.nasa.gov/apod/astropix.html'
sg = sendgrid.SendGridAPIClient(apikey=os.environ.get('SENDGRID_API_KEY'))
from_email = Email("xxx")
to_email = Email("xxx")
subject = "Astronomy Picture of the Day"
content = Content("text/plain", 'https://apod.nasa.gov/apod/astropix.html')
mail = Mail(from_email, subject, to_email, content)
response = sg.client.mail.send.post(request_body=mail.get())

When I load and start my plist task it runs at the specified time, but fails due to not being able to import the requests, bs4 and sendgrid modules. From logging the sys.path outputs, I have identified that my system seems to be loading two subtly different versions of Python from the command line vs when I run the task via launchd (see output at end and plist task).

I have two questions:

  1. How do I resolve this disparity? Also interested to understand why these file paths would be different?
  2. Is there another way to load / reference python modules into a plist task to get it working?

Thanks!

System: OSX El Capitan 10.11.3

Plist task

<?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>
    <!-- The label should be the same as the filename without the extension -->
    <string>com.alexanderhandy.nasa</string>
    <!-- Specify how to run your program here -->
    <key>ProgramArguments</key>
    <array>
        <string>/usr/bin/python</string>
        <string>/Users/alexanderhandy/Documents/Programming/Scripts/dailyemail.py</string>
    </array>
    <!-- Run every dat -->
    <key>StandardErrorPath</key>
    <string>/tmp/ahnasa.err</string>
    <key>StandardOutPath</key>
    <string>/tmp/ahnasa.out</string>
    <key>StartCalendarInterval</key>
      <dict>
          <key>Hour</key>
          <integer>12</integer>
          <key>Minute</key>
          <integer>34</integer>
      </dict>
</dict>
</plist>

Error logs

*Command line python sys.path*
 /Users/alexanderhandy/Documents/Programming/Scripts/usr/local/lib/python2.7/site-packages/setuptools-17.0-py2.7.egg/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python27.zip/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-darwin/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac/lib-scriptpackages/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-old/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload/usr/local/lib/python2.7/site-packages/Library/Python/2.7/site-packages

*plist task python sys.path*
/Users/alexanderhandy/Documents/Programming/Scripts/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python27.zip/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-darwin/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac/lib-scriptpackages/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-old/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/PyObjC/Library/Python/2.7/site-packages

Solution

  • Ok, not 100% sure here so others are welcomed to improve upon this answer.

    -

    Reading up on system.path for Python2.7 in the relevant PythonDocs and then interpreting the output it gives it seems that your commandline script version of Python is unstalled under /usr/local/lib, meaning it's a local install while LaunchDaemon is running a system based install located within the /System/Library/Frameworks/ folder.

    My hunch is you might have installed your Python2.7 via Homebrew, causing you to end up with 2 different versions of Python. There are many relevant questions/answers upon the possible up- and downsides of this all over StackOverflow. You've probably installed your modules against Homebrew's version, yet LaunchDaemon is using macOS pre-installed version.

    Reading the Homebrew Docs on Python my next hunch would be that if you change:

    <string>/usr/bin/python</string>

    In your plist task to:

    <string>/usr/bin/python2</string>

    You should probably be fine.

    If not, please let me know cause there probably are multiple ways to solving your issue.