ZwiTrader
ZwiTrader

Reputation: 345

Initiating Python script from AppleScript yields EOFError when reading a line

I am hoping to use AppleScript to launch a Python script which has as its first few lines:

x = datetime.datetime.now()
varyear = str(x.year)
varday = str(x.day).zfill(2)
varmonth = str(x.month).zfill(2)
dateStamp = varyear + '-' + varday + '-' + varmonth

userDate = ""

userDate = input("Enter date of Bookmap replay data as YYYY-DD-MM: ")

if userDate != "":
    dateStamp=userDate

When running the Python script from Terminal (on a Mac), I have no problems: I am prompted for a date and if I do not provide one, the script uses the current date.

However, when initiating the Python script from AppleScript I am unable to provide input, as the EOF error is thrown immediately.

My entire AppleScript consists of

do shell script "source ~/.bash_profile; python3 /PythonScript.py"

and is used simply as a way to launch the Python script.

As a work around, I created a shell script (ExecutePythonScript.sh) with the text python3 /PythonScript.py and then adjusted my AppleScript to launch the shell script:

do shell script "/ExecutePythonScript.sh"

Unfortunately, this additional step of distance between AppleScript and Python did not help.

Is there any way to allow input via Terminal, even though launching from AppleScript?

If not, is there a way to ask for input via AppleScript and pass that input to the Python script?

For the curious, I am using AppleScript to execute other functions (via Hammerspoon) prior to launching Python. So AppleScript is functioning as sort of central command for the various things that need to happen.

Thanks for any help.

Upvotes: 0

Views: 144

Answers (2)

RobC
RobC

Reputation: 25002

I'm not familiar with Hammerspoon, however here are a couple of examples to consider.

Simple Example

Consider the following simple .py example that prompts for input via AppleScript and passes that input back to the Python script.

python-script.py

import datetime
from subprocess import Popen, PIPE

x = datetime.datetime.now()
varyear = str(x.year)
varday = str(x.day).zfill(2)
varmonth = str(x.month).zfill(2)
dateStamp = varyear + '-' + varday + '-' + varmonth

userDate = ""

userDatePrompt = """
  on getDateFromUser()
    tell application "System Events"
      activate
      set mssg to "Enter date of Bookmap replay data as YYYY-DD-MM:"
      set usrPrompt to display dialog mssg default answer "" buttons {"Cancel", "Continue"} default button "Continue"
      return text returned of usrPrompt
    end tell
  end getDateFromUser

  return getDateFromUser()
"""

proc = Popen(['osascript', '-'], stdin=PIPE, stdout=PIPE, stderr=PIPE, universal_newlines=True)
userDate, error = proc.communicate(userDatePrompt)

userDate = userDate.split("\n")[0]

if userDate != "":
    dateStamp = userDate

Explanation

  • This utilizes Python's Popen.communicate() to essentially spawn a new processes that invokes an AppleScript dialog - prompting the user to input a date.

  • The AppleScript utilized for the prompt dialog is assigned to the userDatePrompt variable.

  • After the user has input some value, (which is assumed to be a date), it is passed back to the Python script and assigned to the userData variable.

  • Note the line that reads:

    userDate = userDate.split("\n")[0]
    

    We firstly split the string assigned to the userDate variable, using a newline character \n as a delimiter and then access the first item in the Array (i.e. at index 0), before checking that the value is not an empty string (""). We do this to ensure that if the user either; clicks the Cancel button, or clicks the Continue button without entering a value, we use your default date value (i.e. Today/Now)

Note: The main issue with the aforementioned script is that we do not in any way validate the user input. They could for example; enter foo bar quux, then click the Continue button, and our script will happily assume the value assigned to the userDate variable is a valid date. Ultimately resulting in our program breaking somewhere down the line when we come to use the value.


Example with validation

The following somewhat more comprehensive example .py additionally validates whether the value entered by the user is a valid date conforming to YYYY-DD-MM.

python-script.py

import datetime
from subprocess import Popen, PIPE


x = datetime.datetime.now()
varyear = str(x.year)
varday = str(x.day).zfill(2)
varmonth = str(x.month).zfill(2)
dateStamp = varyear + '-' + varday + '-' + varmonth

userDate = ""

userDatePrompt = """
  on dateIsValid(givenDate)
    set statusCode to do shell script "date -j -f \\"%Y-%d-%m\\" " & quoted form of givenDate & " > /dev/null 2>&1; echo $?"
    if statusCode is equal to "0" then
      return true
    else
      return false
    end if
  end dateIsValid

  on getDateFromUser()
    tell application "System Events"
      activate
      set mssg to "Enter date of Bookmap replay data as YYYY-DD-MM:"
      set usrPrompt to display dialog mssg default answer "" buttons {"Use Default Date", "Continue"} default button "Continue" cancel button "Use Default Date"
      return text returned of usrPrompt
    end tell
  end getDateFromUser

  repeat
    set dateReceived to my getDateFromUser()
    set isValidDate to dateIsValid(dateReceived)
    try
      if isValidDate then
        exit repeat
      else
        error
      end if
    on error
      tell application "System Events"
        activate
        display dialog quote & dateReceived & quote & " is not a valid date." & return & return & "Please try again." buttons {"OK"} default button 1 with icon caution
      end tell
    end try
  end repeat

  return dateReceived
"""

proc = Popen(['osascript', '-'], stdin=PIPE, stdout=PIPE, stderr=PIPE, universal_newlines=True)
userDate, error = proc.communicate(userDatePrompt)

userDate = userDate.split("\n")[0]

if userDate != "":
    dateStamp = userDate

Explanation

  • This essentially does the same as the previous (simple) example, however it provides additional validation that is all handled via the AppleScript.
  • As per the previous example we prompt the user to enter the date via the getDateFromUser function. This time the Cancel button has been changed to a Use Default Date to better indicate what it actually does.
  • Subsequently we validate the user input via the dateIsValid function. This function utilizes the shells date utility/command to check the date conforms to YYYY-DD-MM.
  • Via the repeat statement we essentially repeatedly prompt the user until a valid date is provided - only exiting when a valid date is encountered.

Running

  • Via AppleScript:

    Similar your example, to run python-script.py via AppleScript utilize the following do shell script command

    do shell script "source ~/.bash_profile; python3 ~/Desktop/python-script.py"
    
  • Via Terminal:

    Or via the Terminal run:

    source ~/.bash_profile; python3 ~/Desktop/python-script.py
    

Note: The pathname; ~/Desktop/python-script.py, in both examples will need to be redefined as necessary. Also given the examples provided in my answer the source ~/.bash_profile; part is not strictly necessary.

Upvotes: 1

Lorccan
Lorccan

Reputation: 823

I'm not sure what's happening when running your py script from AppleScript. It's most likely an environmental issue. Try reading this: https://developer.apple.com/library/archive/technotes/tn2065/_index.html

EDIT: what happens if you try:

do shell script "/full/path/to/python3 /full/path/to/pythonScript.py"

To answer another of your questions, you can pass data from AppleScript to other scripts. Suppose you read a value into the variable input and you want to supply this as an argument to a script pyScript.py you could do:

do shell script "pyScript.py " & input

[NOTE: the space in the string is needed.]

Another way to do this is to build the script executing string and run that:

set shellScript to "pyScript.py " & input
do shell script "shellScript"

Upvotes: 0

Related Questions