微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Python macOS构建从终端运行,但在Finder启动时崩溃

如何解决Python macOS构建从终端运行,但在Finder启动时崩溃

通过双击启动应用程序时,我的py2app构建显示带有“终止”按钮的错误消息。但是,如果我直接在Terminal中打开它,则效果很好。以下所有“终端”命令均会成功启动该应用程序:

# .MyApp.app/Contents/MacOS/MyApp
# open MyApp.app
# open -a MyApp.app

我已经读过several posts关于similar py2app errors的信息,但是我不知道这里可能是什么问题。这个问题似乎已经存在了至少四年,不是特定于py2app,而且似乎与macOS上的Python有关。 pyinstaller的用户正在报告完全相同的问题,而AFAIK却没有明显的解决方案。

我的setup.py文件

from setuptools import setup

APP = ['myapp.py']
DATA_FILES = [('img',['img/myapp-logo.png']),('data',['data/data.yml'])]
OPTIONS = {'iconfile': 'icon.icns'}

setup(
    app=APP,name='My App',data_files=DATA_FILES,options={'py2app': OPTIONS},setup_requires=['py2app'],)

macOS控制台在system.log中显示以下消息:com.apple.xpc.launchd[1] (org.pythonmac.unspecified.MyApp.4952[16783]): Service exited with abnormal code: 255

可疑1:文件访问权限?

pyinstaller GitHub问题页面上的

This thread有多个人报告了相同的错误,但没有明确的解决方法。这个proposed solution似乎与pyinstaller线程中描述的工作目录问题有关,但不能解决我系统上的问题。

我的应用仅从其工作目录中读取一个yml文件,并且不向磁盘写入任何内容。它只不过是一个简单的文件访问语句:

file = os.path.realpath('data/data.yml')
with open(file) as f:
    # etc

在Catalina中,我已将该应用程序添加到“安全性偏好设置”中允许“完全磁盘访问”的应用程序列表中,但这也不能解决该问题(但是,由于执行了open命令,这样做确实令人惊讶。如上所述)。

可疑2:Tkinter?

pyinstaller GitHub上的

This thread表示问题可能与Tkinter版本有关。提议的解决方案似乎已经解决 some 用户的问题。但是,最后,我有一个早期版本的有效应用程序捆绑包,然后添加了打开文件语句,该语句在双击时可以很好地启动。

怀疑1 vs 2

我将代码分为两个分支,一个完全删除了对Tkinter的引用,另一个删除了对文件的访问,而希望通过代码中的变量进行初始化。在第二种情况下,崩溃不会 not 发生。尽管a hack to Tkinter很奇怪已为其他用户解决了这个问题,但这似乎可以排除Tkinter是问题的根源。

macOS版本

我已经在Catalina 10.15.6和El Capitan 10.11.6上测试了两种捆绑软件版本(工作和不工作),并且行为相同。

死亡哨兵的射击

在通过Finder正常启动应用后,控制台日志如下所示:

default 21:26:13.836380+0200    runningboardd   Acquiring assertion targeting executable<MyPythonApplication(501)> from originator [daemon<com.apple.coreservices.launchservicesd>:165] with description <RBSAssertionDescriptor; frontmost:27280; ID: 391-165-34219; target: 27280> attributes = {
    <RBSDomainAttribute: 0x7f9f1a712910; domain: com.apple.launchservicesd; name: RoleUserInteractiveFocal; sourceEnvironment: 0x0>;
}
default 21:26:13.836649+0200    runningboardd   Assertion 391-165-34219 (target:executable<MyPythonApplication(501)>) will be created as active
default 21:26:13.838258+0200    runningboardd   [executable<MyPythonApplication(501)>:27280] Ignoring jetsam update because this process is not memory-managed
default 21:26:13.839703+0200    runningboardd   [executable<MyPythonApplication(501)>:27280] Set darwin role to: UserInteractiveFocal
default 21:26:13.840634+0200    runningboardd   [executable<MyPythonApplication(501)>:27280] Ignoring GPU update because this process is not GPU managed
default 21:26:13.840912+0200    runningboardd   Finished acquiring assertion 391-165-34219 (target:executable<MyPythonApplication(501)>)
default 21:26:15.166436+0200    hidd    Connection removed: IOHIDEventSystemConnection uuid:B1D40AB3-FD55-455C-9E1B-2E4C4C6E4982 pid:27280 process:MyPythonApplication type:Passive entitlements:0x0 caller:HIToolBox: ___GetIOHIDEventSystemClient_block_invoke + 26 attributes:(null) state:0x1 events:0 mask:0x0
default 21:26:15.171128+0200    runningboardd   [executable<MyPythonApplication(501)>:27280] Death sentinel fired!
default 21:26:15.174315+0200    runningboardd   Invalidating assertion 391-165-34219 (target:executable<MyPythonApplication(501)>) from originator 165
default 21:26:15.176832+0200    loginwindow -[PersistentAppsSupport applicationQuit:] | for app:MyPythonApplication,_appTrackingState = 2
default 21:26:15.176856+0200    loginwindow -[PersistentAppsSupport applicationQuit:] | App: MyPythonApplication,quit,updating active tracking timer
default 21:26:15.179589+0200    runningboardd   Invalidating assertion 391-165-34209 (target:executable<MyPythonApplication(501)>) from originator 165
default 21:26:15.281529+0200    runningboardd   Removing process: [executable<MyPythonApplication(501)>:27280]
default 21:26:15.282124+0200    runningboardd   Removing assertions for terminated process: [executable<MyPythonApplication(501)>:27280]
error   21:26:15.292603+0200    runningboardd   RBsstateCapture remove item called for untracked item 391-165-34209 (target:executable<MyPythonApplication(501)>)
error   21:26:15.292622+0200    runningboardd   RBsstateCapture remove item called for untracked item 391-165-34219 (target:executable<MyPythonApplication(501)>)

很明显,在此行中提到了错误,以防有人可以从中做出任何事情:

hidd    Connection removed: IOHIDEventSystemConnection uuid:foo pid:bar process:MyPythonApplication type:Passive entitlements:0x0 caller:HIToolBox: ___GetIOHIDEventSystemClient_block_invoke + 26 attributes:(null) state:0x1 events:0 mask:0x0

紧随其后的是RunningBoard发射死亡哨兵,无论该实体可能是什么。该系统已在this article by Howard Oakley中进行了描述,但远远超出了我的专业水平。

Ulbow收集到的更多信息会通过高度提示性消息“ MacOS错误:-67062”来确认错误

com.apple.runningboard 4941060 391 Received message from [daemon<com.apple.coreservices.launchservicesd>:165] (euid 0): acquireAssertionWithDescriptor:error: 
com.apple.launchservices 4940599 802 Osstatus _LSLaunch(LSContext *,FSNode *,LSLaunchFlags,void *,CFArrayRef,const AppleEvent *,const AEDescList *,CFDictionaryRef,LSBundleID,const audit_token_t *,const _LSOpen2Options *,ProcessSerialNumber *,Boolean *,NSError **): launching '<private>' result=0 
com.apple.securityd 9807 904 MacOS error: -67062 
com.apple.runningboard 4941063 391 Received message from [daemon<com.apple.coreservices.launchservicesd>:165] (euid 0): acquireAssertionWithDescriptor:error: 
com.apple.sharedfilelist 4940599 802 -[SFLGenericList _insertItem:atIndex:error:]_block_invoke com.apple.LSSharedFileList.RecentApplications 
com.apple.securityd 4940933 530 UNIX error exception: 8 
 4941117 27642 MyPythonApplication Error 
com.apple.securityd 4941064 165 UNIX error exception: 22 
com.apple.securityd 4941064 165 MacOS error: -67062 
com.apple.runningboard 4941002 391 Received message from [daemon<com.apple.coreservices.launchservicesd>:165] (euid 0): acquireAssertionWithDescriptor:error: 
com.apple.securityd 4941161 198 MacOS error: -67062 
com.apple.TCC 4941161 198 Failed to copy signing info for 27642,responsible for file:///Users/me/Files/Docs/Code/Python/MyPythonApplication/dist/MyPythonApplication.app/Contents/MacOS/Pandemic%20Deck%20Tracker: #-67062: Error Domain=NSOsstatusErrorDomain Code=-67062 "(null)" 
com.apple.securityd 4941161 198 MacOS error: -67062 
com.apple.securityd 4941161 198 MacOS error: -67062 
com.apple.launchservices 4941064 165 CLIENT: 0x7fcec00b33b0/27642 Received xpc_ERROR on connection from our client,so invalidating it and our connection. 
com.apple.appleevents 4941120 462 CONNECTION: peer=? peer-pid=27642 got event (Error "Connection invalid") 
com.apple.appleevents 4941120 462 CONNECTION: Recevied xpc_ERROR on a connection from 27642,a client of ours,so unregistering any application with that pid. 
com.apple.appleevents 4941120 462 CONNECTION: releasing app App:"MyPythonApplication"/"MyPythonApplication"/"org.pythonmac.unspecified.MyPythonApplication" 27642/0x0:0x308d08a ????1010 sess=100020 because we received error on its connection. 
com.apple.runningboard 4941002 391 Received message from [daemon<com.apple.coreservices.launchservicesd>:165] (euid 0): acquireAssertionWithDescriptor:error: 

可以在this GitHub issues page上找到有关最近测试的几次交流,以试图找出导致错误的原因,但这仍然是一个谜(也是一个非常奇怪的谜)。

Apple工程师应该真正了解一下。

解决方法

pyinstaller的维护者已对this GitHub issue page进行了非常彻底的调查。事实证明,如果Python脚本使用内置的Python os模块访问macOS文件系统上的资源,则应用程序包将崩溃。如果脚本是直接从终端命令运行的,则捆绑包不会崩溃。

建议的解决方案是检查脚本是否在macOS上运行,在这种情况下,请使用AppKit打开文件。这需要安装pyobjc模块,但是这不是主要的麻烦。

代替这样做:

import os

file = os.path.realpath('path/somefile.ext')
with open(file) as f:
    # ...

执行此操作:

import os
import platform

def get_path(filename):
    name = os.path.splitext(filename)[0]
    ext = os.path.splitext(filename)[1]

    if platform.system() == "Darwin":
        from AppKit import NSBundle
        file = NSBundle.mainBundle().pathForResource_ofType_(name,ext)
        return file or os.path.realpath(filename)
    else:
        return os.path.realpath(filename)

file = get_path('path/somefile.ext')
with open(file) as f:
    # ...

我可以使用pyinstaller确认在macOS Catalina上可以使用。我没有机会在Windows的.exe版本上对此进行测试。

,

经过几个小时的挖掘,我终于找到了答案!即使在我计算的每一步之后都会获得正确的 /Contents/Resources 路径。

首先我必须让这个工作:

from AppKit import NSBundle
    file = NSBundle.mainBundle().pathForResource_ofType_(name,ext)

对于我的 Python 应用程序,找到我必须安装的“AppKit”模块

pip install pyobjc

但即便如此,对于

的路径
NSBundle.mainBundle().pathForResource_ofType_(name,ext)

很奇怪。这是 "/private/var/" 中我没有写访问权限

的路径

最终答案是运行

xattr -d com.apple.quarantine /Applications/YouPythonApp.app/

然后我终于找到了正确的道路

/Applications/YouPythonApp.app/Contents/Resources/

我也有写权限,现在我可以保存我的 .yaml 文件

with open(file,"w") as f:
        yaml=YAML()
        yaml.default_flow_style = False
        yaml.dump(config,f)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。