py2exe与文件系统

4 投票
5 回答
5282 浏览
提问于 2025-04-15 14:45

我有一个用Python写的应用程序。它通过一些操作来加载配置文件(还有其他各种文件),比如:

_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
CONFIG_DIR = os.path.join(_path, 'conf')

这样做是没问题的。但是,当我用py2exe打包这个应用时,就出现了一些麻烦:

  File "proj\config.pyc", line 8, in <module>
WindowsError: [Error 3] The system cannot find the path specified: 'C:\\proj\
\dist\\library.zip\\conf'

显然,这个路径是无效的……有没有更稳妥的方法来处理这个问题?我不想在程序里写绝对路径,因为它可能会放在不同的文件夹里。我是不是应该说“如果文件夹名称是'library.zip',那就再往下找一层到'dist'文件夹”呢?

需要注意的是,我的目录结构比较复杂……比如,我有一个模块叫做gui.utils.images,存放在"gui/utils/images.py"里,它用自己的路径去访问"gui/images/ok.png"。现在py2exe版本会尝试去访问"proj/dist/library.zip/gui/images/ok.png"之类的路径,这根本行不通。

5 个回答

3

这是我的解决方案(在Windows、Linux和Mac上测试过)。如果应用程序或Python脚本是通过符号链接启动的,这个方法也能正常工作。

# Get if frozen
is_frozen = bool( getattr(sys, 'frozen', None) )

# Get path variable
path = sys.path if is_frozen else sys.argv

# Get nonempty first element or raise error
if path and path[0]:
    apppath = path[0]
elif is_frozen():
    raise RuntimeError('Cannot determine app path because sys.path[0] is empty.')
else:
    raise RuntimeError('Cannot determine app path in interpreter mode.')

# Make absolute (eliminate symbolic links)
apppath = os.path.abspath( os.path.realpath(apppath) )

# Split and return
appdir, appname = os.path.split(apppath)
11

通常处理这种事情的方法(如果我理解正确的话)是这样的:

  1. 检查 sys.frozen,这是 py2exe 特意设置的,可以用类似 getattr(sys, 'frozen', '') 的方式来查看。
  2. 如果没有设置这个,就用普通的方法,因为你是从源代码运行的。
  3. 如果设置了这个,就检查 sys.executable,因为它会指向 .exe 文件的位置(而不是通常指向 python.exe 的位置)。可以用 os.path.dirname(sys.executable) 来获取这个路径,然后把它和你的相对路径结合起来,找到子文件夹等。

举个例子:

frozen = getattr(sys, 'frozen', '')

if not frozen:
    # not frozen: in regular python interpreter
    approot = os.path.dirname(__file__)

elif frozen in ('dll', 'console_exe', 'windows_exe'):
    # py2exe:
    approot = os.path.dirname(sys.executable)

elif frozen in ('macosx_app',):
    # py2app:
    # Notes on how to find stuff on MAC, by an expert (Bob Ippolito):
    # http://mail.python.org/pipermail/pythonmac-sig/2004-November/012121.html
    approot = os.environ['RESOURCEPATH']

或者其他类似的变体……后面那个处理了 py2app 的使用。如果需要的话,你可以把这个方法扩展到其他构建工具。

2

你觉得对所有包含的文件使用相对路径怎么样?我想应该可以用 sys.path.append(".." + os.path.sep + "images") 来处理你提到的 ok.png 的例子,这样你就可以直接用 open("ok.png", "rb") 来打开它。使用相对路径应该能解决 py2exe 生成的 library.zip 文件的问题,至少对我来说是这样。

撰写回答