使用 libnotify 的 Python 3 脚本作为 cron 作业失败
Posted
技术标签:
【中文标题】使用 libnotify 的 Python 3 脚本作为 cron 作业失败【英文标题】:Python 3 script using libnotify fails as cron job 【发布时间】:2013-06-21 03:32:03 【问题描述】:我有一个 Python 3 脚本,它从 URL 获取一些 JSON,对其进行处理,并在我获得的数据有任何重大变化时通知我。我尝试过使用notify2 和PyGObject 的libnotify 绑定(gi.repository.Notify) 并使用任何一种方法获得类似的结果。当我从终端运行该脚本时,它可以正常工作,但是当 cron 尝试运行它时会阻塞。
import notify2
from gi.repository import Notify
def notify_pygobject(new_stuff):
Notify.init('My App')
notify_str = '\n'.join(new_stuff)
print(notify_str)
popup = Notify.Notification.new('Hey! Listen!', notify_str,
'dialog-information')
popup.show()
def notify_notify2(new_stuff):
notify2.init('My App')
notify_str = '\n'.join(new_stuff)
print(notify_str)
popup = notify2.Notification('Hey! Listen!', notify_str,
'dialog-information')
popup.show()
现在,如果我创建一个使用字符串列表调用 notify_pygobject
的脚本,cron 会通过邮件假脱机向我抛出此错误:
Traceback (most recent call last):
File "/home/p0lar_bear/Documents/devel/notify-test/test1.py", line 3, in <module>
main()
File "/home/p0lar_bear/Documents/devel/notify-test/test1.py", line 4, in main
testlib.notify(notify_projects)
File "/home/p0lar_bear/Documents/devel/notify-test/testlib.py", line 8, in notify
popup.show()
File "/usr/lib/python3/dist-packages/gi/types.py", line 113, in function
return info.invoke(*args, **kwargs)
gi._glib.GError: Error spawning command line `dbus-launch --autolaunch=776643a88e264621544719c3519b8310 --binary-syntax --close-stderr': Child process exited with code 1
...如果我改为调用notify_notify2()
:
Traceback (most recent call last):
File "/home/p0lar_bear/Documents/devel/notify-test/test2.py", line 3, in <module>
main()
File "/home/p0lar_bear/Documents/devel/notify-test/test2.py", line 4, in main
testlib.notify(notify_projects)
File "/home/p0lar_bear/Documents/devel/notify-test/testlib.py", line 13, in notify
notify2.init('My App')
File "/usr/lib/python3/dist-packages/notify2.py", line 93, in init
bus = dbus.SessionBus(mainloop=mainloop)
File "/usr/lib/python3/dist-packages/dbus/_dbus.py", line 211, in __new__
mainloop=mainloop)
File "/usr/lib/python3/dist-packages/dbus/_dbus.py", line 100, in __new__
bus = BusConnection.__new__(subclass, bus_type, mainloop=mainloop)
File "/usr/lib/python3/dist-packages/dbus/bus.py", line 122, in __new__
bus = cls._new_for_bus(address_or_type, mainloop=mainloop)
dbus.exceptions.DBusException: org.freedesktop.DBus.Error.NotSupported: Unable to autolaunch a dbus-daemon without a $DISPLAY for X11
我做了一些研究并看到了将PATH=
放入我的crontab 或导出$DISPLAY
的建议(我通过调用os.system('export DISPLAY=:0')
在脚本中执行此操作),但都没有导致任何更改...
【问题讨论】:
【参考方案1】:你在正确的轨道上。这种行为是因为 cron 在多用户无头环境中运行(将其视为在没有 GUI 的终端中以 root 身份运行,有点),所以他不知道要显示什么(X Window Server 会话)和用户目标。如果您的应用程序打开,例如,打开某个用户桌面的窗口或通知,则会引发此问题。
我假设您使用 crontab -e
编辑您的 cron,条目如下所示:
m h dom mon dow command
类似:
0 5 * * 1 /usr/bin/python /home/foo/myscript.py
请注意,我使用 Python 的完整路径,如果 PATH 环境变量可能不同的这种情况会更好。
然后改成:
0 5 * * 1 export DISPLAY=:0 && /usr/bin/python /home/foo/myscript.py
如果这仍然不起作用,您需要允许您的用户控制 X Windows 服务器:
添加到您的.bash_rc
:
xhost +si:localuser:$(whoami)
【讨论】:
将导出添加到 cron 作业本身修复了它!谢谢!另外我想我会在可执行的 python 3.x 脚本中使用这个 shebang:#!/usr/bin/env python3
【参考方案2】:
如果你想像使用os.system('export DISPLAY=:0')
那样在 python 中设置 DISPLAY,你可以这样做
import os
if not 'DISPLAY' in os.environ:
os.environ['DISPLAY'] = ':0'
这将尊重用户在多座位包厢上可能拥有的任何 DISPLAY,并退回到主头:0。
【讨论】:
【参考方案3】:如果你的通知函数,不管 Python 版本或通知库,不跟踪通知 id [在 Python 列表中] 并在队列完全满或出错之前删除最旧的,然后取决于 dbus 设置(在Ubuntu 最多 21 个通知)dbus 会抛出错误,已达到最大通知!
from gi.repository import Notify
from gi.repository.GLib import GError
# Normally implemented as class variables.
DBUS_NOTIFICATION_MAX = 21
lstNotify = []
def notify_show(strSummary, strBody, strIcon="dialog-information"):
try:
# full queue, delete oldest
if len(lstNotify)==DBUS_NOTIFICATION_MAX:
#Get oldest id
lngOldID = lstNotify.pop(0)
Notify.Notification.clear(lngOldID)
del lngOldID
if len(lstNotify)==0:
lngLastID = 0
else:
lngLastID = lstNotify[len(lstNotify) -1] + 1
lstNotify.append(lngLastID)
notify = Notify.Notification.new(strSummary, strBody, strIcon)
notify.set_property('id', lngLastID)
print("notify_show id %(id)d " % 'id': notify.props.id )
#notify.set_urgency(Notify.URGENCY_LOW)
notify.show()
except GError as e:
# Most likely exceeded max notifications
print("notify_show error ", e )
finally:
if notify is not None:
del notify
尽管有可能以某种方式询问 dbus 通知队列的最大限制是多少。也许有人可以帮助...改进直到完美。
请
因为 gi.repository 完整的答案是多余的。
【讨论】:
以上是关于使用 libnotify 的 Python 3 脚本作为 cron 作业失败的主要内容,如果未能解决你的问题,请参考以下文章
程序员的算法趣题Q56: 鬼脚图中的横线(思路2的Python题解)
python使用fpdf生成pdf章节(chapter)文件包含:页眉页脚章节主体章节内容等;