从 AppleScript 启动 Python 脚本在读取行时生成 EOFError

Initiating Python script from AppleScript yields EOFError when reading a line

提问人:ZwiTrader 提问时间:6/28/2021 更新时间:6/29/2021 访问量:132

问:

我希望使用 AppleScript 启动一个 Python 脚本,该脚本的前几行:

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

从终端(在 Mac 上)运行 Python 脚本时,我没有问题:系统会提示我输入日期,如果我没有提供日期,脚本将使用当前日期。

但是,当从AppleScript启动Python脚本时,我无法提供输入,因为会立即抛出EOF错误。

我的整个AppleScript包括

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

并仅用作启动 Python 脚本的一种方式。

作为解决方法,我创建了一个带有文本的 shell 脚本 (),然后调整了我的 AppleScript 以启动 shell 脚本:ExecutePythonScript.shpython3 /PythonScript.py

do shell script "/ExecutePythonScript.sh"

不幸的是,AppleScript 和 Python 之间的这一额外距离步骤无济于事。

有没有办法允许通过终端输入,即使从AppleScript启动?

如果没有,有没有办法通过 AppleScript 请求输入并将该输入传递给 Python 脚本?

出于好奇,在启动 Python 之前,我正在使用 AppleScript 执行其他功能(通过 Hammerspoon)。因此,AppleScript正在充当各种需要发生的事情的中央命令。

感谢您的帮助。

python-3.x Applescript EOF eofError

评论


答:

0赞 Lorccan 6/28/2021 #1

我不确定从 AppleScript 运行 py 脚本时会发生什么。这很可能是一个环境问题。试着读这个: https://developer.apple.com/library/archive/technotes/tn2065/_index.html

编辑:如果您尝试以下情况,会发生什么:

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

要回答您的另一个问题,您可以将数据从 AppleScript 传递到其他脚本。假设您在变量中读出一个值,并且想要将其作为参数提供给脚本,您可以执行以下操作:inputpyScript.py

do shell script "pyScript.py " & input

[注意:字符串中的空格是必需的。

另一种方法是构建执行字符串的脚本并运行它:

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

评论

0赞 ZwiTrader 6/29/2021
谢谢,但我仍然遇到您提出的解决方案的麻烦。我的解决方案是将我的 python 脚本保存为 .command 文件,然后我就可以直接从 Hammerspoon 运行它(完全绕过 AppleScript)
1赞 RobC 6/29/2021 #2

我不熟悉 Hammerspoon,但这里有几个例子可以考虑。

简单示例

请考虑以下简单示例,该示例通过 AppleScript 提示输入,并将该输入传递回 Python 脚本。.py

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

解释

  • 这利用了 Python 的 Popen.communicate() 来生成一个调用 AppleScript 对话框的新进程 - 提示用户输入日期。

  • 用于提示对话框的 AppleScript 将分配给该变量。userDatePrompt

  • 在用户输入某个值(假定为日期)后,该值将传递回 Python 脚本并分配给变量。userData

  • 请注意以下行:

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

    我们首先使用换行符作为分隔符,将字符串分配给变量,然后访问数组中的第一项(即在索引处),然后检查该值是否为空字符串()。我们这样做是为了确保如果用户;单击按钮,或单击按钮而不输入值,我们将使用您的默认日期值(即今天/现在)splituserDate\n0""CancelContinue

注意:上述脚本的主要问题是我们不以任何方式验证用户输入。例如,他们可以;输入 foo bar quux,然后单击 Continue 按钮,我们的脚本将愉快地假设分配给 userDate 变量的值是有效日期。最终导致我们的程序在使用该值时在某个地方崩溃。


验证示例

以下更全面的示例还验证了用户输入的值是否为符合 的有效日期。.pyYYYY-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

解释

  • 这基本上与前面的(简单)示例相同,但它提供了额外的验证,这些验证都是通过 AppleScript 处理的。
  • 根据前面的示例,我们通过函数提示用户输入日期。这一次,该按钮已更改为 a,以更好地指示它的实际作用。getDateFromUserCancelUse Default Date
  • 随后,我们通过函数验证用户输入。此函数利用 shells date 实用程序/命令来检查日期是否符合 。dateIsValidYYYY-DD-MM
  • 通过 repeat 语句,我们基本上会重复提示用户,直到提供有效日期 - 只有在遇到有效日期时才退出。

运行

  • 通过 AppleScript:

    与您的示例类似,要通过AppleScript运行,请使用以下do shell脚本命令python-script.py

    do shell script "source ~/.bash_profile; python3 ~/Desktop/python-script.py"
    
  • 通过终端:

    或通过终端运行:

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

注意:路径名;~/Desktop/python-script.py,在这两个示例中都需要根据需要重新定义。此外,鉴于我的回答中提供的例子,源 ~/.bash_profile; 部分并不是绝对必要的。